aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2021-06-21 14:26:56 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2021-06-21 14:26:56 +0000
commit4951e5db577389a34845b15c519f82cdc1d94c2d (patch)
tree6f59a75348d07612173246ac6c4fca04bde0df2c
parent678a3d564813c41376a7ed56ac4e63acf4043346 (diff)
parent313f77a7c9ef6b6f5e4c83e6ebbadb13699f6cb1 (diff)
downloaddagger2-android-mainline-12.0.0_r21.tar.gz
Change-Id: Ia38bbd38424f7ca7e6810117cb5e3201f9af507e
-rw-r--r--.github/workflows/ci.yml214
-rw-r--r--.gitignore4
-rw-r--r--.travis.yml65
-rw-r--r--Android.bp391
-rw-r--r--BUILD129
-rw-r--r--CONTRIBUTING.md16
-rw-r--r--METADATA6
-rw-r--r--README.md232
-rw-r--r--WORKSPACE132
-rwxr-xr-xandroid-annotation-stubs/gen_annotations.sh4
-rw-r--r--android-annotation-stubs/src/net/ltgt/gradle/incap/IncrementalAnnotationProcessorType.java4
-rw-r--r--build_defs.bzl5
-rw-r--r--examples/BUILD18
-rw-r--r--examples/bazel/BUILD22
-rw-r--r--examples/bazel/WORKSPACE89
-rw-r--r--examples/bazel/java/example/common/BUILD25
-rw-r--r--examples/bazel/java/example/common/CoffeeLogger.java39
-rw-r--r--examples/bazel/java/example/common/CoffeeMaker.java41
-rw-r--r--examples/bazel/java/example/common/ElectricHeater.java47
-rw-r--r--examples/bazel/java/example/common/Heater.java24
-rw-r--r--examples/bazel/java/example/common/Pump.java22
-rw-r--r--examples/bazel/java/example/common/Thermosiphon.java38
-rw-r--r--examples/bazel/java/example/dagger/BUILD23
-rw-r--r--examples/bazel/java/example/dagger/CoffeeApp.java43
-rw-r--r--examples/bazel/java/example/dagger/HeaterModule.java30
-rw-r--r--examples/bazel/java/example/dagger/PumpModule.java28
-rw-r--r--examples/bazel/java/example/hilt/AndroidManifest.xml9
-rw-r--r--examples/bazel/java/example/hilt/BUILD48
-rw-r--r--examples/bazel/java/example/hilt/CoffeeApp.java38
-rw-r--r--examples/bazel/java/example/hilt/HeaterModule.java33
-rw-r--r--examples/bazel/java/example/hilt/PumpModule.java31
-rw-r--r--examples/bazel/javatests/example/hilt/BUILD55
-rw-r--r--examples/bazel/javatests/example/hilt/CoffeeAppFakeHeaterTest.java88
-rw-r--r--examples/bazel/javatests/example/hilt/CoffeeAppFakePumpTest.java68
-rw-r--r--examples/maven/coffee/pom.xml59
-rw-r--r--examples/maven/coffee/src/main/java/example/dagger/CoffeeApp.java41
-rw-r--r--examples/maven/coffee/src/main/java/example/dagger/CoffeeLogger.java39
-rw-r--r--examples/maven/coffee/src/main/java/example/dagger/CoffeeMaker.java41
-rw-r--r--examples/maven/coffee/src/main/java/example/dagger/ElectricHeater.java47
-rw-r--r--examples/maven/coffee/src/main/java/example/dagger/Heater.java24
-rw-r--r--examples/maven/coffee/src/main/java/example/dagger/HeaterModule.java28
-rw-r--r--examples/maven/coffee/src/main/java/example/dagger/Pump.java22
-rw-r--r--examples/maven/coffee/src/main/java/example/dagger/PumpModule.java26
-rw-r--r--examples/maven/coffee/src/main/java/example/dagger/Thermosiphon.java38
-rw-r--r--examples/maven/pom.xml70
-rw-r--r--examples/pom.xml70
-rw-r--r--examples/simple/pom.xml59
-rw-r--r--examples/simple/src/main/java/coffee/CoffeeApp.java17
-rw-r--r--examples/simple/src/main/java/coffee/CoffeeMaker.java21
-rw-r--r--examples/simple/src/main/java/coffee/DripCoffeeModule.java12
-rw-r--r--examples/simple/src/main/java/coffee/ElectricHeater.java18
-rw-r--r--examples/simple/src/main/java/coffee/Heater.java7
-rw-r--r--examples/simple/src/main/java/coffee/Pump.java5
-rw-r--r--examples/simple/src/main/java/coffee/PumpModule.java10
-rw-r--r--examples/simple/src/main/java/coffee/Thermosiphon.java18
-rw-r--r--gwt/BUILD6
-rw-r--r--java/dagger/BUILD11
-rw-r--r--java/dagger/Provides.java4
-rw-r--r--java/dagger/android/AndroidInjection.java144
-rw-r--r--java/dagger/android/AndroidInjectionKey.java6
-rw-r--r--java/dagger/android/BUILD56
-rw-r--r--java/dagger/android/DaggerActivity.java2
-rw-r--r--java/dagger/android/DaggerBroadcastReceiver.java2
-rw-r--r--java/dagger/android/DaggerContentProvider.java2
-rw-r--r--java/dagger/android/HasActivityInjector.java28
-rw-r--r--java/dagger/android/HasBroadcastReceiverInjector.java28
-rw-r--r--java/dagger/android/HasContentProviderInjector.java28
-rw-r--r--java/dagger/android/HasFragmentInjector.java28
-rw-r--r--java/dagger/android/HasServiceInjector.java28
-rw-r--r--java/dagger/android/internal/AndroidInjectionKeys.java1
-rw-r--r--java/dagger/android/internal/GenerateAndroidInjectionProguardRules.java32
-rw-r--r--java/dagger/android/internal/proguard/BUILD37
-rw-r--r--java/dagger/android/internal/proguard/ProguardProcessor.java93
-rw-r--r--java/dagger/android/processor/AndroidProcessor.java28
-rw-r--r--java/dagger/android/processor/BUILD17
-rw-r--r--java/dagger/android/proguard.cfg1
-rw-r--r--java/dagger/android/support/AndroidSupportInjection.java70
-rw-r--r--java/dagger/android/support/BUILD62
-rw-r--r--java/dagger/android/support/DaggerAppCompatActivity.java15
-rw-r--r--java/dagger/android/support/DaggerAppCompatDialogFragment.java4
-rw-r--r--java/dagger/android/support/DaggerDialogFragment.java4
-rw-r--r--java/dagger/android/support/DaggerFragment.java13
-rw-r--r--java/dagger/android/support/HasSupportFragmentInjector.java29
-rw-r--r--java/dagger/assisted/Assisted.java82
-rw-r--r--java/dagger/assisted/AssistedFactory.java47
-rw-r--r--java/dagger/assisted/AssistedInject.java80
-rw-r--r--java/dagger/assisted/package-info.java22
-rw-r--r--java/dagger/errorprone/AndroidSupportInjectionModuleMigrator.java2
-rw-r--r--java/dagger/errorprone/BUILD4
-rw-r--r--java/dagger/example/BUILD18
-rw-r--r--java/dagger/example/android/simple/AndroidManifest.xml31
-rw-r--r--java/dagger/example/android/simple/BUILD42
-rw-r--r--java/dagger/example/android/simple/BuildModule.java31
-rw-r--r--java/dagger/example/android/simple/MainActivity.java69
-rw-r--r--java/dagger/example/android/simple/Model.java29
-rw-r--r--java/dagger/example/android/simple/SimpleApplication.java54
-rw-r--r--java/dagger/example/android/simple/res/layout/activity_main.xml31
-rw-r--r--java/dagger/example/android/simple/res/values/strings.xml5
-rw-r--r--java/dagger/example/atm/AccountModule.java30
-rw-r--r--java/dagger/example/atm/AmountsModule.java38
-rw-r--r--java/dagger/example/atm/BUILD33
-rw-r--r--java/dagger/example/atm/BigDecimalCommand.java56
-rw-r--r--java/dagger/example/atm/Command.java82
-rw-r--r--java/dagger/example/atm/CommandLineAtm.java36
-rw-r--r--java/dagger/example/atm/CommandProcessor.java67
-rw-r--r--java/dagger/example/atm/CommandProcessorFactory.java44
-rw-r--r--java/dagger/example/atm/CommandRouter.java66
-rw-r--r--java/dagger/example/atm/CommandsModule.java45
-rw-r--r--java/dagger/example/atm/Database.java35
-rw-r--r--java/dagger/example/atm/DepositCommand.java43
-rw-r--r--java/dagger/example/atm/HelloWorldCommand.java38
-rw-r--r--java/dagger/example/atm/InMemoryDatabase.java75
-rw-r--r--java/dagger/example/atm/InMemoryDatabaseModule.java26
-rw-r--r--java/dagger/example/atm/LoginCommand.java55
-rw-r--r--java/dagger/example/atm/LogoutCommand.java43
-rw-r--r--java/dagger/example/atm/MaximumWithdrawal.java31
-rw-r--r--java/dagger/example/atm/MinimumBalance.java29
-rw-r--r--java/dagger/example/atm/Outputter.java22
-rw-r--r--java/dagger/example/atm/PerSession.java29
-rw-r--r--java/dagger/example/atm/SingleArgCommand.java31
-rw-r--r--java/dagger/example/atm/SystemOutModule.java28
-rw-r--r--java/dagger/example/atm/UserCommandsModule.java41
-rw-r--r--java/dagger/example/atm/UserCommandsRouter.java35
-rw-r--r--java/dagger/example/atm/Username.java29
-rw-r--r--java/dagger/example/atm/WithdrawCommand.java67
-rw-r--r--java/dagger/example/atm/WithdrawalLimiter.java43
-rw-r--r--java/dagger/example/atm/build.gradle44
-rwxr-xr-xjava/dagger/example/atm/gradlew188
-rw-r--r--java/dagger/example/atm/gradlew.bat100
-rw-r--r--java/dagger/example/atm/settings.gradle17
-rw-r--r--java/dagger/example/gradle/android/simple/app/build.gradle41
-rw-r--r--java/dagger/example/gradle/android/simple/app/src/main/AndroidManifest.xml30
-rw-r--r--java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/BuildModule.java31
-rw-r--r--java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/Model.java29
-rw-r--r--java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/SimpleActivity.java73
-rw-r--r--java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/SimpleApplication.java61
-rw-r--r--java/dagger/example/gradle/android/simple/app/src/main/res/layout/activity_main.xml30
-rw-r--r--java/dagger/example/gradle/android/simple/app/src/main/res/values/strings.xml5
-rw-r--r--java/dagger/example/gradle/android/simple/build.gradle35
-rw-r--r--java/dagger/example/gradle/android/simple/gradle.properties1
-rw-r--r--java/dagger/example/gradle/android/simple/gradle/wrapper/gradle-wrapper.jarbin0 -> 55616 bytes
-rw-r--r--java/dagger/example/gradle/android/simple/gradle/wrapper/gradle-wrapper.properties5
-rwxr-xr-xjava/dagger/example/gradle/android/simple/gradlew188
-rw-r--r--java/dagger/example/gradle/android/simple/settings.gradle2
-rw-r--r--java/dagger/example/gradle/simple/SimpleApplication.java48
-rw-r--r--java/dagger/example/gradle/simple/build.gradle40
-rw-r--r--java/dagger/example/gradle/simple/gradle/wrapper/gradle-wrapper.jarbin0 -> 55616 bytes
-rw-r--r--java/dagger/example/gradle/simple/gradle/wrapper/gradle-wrapper.properties5
-rwxr-xr-xjava/dagger/example/gradle/simple/gradlew188
-rw-r--r--java/dagger/example/spi/BUILD7
-rw-r--r--java/dagger/grpc/server/BUILD18
-rw-r--r--java/dagger/grpc/server/README.md2
-rw-r--r--java/dagger/grpc/server/processor/BUILD20
-rw-r--r--java/dagger/grpc/server/processor/GrpcServiceProcessor.java2
-rw-r--r--java/dagger/hilt/BUILD240
-rw-r--r--java/dagger/hilt/DefineComponent.java68
-rw-r--r--java/dagger/hilt/EntryPoint.java50
-rw-r--r--java/dagger/hilt/EntryPoints.java56
-rw-r--r--java/dagger/hilt/GeneratesRootInput.java28
-rw-r--r--java/dagger/hilt/InstallIn.java51
-rw-r--r--java/dagger/hilt/README.md4
-rw-r--r--java/dagger/hilt/android/ActivityRetainedLifecycle.java61
-rw-r--r--java/dagger/hilt/android/AndroidEntryPoint.java72
-rw-r--r--java/dagger/hilt/android/AndroidManifest.xml19
-rw-r--r--java/dagger/hilt/android/BUILD193
-rw-r--r--java/dagger/hilt/android/EntryPointAccessors.java67
-rw-r--r--java/dagger/hilt/android/HiltAndroidApp.java84
-rw-r--r--java/dagger/hilt/android/WithFragmentBindings.java30
-rw-r--r--java/dagger/hilt/android/components/ActivityComponent.java25
-rw-r--r--java/dagger/hilt/android/components/ActivityRetainedComponent.java26
-rw-r--r--java/dagger/hilt/android/components/BUILD62
-rw-r--r--java/dagger/hilt/android/components/FragmentComponent.java25
-rw-r--r--java/dagger/hilt/android/components/ServiceComponent.java26
-rw-r--r--java/dagger/hilt/android/components/ViewComponent.java25
-rw-r--r--java/dagger/hilt/android/components/ViewModelComponent.java58
-rw-r--r--java/dagger/hilt/android/components/ViewWithFragmentComponent.java25
-rw-r--r--java/dagger/hilt/android/components/package-info.java22
-rw-r--r--java/dagger/hilt/android/example/BUILD27
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/build.gradle76
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/androidTest/java/dagger/hilt/android/example/gradle/simple/SettingsActivityEmulatorTest.java60
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/androidTest/java/dagger/hilt/android/example/gradle/simple/SimpleActivityEmulatorTest.java59
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/androidTest/java/dagger/hilt/android/example/gradle/simple/SimpleEmulatorTestRunner.java32
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/debug/AndroidManifest.xml33
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/main/AndroidManifest.xml35
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/Model.java29
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/ModelModule.java36
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SettingsActivity.java40
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SettingsGreeter.java38
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleActivity.java51
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleApplication.java32
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleGreeter.java36
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/UserName.java29
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/UserNameModule.java34
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/main/res/layout/activity_main.xml46
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/main/res/layout/activity_settings.xml12
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/main/res/values/strings.xml17
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/ActivityScenarioRuleTest.java65
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/BindValueTest.java155
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/BroadcastReceiverTest.java202
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/Injection1Test.java100
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/Injection2Test.java90
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/ModuleTest.java157
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/SettingsActivityTest.java76
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/SimpleActivityTest.java63
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/app/src/test/resources/dagger/hilt/android/example/gradle/simple/robolectric.properties2
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/build.gradle41
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/feature/build.gradle50
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/feature/src/main/AndroidManifest.xml9
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureActivity.kt46
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt19
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureModule.kt31
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/feature/src/main/res/layout/activity_feature.xml38
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/gradle.properties2
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/gradle/wrapper/gradle-wrapper.jarbin0 -> 55616 bytes
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/gradle/wrapper/gradle-wrapper.properties5
-rwxr-xr-xjava/dagger/hilt/android/example/gradle/simple/gradlew188
-rw-r--r--java/dagger/hilt/android/example/gradle/simple/settings.gradle3
-rw-r--r--java/dagger/hilt/android/example/gradle/simpleKotlin/app/build.gradle58
-rw-r--r--java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/debug/AndroidManifest.xml24
-rw-r--r--java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/AndroidManifest.xml35
-rw-r--r--java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/ActivityModule.kt31
-rw-r--r--java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/ApplicationModule.kt33
-rw-r--r--java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/KotlinApplication.kt30
-rw-r--r--java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/MainActivity.kt46
-rw-r--r--java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/Model.kt24
-rw-r--r--java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/UserName.kt24
-rw-r--r--java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/layout/activity_main.xml30
-rw-r--r--java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/values/strings.xml23
-rw-r--r--java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/test/java/dagger/hilt/android/example/gradle/simpleKotlin/SimpleTest.kt71
-rw-r--r--java/dagger/hilt/android/example/gradle/simpleKotlin/build.gradle41
-rw-r--r--java/dagger/hilt/android/example/gradle/simpleKotlin/gradle.properties2
-rw-r--r--java/dagger/hilt/android/example/gradle/simpleKotlin/gradle/wrapper/gradle-wrapper.jarbin0 -> 55616 bytes
-rw-r--r--java/dagger/hilt/android/example/gradle/simpleKotlin/gradle/wrapper/gradle-wrapper.properties5
-rwxr-xr-xjava/dagger/hilt/android/example/gradle/simpleKotlin/gradlew188
-rw-r--r--java/dagger/hilt/android/example/gradle/simpleKotlin/settings.gradle2
-rw-r--r--java/dagger/hilt/android/internal/BUILD28
-rw-r--r--java/dagger/hilt/android/internal/ThreadUtil.java42
-rw-r--r--java/dagger/hilt/android/internal/builders/ActivityComponentBuilder.java32
-rw-r--r--java/dagger/hilt/android/internal/builders/ActivityRetainedComponentBuilder.java26
-rw-r--r--java/dagger/hilt/android/internal/builders/BUILD37
-rw-r--r--java/dagger/hilt/android/internal/builders/FragmentComponentBuilder.java29
-rw-r--r--java/dagger/hilt/android/internal/builders/ServiceComponentBuilder.java29
-rw-r--r--java/dagger/hilt/android/internal/builders/ViewComponentBuilder.java30
-rw-r--r--java/dagger/hilt/android/internal/builders/ViewModelComponentBuilder.java30
-rw-r--r--java/dagger/hilt/android/internal/builders/ViewWithFragmentComponentBuilder.java30
-rw-r--r--java/dagger/hilt/android/internal/lifecycle/BUILD43
-rw-r--r--java/dagger/hilt/android/internal/lifecycle/DefaultActivityViewModelFactory.java29
-rw-r--r--java/dagger/hilt/android/internal/lifecycle/DefaultFragmentViewModelFactory.java29
-rw-r--r--java/dagger/hilt/android/internal/lifecycle/DefaultViewModelFactories.java167
-rw-r--r--java/dagger/hilt/android/internal/lifecycle/HiltViewModelFactory.java112
-rw-r--r--java/dagger/hilt/android/internal/lifecycle/HiltViewModelMap.java39
-rw-r--r--java/dagger/hilt/android/internal/managers/ActivityComponentManager.java92
-rw-r--r--java/dagger/hilt/android/internal/managers/ActivityRetainedComponentManager.java168
-rw-r--r--java/dagger/hilt/android/internal/managers/ApplicationComponentManager.java46
-rw-r--r--java/dagger/hilt/android/internal/managers/BUILD60
-rw-r--r--java/dagger/hilt/android/internal/managers/BroadcastReceiverComponentManager.java44
-rw-r--r--java/dagger/hilt/android/internal/managers/ComponentSupplier.java26
-rw-r--r--java/dagger/hilt/android/internal/managers/FragmentComponentManager.java116
-rw-r--r--java/dagger/hilt/android/internal/managers/ServiceComponentManager.java75
-rw-r--r--java/dagger/hilt/android/internal/managers/ViewComponentManager.java201
-rw-r--r--java/dagger/hilt/android/internal/migration/BUILD28
-rw-r--r--java/dagger/hilt/android/internal/migration/InjectedByHilt.java25
-rw-r--r--java/dagger/hilt/android/internal/modules/ActivityModule.java49
-rw-r--r--java/dagger/hilt/android/internal/modules/ApplicationContextModule.java47
-rw-r--r--java/dagger/hilt/android/internal/modules/BUILD37
-rw-r--r--java/dagger/hilt/android/internal/testing/BUILD89
-rw-r--r--java/dagger/hilt/android/internal/testing/InternalTestRoot.java38
-rw-r--r--java/dagger/hilt/android/internal/testing/MarkThatRulesRanRule.java136
-rw-r--r--java/dagger/hilt/android/internal/testing/TestApplicationComponentManager.java332
-rw-r--r--java/dagger/hilt/android/internal/testing/TestApplicationComponentManagerHolder.java24
-rw-r--r--java/dagger/hilt/android/internal/testing/TestApplicationInjector.java24
-rw-r--r--java/dagger/hilt/android/internal/testing/TestComponentData.java79
-rw-r--r--java/dagger/hilt/android/internal/testing/TestComponentDataSupplier.java26
-rw-r--r--java/dagger/hilt/android/internal/testing/TestInjector.java24
-rw-r--r--java/dagger/hilt/android/lifecycle/BUILD39
-rw-r--r--java/dagger/hilt/android/lifecycle/HiltViewModel.java68
-rw-r--r--java/dagger/hilt/android/lifecycle/proguard-rules.pro2
-rw-r--r--java/dagger/hilt/android/migration/BUILD50
-rw-r--r--java/dagger/hilt/android/migration/OptionalInject.java63
-rw-r--r--java/dagger/hilt/android/migration/OptionalInjectCheck.java97
-rw-r--r--java/dagger/hilt/android/migration/package-info.java25
-rw-r--r--java/dagger/hilt/android/package-info.java28
-rw-r--r--java/dagger/hilt/android/plugin/BUILD27
-rw-r--r--java/dagger/hilt/android/plugin/build.gradle156
-rw-r--r--java/dagger/hilt/android/plugin/gradle/wrapper/gradle-wrapper.jarbin0 -> 55616 bytes
-rw-r--r--java/dagger/hilt/android/plugin/gradle/wrapper/gradle-wrapper.properties5
-rwxr-xr-xjava/dagger/hilt/android/plugin/gradlew188
-rw-r--r--java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassTransformer.kt249
-rw-r--r--java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassVisitor.kt197
-rw-r--r--java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointTransform.kt174
-rw-r--r--java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltExtension.kt50
-rw-r--r--java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt298
-rw-r--r--java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltTransformTestClassesTask.kt174
-rw-r--r--java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/CopyTransform.kt29
-rw-r--r--java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/Files.kt14
-rw-r--r--java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/SimpleAGPVersion.kt41
-rw-r--r--java/dagger/hilt/android/plugin/src/main/resources/META-INF/gradle-plugins/dagger.hilt.android.plugin.properties1
-rw-r--r--java/dagger/hilt/android/plugin/src/test/data/android-libraryA/build.gradle28
-rw-r--r--java/dagger/hilt/android/plugin/src/test/data/android-libraryA/src/main/AndroidManifest.xml4
-rw-r--r--java/dagger/hilt/android/plugin/src/test/data/android-libraryA/src/main/java/liba/LibraryA.java27
-rw-r--r--java/dagger/hilt/android/plugin/src/test/data/android-libraryC/build.gradle25
-rw-r--r--java/dagger/hilt/android/plugin/src/test/data/android-libraryC/src/main/AndroidManifest.xml4
-rw-r--r--java/dagger/hilt/android/plugin/src/test/data/android-libraryC/src/main/java/libc/LibraryC.java25
-rw-r--r--java/dagger/hilt/android/plugin/src/test/data/java-libraryA/build.gradle15
-rw-r--r--java/dagger/hilt/android/plugin/src/test/data/java-libraryA/src/main/java/liba/LibraryA.java26
-rw-r--r--java/dagger/hilt/android/plugin/src/test/data/java-libraryB/build.gradle13
-rw-r--r--java/dagger/hilt/android/plugin/src/test/data/java-libraryB/src/main/java/libb/LibraryB.java25
-rw-r--r--java/dagger/hilt/android/plugin/src/test/data/java-libraryB/src/main/java/libb/LibraryBModule.java32
-rw-r--r--java/dagger/hilt/android/plugin/src/test/data/java-libraryB/src/main/java/libb/LibraryBProvided.java20
-rw-r--r--java/dagger/hilt/android/plugin/src/test/data/simple-project/gradle.properties1
-rw-r--r--java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/AndroidManifest.xml18
-rw-r--r--java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/java/simple/Activity1.java29
-rw-r--r--java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/java/simple/Activity2.java29
-rw-r--r--java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/java/simple/Module1.java36
-rw-r--r--java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/java/simple/Module2.java36
-rw-r--r--java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/java/simple/SimpleApp.java26
-rw-r--r--java/dagger/hilt/android/plugin/src/test/kotlin/CompileClasspathTest.kt111
-rw-r--r--java/dagger/hilt/android/plugin/src/test/kotlin/GradleTestRunner.kt214
-rw-r--r--java/dagger/hilt/android/plugin/src/test/kotlin/HiltGradlePluginTest.kt68
-rw-r--r--java/dagger/hilt/android/plugin/src/test/kotlin/IncrementalProcessorTest.kt615
-rw-r--r--java/dagger/hilt/android/plugin/src/test/kotlin/TransformTest.kt313
-rw-r--r--java/dagger/hilt/android/plugin/src/test/kotlin/util/SimpleAGPVersionTest.kt40
-rw-r--r--java/dagger/hilt/android/processor/BUILD100
-rw-r--r--java/dagger/hilt/android/processor/internal/AndroidClassNames.java117
-rw-r--r--java/dagger/hilt/android/processor/internal/BUILD45
-rw-r--r--java/dagger/hilt/android/processor/internal/MoreTypes.java151
-rw-r--r--java/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGenerator.java116
-rw-r--r--java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java522
-rw-r--r--java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointProcessor.java85
-rw-r--r--java/dagger/hilt/android/processor/internal/androidentrypoint/ApplicationGenerator.java162
-rw-r--r--java/dagger/hilt/android/processor/internal/androidentrypoint/BUILD109
-rw-r--r--java/dagger/hilt/android/processor/internal/androidentrypoint/BroadcastReceiverGenerator.java153
-rw-r--r--java/dagger/hilt/android/processor/internal/androidentrypoint/FragmentGenerator.java217
-rw-r--r--java/dagger/hilt/android/processor/internal/androidentrypoint/Generators.java493
-rw-r--r--java/dagger/hilt/android/processor/internal/androidentrypoint/HiltCompilerOptions.java71
-rw-r--r--java/dagger/hilt/android/processor/internal/androidentrypoint/InjectorEntryPointGenerator.java67
-rw-r--r--java/dagger/hilt/android/processor/internal/androidentrypoint/ServiceGenerator.java112
-rw-r--r--java/dagger/hilt/android/processor/internal/androidentrypoint/ViewGenerator.java194
-rw-r--r--java/dagger/hilt/android/processor/internal/bindvalue/BUILD60
-rw-r--r--java/dagger/hilt/android/processor/internal/bindvalue/BindValueGenerator.java160
-rw-r--r--java/dagger/hilt/android/processor/internal/bindvalue/BindValueMetadata.java178
-rw-r--r--java/dagger/hilt/android/processor/internal/bindvalue/BindValueProcessor.java113
-rw-r--r--java/dagger/hilt/android/processor/internal/customtestapplication/BUILD53
-rw-r--r--java/dagger/hilt/android/processor/internal/customtestapplication/CustomTestApplicationGenerator.java116
-rw-r--r--java/dagger/hilt/android/processor/internal/customtestapplication/CustomTestApplicationMetadata.java144
-rw-r--r--java/dagger/hilt/android/processor/internal/customtestapplication/CustomTestApplicationProcessor.java46
-rw-r--r--java/dagger/hilt/android/processor/internal/uninstallmodules/BUILD48
-rw-r--r--java/dagger/hilt/android/processor/internal/uninstallmodules/UninstallModulesProcessor.java80
-rw-r--r--java/dagger/hilt/android/processor/internal/viewmodel/BUILD84
-rw-r--r--java/dagger/hilt/android/processor/internal/viewmodel/ViewModelMetadata.kt107
-rw-r--r--java/dagger/hilt/android/processor/internal/viewmodel/ViewModelModuleGenerator.kt169
-rw-r--r--java/dagger/hilt/android/processor/internal/viewmodel/ViewModelProcessor.kt64
-rw-r--r--java/dagger/hilt/android/processor/internal/viewmodel/ViewModelValidationPlugin.kt92
-rw-r--r--java/dagger/hilt/android/qualifiers/ActivityContext.java29
-rw-r--r--java/dagger/hilt/android/qualifiers/ApplicationContext.java26
-rw-r--r--java/dagger/hilt/android/qualifiers/BUILD40
-rw-r--r--java/dagger/hilt/android/qualifiers/package-info.java18
-rw-r--r--java/dagger/hilt/android/scopes/ActivityRetainedScoped.java30
-rw-r--r--java/dagger/hilt/android/scopes/ActivityScoped.java29
-rw-r--r--java/dagger/hilt/android/scopes/BUILD60
-rw-r--r--java/dagger/hilt/android/scopes/FragmentScoped.java29
-rw-r--r--java/dagger/hilt/android/scopes/ServiceScoped.java29
-rw-r--r--java/dagger/hilt/android/scopes/ViewModelScoped.java76
-rw-r--r--java/dagger/hilt/android/scopes/ViewScoped.java32
-rw-r--r--java/dagger/hilt/android/scopes/package-info.java22
-rw-r--r--java/dagger/hilt/android/testing/AndroidManifest.xml19
-rw-r--r--java/dagger/hilt/android/testing/BUILD238
-rw-r--r--java/dagger/hilt/android/testing/BindElementsIntoSet.java60
-rw-r--r--java/dagger/hilt/android/testing/BindValue.java43
-rw-r--r--java/dagger/hilt/android/testing/BindValueIntoMap.java51
-rw-r--r--java/dagger/hilt/android/testing/BindValueIntoSet.java47
-rw-r--r--java/dagger/hilt/android/testing/CustomTestApplication.java36
-rw-r--r--java/dagger/hilt/android/testing/HiltAndroidRule.java79
-rw-r--r--java/dagger/hilt/android/testing/HiltAndroidTest.java31
-rw-r--r--java/dagger/hilt/android/testing/HiltTestApplication.java51
-rw-r--r--java/dagger/hilt/android/testing/OnComponentReadyRunner.java112
-rw-r--r--java/dagger/hilt/android/testing/UninstallModules.java59
-rw-r--r--java/dagger/hilt/android/testing/package-info.java25
-rw-r--r--java/dagger/hilt/codegen/BUILD39
-rw-r--r--java/dagger/hilt/codegen/OriginatingElement.java52
-rw-r--r--java/dagger/hilt/codegen/package-info.java21
-rw-r--r--java/dagger/hilt/components/BUILD40
-rw-r--r--java/dagger/hilt/components/SingletonComponent.java25
-rw-r--r--java/dagger/hilt/components/package-info.java22
-rw-r--r--java/dagger/hilt/internal/BUILD68
-rw-r--r--java/dagger/hilt/internal/ComponentEntryPoint.java35
-rw-r--r--java/dagger/hilt/internal/GeneratedComponent.java20
-rw-r--r--java/dagger/hilt/internal/GeneratedComponentManager.java23
-rw-r--r--java/dagger/hilt/internal/GeneratedComponentManagerHolder.java23
-rw-r--r--java/dagger/hilt/internal/GeneratedEntryPoint.java26
-rw-r--r--java/dagger/hilt/internal/Preconditions.java88
-rw-r--r--java/dagger/hilt/internal/UnsafeCasts.java29
-rw-r--r--java/dagger/hilt/internal/aliasof/AliasOfPropagatedData.java32
-rw-r--r--java/dagger/hilt/internal/aliasof/BUILD28
-rw-r--r--java/dagger/hilt/internal/definecomponent/BUILD32
-rw-r--r--java/dagger/hilt/internal/definecomponent/DefineComponentClasses.java46
-rw-r--r--java/dagger/hilt/internal/definecomponent/DefineComponentNoParent.java22
-rw-r--r--java/dagger/hilt/internal/generatesrootinput/BUILD28
-rw-r--r--java/dagger/hilt/internal/generatesrootinput/GeneratesRootInputPropagatedData.java33
-rw-r--r--java/dagger/hilt/migration/AliasOf.java46
-rw-r--r--java/dagger/hilt/migration/BUILD57
-rw-r--r--java/dagger/hilt/migration/DisableInstallInCheck.java28
-rw-r--r--java/dagger/hilt/migration/package-info.java22
-rw-r--r--java/dagger/hilt/package-info.java28
-rw-r--r--java/dagger/hilt/processor/BUILD120
-rw-r--r--java/dagger/hilt/processor/internal/AnnotationValues.java186
-rw-r--r--java/dagger/hilt/processor/internal/BUILD149
-rw-r--r--java/dagger/hilt/processor/internal/BadInputException.java42
-rw-r--r--java/dagger/hilt/processor/internal/BaseProcessor.java224
-rw-r--r--java/dagger/hilt/processor/internal/ClassNames.java183
-rw-r--r--java/dagger/hilt/processor/internal/ComponentDescriptor.java95
-rw-r--r--java/dagger/hilt/processor/internal/ComponentGenerator.java172
-rw-r--r--java/dagger/hilt/processor/internal/ComponentNames.java53
-rw-r--r--java/dagger/hilt/processor/internal/ComponentTree.java106
-rw-r--r--java/dagger/hilt/processor/internal/Components.java122
-rw-r--r--java/dagger/hilt/processor/internal/ErrorTypeException.java37
-rw-r--r--java/dagger/hilt/processor/internal/KotlinMetadataUtils.java39
-rw-r--r--java/dagger/hilt/processor/internal/ProcessorErrorHandler.java123
-rw-r--r--java/dagger/hilt/processor/internal/ProcessorErrors.java164
-rw-r--r--java/dagger/hilt/processor/internal/Processors.java940
-rw-r--r--java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDeps.java42
-rw-r--r--java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsGenerator.java86
-rw-r--r--java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsProcessor.java469
-rw-r--r--java/dagger/hilt/processor/internal/aggregateddeps/BUILD88
-rw-r--r--java/dagger/hilt/processor/internal/aggregateddeps/ComponentDependencies.java459
-rw-r--r--java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateEntryPointGenerator.java77
-rw-r--r--java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateMetadata.java93
-rw-r--r--java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateModuleGenerator.java73
-rw-r--r--java/dagger/hilt/processor/internal/aliasof/AliasOfProcessor.java60
-rw-r--r--java/dagger/hilt/processor/internal/aliasof/AliasOfPropagatedDataGenerator.java59
-rw-r--r--java/dagger/hilt/processor/internal/aliasof/AliasOfs.java108
-rw-r--r--java/dagger/hilt/processor/internal/aliasof/BUILD64
-rw-r--r--java/dagger/hilt/processor/internal/definecomponent/BUILD69
-rw-r--r--java/dagger/hilt/processor/internal/definecomponent/DefineComponentBuilderMetadatas.java161
-rw-r--r--java/dagger/hilt/processor/internal/definecomponent/DefineComponentMetadatas.java195
-rw-r--r--java/dagger/hilt/processor/internal/definecomponent/DefineComponentProcessor.java87
-rw-r--r--java/dagger/hilt/processor/internal/definecomponent/DefineComponents.java225
-rw-r--r--java/dagger/hilt/processor/internal/disableinstallincheck/BUILD49
-rw-r--r--java/dagger/hilt/processor/internal/disableinstallincheck/DisableInstallInCheckProcessor.java50
-rw-r--r--java/dagger/hilt/processor/internal/generatesrootinput/BUILD65
-rw-r--r--java/dagger/hilt/processor/internal/generatesrootinput/GeneratesRootInputProcessor.java57
-rw-r--r--java/dagger/hilt/processor/internal/generatesrootinput/GeneratesRootInputPropagatedDataGenerator.java56
-rw-r--r--java/dagger/hilt/processor/internal/generatesrootinput/GeneratesRootInputs.java106
-rw-r--r--java/dagger/hilt/processor/internal/originatingelement/BUILD47
-rw-r--r--java/dagger/hilt/processor/internal/originatingelement/OriginatingElementProcessor.java69
-rw-r--r--java/dagger/hilt/processor/internal/root/BUILD102
-rw-r--r--java/dagger/hilt/processor/internal/root/Root.java54
-rw-r--r--java/dagger/hilt/processor/internal/root/RootFileFormatter.java103
-rw-r--r--java/dagger/hilt/processor/internal/root/RootGenerator.java219
-rw-r--r--java/dagger/hilt/processor/internal/root/RootMetadata.java243
-rw-r--r--java/dagger/hilt/processor/internal/root/RootProcessor.java181
-rw-r--r--java/dagger/hilt/processor/internal/root/RootType.java60
-rw-r--r--java/dagger/hilt/processor/internal/root/TestComponentDataGenerator.java216
-rw-r--r--java/dagger/hilt/processor/internal/root/TestComponentDataSupplierGenerator.java119
-rw-r--r--java/dagger/hilt/processor/internal/root/TestInjectorGenerator.java77
-rw-r--r--java/dagger/hilt/processor/internal/root/TestRootMetadata.java77
-rw-r--r--java/dagger/hilt/testing/BUILD47
-rw-r--r--java/dagger/hilt/testing/TestInstallIn.java57
-rw-r--r--java/dagger/hilt/testing/package-info.java25
-rw-r--r--java/dagger/internal/ConfigureInitializationParameters.java36
-rw-r--r--java/dagger/internal/GenerationOptions.java30
-rw-r--r--java/dagger/internal/InjectedFieldSignature.java32
-rw-r--r--java/dagger/internal/MapBuilder.java8
-rw-r--r--java/dagger/internal/MapFactory.java2
-rw-r--r--java/dagger/internal/MissingBindingFactory.java43
-rw-r--r--java/dagger/internal/ModifiableBinding.java37
-rw-r--r--java/dagger/internal/ModifiableModule.java26
-rw-r--r--java/dagger/internal/Preconditions.java28
-rw-r--r--java/dagger/internal/SetBuilder.java13
-rw-r--r--java/dagger/internal/codegen/AnnotationCreatorGenerator.java167
-rw-r--r--java/dagger/internal/codegen/AnnotationExpression.java207
-rw-r--r--java/dagger/internal/codegen/AnnotationProtoConverter.java275
-rw-r--r--java/dagger/internal/codegen/AnonymousProviderCreationExpression.java58
-rw-r--r--java/dagger/internal/codegen/AnyBindingMethodValidator.java113
-rw-r--r--java/dagger/internal/codegen/AssistedFactoryProcessingStep.java360
-rw-r--r--java/dagger/internal/codegen/AssistedInjectProcessingStep.java87
-rw-r--r--java/dagger/internal/codegen/AssistedProcessingStep.java132
-rw-r--r--java/dagger/internal/codegen/BUILD557
-rw-r--r--java/dagger/internal/codegen/Binding.java314
-rw-r--r--java/dagger/internal/codegen/BindingDeclaration.java62
-rw-r--r--java/dagger/internal/codegen/BindingDeclarationFormatter.java124
-rw-r--r--java/dagger/internal/codegen/BindingElementValidator.java383
-rw-r--r--java/dagger/internal/codegen/BindingExpression.java111
-rw-r--r--java/dagger/internal/codegen/BindingFactory.java508
-rw-r--r--java/dagger/internal/codegen/BindingGraph.java232
-rw-r--r--java/dagger/internal/codegen/BindingGraphConverter.java239
-rw-r--r--java/dagger/internal/codegen/BindingGraphFactory.java1061
-rw-r--r--java/dagger/internal/codegen/BindingGraphPlugins.java77
-rw-r--r--java/dagger/internal/codegen/BindingGraphStatisticsCollector.java93
-rw-r--r--java/dagger/internal/codegen/BindingGraphValidationModule.java78
-rw-r--r--java/dagger/internal/codegen/BindingGraphValidator.java63
-rw-r--r--java/dagger/internal/codegen/BindingMethodProcessingStep.java60
-rw-r--r--java/dagger/internal/codegen/BindingMethodValidator.java303
-rw-r--r--java/dagger/internal/codegen/BindingMethodValidatorsModule.java60
-rw-r--r--java/dagger/internal/codegen/BindingNode.java137
-rw-r--r--java/dagger/internal/codegen/BindingRequest.java125
-rw-r--r--java/dagger/internal/codegen/BindingType.java31
-rw-r--r--java/dagger/internal/codegen/BindsInstanceElementValidator.java38
-rw-r--r--java/dagger/internal/codegen/BindsInstanceMethodValidator.java84
-rw-r--r--java/dagger/internal/codegen/BindsInstanceParameterValidator.java74
-rw-r--r--java/dagger/internal/codegen/BindsInstanceProcessingStep.java66
-rw-r--r--java/dagger/internal/codegen/BindsMethodValidator.java107
-rw-r--r--java/dagger/internal/codegen/BindsOptionalOfMethodValidator.java92
-rw-r--r--java/dagger/internal/codegen/BindsTypeChecker.java109
-rw-r--r--java/dagger/internal/codegen/ChildFactoryMethodEdgeImpl.java42
-rw-r--r--java/dagger/internal/codegen/ClearableCache.java23
-rw-r--r--java/dagger/internal/codegen/CompilerOptions.java119
-rw-r--r--java/dagger/internal/codegen/ComponentAnnotation.java323
-rw-r--r--java/dagger/internal/codegen/ComponentBindingExpressions.java713
-rw-r--r--java/dagger/internal/codegen/ComponentCreatorAnnotation.java150
-rw-r--r--java/dagger/internal/codegen/ComponentCreatorDescriptor.java222
-rw-r--r--java/dagger/internal/codegen/ComponentCreatorImplementation.java51
-rw-r--r--java/dagger/internal/codegen/ComponentCreatorImplementationFactory.java589
-rw-r--r--java/dagger/internal/codegen/ComponentCreatorKind.java42
-rw-r--r--java/dagger/internal/codegen/ComponentCreatorValidator.java390
-rw-r--r--java/dagger/internal/codegen/ComponentDescriptor.java376
-rw-r--r--java/dagger/internal/codegen/ComponentDescriptorFactory.java268
-rw-r--r--java/dagger/internal/codegen/ComponentDescriptorValidator.java484
-rw-r--r--java/dagger/internal/codegen/ComponentGenerator.java71
-rw-r--r--java/dagger/internal/codegen/ComponentHierarchyValidator.java268
-rw-r--r--java/dagger/internal/codegen/ComponentHjarProcessingStep.java267
-rw-r--r--java/dagger/internal/codegen/ComponentImplementation.java929
-rw-r--r--java/dagger/internal/codegen/ComponentImplementationBuilder.java826
-rw-r--r--java/dagger/internal/codegen/ComponentImplementationFactory.java162
-rw-r--r--java/dagger/internal/codegen/ComponentInstanceBindingExpression.java42
-rw-r--r--java/dagger/internal/codegen/ComponentKind.java170
-rw-r--r--java/dagger/internal/codegen/ComponentMethodBindingExpression.java90
-rw-r--r--java/dagger/internal/codegen/ComponentNodeImpl.java64
-rw-r--r--java/dagger/internal/codegen/ComponentProcessingStep.java191
-rw-r--r--java/dagger/internal/codegen/ComponentProcessor.java90
-rw-r--r--java/dagger/internal/codegen/ComponentProvisionBindingExpression.java73
-rw-r--r--java/dagger/internal/codegen/ComponentRequirement.java247
-rw-r--r--java/dagger/internal/codegen/ComponentRequirementBindingExpression.java46
-rw-r--r--java/dagger/internal/codegen/ComponentRequirementExpression.java54
-rw-r--r--java/dagger/internal/codegen/ComponentRequirementExpressions.java334
-rw-r--r--java/dagger/internal/codegen/ComponentTreeTraverser.java180
-rw-r--r--java/dagger/internal/codegen/ComponentValidator.java522
-rw-r--r--java/dagger/internal/codegen/ConfigurationAnnotations.java157
-rw-r--r--java/dagger/internal/codegen/ContributionBinding.java209
-rw-r--r--java/dagger/internal/codegen/ContributionType.java67
-rw-r--r--java/dagger/internal/codegen/CurrentImplementationSubcomponent.java58
-rw-r--r--java/dagger/internal/codegen/DaggerGraphs.java98
-rw-r--r--java/dagger/internal/codegen/DaggerKythePlugin.java194
-rw-r--r--java/dagger/internal/codegen/DaggerStatistics.java90
-rw-r--r--java/dagger/internal/codegen/DaggerStatisticsCollectingProcessingStep.java57
-rw-r--r--java/dagger/internal/codegen/DaggerStatisticsCollector.java94
-rw-r--r--java/dagger/internal/codegen/DaggerStatisticsRecorder.java23
-rw-r--r--java/dagger/internal/codegen/DaggerStreams.java135
-rw-r--r--java/dagger/internal/codegen/DeferredModifiableBindingExpression.java75
-rw-r--r--java/dagger/internal/codegen/DelegateBindingExpression.java133
-rw-r--r--java/dagger/internal/codegen/DelegateDeclaration.java91
-rw-r--r--java/dagger/internal/codegen/DelegatingFrameworkInstanceCreationExpression.java56
-rw-r--r--java/dagger/internal/codegen/DependencyCycleValidator.java319
-rw-r--r--java/dagger/internal/codegen/DependencyEdgeImpl.java55
-rw-r--r--java/dagger/internal/codegen/DependencyMethodProducerCreationExpression.java97
-rw-r--r--java/dagger/internal/codegen/DependencyMethodProviderCreationExpression.java125
-rw-r--r--java/dagger/internal/codegen/DependencyRequestFactory.java237
-rw-r--r--java/dagger/internal/codegen/DependencyRequestFormatter.java151
-rw-r--r--java/dagger/internal/codegen/DependencyRequestValidator.java97
-rw-r--r--java/dagger/internal/codegen/DependencyVariableNamer.java82
-rw-r--r--java/dagger/internal/codegen/DependsOnProductionExecutorValidator.java69
-rw-r--r--java/dagger/internal/codegen/DerivedFromFrameworkInstanceBindingExpression.java82
-rw-r--r--java/dagger/internal/codegen/DeserializedComponentImplementationBuilder.java256
-rw-r--r--java/dagger/internal/codegen/DiagnosticFormatting.java71
-rw-r--r--java/dagger/internal/codegen/DiagnosticReporterFactory.java519
-rw-r--r--java/dagger/internal/codegen/DuplicateBindingsValidator.java332
-rw-r--r--java/dagger/internal/codegen/ElementFormatter.java107
-rw-r--r--java/dagger/internal/codegen/ErrorMessages.java356
-rw-r--r--java/dagger/internal/codegen/FactoryGenerator.java290
-rw-r--r--java/dagger/internal/codegen/FeatureStatus.java23
-rw-r--r--java/dagger/internal/codegen/Formatter.java96
-rw-r--r--java/dagger/internal/codegen/ForwardingCompilerOptions.java122
-rw-r--r--java/dagger/internal/codegen/FrameworkDependency.java61
-rw-r--r--java/dagger/internal/codegen/FrameworkField.java130
-rw-r--r--java/dagger/internal/codegen/FrameworkFieldInitializer.java217
-rw-r--r--java/dagger/internal/codegen/FrameworkInstanceBindingExpression.java88
-rw-r--r--java/dagger/internal/codegen/FrameworkInstanceSupplier.java23
-rw-r--r--java/dagger/internal/codegen/FrameworkType.java222
-rw-r--r--java/dagger/internal/codegen/FrameworkTypeMapper.java85
-rw-r--r--java/dagger/internal/codegen/FrameworkTypes.java66
-rw-r--r--java/dagger/internal/codegen/GenerationCompilerOptions.java30
-rw-r--r--java/dagger/internal/codegen/GenerationOptionsModule.java55
-rw-r--r--java/dagger/internal/codegen/GwtCompatibility.java56
-rw-r--r--java/dagger/internal/codegen/HjarSourceFileGenerator.java125
-rw-r--r--java/dagger/internal/codegen/ImmediateFutureBindingExpression.java74
-rw-r--r--java/dagger/internal/codegen/InaccessibleMapKeyProxyGenerator.java72
-rw-r--r--java/dagger/internal/codegen/IncompatiblyScopedBindingsValidator.java142
-rw-r--r--java/dagger/internal/codegen/InjectBindingRegistry.java68
-rw-r--r--java/dagger/internal/codegen/InjectBindingRegistryImpl.java340
-rw-r--r--java/dagger/internal/codegen/InjectBindingRegistryModule.java26
-rw-r--r--java/dagger/internal/codegen/InjectBindingValidator.java59
-rw-r--r--java/dagger/internal/codegen/InjectProcessingStep.java15
-rw-r--r--java/dagger/internal/codegen/InjectValidator.java332
-rw-r--r--java/dagger/internal/codegen/InjectionAnnotations.java68
-rw-r--r--java/dagger/internal/codegen/InjectionMethod.java210
-rw-r--r--java/dagger/internal/codegen/InjectionMethods.java544
-rw-r--r--java/dagger/internal/codegen/InjectionOrProvisionProviderCreationExpression.java63
-rw-r--r--java/dagger/internal/codegen/InjectionSiteFactory.java161
-rw-r--r--java/dagger/internal/codegen/InnerSwitchingProviders.java106
-rw-r--r--java/dagger/internal/codegen/InstanceFactoryCreationExpression.java57
-rw-r--r--java/dagger/internal/codegen/JavacPluginModule.java184
-rw-r--r--java/dagger/internal/codegen/KeyFactory.java469
-rw-r--r--java/dagger/internal/codegen/KeyVariableNamer.java107
-rw-r--r--java/dagger/internal/codegen/Keys.java91
-rw-r--r--java/dagger/internal/codegen/MapBindingExpression.java176
-rw-r--r--java/dagger/internal/codegen/MapFactoryCreationExpression.java90
-rw-r--r--java/dagger/internal/codegen/MapKeyAccessibility.java78
-rw-r--r--java/dagger/internal/codegen/MapKeyProcessingStep.java9
-rw-r--r--java/dagger/internal/codegen/MapKeyValidator.java65
-rw-r--r--java/dagger/internal/codegen/MapKeys.java225
-rw-r--r--java/dagger/internal/codegen/MapMultibindingValidator.java199
-rw-r--r--java/dagger/internal/codegen/MapType.java158
-rw-r--r--java/dagger/internal/codegen/MemberSelect.java274
-rw-r--r--java/dagger/internal/codegen/MembersInjectionBinding.java140
-rw-r--r--java/dagger/internal/codegen/MembersInjectionBindingExpression.java71
-rw-r--r--java/dagger/internal/codegen/MembersInjectionMethods.java126
-rw-r--r--java/dagger/internal/codegen/MembersInjectionValidator.java149
-rw-r--r--java/dagger/internal/codegen/MembersInjectorGenerator.java204
-rw-r--r--java/dagger/internal/codegen/MembersInjectorProviderCreationExpression.java65
-rw-r--r--java/dagger/internal/codegen/MethodBindingExpression.java323
-rw-r--r--java/dagger/internal/codegen/MethodSignature.java55
-rw-r--r--java/dagger/internal/codegen/MethodSignatureFormatter.java139
-rw-r--r--java/dagger/internal/codegen/MissingBindingExpression.java61
-rw-r--r--java/dagger/internal/codegen/MissingBindingValidator.java131
-rw-r--r--java/dagger/internal/codegen/ModifiableAbstractMethodBindingExpression.java138
-rw-r--r--java/dagger/internal/codegen/ModifiableBindingExpressions.java526
-rw-r--r--java/dagger/internal/codegen/ModifiableBindingMethods.java133
-rw-r--r--java/dagger/internal/codegen/ModifiableBindingType.java99
-rw-r--r--java/dagger/internal/codegen/ModifiableConcreteMethodBindingExpression.java99
-rw-r--r--java/dagger/internal/codegen/ModuleAnnotation.java135
-rw-r--r--java/dagger/internal/codegen/ModuleConstructorProxyGenerator.java85
-rw-r--r--java/dagger/internal/codegen/ModuleDescriptor.java225
-rw-r--r--java/dagger/internal/codegen/ModuleGenerator.java27
-rw-r--r--java/dagger/internal/codegen/ModuleKind.java107
-rw-r--r--java/dagger/internal/codegen/ModuleProcessingStep.java52
-rw-r--r--java/dagger/internal/codegen/ModuleProxies.java81
-rw-r--r--java/dagger/internal/codegen/ModuleValidator.java644
-rw-r--r--java/dagger/internal/codegen/MonitoringModuleGenerator.java100
-rw-r--r--java/dagger/internal/codegen/MonitoringModuleProcessingStep.java55
-rw-r--r--java/dagger/internal/codegen/MoreAnnotationMirrors.java73
-rw-r--r--java/dagger/internal/codegen/MoreAnnotationValues.java75
-rw-r--r--java/dagger/internal/codegen/MultibindingAnnotations.java36
-rw-r--r--java/dagger/internal/codegen/MultibindingAnnotationsProcessingStep.java68
-rw-r--r--java/dagger/internal/codegen/MultibindingDeclaration.java118
-rw-r--r--java/dagger/internal/codegen/MultibindingExpression.java95
-rw-r--r--java/dagger/internal/codegen/MultibindingFactoryCreationExpression.java95
-rw-r--r--java/dagger/internal/codegen/MultibindsMethodValidator.java103
-rw-r--r--java/dagger/internal/codegen/NullableBindingValidator.java82
-rw-r--r--java/dagger/internal/codegen/OptionalBindingDeclaration.java68
-rw-r--r--java/dagger/internal/codegen/OptionalBindingExpression.java92
-rw-r--r--java/dagger/internal/codegen/OptionalFactories.java439
-rw-r--r--java/dagger/internal/codegen/OptionalFactoryInstanceCreationExpression.java67
-rw-r--r--java/dagger/internal/codegen/OptionalType.java159
-rw-r--r--java/dagger/internal/codegen/Optionals.java68
-rw-r--r--java/dagger/internal/codegen/ParentComponent.java30
-rw-r--r--java/dagger/internal/codegen/PerComponentImplementation.java27
-rw-r--r--java/dagger/internal/codegen/PerGeneratedFile.java30
-rw-r--r--java/dagger/internal/codegen/PrivateMethodBindingExpression.java77
-rw-r--r--java/dagger/internal/codegen/ProcessingEnvironmentCompilerOptions.java484
-rw-r--r--java/dagger/internal/codegen/ProcessingEnvironmentModule.java44
-rw-r--r--java/dagger/internal/codegen/ProcessingOptions.java30
-rw-r--r--java/dagger/internal/codegen/ProcessingRoundCacheModule.java26
-rw-r--r--java/dagger/internal/codegen/ProducerCreationExpression.java48
-rw-r--r--java/dagger/internal/codegen/ProducerEntryPointView.java110
-rw-r--r--java/dagger/internal/codegen/ProducerFactoryGenerator.java553
-rw-r--r--java/dagger/internal/codegen/ProducerFromProviderCreationExpression.java62
-rw-r--r--java/dagger/internal/codegen/ProducerNodeInstanceBindingExpression.java67
-rw-r--r--java/dagger/internal/codegen/ProducesMethodValidator.java133
-rw-r--r--java/dagger/internal/codegen/ProductionBinding.java146
-rw-r--r--java/dagger/internal/codegen/ProviderInstanceBindingExpression.java41
-rw-r--r--java/dagger/internal/codegen/ProvidesMethodValidator.java78
-rw-r--r--java/dagger/internal/codegen/ProvisionBinding.java135
-rw-r--r--java/dagger/internal/codegen/ProvisionDependencyOnProducerBindingValidator.java115
-rw-r--r--java/dagger/internal/codegen/PrunedConcreteMethodBindingExpression.java79
-rw-r--r--java/dagger/internal/codegen/RequestKinds.java203
-rw-r--r--java/dagger/internal/codegen/ResolvedBindings.java264
-rw-r--r--java/dagger/internal/codegen/Scopes.java84
-rw-r--r--java/dagger/internal/codegen/SetBindingExpression.java183
-rw-r--r--java/dagger/internal/codegen/SetFactoryCreationExpression.java103
-rw-r--r--java/dagger/internal/codegen/SetType.java120
-rw-r--r--java/dagger/internal/codegen/SimpleAnnotationMirror.java107
-rw-r--r--java/dagger/internal/codegen/SimpleInvocationBindingExpression.java34
-rw-r--r--java/dagger/internal/codegen/SimpleMethodBindingExpression.java191
-rw-r--r--java/dagger/internal/codegen/SimpleTypeAnnotationValue.java45
-rw-r--r--java/dagger/internal/codegen/SourceFileGenerationException.java54
-rw-r--r--java/dagger/internal/codegen/SourceFileGenerator.java124
-rw-r--r--java/dagger/internal/codegen/SourceFileGeneratorsModule.java67
-rw-r--r--java/dagger/internal/codegen/SourceFiles.java348
-rw-r--r--java/dagger/internal/codegen/SpiModule.java12
-rw-r--r--java/dagger/internal/codegen/SubcomponentCreatorBindingEdgeImpl.java55
-rw-r--r--java/dagger/internal/codegen/SubcomponentCreatorBindingExpression.java39
-rw-r--r--java/dagger/internal/codegen/SubcomponentDeclaration.java86
-rw-r--r--java/dagger/internal/codegen/SubcomponentFactoryMethodValidator.java154
-rw-r--r--java/dagger/internal/codegen/SubcomponentNames.java182
-rw-r--r--java/dagger/internal/codegen/SwitchingProviders.java227
-rw-r--r--java/dagger/internal/codegen/SystemComponentsModule.java31
-rw-r--r--java/dagger/internal/codegen/TopLevel.java29
-rw-r--r--java/dagger/internal/codegen/TopLevelImplementationComponent.java41
-rw-r--r--java/dagger/internal/codegen/TypeCheckingProcessingStep.java67
-rw-r--r--java/dagger/internal/codegen/TypeProtoConverter.java122
-rw-r--r--java/dagger/internal/codegen/UniqueNameSet.java45
-rw-r--r--java/dagger/internal/codegen/UnwrappedMapKeyGenerator.java69
-rw-r--r--java/dagger/internal/codegen/Util.java102
-rw-r--r--java/dagger/internal/codegen/Validation.java31
-rw-r--r--java/dagger/internal/codegen/ValidationReport.java288
-rw-r--r--java/dagger/internal/codegen/ValidationType.java41
-rw-r--r--java/dagger/internal/codegen/base/BUILD66
-rw-r--r--java/dagger/internal/codegen/base/ClearableCache.java23
-rw-r--r--java/dagger/internal/codegen/base/ComponentAnnotation.java326
-rw-r--r--java/dagger/internal/codegen/base/ContributionType.java68
-rw-r--r--java/dagger/internal/codegen/base/DiagnosticFormatting.java72
-rw-r--r--java/dagger/internal/codegen/base/ElementFormatter.java107
-rw-r--r--java/dagger/internal/codegen/base/Formatter.java96
-rw-r--r--java/dagger/internal/codegen/base/FrameworkTypes.java66
-rw-r--r--java/dagger/internal/codegen/base/Keys.java91
-rw-r--r--java/dagger/internal/codegen/base/MapKeyAccessibility.java80
-rw-r--r--java/dagger/internal/codegen/base/MapType.java158
-rw-r--r--java/dagger/internal/codegen/base/ModuleAnnotation.java127
-rw-r--r--java/dagger/internal/codegen/base/MoreAnnotationMirrors.java73
-rw-r--r--java/dagger/internal/codegen/base/MoreAnnotationValues.java125
-rw-r--r--java/dagger/internal/codegen/base/MultibindingAnnotations.java36
-rw-r--r--java/dagger/internal/codegen/base/OptionalType.java159
-rw-r--r--java/dagger/internal/codegen/base/RequestKinds.java189
-rw-r--r--java/dagger/internal/codegen/base/Scopes.java80
-rw-r--r--java/dagger/internal/codegen/base/SetType.java120
-rw-r--r--java/dagger/internal/codegen/base/SimpleAnnotationMirror.java107
-rw-r--r--java/dagger/internal/codegen/base/SimpleTypeAnnotationValue.java45
-rw-r--r--java/dagger/internal/codegen/base/SourceFileGenerationException.java54
-rw-r--r--java/dagger/internal/codegen/base/SourceFileGenerator.java139
-rw-r--r--java/dagger/internal/codegen/base/UniqueNameSet.java45
-rw-r--r--java/dagger/internal/codegen/base/Util.java42
-rw-r--r--java/dagger/internal/codegen/binding/AnnotationExpression.java208
-rw-r--r--java/dagger/internal/codegen/binding/AssistedInjectionAnnotations.java299
-rw-r--r--java/dagger/internal/codegen/binding/BUILD47
-rw-r--r--java/dagger/internal/codegen/binding/Binding.java165
-rw-r--r--java/dagger/internal/codegen/binding/BindingDeclaration.java87
-rw-r--r--java/dagger/internal/codegen/binding/BindingDeclarationFormatter.java125
-rw-r--r--java/dagger/internal/codegen/binding/BindingFactory.java578
-rw-r--r--java/dagger/internal/codegen/binding/BindingGraph.java347
-rw-r--r--java/dagger/internal/codegen/binding/BindingGraphConverter.java397
-rw-r--r--java/dagger/internal/codegen/binding/BindingGraphFactory.java980
-rw-r--r--java/dagger/internal/codegen/binding/BindingNode.java138
-rw-r--r--java/dagger/internal/codegen/binding/BindingRequest.java96
-rw-r--r--java/dagger/internal/codegen/binding/BindingType.java31
-rw-r--r--java/dagger/internal/codegen/binding/BindsTypeChecker.java112
-rw-r--r--java/dagger/internal/codegen/binding/ChildFactoryMethodEdgeImpl.java42
-rw-r--r--java/dagger/internal/codegen/binding/ComponentCreatorAnnotation.java152
-rw-r--r--java/dagger/internal/codegen/binding/ComponentCreatorDescriptor.java223
-rw-r--r--java/dagger/internal/codegen/binding/ComponentCreatorKind.java42
-rw-r--r--java/dagger/internal/codegen/binding/ComponentDescriptor.java376
-rw-r--r--java/dagger/internal/codegen/binding/ComponentDescriptorFactory.java273
-rw-r--r--java/dagger/internal/codegen/binding/ComponentKind.java171
-rw-r--r--java/dagger/internal/codegen/binding/ComponentNodeImpl.java64
-rw-r--r--java/dagger/internal/codegen/binding/ComponentRequirement.java301
-rw-r--r--java/dagger/internal/codegen/binding/ConfigurationAnnotations.java157
-rw-r--r--java/dagger/internal/codegen/binding/ContributionBinding.java207
-rw-r--r--java/dagger/internal/codegen/binding/DelegateDeclaration.java92
-rw-r--r--java/dagger/internal/codegen/binding/DependencyEdgeImpl.java56
-rw-r--r--java/dagger/internal/codegen/binding/DependencyRequestFactory.java241
-rw-r--r--java/dagger/internal/codegen/binding/DependencyRequestFormatter.java151
-rw-r--r--java/dagger/internal/codegen/binding/DependencyVariableNamer.java82
-rw-r--r--java/dagger/internal/codegen/binding/ErrorMessages.java358
-rw-r--r--java/dagger/internal/codegen/binding/FrameworkField.java127
-rw-r--r--java/dagger/internal/codegen/binding/FrameworkType.java223
-rw-r--r--java/dagger/internal/codegen/binding/FrameworkTypeMapper.java70
-rw-r--r--java/dagger/internal/codegen/binding/InjectBindingRegistry.java70
-rw-r--r--java/dagger/internal/codegen/binding/InjectionAnnotations.java158
-rw-r--r--java/dagger/internal/codegen/binding/InjectionSiteFactory.java146
-rw-r--r--java/dagger/internal/codegen/binding/KeyFactory.java434
-rw-r--r--java/dagger/internal/codegen/binding/KeyVariableNamer.java107
-rw-r--r--java/dagger/internal/codegen/binding/LegacyBindingGraph.java81
-rw-r--r--java/dagger/internal/codegen/binding/MapKeys.java222
-rw-r--r--java/dagger/internal/codegen/binding/MembersInjectionBinding.java140
-rw-r--r--java/dagger/internal/codegen/binding/MethodSignatureFormatter.java140
-rw-r--r--java/dagger/internal/codegen/binding/ModuleDescriptor.java267
-rw-r--r--java/dagger/internal/codegen/binding/ModuleKind.java115
-rw-r--r--java/dagger/internal/codegen/binding/MultibindingDeclaration.java120
-rw-r--r--java/dagger/internal/codegen/binding/OptionalBindingDeclaration.java68
-rw-r--r--java/dagger/internal/codegen/binding/ProductionBinding.java151
-rw-r--r--java/dagger/internal/codegen/binding/ProvisionBinding.java137
-rw-r--r--java/dagger/internal/codegen/binding/ResolvedBindings.java172
-rw-r--r--java/dagger/internal/codegen/binding/SourceFiles.java311
-rw-r--r--java/dagger/internal/codegen/binding/SubcomponentCreatorBindingEdgeImpl.java55
-rw-r--r--java/dagger/internal/codegen/binding/SubcomponentDeclaration.java88
-rw-r--r--java/dagger/internal/codegen/bindinggraphvalidation/BUILD45
-rw-r--r--java/dagger/internal/codegen/bindinggraphvalidation/BindingGraphValidationModule.java63
-rw-r--r--java/dagger/internal/codegen/bindinggraphvalidation/DependencyCycleValidator.java329
-rw-r--r--java/dagger/internal/codegen/bindinggraphvalidation/DependsOnProductionExecutorValidator.java71
-rw-r--r--java/dagger/internal/codegen/bindinggraphvalidation/DuplicateBindingsValidator.java345
-rw-r--r--java/dagger/internal/codegen/bindinggraphvalidation/IncompatiblyScopedBindingsValidator.java145
-rw-r--r--java/dagger/internal/codegen/bindinggraphvalidation/InjectBindingValidator.java61
-rw-r--r--java/dagger/internal/codegen/bindinggraphvalidation/MapMultibindingValidator.java211
-rw-r--r--java/dagger/internal/codegen/bindinggraphvalidation/MissingBindingValidator.java132
-rw-r--r--java/dagger/internal/codegen/bindinggraphvalidation/NullableBindingValidator.java83
-rw-r--r--java/dagger/internal/codegen/bindinggraphvalidation/ProvisionDependencyOnProducerBindingValidator.java115
-rw-r--r--java/dagger/internal/codegen/bindinggraphvalidation/SubcomponentFactoryMethodValidator.java159
-rw-r--r--java/dagger/internal/codegen/bootstrap/BUILD33
-rw-r--r--java/dagger/internal/codegen/bootstrap/bootstrap_compiler_deploy.jarbin0 -> 7279147 bytes
-rw-r--r--java/dagger/internal/codegen/bootstrap_compiler_deploy.jarbin16279032 -> 0 bytes
-rw-r--r--java/dagger/internal/codegen/compileroption/BUILD38
-rw-r--r--java/dagger/internal/codegen/compileroption/CompilerOptions.java105
-rw-r--r--java/dagger/internal/codegen/compileroption/FeatureStatus.java23
-rw-r--r--java/dagger/internal/codegen/compileroption/JavacPluginCompilerOptions.java121
-rw-r--r--java/dagger/internal/codegen/compileroption/ProcessingEnvironmentCompilerOptions.java520
-rw-r--r--java/dagger/internal/codegen/compileroption/ProcessingOptions.java30
-rw-r--r--java/dagger/internal/codegen/compileroption/ValidationType.java41
-rw-r--r--java/dagger/internal/codegen/componentgenerator/BUILD43
-rw-r--r--java/dagger/internal/codegen/componentgenerator/ComponentCreatorImplementationFactory.java542
-rw-r--r--java/dagger/internal/codegen/componentgenerator/ComponentGenerator.java72
-rw-r--r--java/dagger/internal/codegen/componentgenerator/ComponentGeneratorModule.java48
-rw-r--r--java/dagger/internal/codegen/componentgenerator/ComponentHjarGenerator.java269
-rw-r--r--java/dagger/internal/codegen/componentgenerator/ComponentImplementationBuilder.java524
-rw-r--r--java/dagger/internal/codegen/componentgenerator/ComponentImplementationFactory.java90
-rw-r--r--java/dagger/internal/codegen/componentgenerator/CurrentImplementationSubcomponent.java62
-rw-r--r--java/dagger/internal/codegen/componentgenerator/MethodSignature.java56
-rw-r--r--java/dagger/internal/codegen/componentgenerator/TopLevelImplementationComponent.java42
-rw-r--r--java/dagger/internal/codegen/dagger_statistics.proto25
-rw-r--r--java/dagger/internal/codegen/extension/BUILD33
-rw-r--r--java/dagger/internal/codegen/extension/DaggerCollectors.java155
-rw-r--r--java/dagger/internal/codegen/extension/DaggerGraphs.java98
-rw-r--r--java/dagger/internal/codegen/extension/DaggerStreams.java135
-rw-r--r--java/dagger/internal/codegen/extension/Optionals.java68
-rw-r--r--java/dagger/internal/codegen/javac/BUILD44
-rw-r--r--java/dagger/internal/codegen/javac/JavacPluginModule.java92
-rw-r--r--java/dagger/internal/codegen/javapoet/AnnotationSpecs.java28
-rw-r--r--java/dagger/internal/codegen/javapoet/BUILD8
-rw-r--r--java/dagger/internal/codegen/javapoet/TypeNames.java5
-rw-r--r--java/dagger/internal/codegen/kotlin/BUILD41
-rw-r--r--java/dagger/internal/codegen/kotlin/KotlinMetadata.java472
-rw-r--r--java/dagger/internal/codegen/kotlin/KotlinMetadataFactory.java63
-rw-r--r--java/dagger/internal/codegen/kotlin/KotlinMetadataUtil.java157
-rw-r--r--java/dagger/internal/codegen/kythe/BUILD51
-rw-r--r--java/dagger/internal/codegen/kythe/DaggerKythePlugin.java197
-rw-r--r--java/dagger/internal/codegen/kythe/kythe_plugin_deploy.jar (renamed from java/dagger/internal/codegen/kythe_plugin_deploy.jar)bin7466334 -> 7466334 bytes
-rw-r--r--java/dagger/internal/codegen/langmodel/BUILD11
-rw-r--r--java/dagger/internal/codegen/langmodel/DaggerElements.java179
-rw-r--r--java/dagger/internal/codegen/langmodel/DaggerTypes.java4
-rw-r--r--java/dagger/internal/codegen/package-info.java1
-rw-r--r--java/dagger/internal/codegen/serialization/BUILD41
-rw-r--r--java/dagger/internal/codegen/serialization/ProtoSerialization.java81
-rw-r--r--java/dagger/internal/codegen/serialization/serialization.proto167
-rw-r--r--java/dagger/internal/codegen/validation/AnyBindingMethodValidator.java120
-rw-r--r--java/dagger/internal/codegen/validation/BUILD50
-rw-r--r--java/dagger/internal/codegen/validation/BindingElementValidator.java408
-rw-r--r--java/dagger/internal/codegen/validation/BindingGraphPlugins.java78
-rw-r--r--java/dagger/internal/codegen/validation/BindingGraphValidator.java107
-rw-r--r--java/dagger/internal/codegen/validation/BindingMethodProcessingStep.java61
-rw-r--r--java/dagger/internal/codegen/validation/BindingMethodValidator.java319
-rw-r--r--java/dagger/internal/codegen/validation/BindingMethodValidatorsModule.java60
-rw-r--r--java/dagger/internal/codegen/validation/BindsInstanceElementValidator.java43
-rw-r--r--java/dagger/internal/codegen/validation/BindsInstanceMethodValidator.java88
-rw-r--r--java/dagger/internal/codegen/validation/BindsInstanceParameterValidator.java77
-rw-r--r--java/dagger/internal/codegen/validation/BindsInstanceProcessingStep.java66
-rw-r--r--java/dagger/internal/codegen/validation/BindsMethodValidator.java125
-rw-r--r--java/dagger/internal/codegen/validation/BindsOptionalOfMethodValidator.java98
-rw-r--r--java/dagger/internal/codegen/validation/ComponentCreatorValidator.java408
-rw-r--r--java/dagger/internal/codegen/validation/ComponentDescriptorValidator.java493
-rw-r--r--java/dagger/internal/codegen/validation/ComponentHierarchyValidator.java273
-rw-r--r--java/dagger/internal/codegen/validation/ComponentValidator.java558
-rw-r--r--java/dagger/internal/codegen/validation/CompositeBindingGraphPlugin.java284
-rw-r--r--java/dagger/internal/codegen/validation/DependencyRequestValidator.java173
-rw-r--r--java/dagger/internal/codegen/validation/DiagnosticMessageGenerator.java389
-rw-r--r--java/dagger/internal/codegen/validation/DiagnosticReporterFactory.java191
-rw-r--r--java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java359
-rw-r--r--java/dagger/internal/codegen/validation/InjectBindingRegistryModule.java27
-rw-r--r--java/dagger/internal/codegen/validation/InjectValidator.java420
-rw-r--r--java/dagger/internal/codegen/validation/MapKeyValidator.java65
-rw-r--r--java/dagger/internal/codegen/validation/MembersInjectionValidator.java152
-rw-r--r--java/dagger/internal/codegen/validation/ModuleValidator.java712
-rw-r--r--java/dagger/internal/codegen/validation/MonitoringModuleGenerator.java102
-rw-r--r--java/dagger/internal/codegen/validation/MonitoringModuleProcessingStep.java55
-rw-r--r--java/dagger/internal/codegen/validation/MultibindingAnnotationsProcessingStep.java68
-rw-r--r--java/dagger/internal/codegen/validation/MultibindsMethodValidator.java111
-rw-r--r--java/dagger/internal/codegen/validation/PackageNameCompressor.java181
-rw-r--r--java/dagger/internal/codegen/validation/ProducesMethodValidator.java140
-rw-r--r--java/dagger/internal/codegen/validation/ProvidesMethodValidator.java84
-rw-r--r--java/dagger/internal/codegen/validation/TypeCheckingProcessingStep.java67
-rw-r--r--java/dagger/internal/codegen/validation/TypeHierarchyValidator.java56
-rw-r--r--java/dagger/internal/codegen/validation/Validation.java31
-rw-r--r--java/dagger/internal/codegen/validation/ValidationReport.java285
-rw-r--r--java/dagger/internal/codegen/writing/AnnotationCreatorGenerator.java169
-rw-r--r--java/dagger/internal/codegen/writing/AnonymousProviderCreationExpression.java60
-rw-r--r--java/dagger/internal/codegen/writing/AssistedFactoryBindingExpression.java111
-rw-r--r--java/dagger/internal/codegen/writing/BUILD47
-rw-r--r--java/dagger/internal/codegen/writing/BindingExpression.java63
-rw-r--r--java/dagger/internal/codegen/writing/ComponentBindingExpressions.java721
-rw-r--r--java/dagger/internal/codegen/writing/ComponentCreatorImplementation.java44
-rw-r--r--java/dagger/internal/codegen/writing/ComponentImplementation.java432
-rw-r--r--java/dagger/internal/codegen/writing/ComponentInstanceBindingExpression.java43
-rw-r--r--java/dagger/internal/codegen/writing/ComponentMethodBindingExpression.java92
-rw-r--r--java/dagger/internal/codegen/writing/ComponentProvisionBindingExpression.java73
-rw-r--r--java/dagger/internal/codegen/writing/ComponentRequirementBindingExpression.java48
-rw-r--r--java/dagger/internal/codegen/writing/ComponentRequirementExpression.java55
-rw-r--r--java/dagger/internal/codegen/writing/ComponentRequirementExpressions.java233
-rw-r--r--java/dagger/internal/codegen/writing/DelegateBindingExpression.java133
-rw-r--r--java/dagger/internal/codegen/writing/DelegatingFrameworkInstanceCreationExpression.java57
-rw-r--r--java/dagger/internal/codegen/writing/DependencyMethodProducerCreationExpression.java100
-rw-r--r--java/dagger/internal/codegen/writing/DependencyMethodProviderCreationExpression.java130
-rw-r--r--java/dagger/internal/codegen/writing/DerivedFromFrameworkInstanceBindingExpression.java69
-rw-r--r--java/dagger/internal/codegen/writing/FactoryGenerator.java294
-rw-r--r--java/dagger/internal/codegen/writing/FrameworkFieldInitializer.java210
-rw-r--r--java/dagger/internal/codegen/writing/FrameworkInstanceBindingExpression.java89
-rw-r--r--java/dagger/internal/codegen/writing/FrameworkInstanceSupplier.java23
-rw-r--r--java/dagger/internal/codegen/writing/GwtCompatibility.java57
-rw-r--r--java/dagger/internal/codegen/writing/HjarSourceFileGenerator.java124
-rw-r--r--java/dagger/internal/codegen/writing/ImmediateFutureBindingExpression.java73
-rw-r--r--java/dagger/internal/codegen/writing/InaccessibleMapKeyProxyGenerator.java76
-rw-r--r--java/dagger/internal/codegen/writing/InjectionMethods.java571
-rw-r--r--java/dagger/internal/codegen/writing/InjectionOrProvisionProviderCreationExpression.java64
-rw-r--r--java/dagger/internal/codegen/writing/InnerSwitchingProviders.java107
-rw-r--r--java/dagger/internal/codegen/writing/InstanceFactoryCreationExpression.java57
-rw-r--r--java/dagger/internal/codegen/writing/MapBindingExpression.java170
-rw-r--r--java/dagger/internal/codegen/writing/MapFactoryCreationExpression.java87
-rw-r--r--java/dagger/internal/codegen/writing/MemberSelect.java263
-rw-r--r--java/dagger/internal/codegen/writing/MembersInjectionBindingExpression.java72
-rw-r--r--java/dagger/internal/codegen/writing/MembersInjectionMethods.java135
-rw-r--r--java/dagger/internal/codegen/writing/MembersInjectorGenerator.java227
-rw-r--r--java/dagger/internal/codegen/writing/MembersInjectorProviderCreationExpression.java93
-rw-r--r--java/dagger/internal/codegen/writing/MethodBindingExpression.java294
-rw-r--r--java/dagger/internal/codegen/writing/ModuleGenerator.java28
-rw-r--r--java/dagger/internal/codegen/writing/ModuleProxies.java154
-rw-r--r--java/dagger/internal/codegen/writing/MultibindingFactoryCreationExpression.java75
-rw-r--r--java/dagger/internal/codegen/writing/OptionalBindingExpression.java94
-rw-r--r--java/dagger/internal/codegen/writing/OptionalFactories.java443
-rw-r--r--java/dagger/internal/codegen/writing/OptionalFactoryInstanceCreationExpression.java68
-rw-r--r--java/dagger/internal/codegen/writing/ParentComponent.java30
-rw-r--r--java/dagger/internal/codegen/writing/PerComponentImplementation.java27
-rw-r--r--java/dagger/internal/codegen/writing/PerGeneratedFile.java30
-rw-r--r--java/dagger/internal/codegen/writing/PrivateMethodBindingExpression.java96
-rw-r--r--java/dagger/internal/codegen/writing/ProducerCreationExpression.java49
-rw-r--r--java/dagger/internal/codegen/writing/ProducerEntryPointView.java110
-rw-r--r--java/dagger/internal/codegen/writing/ProducerFactoryGenerator.java567
-rw-r--r--java/dagger/internal/codegen/writing/ProducerFromProviderCreationExpression.java64
-rw-r--r--java/dagger/internal/codegen/writing/ProducerNodeInstanceBindingExpression.java69
-rw-r--r--java/dagger/internal/codegen/writing/ProviderInstanceBindingExpression.java43
-rw-r--r--java/dagger/internal/codegen/writing/SetBindingExpression.java176
-rw-r--r--java/dagger/internal/codegen/writing/SetFactoryCreationExpression.java92
-rw-r--r--java/dagger/internal/codegen/writing/SimpleInvocationBindingExpression.java35
-rw-r--r--java/dagger/internal/codegen/writing/SimpleMethodBindingExpression.java186
-rw-r--r--java/dagger/internal/codegen/writing/SubcomponentCreatorBindingExpression.java40
-rw-r--r--java/dagger/internal/codegen/writing/SubcomponentNames.java156
-rw-r--r--java/dagger/internal/codegen/writing/SwitchingProviders.java228
-rw-r--r--java/dagger/internal/codegen/writing/TopLevel.java29
-rw-r--r--java/dagger/internal/codegen/writing/UnwrappedMapKeyGenerator.java69
-rw-r--r--java/dagger/internal/guava/BUILD68
-rw-r--r--java/dagger/lint/AndroidManifest.xml19
-rw-r--r--java/dagger/lint/BUILD85
-rw-r--r--java/dagger/lint/DaggerIssueRegistry.kt40
-rw-r--r--java/dagger/lint/DaggerKotlinIssueDetector.kt262
-rw-r--r--java/dagger/model/BUILD26
-rw-r--r--java/dagger/model/BindingGraph.java84
-rw-r--r--java/dagger/model/BindingGraphProxies.java29
-rw-r--r--java/dagger/model/BindingKind.java9
-rw-r--r--java/dagger/model/testing/BUILD12
-rw-r--r--java/dagger/model/testing/BindingGraphSubject.java6
-rw-r--r--java/dagger/producers/BUILD45
-rw-r--r--java/dagger/producers/internal/MissingBindingProducer.java46
-rw-r--r--java/dagger/producers/monitoring/TimingProducerMonitor.java1
-rw-r--r--java/dagger/producers/monitoring/TimingRecorders.java1
-rw-r--r--java/dagger/spi/BUILD49
-rw-r--r--java/dagger/testing/compile/BUILD32
-rw-r--r--java/dagger/testing/compile/CompilerTests.java79
-rw-r--r--java/dagger/testing/compile/macros.bzl89
-rw-r--r--javatests/artifacts/BUILD18
-rw-r--r--javatests/artifacts/dagger-android/simple/app/build.gradle75
-rw-r--r--javatests/artifacts/dagger-android/simple/app/src/main/AndroidManifest.xml30
-rw-r--r--javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/Model.java29
-rw-r--r--javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/ModelModule.java31
-rw-r--r--javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/SimpleActivity.java73
-rw-r--r--javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/SimpleApplication.java59
-rw-r--r--javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/UserName.java29
-rw-r--r--javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/UserNameModule.java31
-rw-r--r--javatests/artifacts/dagger-android/simple/app/src/main/res/layout/activity_main.xml30
-rw-r--r--javatests/artifacts/dagger-android/simple/app/src/main/res/values/strings.xml24
-rw-r--r--javatests/artifacts/dagger-android/simple/app/src/sharedTest/java/dagger/android/simple/SimpleActivityTest.java57
-rw-r--r--javatests/artifacts/dagger-android/simple/build.gradle38
-rw-r--r--javatests/artifacts/dagger-android/simple/gradle.properties1
-rw-r--r--javatests/artifacts/dagger-android/simple/gradle/wrapper/gradle-wrapper.jarbin0 -> 55616 bytes
-rw-r--r--javatests/artifacts/dagger-android/simple/gradle/wrapper/gradle-wrapper.properties5
-rwxr-xr-xjavatests/artifacts/dagger-android/simple/gradlew188
-rw-r--r--javatests/artifacts/dagger-android/simple/settings.gradle2
-rw-r--r--javatests/artifacts/dagger/simple/build.gradle37
-rw-r--r--javatests/artifacts/dagger/simple/gradle/wrapper/gradle-wrapper.jarbin0 -> 55616 bytes
-rw-r--r--javatests/artifacts/dagger/simple/gradle/wrapper/gradle-wrapper.properties5
-rwxr-xr-xjavatests/artifacts/dagger/simple/gradlew188
-rw-r--r--javatests/artifacts/dagger/simple/src/main/java/dagger/simple/AssistedInjects.java66
-rw-r--r--javatests/artifacts/dagger/simple/src/main/java/dagger/simple/SimpleApplication.java48
-rw-r--r--javatests/artifacts/hilt-android/gradleConfigCache/app/build.gradle69
-rw-r--r--javatests/artifacts/hilt-android/gradleConfigCache/app/src/androidTest/java/dagger/hilt/android/gradleConfigCache/EmulatorTest.kt43
-rw-r--r--javatests/artifacts/hilt-android/gradleConfigCache/app/src/androidTest/java/dagger/hilt/android/gradleConfigCache/TestRunner.kt28
-rw-r--r--javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/AndroidManifest.xml34
-rw-r--r--javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/java/dagger/hilt/android/gradleConfigCache/App.kt23
-rw-r--r--javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/java/dagger/hilt/android/gradleConfigCache/Foo.kt5
-rw-r--r--javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/java/dagger/hilt/android/gradleConfigCache/MainActivity.kt36
-rw-r--r--javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/res/layout/activity_main.xml22
-rw-r--r--javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/res/values/strings.xml20
-rw-r--r--javatests/artifacts/hilt-android/gradleConfigCache/app/src/test/java/dagger/hilt/android/gradleConfigCache/LocalTest.kt46
-rw-r--r--javatests/artifacts/hilt-android/gradleConfigCache/build.gradle41
-rw-r--r--javatests/artifacts/hilt-android/gradleConfigCache/gradle.properties8
-rw-r--r--javatests/artifacts/hilt-android/gradleConfigCache/gradle/wrapper/gradle-wrapper.jarbin0 -> 58910 bytes
-rw-r--r--javatests/artifacts/hilt-android/gradleConfigCache/gradle/wrapper/gradle-wrapper.properties5
-rwxr-xr-xjavatests/artifacts/hilt-android/gradleConfigCache/gradlew185
-rw-r--r--javatests/artifacts/hilt-android/gradleConfigCache/gradlew.bat104
-rw-r--r--javatests/artifacts/hilt-android/gradleConfigCache/settings.gradle2
-rw-r--r--javatests/artifacts/hilt-android/simple/app/build.gradle105
-rw-r--r--javatests/artifacts/hilt-android/simple/app/src/androidTest/java/dagger/hilt/android/simple/SimpleEmulatorTestRunner.java32
-rw-r--r--javatests/artifacts/hilt-android/simple/app/src/debug/AndroidManifest.xml33
-rw-r--r--javatests/artifacts/hilt-android/simple/app/src/main/AndroidManifest.xml35
-rw-r--r--javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/Model.java29
-rw-r--r--javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/ModelModule.java36
-rw-r--r--javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/SimpleActivity.java53
-rw-r--r--javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/SimpleApplication.java39
-rw-r--r--javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/UserName.java29
-rw-r--r--javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/UserNameModule.java34
-rw-r--r--javatests/artifacts/hilt-android/simple/app/src/main/res/layout/activity_main.xml38
-rw-r--r--javatests/artifacts/hilt-android/simple/app/src/main/res/values/strings.xml27
-rw-r--r--javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/ActivityScenarioRuleTest.java80
-rw-r--r--javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/BindValueTest.java155
-rw-r--r--javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/DelayComponentReadyTest.java116
-rw-r--r--javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/Injection1Test.java100
-rw-r--r--javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/Injection2Test.java90
-rw-r--r--javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/ModuleTest.java157
-rw-r--r--javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/SimpleActivityTest.java105
-rw-r--r--javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/TransitiveDependenciesTest.java43
-rw-r--r--javatests/artifacts/hilt-android/simple/app/src/test/resources/dagger/hilt/android/simple/robolectric.properties2
-rw-r--r--javatests/artifacts/hilt-android/simple/build.gradle42
-rw-r--r--javatests/artifacts/hilt-android/simple/deep-android-lib/build.gradle26
-rw-r--r--javatests/artifacts/hilt-android/simple/deep-android-lib/src/main/AndroidManifest.xml21
-rw-r--r--javatests/artifacts/hilt-android/simple/deep-android-lib/src/main/java/dagger/hilt/android/simple/deep/DeepAndroidLib.java41
-rw-r--r--javatests/artifacts/hilt-android/simple/deep-lib/build.gradle13
-rw-r--r--javatests/artifacts/hilt-android/simple/deep-lib/src/main/java/dagger/hilt/android/deep/DeepLib.java39
-rw-r--r--javatests/artifacts/hilt-android/simple/feature/build.gradle57
-rw-r--r--javatests/artifacts/hilt-android/simple/feature/src/main/AndroidManifest.xml26
-rw-r--r--javatests/artifacts/hilt-android/simple/feature/src/main/java/dagger/hilt/android/simple/feature/FeatureActivity.kt46
-rw-r--r--javatests/artifacts/hilt-android/simple/feature/src/main/java/dagger/hilt/android/simple/feature/FeatureCounter.kt19
-rw-r--r--javatests/artifacts/hilt-android/simple/feature/src/main/java/dagger/hilt/android/simple/feature/FeatureEntryPoints.java31
-rw-r--r--javatests/artifacts/hilt-android/simple/feature/src/main/java/dagger/hilt/android/simple/feature/FeatureModule.kt31
-rw-r--r--javatests/artifacts/hilt-android/simple/feature/src/main/res/layout/activity_feature.xml38
-rw-r--r--javatests/artifacts/hilt-android/simple/gradle.properties2
-rw-r--r--javatests/artifacts/hilt-android/simple/gradle/wrapper/gradle-wrapper.jarbin0 -> 55616 bytes
-rw-r--r--javatests/artifacts/hilt-android/simple/gradle/wrapper/gradle-wrapper.properties5
-rwxr-xr-xjavatests/artifacts/hilt-android/simple/gradlew188
-rw-r--r--javatests/artifacts/hilt-android/simple/lib/build.gradle29
-rw-r--r--javatests/artifacts/hilt-android/simple/lib/src/main/java/dagger/hilt/android/simple/lib/Thing.java20
-rw-r--r--javatests/artifacts/hilt-android/simple/lib/src/main/java/dagger/hilt/android/simple/lib/ThingImpl.java35
-rw-r--r--javatests/artifacts/hilt-android/simple/settings.gradle6
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/android-library/build.gradle31
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/android-library/src/main/AndroidManifest.xml5
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/android-library/src/main/java/dagger/hilt/android/simpleKotlin/lib/AndroidLibraryEntryPoints.kt12
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/build.gradle96
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/SimpleEmulatorTestRunner.kt28
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/TestRunner.kt28
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/ActivityInjectionTest.kt59
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/BaseActivityInjectionTest.kt57
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/BaseFragmentInjectionTest.kt68
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/Bindings.kt36
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/FragmentInjectionTest.kt129
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/MyViewModels.kt49
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/ViewModelScopedTest.kt106
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/debug/AndroidManifest.xml29
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/main/AndroidManifest.xml35
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/ActivityRetainedModule.kt31
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/ApplicationModule.kt33
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/DefinedStrings.kt21
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/KotlinApplication.kt30
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/MainActivity.kt48
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/Model.kt24
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/UserName.kt24
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/main/res/layout/activity_main.xml30
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/main/res/values/strings.xml23
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/sharedTest/java/dagger/hilt/android/simpleKotlin/InternalAccessTest.kt33
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/sharedTest/java/dagger/hilt/android/simpleKotlin/TransitiveDependenciesTest.kt51
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/test/java/dagger/hilt/android/simpleKotlin/SimpleTest.kt83
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/app/src/test/resources/dagger/hilt/android/simpleKotlin/robolectric.properties2
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/build.gradle41
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/deep-android-lib/build.gradle46
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/deep-android-lib/src/androidTest/java/dagger/hilt/android/simpleKotlin/deep/InternalAccessEmulatorTest.kt32
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/deep-android-lib/src/main/AndroidManifest.xml5
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/deep-android-lib/src/main/java/dagger/hilt/android/simpleKotlin/deep/DeepAndroidLib.kt40
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/deep-android-lib/src/test/java/dagger/hilt/android/simpleKotlin/deep/InternalAccessLocalTest.kt29
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/deep-kotlin-lib/build.gradle16
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/deep-kotlin-lib/src/main/java/dagger/hilt/android/simpleKotlin/deep/DeepLib.kt36
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/gradle.properties2
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/gradle/wrapper/gradle-wrapper.jarbin0 -> 55616 bytes
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/gradle/wrapper/gradle-wrapper.properties5
-rwxr-xr-xjavatests/artifacts/hilt-android/simpleKotlin/gradlew188
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/kotlin-library/build.gradle17
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/kotlin-library/src/main/java/dagger/hilt/android/simpleKotlin/lib/KotlinLibraryEntryPoints.kt9
-rw-r--r--javatests/artifacts/hilt-android/simpleKotlin/settings.gradle6
-rw-r--r--javatests/dagger/BUILD7
-rw-r--r--javatests/dagger/android/AndroidInjectionTest.java45
-rw-r--r--javatests/dagger/android/AndroidProguardTest.java57
-rw-r--r--javatests/dagger/android/BUILD6
-rw-r--r--javatests/dagger/android/DispatchingAndroidInjectorTest.java2
-rw-r--r--javatests/dagger/android/processor/AndroidProcessorTest.java81
-rw-r--r--javatests/dagger/android/processor/BUILD19
-rw-r--r--javatests/dagger/android/support/AndroidSupportInjectionTest.java12
-rw-r--r--javatests/dagger/android/support/BUILD12
-rw-r--r--javatests/dagger/android/support/functional/AndroidManifest.xml3
-rw-r--r--javatests/dagger/android/support/functional/BUILD17
-rw-r--r--javatests/dagger/android/support/functional/InjectorsTest.java4
-rw-r--r--javatests/dagger/android/support/functional/TestContentProvider.java2
-rw-r--r--javatests/dagger/functional/BUILD18
-rw-r--r--javatests/dagger/functional/BoundedGenerics.java1
-rw-r--r--javatests/dagger/functional/ComponentDependenciesTest.java92
-rw-r--r--javatests/dagger/functional/Generic2.java1
-rw-r--r--javatests/dagger/functional/GenericNoDeps.java1
-rw-r--r--javatests/dagger/functional/MultibindingTest.java2
-rw-r--r--javatests/dagger/functional/NeedsFactory.java14
-rw-r--r--javatests/dagger/functional/NeedsProviderOfFactory.java11
-rw-r--r--javatests/dagger/functional/aot/DependsOnMissingArrayKey.java40
-rw-r--r--javatests/dagger/functional/aot/MapFrameworkInstanceWithContributionsInMultipleImplementationsTest.java94
-rw-r--r--javatests/dagger/functional/aot/MissingBindingReplacedWithGeneratedInstance.java56
-rw-r--r--javatests/dagger/functional/aot/ModifiedFrameworkInstancesTest.java78
-rw-r--r--javatests/dagger/functional/aot/PrunedBindingDependedOnInSuperInitializationTest.java63
-rw-r--r--javatests/dagger/functional/aot/PrunedFrameworkInstanceWithModuleInstanceTest.java83
-rw-r--r--javatests/dagger/functional/aot/ScopedBindsWithMissingDependency.java55
-rw-r--r--javatests/dagger/functional/aot/SubcomponentWithInaccessibleMissingBindingMethod.java27
-rw-r--r--javatests/dagger/functional/aot/SubcomponentWithModifiedInaccessibleDependency.java32
-rw-r--r--javatests/dagger/functional/aot/sub/BindsPackagePrivateModule.java28
-rw-r--r--javatests/dagger/functional/aot/sub/PackagePrivate.java19
-rw-r--r--javatests/dagger/functional/aot/sub/PublicTypeWithPackagePrivateMissingDep.java24
-rw-r--r--javatests/dagger/functional/aot/sub/PublicTypeWithPackagePrivateOptionalDep.java25
-rw-r--r--javatests/dagger/functional/aot/sub/SubcomponentWithInaccessibleOptionalBindingMethod.java43
-rw-r--r--javatests/dagger/functional/assisted/AssistedFactoryBindsTest.java89
-rw-r--r--javatests/dagger/functional/assisted/AssistedFactoryInaccessibleTest.java69
-rw-r--r--javatests/dagger/functional/assisted/AssistedFactoryParameterizedTest.java265
-rw-r--r--javatests/dagger/functional/assisted/AssistedFactoryTest.java332
-rw-r--r--javatests/dagger/functional/assisted/AssistedFactoryWithArrayTypesTest.java61
-rw-r--r--javatests/dagger/functional/assisted/AssistedFactoryWithAssistedInjectParamTest.java74
-rw-r--r--javatests/dagger/functional/assisted/AssistedFactoryWithQualifiedTypesTest.java151
-rw-r--r--javatests/dagger/functional/assisted/BUILD45
-rw-r--r--javatests/dagger/functional/assisted/kotlin/BUILD49
-rw-r--r--javatests/dagger/functional/assisted/kotlin/KotlinAssistedInjectionClasses.kt44
-rw-r--r--javatests/dagger/functional/assisted/kotlin/KotlinAssistedInjectionTest.java52
-rw-r--r--javatests/dagger/functional/assisted/subpackage/AccessibleFoo.java33
-rw-r--r--javatests/dagger/functional/assisted/subpackage/AssistedDep.java20
-rw-r--r--javatests/dagger/functional/assisted/subpackage/BUILD27
-rw-r--r--javatests/dagger/functional/assisted/subpackage/Dep.java25
-rw-r--r--javatests/dagger/functional/assisted/subpackage/InaccessibleFoo.java33
-rw-r--r--javatests/dagger/functional/assisted/subpackage/InaccessibleFooFactory.java25
-rw-r--r--javatests/dagger/functional/binds/SimpleBindingModule.java9
-rw-r--r--javatests/dagger/functional/builder/BuildMethodCovariantReturnInherited.java1
-rw-r--r--javatests/dagger/functional/builder/BuilderBindsInstanceParameterTest.java1
-rw-r--r--javatests/dagger/functional/builder/GenericParent.java1
-rw-r--r--javatests/dagger/functional/cycle/Cycles.java10
-rw-r--r--javatests/dagger/functional/cycle/DoubleCheckCycleTest.java1
-rw-r--r--javatests/dagger/functional/guava/BUILD7
-rw-r--r--javatests/dagger/functional/jdk8/BUILD9
-rw-r--r--javatests/dagger/functional/kotlin/BUILD79
-rw-r--r--javatests/dagger/functional/kotlin/CompanionModuleTest.java43
-rw-r--r--javatests/dagger/functional/kotlin/FooWithInjectedQualifier.kt33
-rw-r--r--javatests/dagger/functional/kotlin/JavaTestQualifier.java26
-rw-r--r--javatests/dagger/functional/kotlin/JavaTestQualifierWithTarget.java29
-rw-r--r--javatests/dagger/functional/kotlin/ObjectModuleTest.java39
-rw-r--r--javatests/dagger/functional/kotlin/PropertyQualifierTest.java50
-rw-r--r--javatests/dagger/functional/kotlin/PublicModuleWithNonPublicInclude.java23
-rw-r--r--javatests/dagger/functional/kotlin/TestComponentWithCompanionModule.kt97
-rw-r--r--javatests/dagger/functional/kotlin/TestComponentWithObjectModule.kt70
-rw-r--r--javatests/dagger/functional/kotlin/TestComponentWithQualifier.kt87
-rw-r--r--javatests/dagger/functional/kotlin/TestKotlinClasses.kt30
-rw-r--r--javatests/dagger/functional/kotlin/processor/BUILD37
-rw-r--r--javatests/dagger/functional/kotlin/processor/TestGeneratedTypeProcessor.kt48
-rw-r--r--javatests/dagger/functional/kotlin/processor/TriggerGeneratedTypeProcessor.kt4
-rw-r--r--javatests/dagger/functional/membersinject/MembersInjectGenericParent.java1
-rw-r--r--javatests/dagger/functional/membersinject/MembersInjectModule.java2
-rw-r--r--javatests/dagger/functional/membersinject/MembersInjectTest.java34
-rw-r--r--javatests/dagger/functional/membersinject/MembersWithInstanceNameTest.java59
-rw-r--r--javatests/dagger/functional/modules/ModuleIncludesTest.java3
-rw-r--r--javatests/dagger/functional/modules/subpackage/FooForProvision.java20
-rw-r--r--javatests/dagger/functional/modules/subpackage/NonAbstractPackagePrivateModule.java36
-rw-r--r--javatests/dagger/functional/modules/subpackage/PublicModule.java2
-rw-r--r--javatests/dagger/functional/multibindings/ComplexMapKeysInDifferentOrderTest.java3
-rw-r--r--javatests/dagger/functional/multibindings/MapKeyWithDefaultTest.java3
-rw-r--r--javatests/dagger/functional/producers/BUILD12
-rw-r--r--javatests/dagger/functional/producers/ComponentDependenciesTest.java96
-rw-r--r--javatests/dagger/functional/producers/ProducerFactoryTest.java2
-rw-r--r--javatests/dagger/functional/producers/monitoring/MonitoringTest.java2
-rw-r--r--javatests/dagger/functional/producers/multibindings/MultibindingComponent.java1
-rw-r--r--javatests/dagger/functional/spi/BUILD9
-rw-r--r--javatests/dagger/functional/subcomponent/GenericParentComponent.java1
-rw-r--r--javatests/dagger/functional/subcomponent/SubcomponentTest.java1
-rw-r--r--javatests/dagger/functional/subcomponent/hiding/ChildComponent.java1
-rw-r--r--javatests/dagger/functional/tck/BUILD4
-rw-r--r--javatests/dagger/grpc/functional/server/BUILD5
-rw-r--r--javatests/dagger/hilt/android/processor/AndroidCompilers.java79
-rw-r--r--javatests/dagger/hilt/android/processor/BUILD38
-rw-r--r--javatests/dagger/hilt/android/processor/internal/aggregateddeps/BUILD51
-rw-r--r--javatests/dagger/hilt/android/processor/internal/aggregateddeps/InstallInModule.java30
-rw-r--r--javatests/dagger/hilt/android/processor/internal/aggregateddeps/TestInstallInTest.java365
-rw-r--r--javatests/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGeneratorTest.java76
-rw-r--r--javatests/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointProcessorTest.java169
-rw-r--r--javatests/dagger/hilt/android/processor/internal/androidentrypoint/BUILD67
-rw-r--r--javatests/dagger/hilt/android/processor/internal/androidentrypoint/KotlinAndroidEntryPointProcessorTest.java66
-rw-r--r--javatests/dagger/hilt/android/processor/internal/viewmodel/BUILD112
-rw-r--r--javatests/dagger/hilt/android/processor/internal/viewmodel/TestUtils.kt15
-rw-r--r--javatests/dagger/hilt/android/processor/internal/viewmodel/ViewModelGeneratorTest.kt527
-rw-r--r--javatests/dagger/hilt/android/processor/internal/viewmodel/ViewModelProcessorTest.kt184
-rw-r--r--javatests/dagger/hilt/android/processor/internal/viewmodel/ViewModelValidationPluginTest.kt352
-rw-r--r--javatests/dagger/hilt/processor/internal/BUILD40
-rw-r--r--javatests/dagger/hilt/processor/internal/GeneratedImport.java36
-rw-r--r--javatests/dagger/hilt/processor/internal/ProcessorsTest.java85
-rw-r--r--javatests/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsProcessorErrorsTest.java286
-rw-r--r--javatests/dagger/hilt/processor/internal/aggregateddeps/BUILD49
-rw-r--r--javatests/dagger/hilt/processor/internal/definecomponent/BUILD46
-rw-r--r--javatests/dagger/hilt/processor/internal/definecomponent/DefineComponentProcessorTest.java401
-rw-r--r--javatests/dagger/hilt/processor/internal/disableinstallincheck/BUILD43
-rw-r--r--javatests/dagger/hilt/processor/internal/disableinstallincheck/DisableInstallInCheckProcessorErrorsTest.java69
-rw-r--r--javatests/dagger/hilt/processor/internal/generatesrootinput/BUILD45
-rw-r--r--javatests/dagger/hilt/processor/internal/generatesrootinput/GeneratesRootInputProcessorTest.java99
-rw-r--r--javatests/dagger/internal/MapProviderFactoryTest.java2
-rw-r--r--javatests/dagger/internal/codegen/AheadOfTimeSubcomponentsMultibindingsTest.java2838
-rw-r--r--javatests/dagger/internal/codegen/AheadOfTimeSubcomponentsTest.java5677
-rw-r--r--javatests/dagger/internal/codegen/AnnotationProtoConverterTest.java97
-rw-r--r--javatests/dagger/internal/codegen/AssistedErrorsTest.java104
-rw-r--r--javatests/dagger/internal/codegen/AssistedFactoryErrorsTest.java839
-rw-r--r--javatests/dagger/internal/codegen/AssistedFactoryTest.java247
-rw-r--r--javatests/dagger/internal/codegen/AssistedInjectErrorsTest.java234
-rw-r--r--javatests/dagger/internal/codegen/BUILD70
-rw-r--r--javatests/dagger/internal/codegen/BindsDependsOnSubcomponentValidationTest.java340
-rw-r--r--javatests/dagger/internal/codegen/BindsMethodValidationTest.java6
-rw-r--r--javatests/dagger/internal/codegen/BindsMissingDelegateValidationTest.java2
-rw-r--r--javatests/dagger/internal/codegen/BindsOptionalOfMethodValidationTest.java2
-rw-r--r--javatests/dagger/internal/codegen/CompilerMode.java3
-rw-r--r--javatests/dagger/internal/codegen/Compilers.java48
-rw-r--r--javatests/dagger/internal/codegen/ComponentBuilderTest.java29
-rw-r--r--javatests/dagger/internal/codegen/ComponentCreatorTest.java73
-rw-r--r--javatests/dagger/internal/codegen/ComponentCreatorTestHelper.java11
-rw-r--r--javatests/dagger/internal/codegen/ComponentDependenciesTest.java136
-rw-r--r--javatests/dagger/internal/codegen/ComponentFactoryTest.java17
-rw-r--r--javatests/dagger/internal/codegen/ComponentHierarchyValidationTest.java4
-rw-r--r--javatests/dagger/internal/codegen/ComponentProcessorTest.java196
-rw-r--r--javatests/dagger/internal/codegen/ComponentRequirementFieldTest.java39
-rw-r--r--javatests/dagger/internal/codegen/ComponentShardTest.java281
-rw-r--r--javatests/dagger/internal/codegen/ComponentValidationTest.java19
-rw-r--r--javatests/dagger/internal/codegen/DaggerModuleMethodSubject.java2
-rw-r--r--javatests/dagger/internal/codegen/DelegateBindingExpressionTest.java93
-rw-r--r--javatests/dagger/internal/codegen/DependencyCycleValidationTest.java168
-rw-r--r--javatests/dagger/internal/codegen/DiagnosticFormattingTest.java1
-rw-r--r--javatests/dagger/internal/codegen/DuplicateBindingsValidationTest.java168
-rw-r--r--javatests/dagger/internal/codegen/ElementDescriptorsTest.java239
-rw-r--r--javatests/dagger/internal/codegen/ElidedFactoriesTest.java39
-rw-r--r--javatests/dagger/internal/codegen/FrameworkFieldTest.java1
-rw-r--r--javatests/dagger/internal/codegen/FrameworkTypeMapperTest.java2
-rw-r--r--javatests/dagger/internal/codegen/FullBindingGraphValidationTest.java152
-rw-r--r--javatests/dagger/internal/codegen/GeneratedLines.java16
-rw-r--r--javatests/dagger/internal/codegen/InjectConstructorFactoryGeneratorTest.java251
-rw-r--r--javatests/dagger/internal/codegen/KeyFactoryTest.java42
-rw-r--r--javatests/dagger/internal/codegen/KotlinInjectedQualifier.kt26
-rw-r--r--javatests/dagger/internal/codegen/KotlinObjectWithMemberInjection.kt57
-rw-r--r--javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java86
-rw-r--r--javatests/dagger/internal/codegen/MapBindingExpressionTest.java30
-rw-r--r--javatests/dagger/internal/codegen/MapBindingExpressionWithGuavaTest.java53
-rw-r--r--javatests/dagger/internal/codegen/MapKeyProcessorTest.java6
-rw-r--r--javatests/dagger/internal/codegen/MapMultibindingValidationTest.java90
-rw-r--r--javatests/dagger/internal/codegen/MembersInjectionTest.java1012
-rw-r--r--javatests/dagger/internal/codegen/MembersInjectionValidationTest.java173
-rw-r--r--javatests/dagger/internal/codegen/MethodSignatureFormatterTest.java46
-rw-r--r--javatests/dagger/internal/codegen/MissingBindingSuggestionsTest.java163
-rw-r--r--javatests/dagger/internal/codegen/MissingBindingValidationTest.java305
-rw-r--r--javatests/dagger/internal/codegen/ModuleFactoryGeneratorTest.java110
-rw-r--r--javatests/dagger/internal/codegen/ModuleValidationTest.java10
-rw-r--r--javatests/dagger/internal/codegen/MultibindingTest.java4
-rw-r--r--javatests/dagger/internal/codegen/NullableBindingValidationTest.java419
-rw-r--r--javatests/dagger/internal/codegen/OptionalBindingRequestFulfillmentTest.java11
-rw-r--r--javatests/dagger/internal/codegen/OptionalBindingTest.java2
-rw-r--r--javatests/dagger/internal/codegen/PluginsVisitFullBindingGraphTest.java143
-rw-r--r--javatests/dagger/internal/codegen/ProducerModuleFactoryGeneratorTest.java5
-rw-r--r--javatests/dagger/internal/codegen/ProductionComponentProcessorTest.java53
-rw-r--r--javatests/dagger/internal/codegen/ProductionGraphValidationTest.java24
-rw-r--r--javatests/dagger/internal/codegen/ScopingValidationTest.java263
-rw-r--r--javatests/dagger/internal/codegen/SetBindingRequestFulfillmentTest.java18
-rw-r--r--javatests/dagger/internal/codegen/SetBindingRequestFulfillmentWithGuavaTest.java31
-rw-r--r--javatests/dagger/internal/codegen/SourceFilesTest.java3
-rw-r--r--javatests/dagger/internal/codegen/SubcomponentBuilderValidationTest.java5
-rw-r--r--javatests/dagger/internal/codegen/SubcomponentCreatorRequestFulfillmentTest.java9
-rw-r--r--javatests/dagger/internal/codegen/SubcomponentCreatorValidationTest.java28
-rw-r--r--javatests/dagger/internal/codegen/SubcomponentValidationTest.java110
-rw-r--r--javatests/dagger/internal/codegen/SwitchingProviderTest.java24
-rw-r--r--javatests/dagger/internal/codegen/TypeProtoConverterTest.java112
-rw-r--r--javatests/dagger/internal/codegen/ValidationReportTest.java3
-rw-r--r--javatests/dagger/internal/codegen/javapoet/BUILD6
-rw-r--r--javatests/dagger/lint/BUILD33
-rw-r--r--javatests/dagger/lint/DaggerKotlinIssueDetectorTest.kt246
-rw-r--r--javatests/dagger/producers/BUILD7
-rw-r--r--javatests/dagger/producers/internal/AbstractProducesMethodProducerTest.java5
-rw-r--r--javatests/dagger/producers/monitoring/TimingProductionComponentMonitorTest.java2
-rw-r--r--javatests/dagger/producers/monitoring/TimingRecordersTest.java29
-rw-r--r--javatests/dagger/producers/monitoring/internal/MonitorsTest.java27
-rw-r--r--javatests/dagger/spi/BUILD8
-rw-r--r--javatests/dagger/spi/FailingPlugin.java1
-rw-r--r--javatests/dagger/spi/SpiPluginTest.java20
-rw-r--r--lib/NOTICE202
-rw-r--r--lib/auto-common-0.10-sources.jarbin37921 -> 0 bytes
-rw-r--r--lib/auto-common-0.10-sources.jar.asc11
-rw-r--r--lib/auto-common-0.10-sources.jar.sha11
-rw-r--r--lib/auto-common-0.10.jarbin89662 -> 0 bytes
-rw-r--r--lib/auto-common-0.10.jar.asc11
-rw-r--r--lib/auto-common-0.10.jar.sha11
-rw-r--r--lib/auto-factory-1.0-beta6-sources.jarbin35444 -> 0 bytes
-rw-r--r--lib/auto-factory-1.0-beta6-sources.jar.asc11
-rw-r--r--lib/auto-factory-1.0-beta6-sources.jar.sha11
-rw-r--r--lib/auto-factory-1.0-beta6.jarbin75628 -> 0 bytes
-rw-r--r--lib/auto-factory-1.0-beta6.jar.asc11
-rw-r--r--lib/auto-factory-1.0-beta6.jar.sha11
-rw-r--r--lib/auto-service-1.0-rc5-sources.jarbin6960 -> 0 bytes
-rw-r--r--lib/auto-service-1.0-rc5-sources.jar.asc11
-rw-r--r--lib/auto-service-1.0-rc5-sources.jar.sha11
-rw-r--r--lib/auto-service-1.0-rc5.jarbin11585 -> 0 bytes
-rw-r--r--lib/auto-service-1.0-rc5.jar.2bin11585 -> 0 bytes
-rw-r--r--lib/auto-service-1.0-rc5.jar.asc11
-rw-r--r--lib/auto-service-1.0-rc5.jar.sha11
-rw-r--r--lib/auto-service-annotations-1.0-rc5-sources.jarbin1724 -> 0 bytes
-rw-r--r--lib/auto-service-annotations-1.0-rc5-sources.jar.asc11
-rw-r--r--lib/auto-service-annotations-1.0-rc5-sources.jar.sha11
-rw-r--r--lib/auto-service-annotations-1.0-rc5.jarbin3223 -> 0 bytes
-rw-r--r--lib/auto-service-annotations-1.0-rc5.jar.asc11
-rw-r--r--lib/auto-service-annotations-1.0-rc5.jar.sha11
-rw-r--r--lib/auto-value-1.6.5-sources.jarbin128898 -> 0 bytes
-rw-r--r--lib/auto-value-1.6.5-sources.jar.asc12
-rw-r--r--lib/auto-value-1.6.5-sources.jar.md51
-rw-r--r--lib/auto-value-1.6.5-sources.jar.sha11
-rw-r--r--lib/auto-value-1.6.5.jarbin1641145 -> 0 bytes
-rw-r--r--lib/auto-value-1.6.5.jar.asc12
-rw-r--r--lib/auto-value-1.6.5.jar.md51
-rw-r--r--lib/auto-value-1.6.5.jar.sha11
-rw-r--r--lib/auto-value-annotations-1.6.5-sources.jarbin128898 -> 0 bytes
-rw-r--r--lib/auto-value-annotations-1.6.5-sources.jar.asc12
-rw-r--r--lib/auto-value-annotations-1.6.5-sources.jar.sha11
-rw-r--r--lib/auto-value-annotations-1.6.5.jarbin5909 -> 0 bytes
-rw-r--r--lib/auto-value-annotations-1.6.5.jar.asc12
-rw-r--r--lib/auto-value-annotations-1.6.5.jar.sha11
-rw-r--r--lib/google-java-format-1.7-all-deps.jarbin6093741 -> 0 bytes
-rw-r--r--lib/google-java-format-1.7-all-deps.jar.asc16
-rw-r--r--lib/google-java-format-1.7-all-deps.jar.sha11
-rw-r--r--lib/google-java-format-1.7-sources.jarbin120624 -> 0 bytes
-rw-r--r--lib/google-java-format-1.7-sources.jar.asc16
-rw-r--r--lib/google-java-format-1.7-sources.jar.sha11
-rw-r--r--lib/google-java-format-1.7.jarbin225127 -> 0 bytes
-rw-r--r--lib/google-java-format-1.7.jar.asc16
-rw-r--r--lib/google-java-format-1.7.jar.sha11
-rw-r--r--lib/javax.inject-1-sources.jarbin10928 -> 0 bytes
-rw-r--r--lib/javax.inject-1-sources.jar.sha11
-rw-r--r--lib/javax.inject-1.jarbin2497 -> 0 bytes
-rw-r--r--lib/javax.inject-1.jar.sha11
-rw-r--r--lint-baseline.xml15
-rw-r--r--test_defs.bzl19
-rw-r--r--tools/BUILD22
-rw-r--r--tools/dejetify.bzl47
-rw-r--r--tools/maven.bzl367
-rw-r--r--tools/maven_info.bzl243
-rw-r--r--tools/pom-template.xml12
-rwxr-xr-xutil/deploy-dagger.sh127
-rwxr-xr-xutil/deploy-hilt.sh88
-rwxr-xr-xutil/deploy-library.sh92
-rwxr-xr-xutil/deploy-to-maven-central.sh67
-rwxr-xr-xutil/execute-deploy.sh110
-rw-r--r--util/find_pom_value.py19
-rwxr-xr-xutil/generate-latest-docs.sh33
-rwxr-xr-xutil/install-local-snapshot.sh37
-rwxr-xr-xutil/publish-snapshot-on-commit.sh20
-rwxr-xr-xutil/run-local-emulator-tests.sh18
-rwxr-xr-xutil/run-local-gradle-android-tests.sh22
-rwxr-xr-xutil/run-local-gradle-tests.sh14
-rwxr-xr-xutil/run-local-tests.sh21
-rw-r--r--workspace_defs.bzl292
1366 files changed, 84533 insertions, 50133 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 000000000..328d4b1df
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,214 @@
+name: CI
+
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+
+jobs:
+ bazel-test:
+ name: 'Bazel tests'
+ runs-on: ubuntu-latest
+ steps:
+ - name: 'Check out repository'
+ uses: actions/checkout@v2
+ - name: 'Cache local Maven repository'
+ uses: actions/cache@v2
+ with:
+ path: |
+ ~/.m2/repository
+ !~/.m2/repository/com/google/dagger
+ key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-maven-
+ - name: 'Cache Bazel files'
+ uses: actions/cache@v2
+ with:
+ path: ~/.cache/bazel
+ key: ${{ runner.os }}-bazel-${{ github.sha }}
+ restore-keys: |
+ ${{ runner.os }}-bazel-
+ - name: 'Cache Gradle files'
+ uses: actions/cache@v2
+ with:
+ path: |
+ ~/.gradle/caches
+ ~/.gradle/wrapper
+ key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
+ restore-keys: |
+ ${{ runner.os }}-gradle-
+ - name: 'Run Bazel tests'
+ run: bazel test --test_output=errors //...
+ shell: bash
+ - name: 'Install local snapshot'
+ run: ./util/install-local-snapshot.sh
+ shell: bash
+ - name: 'Upload local snapshot for tests'
+ uses: actions/upload-artifact@v2
+ with:
+ name: local-snapshot
+ path: ~/.m2/repository/com/google/dagger
+ artifact-java-local-tests:
+ name: 'Artifact Java local tests'
+ needs: bazel-test
+ runs-on: ubuntu-latest
+ steps:
+ - name: 'Check out repository'
+ uses: actions/checkout@v2
+ - name: 'Cache Gradle files'
+ uses: actions/cache@v2
+ with:
+ path: |
+ ~/.gradle/caches
+ ~/.gradle/wrapper
+ key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
+ restore-keys: |
+ ${{ runner.os }}-gradle-
+ - name: 'Download local snapshot for tests'
+ uses: actions/download-artifact@v2
+ with:
+ name: local-snapshot
+ path: ~/.m2/repository/com/google/dagger
+ - name: 'Gradle Java local tests'
+ run: ./util/run-local-gradle-tests.sh
+ shell: bash
+ artifact-android-local-tests:
+ name: 'Artifact Android local tests (AGP ${{ matrix.agp }})'
+ needs: bazel-test
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ agp: ['4.1.0', '4.2.0-beta04']
+ steps:
+ - name: 'Check out repository'
+ uses: actions/checkout@v2
+ - name: 'Cache Gradle files'
+ uses: actions/cache@v2
+ with:
+ path: |
+ ~/.gradle/caches
+ ~/.gradle/wrapper
+ key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
+ restore-keys: |
+ ${{ runner.os }}-gradle-
+ - name: 'Download local snapshot for tests'
+ uses: actions/download-artifact@v2
+ with:
+ name: local-snapshot
+ path: ~/.m2/repository/com/google/dagger
+ - name: 'Gradle Android local tests (AGP ${{ matrix.agp }})'
+ run: ./util/run-local-gradle-android-tests.sh "${{ matrix.agp }}"
+ shell: bash
+ artifact-android-emulator-tests:
+ name: 'Artifact Android emulator tests (API 30)'
+ needs: bazel-test
+ # It's recommended to run emulator tests on macOS
+ # See https://github.com/marketplace/actions/android-emulator-runner
+ runs-on: macos-latest
+ steps:
+ - name: 'Check out repository'
+ uses: actions/checkout@v2
+ - name: 'Cache Gradle files'
+ uses: actions/cache@v2
+ with:
+ path: |
+ ~/.gradle/caches
+ ~/.gradle/wrapper
+ key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
+ restore-keys: |
+ ${{ runner.os }}-gradle-
+ - name: 'Download local snapshot for tests'
+ uses: actions/download-artifact@v2
+ with:
+ name: local-snapshot
+ path: ~/.m2/repository/com/google/dagger
+ - name: 'Gradle Android emulator tests (API 30)'
+ uses: reactivecircus/android-emulator-runner@v2
+ timeout-minutes: 25
+ with:
+ api-level: 30
+ target: google_apis
+ script: ./util/run-local-emulator-tests.sh
+ publish-snapshot:
+ name: 'Publish snapshot'
+ # TODO(bcorso): Consider also waiting on artifact-android-emulator-tests
+ # and artifact-android-emulator-legacy-api-tests after checking flakiness.
+ needs: [bazel-test, artifact-java-local-tests, artifact-android-local-tests]
+ if: github.event_name == 'push' && github.repository == 'google/dagger' && github.ref == 'refs/heads/master'
+ runs-on: ubuntu-latest
+ steps:
+ - name: 'Check out repository'
+ uses: actions/checkout@v2
+ - name: 'Cache local Maven repository'
+ uses: actions/cache@v2
+ with:
+ path: |
+ ~/.m2/repository
+ !~/.m2/repository/com/google/dagger
+ key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-maven-
+ - name: 'Cache Bazel files'
+ uses: actions/cache@v2
+ with:
+ path: ~/.cache/bazel
+ key: ${{ runner.os }}-bazel-${{ github.sha }}
+ restore-keys: |
+ ${{ runner.os }}-bazel-
+ - name: 'Cache Gradle files'
+ uses: actions/cache@v2
+ with:
+ path: |
+ ~/.gradle/caches
+ ~/.gradle/wrapper
+ key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
+ restore-keys: |
+ ${{ runner.os }}-gradle-
+ - name: 'Publish latest docs'
+ run: ./util/generate-latest-docs.sh
+ shell: bash
+ env:
+ GH_TOKEN: ${{ github.token }}
+ - name: 'Publish latest snapshot'
+ run: ./util/publish-snapshot-on-commit.sh
+ shell: bash
+ env:
+ CI_DEPLOY_USERNAME: ${{ secrets.CI_DEPLOY_USERNAME }}
+ CI_DEPLOY_PASSWORD: ${{ secrets.CI_DEPLOY_PASSWORD }}
+ artifact-android-emulator-legacy-api-tests:
+ name: 'Artifact Android emulator tests (API ${{ matrix.api-level }})'
+ # We only run this on master push (essentially a postsubmit) since these
+ # can take a while to run
+ if: github.event_name == 'push' && github.repository == 'google/dagger' && github.ref == 'refs/heads/master'
+ needs: bazel-test
+ # It's recommended to run emulator tests on macOS
+ # See https://github.com/marketplace/actions/android-emulator-runner
+ runs-on: macos-latest
+ strategy:
+ matrix:
+ api-level: [16, 17, 18, 19, 21, 22, 23, 24, 25, 26, 27, 29]
+ steps:
+ - name: 'Check out repository'
+ uses: actions/checkout@v2
+ - name: 'Cache Gradle files'
+ uses: actions/cache@v2
+ with:
+ path: |
+ ~/.gradle/caches
+ ~/.gradle/wrapper
+ key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
+ restore-keys: |
+ ${{ runner.os }}-gradle-
+ - name: 'Download local snapshot for tests'
+ uses: actions/download-artifact@v2
+ with:
+ name: local-snapshot
+ path: ~/.m2/repository/com/google/dagger
+ - name: 'Gradle Android emulator tests (API ${{ matrix.api-level }})'
+ uses: reactivecircus/android-emulator-runner@v2
+ timeout-minutes: 25
+ with:
+ api-level: ${{ matrix.api-level }}
+ target: google_apis
+ script: ./util/run-local-emulator-tests.sh
diff --git a/.gitignore b/.gitignore
index fabb38faa..1f7ead8c0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,8 @@ dependency-reduced-pom.xml
gen-external-apklibs
-/bazel-*
+bazel-*
*.pyc
+
+.gradle
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 9015f2ea3..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,65 +0,0 @@
-language: android
-
-os: linux
-dist: trusty
-sudo: required
-addons:
- apt:
- sources:
- - ubuntu-toolchain-r-test
- packages:
- - libstdc++-4.9-dev # https://github.com/nodegit/nodegit/issues/853
- - gcc-4.8
- - g++-4.8
-
-jdk:
- - &jdk_for_publishing oraclejdk8
-
-android:
- components:
- - tools
- - tools # Duplicated as per https://github.com/travis-ci/travis-ci/issues/6040#issuecomment-219367943
- - build-tools-26.0.2
- - android-26
- - platform-tools
- - extra-android-m2repository
-
-before_install:
- - wget https://github.com/bazelbuild/bazel/releases/download/"${BAZEL_VERSION}"/bazel_"${BAZEL_VERSION}"-linux-x86_64.deb
- - sudo dpkg -i bazel_"${BAZEL_VERSION}"-linux-x86_64.deb
- - sudo rm -f /etc/mavenrc
- - wget http://www.us.apache.org/dist/maven/maven-3/3.1.1/binaries/apache-maven-3.1.1-bin.tar.gz
- - tar -zxf apache-maven-3.1.1-bin.tar.gz
- - export PATH="$PWD/apache-maven-3.1.1/bin:$PATH"
- - mkdir travis_bin
- - ln -s $(which gcc-4.8) travis_bin/gcc
- - ln -s $(which g++-4.8) travis_bin/g++
- - export PATH="${PWD}/travis_bin:${PATH}"
-
-script:
- - bazel test --test_output errors //...
- - pushd examples && mvn compile && popd
-
-env:
- global:
- # Encrypted credentials for deploying snapshots.
- - secure: eGc3LHBRIPmTnXLM1YoIqG1do9BkpFI2pJm3fz5Cd8UaXtf7Oefa+Ts3rcn4ipee5A+lf8kEouPshSoaQs81KZ2/qf8rSTCIqeFjHR8hzmOVYo/0zRfS/VSUT0yqN+jeRhuNk3+A49RTPlcfJqPv3tyddtrM1vF7axhCJPQIRJM=
- - secure: LTzrlqcSNeZTOV52D3ibY9RBdxY4Yu8dUOYhAonrWLE+eDTzuoyCzcPw8pEcYVNUi1rG6Q7v3QBDTnBztsPoCbcN5tEGjw5cQEbfEzSTkWaNCFjncWn36cLwx9lgbF+5Db/L0mYJ36unDKUpKVC8AgOtxQibfv/ffugfxxj8ohY=
-
- # Encrypted GitHub access token to allow util/generate-latest-docs.sh to
- # push Javadoc to gh-pages.
- # This uses an access token created by cgdecker and will need to be updated
- # (see util/generate-latest-docs.sh for a link) if he no longer has
- # permission to push to the repo.
- - secure: "UpTUhCQzAGbr5JetRg2GZxp/dPDep/7Il3yGeyDECopciWdx41OPk/QNqAXBhNtKuEaMVsmASyoteuhgaTryQdV4qUIGVOMhES6kbOlYy3nwK44VdsNeeepwVospyDyZbxMtXq5LuHWuTADmAl1mdjNPNoziXc523zjnUzUx/EQ="
- - JDK_FOR_PUBLISHING: *jdk_for_publishing
- - BAZEL_VERSION="0.24.1"
-
-after_success:
- - util/generate-latest-docs.sh
- - util/publish-snapshot-on-commit.sh
-
-branches:
- only:
- - master
- - /^release.*$/
diff --git a/Android.bp b/Android.bp
index 27a05c5d1..1bdbd1b02 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12,87 +12,46 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-java_import_host {
- name: "dagger2-auto-common",
- jars: ["lib/auto-common-0.10.jar"],
-}
-
-java_import_host {
- name: "dagger2-auto-factory-jar",
- jars: ["lib/auto-factory-1.0-beta6.jar"],
-}
-
-java_plugin {
- name: "dagger2-auto-factory",
- processor_class: "com.google.auto.factory.processor.AutoFactoryProcessor",
- static_libs: [
- "dagger2-auto-factory-jar",
- "dagger2-auto-common",
- "guava",
- "javapoet",
- "dagger2-google-java-format",
- ],
-}
-
-java_import_host {
- name: "dagger2-auto-service-jar",
- jars: ["lib/auto-service-1.0-rc5.jar"],
-}
-
-java_import_host {
- name: "dagger2-auto-service-annotations",
- jars: ["lib/auto-service-annotations-1.0-rc5.jar"],
+package {
+ default_visibility: [":__subpackages__"],
+ default_applicable_licenses: ["external_dagger2_license"],
}
-java_plugin {
- name: "dagger2-auto-service",
- processor_class: "com.google.auto.service.processor.AutoServiceProcessor",
- static_libs: [
- "dagger2-auto-common",
- "dagger2-auto-service-jar",
- "dagger2-auto-service-annotations",
- "guava",
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+//
+// large-scale-change included anything that looked like it might be a license
+// text as a license_text. e.g. LICENSE, NOTICE, COPYING etc.
+//
+// Please consider removing redundant or irrelevant files from 'license_text:'.
+// See: http://go/android-license-faq
+license {
+ name: "external_dagger2_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ "legacy_not_a_contribution",
],
-}
-
-java_import_host {
- name: "dagger2-auto-value-jar",
- jars: ["lib/auto-value-1.6.5.jar"],
-}
-
-java_import_host {
- name: "dagger2-auto-value-annotations",
- jars: ["lib/auto-value-annotations-1.6.5.jar"],
-}
-
-java_plugin {
- name: "dagger2-auto-value",
- processor_class: "com.google.auto.value.processor.AutoValueProcessor",
- static_libs: [
- "dagger2-auto-value-jar",
- "dagger2-auto-value-annotations",
+ license_text: [
+ "LICENSE.txt",
+ "NOTICE",
],
}
-java_plugin {
- name: "dagger2-auto-annotation",
- processor_class: "com.google.auto.value.processor.AutoAnnotationProcessor",
- static_libs: ["dagger2-auto-value-jar"],
-}
-
-java_import_host {
- name: "dagger2-google-java-format",
- jars: ["lib/google-java-format-1.7-all-deps.jar"],
-}
-
-java_import_host {
- name: "dagger2-inject",
- jars: ["lib/javax.inject-1.jar"],
-}
-
java_import_host {
name: "dagger2-bootstrap-compiler-jar",
- jars: ["java/dagger/internal/codegen/bootstrap_compiler_deploy.jar"],
+ jars: ["java/dagger/internal/codegen/bootstrap/bootstrap_compiler_deploy.jar"],
}
java_plugin {
@@ -100,36 +59,42 @@ java_plugin {
processor_class: "dagger.internal.codegen.ComponentProcessor",
generates_api: true,
static_libs: ["dagger2-bootstrap-compiler-jar"],
+ jarjar_rules: "jarjar-rules.txt",
}
-java_library_host {
+java_library {
name: "dagger2",
+ visibility: ["//visibility:public"],
+ host_supported: true,
srcs: [
"java/dagger/*.java",
+ "java/dagger/assisted/*.java",
"java/dagger/internal/*.java",
"java/dagger/multibindings/*.java",
"java/dagger/releasablereferences/*.java",
],
exclude_srcs: ["java/dagger/android/**/*.java"],
- static_libs: ["dagger2-inject"],
-
- libs: ["guava"],
+ libs: [
+ "guava",
+ "jsr330",
+ ],
java_version: "1.8",
+ sdk_version: "core_current",
}
// build dagger2 producers library
// ============================================================
-java_library_host {
+java_library {
name: "dagger2-producers",
+ host_supported: true,
srcs: ["java/dagger/producers/**/*.java"],
static_libs: [
- "dagger2-inject",
"error_prone_annotations",
],
@@ -137,9 +102,11 @@ java_library_host {
"dagger2",
"dagger2-android-annotation-stubs",
"guava",
+ "jsr330",
],
java_version: "1.8",
+ sdk_version: "core_current",
}
// build dagger2 compiler plugin
@@ -147,8 +114,16 @@ java_library_host {
java_plugin {
name: "dagger2-compiler",
+ visibility: ["//visibility:public"],
processor_class: "dagger.internal.codegen.ComponentProcessor",
generates_api: true,
+ static_libs: ["dagger2-compiler-lib"],
+ // shade guava to avoid conflicts with guava embedded in Error Prone.
+ jarjar_rules: "jarjar-rules.txt",
+}
+
+java_library_host {
+ name: "dagger2-compiler-lib",
use_tools_jar: true,
srcs: [
@@ -161,7 +136,7 @@ java_plugin {
exclude_srcs: [
"java/dagger/internal/codegen/BindingGraphStatisticsCollector.java",
- "java/dagger/internal/codegen/DaggerKythePlugin.java",
+ "java/dagger/internal/codegen/kythe/DaggerKythePlugin.java",
],
// Manually include META-INF/services/javax.annotation.processing.Processor
@@ -169,30 +144,32 @@ java_plugin {
java_resource_dirs: ["resources"],
static_libs: [
+ "auto_common",
"dagger2",
- "dagger2-auto-common",
- "dagger2-auto-factory",
- "dagger2-auto-service",
- "dagger2-auto-value",
- "dagger2-google-java-format",
- "dagger2-inject",
"dagger2-producers",
+ "google_java_format",
"guava",
"javapoet",
+ "jsr330",
+ "kotlin-stdlib",
+ "kotlinx_metadata_jvm",
],
- // shade guava to avoid conflicts with guava embedded in Error Prone.
- jarjar_rules: "jarjar-rules.txt",
libs: [
+ "auto_factory_annotations",
+ "auto_service_annotations",
+ "auto_value_annotations",
+ "auto_value_memoized_extension_annotations",
"dagger2-android-annotation-stubs",
],
plugins: [
- "dagger2-auto-factory",
- "dagger2-auto-service",
- "dagger2-auto-value",
- "dagger2-auto-annotation",
+ "auto_factory_plugin",
+ "auto_service_plugin",
+ "auto_value_plugin",
+ "auto_value_memoized_extension_plugin",
+ "auto_annotation_plugin",
"dagger2-bootstrap-compiler",
],
@@ -212,3 +189,233 @@ java_library {
sdk_version: "core_current",
srcs: ["android-annotation-stubs/src/**/*.java"],
}
+
+// build core hilt library
+
+java_library {
+ name: "hilt_core",
+ srcs: [
+ "java/dagger/hilt/*.java",
+ "java/dagger/hilt/codegen/*.java",
+ "java/dagger/hilt/components/*.java",
+ "java/dagger/hilt/internal/*.java",
+ "java/dagger/hilt/internal/aliasof/*.java",
+ "java/dagger/hilt/internal/definecomponent/*.java",
+ "java/dagger/hilt/internal/generatesrootinput/*.java",
+ "java/dagger/hilt/migration/*.java",
+ "java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDeps.java",
+ ],
+ static_libs: [
+ "jsr305",
+ "jsr330",
+ "dagger2",
+ ],
+ sdk_version: "core_current",
+ plugins: [
+ "hilt_define_component_processor",
+ "hilt_generates_root_input_processor",
+ ],
+}
+
+// Build the android hilt library. Depending on this will enable the Hilt annotation processors.
+
+android_library {
+ name: "hilt_android",
+ visibility: ["//visibility:public"],
+
+ srcs: [
+ "java/dagger/hilt/android/*.java",
+ "java/dagger/hilt/android/components/*.java",
+ "java/dagger/hilt/android/migration/*.java",
+ "java/dagger/hilt/android/qualifiers/*.java",
+ "java/dagger/hilt/android/scopes/*.java",
+ "java/dagger/hilt/android/internal/*.java",
+ "java/dagger/hilt/android/internal/builders/*.java",
+ "java/dagger/hilt/android/internal/lifecycle/*.java",
+ "java/dagger/hilt/android/internal/managers/*.java",
+ "java/dagger/hilt/android/internal/migration/*.java",
+ "java/dagger/hilt/android/internal/modules/*.java",
+ "java/dagger/hilt/android/lifecycle/*.java",
+ ],
+ manifest: "java/dagger/hilt/android/AndroidManifest.xml",
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.fragment_fragment",
+ "jsr305",
+ "jsr330",
+ "dagger2",
+ "hilt_core",
+ ],
+ sdk_version: "current",
+ min_sdk_version: "14",
+ plugins: [
+ "dagger2-compiler",
+ "hilt_android_entry_point_processor",
+ "hilt_aggregated_deps_processor",
+ "hilt_define_component_processor",
+ "hilt_generates_root_input_processor",
+ "hilt_originating_element_processor",
+ "hilt_root_processor",
+ ],
+ exported_plugins: [
+ "dagger2-compiler",
+ "hilt_android_entry_point_processor",
+ "hilt_aggregated_deps_processor",
+ "hilt_alias_of_processor",
+ "hilt_define_component_processor",
+ "hilt_generates_root_input_processor",
+ "hilt_originating_element_processor",
+ "hilt_root_processor",
+ "hilt_viewmodel_processor",
+ ],
+}
+
+android_library {
+ name: "hilt_android_testing",
+ visibility: ["//visibility:public"],
+
+ srcs: [
+ "java/dagger/hilt/android/internal/testing/*.java",
+ "java/dagger/hilt/android/testing/*.java",
+ ],
+ manifest: "java/dagger/hilt/android/testing/AndroidManifest.xml",
+ static_libs: [
+ "auto_value_annotations",
+ "androidx.annotation_annotation",
+ "androidx.fragment_fragment",
+ "androidx.annotation_annotation",
+ "androidx.fragment_fragment",
+ "androidx.test.core",
+ "android-support-multidex",
+ "jsr305",
+ "dagger2",
+ "hilt_core",
+ "junit",
+ ],
+ sdk_version: "current",
+ min_sdk_version: "14",
+ plugins: [
+ "auto_value_plugin",
+ "dagger2-compiler",
+ "hilt_generates_root_input_processor",
+ ],
+ exported_plugins: [
+ "dagger2-compiler",
+ "hilt_android_entry_point_processor",
+ "hilt_aggregated_deps_processor",
+ "hilt_define_component_processor",
+ "hilt_generates_root_input_processor",
+ "hilt_originating_element_processor",
+ "hilt_root_processor",
+ "hilt_viewmodel_processor",
+ "hilt_custom_test_application_processor",
+ "hilt_bindvalue_processor",
+ "hilt_uninstall_modules_processor",
+ ],
+}
+
+// Hilt has many annotation processors. To reduce compilation and runtime cost, they are all compiled
+// into hilt_android_processors. A java_plugin can only expose a single processor class, so each has
+// to be defined separately. Since they are not visible outside this package and will always be
+// exported together, only the first actually contains the annotation processor classes.
+java_plugin {
+ name: "hilt_generates_root_input_processor",
+ generates_api: true,
+ processor_class: "dagger.hilt.processor.internal.generatesrootinput.GeneratesRootInputProcessor",
+ static_libs: ["hilt_android_processors"],
+}
+
+java_plugin {
+ name: "hilt_android_entry_point_processor",
+ generates_api: true,
+ processor_class: "dagger.hilt.android.processor.internal.androidentrypoint.AndroidEntryPointProcessor",
+}
+
+java_plugin {
+ name: "hilt_aggregated_deps_processor",
+ generates_api: true,
+ processor_class: "dagger.hilt.processor.internal.aggregateddeps.AggregatedDepsProcessor",
+}
+
+java_plugin {
+ name: "hilt_alias_of_processor",
+ generates_api: true,
+ processor_class: "dagger.hilt.processor.internal.aliasof.AliasOfProcessor",
+}
+
+java_plugin {
+ name: "hilt_define_component_processor",
+ generates_api: true,
+ processor_class: "dagger.hilt.processor.internal.definecomponent.DefineComponentProcessor",
+}
+
+java_plugin {
+ name: "hilt_originating_element_processor",
+ generates_api: true,
+ processor_class: "dagger.hilt.processor.internal.originatingelement.OriginatingElementProcessor",
+}
+
+java_plugin {
+ name: "hilt_root_processor",
+ generates_api: true,
+ processor_class: "dagger.hilt.processor.internal.root.RootProcessor",
+}
+
+java_plugin {
+ name: "hilt_viewmodel_processor",
+ generates_api: true,
+ processor_class: "dagger.hilt.android.processor.internal.viewmodel.ViewModelProcessor",
+}
+
+// Hilt testing processors
+java_plugin {
+ name: "hilt_custom_test_application_processor",
+ generates_api: true,
+ processor_class: "dagger.hilt.android.processor.internal.customtestapplication.CustomTestApplicationProcessor",
+}
+
+java_plugin {
+ name: "hilt_bindvalue_processor",
+ generates_api: true,
+ processor_class: "dagger.hilt.android.processor.internal.bindvalue.BindValueProcessor",
+}
+
+java_plugin {
+ name: "hilt_uninstall_modules_processor",
+ generates_api: true,
+ processor_class: "dagger.hilt.android.processor.internal.uninstallmodules.UninstallModulesProcessor",
+}
+
+java_library_host {
+ name: "hilt_android_processors",
+ use_tools_jar: true,
+ srcs: [
+ "java/dagger/hilt/android/processor/**/*.java",
+ "java/dagger/hilt/android/processor/**/*.kt",
+ "java/dagger/hilt/codegen/*.java",
+ "java/dagger/hilt/processor/internal/**/*.java",
+ ],
+ plugins: [
+ "auto_service_plugin",
+ "auto_value_plugin",
+ "auto_value_memoized_extension_plugin",
+ "dagger2-compiler",
+ ],
+ static_libs: [
+ "auto_common",
+ "auto_service_annotations",
+ "auto_value_annotations",
+ "auto_value_memoized_extension_annotations",
+ "guava",
+ "jsr305",
+ "dagger2-compiler-lib",
+ "dagger2",
+ "javapoet",
+ "jsr330",
+ "kotlin-stdlib",
+ "kotlinx_metadata_jvm",
+ "dagger2-android-annotation-stubs",
+ ],
+ // shade guava to avoid conflicts with guava embedded in Error Prone.
+ jarjar_rules: "jarjar-rules.txt",
+}
diff --git a/BUILD b/BUILD
index 8becb3e7d..015e849de 100644
--- a/BUILD
+++ b/BUILD
@@ -12,15 +12,25 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+load("@rules_java//java:defs.bzl", "java_library")
+load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
+load("@google_bazel_common//tools/jarjar:jarjar.bzl", "jarjar_library")
+load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "define_kt_toolchain")
+
package(default_visibility = ["//visibility:public"])
+define_kt_toolchain(
+ name = "kotlin_toolchain",
+ api_version = "1.4",
+ jvm_target = "1.8",
+ language_version = "1.4",
+)
+
package_group(
name = "src",
packages = ["//..."],
)
-load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
-
java_library(
name = "dagger_with_compiler",
exported_plugins = ["//java/dagger/internal/codegen:component-codegen"],
@@ -35,6 +45,22 @@ java_library(
],
)
+java_library(
+ name = "spi",
+ exports = ["//java/dagger/spi"],
+)
+
+java_library(
+ name = "compiler_internals",
+ exports = [
+ "//java/dagger/internal/codegen:processor",
+ "//java/dagger/internal/codegen/base",
+ "//java/dagger/internal/codegen/binding",
+ "//java/dagger/internal/codegen/validation",
+ "//java/dagger/internal/codegen/writing",
+ ],
+)
+
android_library(
name = "android",
exported_plugins = ["//java/dagger/android/processor:plugin"],
@@ -49,118 +75,48 @@ android_library(
],
)
-load("@google_bazel_common//tools/jarjar:jarjar.bzl", "jarjar_library")
-
-SHADE_RULES = ["rule com.google.auto.common.** dagger.shaded.auto.common.@1"]
-
-jarjar_library(
- name = "shaded_compiler",
- jars = [
- "//java/dagger/internal/codegen:base",
- "//java/dagger/internal/codegen:binding",
- "//java/dagger/internal/codegen:binding_graph_validation",
- "//java/dagger/internal/codegen:jdk-and-guava-extras",
- "//java/dagger/internal/codegen:processor",
- "//java/dagger/internal/codegen:validation",
- "//java/dagger/internal/codegen:writing",
- "//java/dagger/internal/codegen/javapoet",
- "//java/dagger/internal/codegen/langmodel",
- "//java/dagger/internal/codegen/serialization",
- "//java/dagger/model:internal-proxies",
- "//java/dagger/errorprone",
- "@com_google_auto_auto_common//jar",
- ],
- rules = SHADE_RULES,
-)
-
-jarjar_library(
- name = "shaded_compiler_src",
- jars = [
- "//java/dagger/internal/codegen:libbase-src.jar",
- "//java/dagger/internal/codegen:libbinding-src.jar",
- "//java/dagger/internal/codegen:libbinding_graph_validation-src.jar",
- "//java/dagger/internal/codegen:libjdk-and-guava-extras-src.jar",
- "//java/dagger/internal/codegen:libprocessor-src.jar",
- "//java/dagger/internal/codegen:libvalidation-src.jar",
- "//java/dagger/internal/codegen:libwriting-src.jar",
- "//java/dagger/internal/codegen/javapoet:libjavapoet-src.jar",
- "//java/dagger/internal/codegen/langmodel:liblangmodel-src.jar",
- # TODO(ronshapiro): is there a generated src.jar for protos in Bazel?
- "//java/dagger/errorprone:liberrorprone-src.jar",
- ],
-)
-
-jarjar_library(
- name = "shaded_spi",
- jars = [
- "//java/dagger/internal/codegen:jdk-and-guava-extras",
- "//java/dagger/model",
- "//java/dagger/spi",
- "@com_google_auto_auto_common//jar",
- ],
- rules = SHADE_RULES,
-)
-
-jarjar_library(
- name = "shaded_spi_src",
- jars = [
- "//java/dagger/internal/codegen:libjdk-and-guava-extras-src.jar",
- "//java/dagger/model:libmodel-src.jar",
- "//java/dagger/spi:libspi-src.jar",
- ],
-)
-
-javadoc_library(
- name = "spi-javadoc",
- srcs = [
- "//java/dagger/model:model-srcs",
- "//java/dagger/spi:spi-srcs",
- ],
- root_packages = [
- "dagger.model",
- "dagger.spi",
- ],
- deps = [
- "//java/dagger/model",
- "//java/dagger/spi",
- ],
-)
-
jarjar_library(
name = "shaded_android_processor",
jars = [
"//java/dagger/android/processor",
- "@com_google_auto_auto_common//jar",
+ "@maven//:com_google_auto_auto_common",
+ ],
+ rules = [
+ "rule com.google.auto.common.** dagger.android.shaded.auto.common.@1",
],
- rules = SHADE_RULES,
)
jarjar_library(
name = "shaded_grpc_server_processor",
jars = [
"//java/dagger/grpc/server/processor",
- "@com_google_auto_auto_common//jar",
+ "@maven//:com_google_auto_auto_common",
+ ],
+ rules = [
+ "rule com.google.auto.common.** dagger.grpc.shaded.auto.common.@1",
],
- rules = SHADE_RULES,
)
# coalesced javadocs used for the gh-pages site
javadoc_library(
name = "user-docs",
+ testonly = 1,
srcs = [
"//java/dagger:javadoc-srcs",
"//java/dagger/android:android-srcs",
"//java/dagger/android/support:support-srcs",
"//java/dagger/grpc/server:javadoc-srcs",
"//java/dagger/grpc/server/processor:javadoc-srcs",
- "//java/dagger/model:model-srcs",
+ "//java/dagger/hilt:javadoc-srcs",
"//java/dagger/producers:producers-srcs",
"//java/dagger/spi:spi-srcs",
],
- android_api_level = 26,
+ android_api_level = 30,
# TODO(ronshapiro): figure out how to specify the version number for release builds
doctitle = "Dagger Dependency Injection API",
exclude_packages = [
+ "dagger.hilt.android.internal",
+ "dagger.hilt.internal",
"dagger.internal",
"dagger.producers.internal",
"dagger.producers.monitoring.internal",
@@ -172,7 +128,8 @@ javadoc_library(
"//java/dagger/android/support",
"//java/dagger/grpc/server",
"//java/dagger/grpc/server/processor",
- "//java/dagger/model",
+ "//java/dagger/hilt/android:artifact-lib",
+ "//java/dagger/hilt/android/testing:artifact-lib",
"//java/dagger/producers",
"//java/dagger/spi",
],
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 45f15c4aa..54fb50aa9 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -33,11 +33,19 @@ Dagger is built with [`bazel`](https://bazel.build).
* [Install Bazel](https://docs.bazel.build/versions/master/install.html)
* Build the Dagger project with `bazel build <target>`
* Learn more about Bazel targets [here][bazel targets].
- * If you see an error similar to `ERROR: missing input file
- '@androidsdk//:build-tools/26.0.2/aapt'`, install the missing build
- tools version with the android `sdkmanager` tool.
+ * Building Dagger's Android targets requires additional setup:
+ * Set the `ANDROID_HOME` environment variable to point to a directory
+ containing the Android SDK. If you do not have the Android SDK
+ installed, you'll have to
+ [download](https://developer.android.com/studio#command-tools)
+ and unzip it first.
+ * Install the necessary components. For example, under Linux, run:
+ `$ANDROID_HOME/tools/bin/sdkmanager "platforms;android-30" "build-tools;30.0.2"`
+ * If you skip this step, you will see an error similar to
+ `ERROR: missing input file '@androidsdk//:build-tools/30.0.2/aapt'`.
+ * You may also need to run `bazel sync`.
* Run tests with `bazel test <target>`, or `bazel test //...` to run all
- tests
+ tests.
* You can install the Dagger libraries in your **local maven repository** by
running the `./util/install-local-snapshot.sh` script.
* It will build the libraries and install them with a `LOCAL-SNAPSHOT`
diff --git a/METADATA b/METADATA
index 59b9e2516..5eef5c3b8 100644
--- a/METADATA
+++ b/METADATA
@@ -11,7 +11,7 @@ third_party {
type: GIT
value: "https://github.com/google/dagger"
}
- version: "dagger-2.23.1"
- last_upgrade_date { year: 2019 month: 6 day: 14 }
- license_type: PERMISSIVE
+ version: "dagger-2.19.1"
+ last_upgrade_date { year: 2020 month: 11 day: 18 }
+ license_type: NOTICE
}
diff --git a/README.md b/README.md
index a9ebc26eb..393e2e7c7 100644
--- a/README.md
+++ b/README.md
@@ -1,34 +1,23 @@
-# Dagger 2
+# Dagger
[![Maven Central][mavenbadge-svg]][mavencentral]
-A fast dependency injector for Android and Java.
+A fast dependency injector for Java and Android.
-## About Google's Fork
+Dagger is a compile-time framework for dependency injection. It uses no
+reflection or runtime bytecode generation, does all its analysis at
+compile-time, and generates plain Java source code.
-Dagger 2 is a compile-time evolution approach to dependency injection.
-Taking the approach started in Dagger 1.x to its ultimate conclusion,
-Dagger 2.x eliminates all reflection, and improves code clarity by
-removing the traditional ObjectGraph/Injector in favor of user-specified
-`@Component` interfaces.
-
-This github project represents the Dagger 2 development stream. The earlier
-[project page][square] (Square, Inc's repository) represents the earlier 1.0
-development stream. Both versions have benefited from strong involvement from
-Square, Google, and other contributors.
-
-Dagger is currently in active development, primarily internally at Google,
-with regular pushes to the open-source community. Snapshot releases are
-auto-deployed to sonatype's central maven repository on every clean build with
-the version `HEAD-SNAPSHOT`.
-
-> [Dagger 2's main documentation website can be found here.][website]
+Dagger is actively maintained by the same team that works on [Guava]. Snapshot
+releases are auto-deployed to Sonatype's central Maven repository on every clean
+build with the version `HEAD-SNAPSHOT`. The current version builds upon previous
+work done at [Square][square].
## Documentation
You can [find the dagger documentation here][website] which has extended usage
-instructions and other useful information. Substantial usage information can be
-found in the [API documentation][20api].
+instructions and other useful information. More detailed information can be
+found in the [API documentation][latestapi].
You can also learn more from [the original proposal][proposal],
[this talk by Greg Kick][gaktalk], and on the dagger-discuss@googlegroups.com
@@ -38,19 +27,154 @@ mailing list.
### Bazel
-If you build with `bazel`, follow the [`bazel` documentation for referencing
-external projects][bazel-external-deps] to include Dagger in your build.
+First, import the Dagger repository into your `WORKSPACE` file using
+[`http_archive`][bazel-external-deps].
-Given the following `WORKSPACE` definition, you can reference dagger via
-`@com_google_dagger//:dagger_with_compiler` in your deps.
+Note: The `http_archive` must point to a tagged release of Dagger, not just any
+commit. The version of the Dagger artifacts will match the version of the tagged
+release.
```python
+# Top-level WORKSPACE file
+
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+DAGGER_TAG = "2.28.1"
+DAGGER_SHA = "9e69ab2f9a47e0f74e71fe49098bea908c528aa02fa0c5995334447b310d0cdd"
http_archive(
- name = "com_google_dagger",
- urls = ["https://github.com/google/dagger/archive/dagger-<version>.zip"],
+ name = "dagger",
+ strip_prefix = "dagger-dagger-%s" % DAGGER_TAG,
+ sha256 = DAGGER_SHA,
+ urls = ["https://github.com/google/dagger/archive/dagger-%s.zip" % DAGGER_TAG],
+)
+```
+
+Next you will need to setup targets that export the proper dependencies
+and plugins. Follow the sections below to setup the dependencies you need.
+
+#### Dagger Setup
+
+First, load the Dagger artifacts and repositories, and add them to your list of
+[`maven_install`] artifacts.
+
+```python
+# Top-level WORKSPACE file
+
+load("@dagger//:workspace_defs.bzl", "DAGGER_ARTIFACTS", "DAGGER_REPOSITORIES")
+
+maven_install(
+ artifacts = DAGGER_ARTIFACTS + [...],
+ repositories = DAGGER_REPOSITORIES + [...],
+)
+```
+
+Next, load and call [`dagger_rules`](https://github.com/google/dagger/blob/master/workspace_defs.bzl)
+in your top-level `BUILD` file:
+
+```python
+# Top-level BUILD file
+
+load("@dagger//:workspace_defs.bzl", "dagger_rules")
+
+dagger_rules()
+```
+
+This will add the following Dagger build targets:
+(Note that these targets already export all of the dependencies and processors
+they need).
+
+```python
+deps = [
+ ":dagger", # For Dagger
+ ":dagger-spi", # For Dagger SPI
+ ":dagger-producers", # For Dagger Producers
+]
+```
+
+#### Dagger Android Setup
+
+First, load the Dagger Android artifacts and repositories, and add them to your
+list of [`maven_install`] artifacts.
+
+```python
+# Top-level WORKSPACE file
+
+load(
+ "@dagger//:workspace_defs.bzl",
+ "DAGGER_ANDROID_ARTIFACTS",
+ "DAGGER_ANDROID_REPOSITORIES"
+)
+
+maven_install(
+ artifacts = DAGGER_ANDROID_ARTIFACTS + [...],
+ repositories = DAGGER_ANDROID_REPOSITORIES + [...],
)
```
+Next, load and call [`dagger_android_rules`](https://github.com/google/dagger/blob/master/workspace_defs.bzl)
+in your top-level `BUILD` file:
+
+```python
+# Top-level BUILD file
+
+load("@dagger//:workspace_defs.bzl", "dagger_android_rules")
+
+dagger_android_rules()
+```
+
+This will add the following Dagger Android build targets:
+(Note that these targets already export all of the dependencies and processors
+they need).
+
+```python
+deps = [
+ ":dagger-android", # For Dagger Android
+ ":dagger-android-support", # For Dagger Android (Support)
+]
+```
+
+#### Hilt Android Setup
+
+First, load the Hilt Android artifacts and repositories, and add them to your
+list of [`maven_install`] artifacts.
+
+```python
+# Top-level WORKSPACE file
+
+load(
+ "@dagger//:workspace_defs.bzl",
+ "HILT_ANDROID_ARTIFACTS",
+ "HILT_ANDROID_REPOSITORIES"
+)
+
+maven_install(
+ artifacts = HILT_ANDROID_ARTIFACTS + [...],
+ repositories = HILT_ANDROID_REPOSITORIES + [...],
+)
+```
+
+Next, load and call [`hilt_android_rules`](https://github.com/google/dagger/blob/master/workspace_defs.bzl)
+in your top-level `BUILD` file:
+
+```python
+# Top-level BUILD file
+
+load("@dagger//:workspace_defs.bzl", "hilt_android_rules")
+
+hilt_android_rules()
+```
+
+This will add the following Hilt Android build targets:
+(Note that these targets already export all of the dependencies and processors
+they need).
+
+```python
+deps = [
+ ":hilt-android", # For Hilt Android
+ ":hilt-android-testing", # For Hilt Android Testing
+]
+```
+
### Other build systems
You will need to include the `dagger-2.x.jar` in your application's runtime.
@@ -124,25 +248,11 @@ parallelizable execution graphs), then add this to your maven configuration:
</dependencies>
```
-#### Java Gradle
-```groovy
-// Add plugin https://plugins.gradle.org/plugin/net.ltgt.apt
-plugins {
- id "net.ltgt.apt" version "0.10"
-}
-
-// Add Dagger dependencies
-dependencies {
- compile 'com.google.dagger:dagger:2.x'
- apt 'com.google.dagger:dagger-compiler:2.x'
-}
-```
-
-#### Android Gradle
+#### Gradle
```groovy
// Add Dagger dependencies
dependencies {
- compile 'com.google.dagger:dagger:2.x'
+ implementation 'com.google.dagger:dagger:2.x'
annotationProcessor 'com.google.dagger:dagger-compiler:2.x'
}
```
@@ -150,13 +260,19 @@ dependencies {
If you're using classes in `dagger.android` you'll also want to include:
```groovy
-compile 'com.google.dagger:dagger-android:2.x'
-compile 'com.google.dagger:dagger-android-support:2.x' // if you use the support libraries
+implementation 'com.google.dagger:dagger-android:2.x'
+implementation 'com.google.dagger:dagger-android-support:2.x' // if you use the support libraries
annotationProcessor 'com.google.dagger:dagger-android-processor:2.x'
```
-If you're using a version of the Android gradle plugin below `2.2`, see
-https://bitbucket.org/hvisser/android-apt.
+Notes:
+
+- We use `implementation` instead of `api` for better compilation performance.
+ - See the [Gradle documentation][gradle-api-implementation] for more
+ information on how to select appropriately, and the [Android Gradle
+ plugin documentation][gradle-api-implementation-android] for Android
+ projects.
+- For Kotlin projects, use [`kapt`] in place of `annotationProcessor`.
If you're using the [Android Databinding library][databinding], you may want to
increase the number of errors that `javac` will print. When Dagger prints an
@@ -172,16 +288,11 @@ gradle.projectsEvaluated {
}
```
-### Download
+### Resources
- * 2.x (google/dagger)
- * [Dagger 2.0 Documentation][website]
- * [Dagger 2.0 Javadocs][20api]
- * [Dagger development Javadocs][latestapi] (from the `master` branch
- on GitHub)
- * [Google's Dagger project site on GitHub][project]
- * 1.x (square/dagger)
- * [Square's original Dagger project site on GitHub][square]
+* [Documentation][website]
+* [Javadocs][latestapi]
+* [GitHub Issues]
If you do not use maven, gradle, ivy, or other build systems that consume
@@ -212,13 +323,18 @@ See [the CONTRIBUTING.md docs][Building Dagger].
See the License for the specific language governing permissions and
limitations under the License.
-[20api]: https://dagger.dev/api/2.0/
[`bazel`]: https://bazel.build
[bazel-external-deps]: https://docs.bazel.build/versions/master/external.html#depending-on-other-bazel-projects
+[`maven_install`]: https://github.com/bazelbuild/rules_jvm_external#exporting-and-consuming-artifacts-from-external-repositories
[Building Dagger]: CONTRIBUTING.md#building-dagger
[dagger-snap]: https://oss.sonatype.org/content/repositories/snapshots/com/google/dagger/
[databinding]: https://developer.android.com/topic/libraries/data-binding/
[gaktalk]: https://www.youtube.com/watch?v=oK_XtfXPkqw
+[GitHub Issues]: https://github.com/google/dagger/issues
+[gradle-api-implementation]: https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_separation
+[gradle-api-implementation-android]: https://developer.android.com/studio/build/dependencies#dependency_configurations
+[Guava]: https://github.com/google/guava
+[`kapt`]: https://kotlinlang.org/docs/reference/kapt.html
[latestapi]: https://dagger.dev/api/latest/
[mavenbadge-svg]: https://maven-badges.herokuapp.com/maven-central/com.google.dagger/dagger/badge.svg
[mavencentral]: https://search.maven.org/artifact/com.google.dagger/dagger
diff --git a/WORKSPACE b/WORKSPACE
index 8758b38f4..6c2b6b0ea 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -12,23 +12,143 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+# Declare the nested workspace so that the top-level workspace doesn't try to
+# traverse it when calling `bazel build //...`
+local_repository(
+ name = "examples_bazel",
+ path = "examples/bazel",
+)
+
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "google_bazel_common",
- strip_prefix = "bazel-common-26011657fee96a949c66500b1662c4c7288a4968",
- urls = ["https://github.com/google/bazel-common/archive/26011657fee96a949c66500b1662c4c7288a4968.zip"],
+ sha256 = "d8aa0ef609248c2a494d5dbdd4c89ef2a527a97c5a87687e5a218eb0b77ff640",
+ strip_prefix = "bazel-common-4a8d451e57fb7e1efecbf9495587a10684a19eb2",
+ urls = ["https://github.com/google/bazel-common/archive/4a8d451e57fb7e1efecbf9495587a10684a19eb2.zip"],
)
load("@google_bazel_common//:workspace_defs.bzl", "google_common_workspace_rules")
google_common_workspace_rules()
-# This fixes an issue with protobuf starting to use zlib by default in 3.7.0.
-# TODO(ronshapiro): Figure out if this is in fact necessary, or if proto can depend on the
+RULES_JVM_EXTERNAL_TAG = "2.7"
+
+RULES_JVM_EXTERNAL_SHA = "f04b1466a00a2845106801e0c5cec96841f49ea4e7d1df88dc8e4bf31523df74"
+
+http_archive(
+ name = "rules_jvm_external",
+ sha256 = RULES_JVM_EXTERNAL_SHA,
+ strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,
+ url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG,
+)
+
+# rules_python and zlib are required by protobuf.
+# TODO(ronshapiro): Figure out if zlib is in fact necessary, or if proto can depend on the
# @bazel_tools library directly. See discussion in
# https://github.com/protocolbuffers/protobuf/pull/5389#issuecomment-481785716
-bind(
+# TODO(cpovirk): Should we eventually get rules_python from "Bazel Federation?"
+# https://github.com/bazelbuild/rules_python#getting-started
+
+http_archive(
+ name = "rules_python",
+ sha256 = "e5470e92a18aa51830db99a4d9c492cc613761d5bdb7131c04bd92b9834380f6",
+ strip_prefix = "rules_python-4b84ad270387a7c439ebdccfd530e2339601ef27",
+ urls = ["https://github.com/bazelbuild/rules_python/archive/4b84ad270387a7c439ebdccfd530e2339601ef27.tar.gz"],
+)
+
+http_archive(
name = "zlib",
- actual = "@bazel_tools//third_party/zlib",
+ build_file = "@com_google_protobuf//:third_party/zlib.BUILD",
+ sha256 = "629380c90a77b964d896ed37163f5c3a34f6e6d897311f1df2a7016355c45eff",
+ strip_prefix = "zlib-1.2.11",
+ urls = ["https://github.com/madler/zlib/archive/v1.2.11.tar.gz"],
+)
+
+RULES_KOTLIN_COMMIT = "2c283821911439e244285b5bfec39148e7d90e21"
+
+RULES_KOTLIN_SHA = "b04cd539e7e3571745179da95069586b6fa76a64306b24bb286154e652010608"
+
+http_archive(
+ name = "io_bazel_rules_kotlin",
+ sha256 = RULES_KOTLIN_SHA,
+ strip_prefix = "rules_kotlin-%s" % RULES_KOTLIN_COMMIT,
+ type = "zip",
+ urls = ["https://github.com/bazelbuild/rules_kotlin/archive/%s.zip" % RULES_KOTLIN_COMMIT],
+)
+
+load("@io_bazel_rules_kotlin//kotlin:dependencies.bzl", "kt_download_local_dev_dependencies")
+
+kt_download_local_dev_dependencies()
+
+load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kotlin_repositories")
+
+KOTLIN_VERSION = "1.4.20"
+
+KOTLINC_RELEASE_SHA = "11db93a4d6789e3406c7f60b9f267eba26d6483dcd771eff9f85bb7e9837011f"
+
+KOTLINC_RELEASE = {
+ "sha256": KOTLINC_RELEASE_SHA,
+ "urls": ["https://github.com/JetBrains/kotlin/releases/download/v{v}/kotlin-compiler-{v}.zip".format(v = KOTLIN_VERSION)],
+}
+
+kotlin_repositories(compiler_release = KOTLINC_RELEASE)
+
+register_toolchains("//:kotlin_toolchain")
+
+load("@rules_jvm_external//:defs.bzl", "maven_install")
+
+ANDROID_LINT_VERSION = "26.6.2"
+
+maven_install(
+ artifacts = [
+ "androidx.annotation:annotation:1.1.0",
+ "androidx.appcompat:appcompat:1.2.0",
+ "androidx.activity:activity:1.1.0",
+ "androidx.fragment:fragment:1.2.5",
+ "androidx.lifecycle:lifecycle-viewmodel:2.2.0",
+ "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0",
+ "androidx.multidex:multidex:2.0.1",
+ "androidx.savedstate:savedstate:1.0.0",
+ "androidx.test:monitor:1.1.1",
+ "androidx.test:core:1.1.0",
+ "com.google.auto:auto-common:0.11",
+ "com.android.support:appcompat-v7:25.0.0",
+ "com.android.support:support-annotations:25.0.0",
+ "com.android.support:support-fragment:25.0.0",
+ "com.android.tools.external.org-jetbrains:uast:%s" % ANDROID_LINT_VERSION,
+ "com.android.tools.external.com-intellij:intellij-core:%s" % ANDROID_LINT_VERSION,
+ "com.android.tools.external.com-intellij:kotlin-compiler:%s" % ANDROID_LINT_VERSION,
+ "com.android.tools.lint:lint:%s" % ANDROID_LINT_VERSION,
+ "com.android.tools.lint:lint-api:%s" % ANDROID_LINT_VERSION,
+ "com.android.tools.lint:lint-checks:%s" % ANDROID_LINT_VERSION,
+ "com.android.tools.lint:lint-tests:%s" % ANDROID_LINT_VERSION,
+ "com.android.tools:testutils:%s" % ANDROID_LINT_VERSION,
+ "com.github.tschuchortdev:kotlin-compile-testing:1.2.8",
+ "com.google.guava:guava:27.1-android",
+ "org.jetbrains.kotlin:kotlin-stdlib:%s" % KOTLIN_VERSION,
+ "org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.1.0",
+ "org.robolectric:robolectric:4.3.1",
+ ],
+ repositories = [
+ "https://repo1.maven.org/maven2",
+ "https://maven.google.com",
+ "https://jcenter.bintray.com/", # Lint has one trove4j dependency in jCenter
+ ],
)
+
+BAZEL_SKYLIB_VERSION = "1.0.2"
+
+BAZEL_SKYLIB_SHA = "97e70364e9249702246c0e9444bccdc4b847bed1eb03c5a3ece4f83dfe6abc44"
+
+http_archive(
+ name = "bazel_skylib",
+ sha256 = BAZEL_SKYLIB_SHA,
+ urls = [
+ "https://github.com/bazelbuild/bazel-skylib/releases/download/{version}/bazel-skylib-{version}.tar.gz".format(version = BAZEL_SKYLIB_VERSION),
+ ],
+)
+
+load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
+
+bazel_skylib_workspace()
diff --git a/android-annotation-stubs/gen_annotations.sh b/android-annotation-stubs/gen_annotations.sh
index 21aeb4696..d4a029029 100755
--- a/android-annotation-stubs/gen_annotations.sh
+++ b/android-annotation-stubs/gen_annotations.sh
@@ -59,6 +59,8 @@ cat > ${f} <<EOF
package net.ltgt.gradle.incap;
public enum IncrementalAnnotationProcessorType {
- DYNAMIC
+ AGGREGATING,
+ DYNAMIC,
+ ISOLATING
}
EOF
diff --git a/android-annotation-stubs/src/net/ltgt/gradle/incap/IncrementalAnnotationProcessorType.java b/android-annotation-stubs/src/net/ltgt/gradle/incap/IncrementalAnnotationProcessorType.java
index 83e35902b..ef86328ae 100644
--- a/android-annotation-stubs/src/net/ltgt/gradle/incap/IncrementalAnnotationProcessorType.java
+++ b/android-annotation-stubs/src/net/ltgt/gradle/incap/IncrementalAnnotationProcessorType.java
@@ -16,5 +16,7 @@
package net.ltgt.gradle.incap;
public enum IncrementalAnnotationProcessorType {
- DYNAMIC
+ AGGREGATING,
+ DYNAMIC,
+ ISOLATING
}
diff --git a/build_defs.bzl b/build_defs.bzl
index 0bd74022f..932ca086a 100644
--- a/build_defs.bzl
+++ b/build_defs.bzl
@@ -24,3 +24,8 @@ SOURCE_7_TARGET_7 = [
"-target",
"1.7",
]
+
+POM_VERSION = "2.29.1"
+
+# DO NOT remove the comment on the next line. It's used in deploy-to-maven-central.sh
+POM_VERSION_ALPHA = POM_VERSION + "-alpha"
diff --git a/examples/BUILD b/examples/BUILD
new file mode 100644
index 000000000..5cd2290cc
--- /dev/null
+++ b/examples/BUILD
@@ -0,0 +1,18 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Description:
+# Dagger examples
+
+package(default_visibility = ["//:src"])
diff --git a/examples/bazel/BUILD b/examples/bazel/BUILD
new file mode 100644
index 000000000..0f9a621b0
--- /dev/null
+++ b/examples/bazel/BUILD
@@ -0,0 +1,22 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Description:
+# Dagger Bazel examples
+
+load("@dagger//:workspace_defs.bzl", "dagger_rules", "hilt_android_rules")
+
+dagger_rules()
+
+hilt_android_rules()
diff --git a/examples/bazel/WORKSPACE b/examples/bazel/WORKSPACE
new file mode 100644
index 000000000..23f93b11c
--- /dev/null
+++ b/examples/bazel/WORKSPACE
@@ -0,0 +1,89 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Description:
+# Defines the Bazel workspace rules for the Dagger examples.
+
+########################
+# Load Dagger repository
+########################
+
+# TODO(bcorso): Replace with `http_archive` pointing to tagged released.
+local_repository(
+ name = "dagger",
+ path = "../../",
+)
+
+load(
+ "@dagger//:workspace_defs.bzl",
+ "DAGGER_ARTIFACTS",
+ "DAGGER_REPOSITORIES",
+ "HILT_ANDROID_ARTIFACTS",
+ "HILT_ANDROID_REPOSITORIES",
+)
+
+#########################
+# Load Android repository
+#########################
+
+android_sdk_repository(
+ name = "androidsdk",
+ api_level = 30,
+ build_tools_version = "30.0.2",
+)
+
+#############################
+# Load Robolectric repository
+#############################
+
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+http_archive(
+ name = "robolectric",
+ strip_prefix = "robolectric-bazel-4.1",
+ urls = ["https://github.com/robolectric/robolectric-bazel/archive/4.1.tar.gz"],
+)
+
+load("@robolectric//bazel:robolectric.bzl", "robolectric_repositories")
+
+robolectric_repositories()
+
+#########################
+# Load Maven repositories
+#########################
+
+RULES_JVM_EXTERNAL_TAG = "2.7"
+
+RULES_JVM_EXTERNAL_SHA = "f04b1466a00a2845106801e0c5cec96841f49ea4e7d1df88dc8e4bf31523df74"
+
+http_archive(
+ name = "rules_jvm_external",
+ sha256 = RULES_JVM_EXTERNAL_SHA,
+ strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,
+ url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG,
+)
+
+load("@rules_jvm_external//:defs.bzl", "maven_install")
+
+maven_install(
+ artifacts = DAGGER_ARTIFACTS + HILT_ANDROID_ARTIFACTS + [
+ "androidx.test.ext:junit:1.1.1",
+ "androidx.test:runner:1.1.1",
+ "com.google.truth:truth:1.0.1",
+ "junit:junit:4.13",
+ "org.robolectric:robolectric:4.1",
+ "org.robolectric:annotations:4.1",
+ ],
+ repositories = DAGGER_REPOSITORIES + HILT_ANDROID_REPOSITORIES,
+)
diff --git a/examples/bazel/java/example/common/BUILD b/examples/bazel/java/example/common/BUILD
new file mode 100644
index 000000000..4de78ae2c
--- /dev/null
+++ b/examples/bazel/java/example/common/BUILD
@@ -0,0 +1,25 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+java_library(
+ name = "common",
+ srcs = glob(["*.java"]),
+ visibility = [
+ "//java/example:__subpackages__",
+ "//javatests/example:__subpackages__",
+ ],
+ deps = [
+ "//:dagger",
+ ],
+)
diff --git a/examples/bazel/java/example/common/CoffeeLogger.java b/examples/bazel/java/example/common/CoffeeLogger.java
new file mode 100644
index 000000000..10c1849bc
--- /dev/null
+++ b/examples/bazel/java/example/common/CoffeeLogger.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.common;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** A logger to logs steps while brewing coffee. */
+@Singleton
+public final class CoffeeLogger {
+ private final List<String> logs = new ArrayList<>();
+
+ @Inject
+ CoffeeLogger() {}
+
+ public void log(String msg) {
+ logs.add(msg);
+ }
+
+ public List<String> logs() {
+ return new ArrayList<>(logs);
+ }
+}
diff --git a/examples/bazel/java/example/common/CoffeeMaker.java b/examples/bazel/java/example/common/CoffeeMaker.java
new file mode 100644
index 000000000..796638d6a
--- /dev/null
+++ b/examples/bazel/java/example/common/CoffeeMaker.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.common;
+
+import dagger.Lazy;
+import javax.inject.Inject;
+
+/** A coffee maker to brew the coffee. */
+public class CoffeeMaker {
+ private final CoffeeLogger logger;
+ private final Lazy<Heater> heater; // Create a possibly costly heater only when we use it.
+ private final Pump pump;
+
+ @Inject
+ CoffeeMaker(CoffeeLogger logger, Lazy<Heater> heater, Pump pump) {
+ this.logger = logger;
+ this.heater = heater;
+ this.pump = pump;
+ }
+
+ public void brew() {
+ heater.get().on();
+ pump.pump();
+ logger.log(" [_]P coffee! [_]P ");
+ heater.get().off();
+ }
+}
diff --git a/examples/bazel/java/example/common/ElectricHeater.java b/examples/bazel/java/example/common/ElectricHeater.java
new file mode 100644
index 000000000..981a41a78
--- /dev/null
+++ b/examples/bazel/java/example/common/ElectricHeater.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.common;
+
+import javax.inject.Inject;
+
+/** An electric heater to heat the coffee. */
+public class ElectricHeater implements Heater {
+
+ private final CoffeeLogger logger;
+ private boolean heating;
+
+ @Inject
+ ElectricHeater(CoffeeLogger logger) {
+ this.logger = logger;
+ }
+
+ @Override
+ public void on() {
+ this.heating = true;
+ logger.log("~ ~ ~ heating ~ ~ ~");
+ }
+
+ @Override
+ public void off() {
+ this.heating = false;
+ }
+
+ @Override
+ public boolean isHot() {
+ return heating;
+ }
+}
diff --git a/examples/bazel/java/example/common/Heater.java b/examples/bazel/java/example/common/Heater.java
new file mode 100644
index 000000000..9f9df5065
--- /dev/null
+++ b/examples/bazel/java/example/common/Heater.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.common;
+
+/** A heater to heat the coffee. */
+public interface Heater {
+ void on();
+ void off();
+ boolean isHot();
+}
diff --git a/examples/bazel/java/example/common/Pump.java b/examples/bazel/java/example/common/Pump.java
new file mode 100644
index 000000000..4a627cf3e
--- /dev/null
+++ b/examples/bazel/java/example/common/Pump.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.common;
+
+/** A pump to pump the coffee. */
+public interface Pump {
+ void pump();
+}
diff --git a/examples/bazel/java/example/common/Thermosiphon.java b/examples/bazel/java/example/common/Thermosiphon.java
new file mode 100644
index 000000000..6509c80f6
--- /dev/null
+++ b/examples/bazel/java/example/common/Thermosiphon.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.common;
+
+import javax.inject.Inject;
+
+/** A thermosiphon to pump the coffee. */
+public class Thermosiphon implements Pump {
+ private final CoffeeLogger logger;
+ private final Heater heater;
+
+ @Inject
+ Thermosiphon(CoffeeLogger logger, Heater heater) {
+ this.logger = logger;
+ this.heater = heater;
+ }
+
+ @Override
+ public void pump() {
+ if (heater.isHot()) {
+ logger.log("=> => pumping => =>");
+ }
+ }
+}
diff --git a/examples/bazel/java/example/dagger/BUILD b/examples/bazel/java/example/dagger/BUILD
new file mode 100644
index 000000000..7af63b9ba
--- /dev/null
+++ b/examples/bazel/java/example/dagger/BUILD
@@ -0,0 +1,23 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+java_binary(
+ name = "dagger",
+ srcs = glob(["*.java"]),
+ main_class = "dagger.coffee.CoffeeApp",
+ deps = [
+ "//:dagger",
+ "//java/example/common",
+ ],
+)
diff --git a/examples/bazel/java/example/dagger/CoffeeApp.java b/examples/bazel/java/example/dagger/CoffeeApp.java
new file mode 100644
index 000000000..624fdf7c2
--- /dev/null
+++ b/examples/bazel/java/example/dagger/CoffeeApp.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.dagger;
+
+import dagger.Component;
+import example.common.CoffeeLogger;
+import example.common.CoffeeMaker;
+import javax.inject.Singleton;
+
+/** The main app responsible for brewing the coffee and printing the logs. */
+public class CoffeeApp {
+ @Singleton
+ @Component(
+ modules = {
+ HeaterModule.class,
+ PumpModule.class
+ }
+ )
+ public interface CoffeeShop {
+ CoffeeMaker maker();
+ CoffeeLogger logger();
+ }
+
+ public static void main(String[] args) {
+ CoffeeShop coffeeShop = DaggerCoffeeApp_CoffeeShop.builder().build();
+ coffeeShop.maker().brew();
+ coffeeShop.logger().logs().forEach(log -> System.out.println(log));
+ }
+}
diff --git a/examples/bazel/java/example/dagger/HeaterModule.java b/examples/bazel/java/example/dagger/HeaterModule.java
new file mode 100644
index 000000000..0709222b7
--- /dev/null
+++ b/examples/bazel/java/example/dagger/HeaterModule.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.dagger;
+
+import dagger.Binds;
+import dagger.Module;
+import example.common.ElectricHeater;
+import example.common.Heater;
+import javax.inject.Singleton;
+
+@Module
+interface HeaterModule {
+ @Binds
+ @Singleton
+ Heater bindHeater(ElectricHeater impl);
+}
diff --git a/examples/bazel/java/example/dagger/PumpModule.java b/examples/bazel/java/example/dagger/PumpModule.java
new file mode 100644
index 000000000..3af70524f
--- /dev/null
+++ b/examples/bazel/java/example/dagger/PumpModule.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.dagger;
+
+import dagger.Binds;
+import dagger.Module;
+import example.common.Pump;
+import example.common.Thermosiphon;
+
+@Module
+abstract class PumpModule {
+ @Binds
+ abstract Pump providePump(Thermosiphon pump);
+}
diff --git a/examples/bazel/java/example/hilt/AndroidManifest.xml b/examples/bazel/java/example/hilt/AndroidManifest.xml
new file mode 100644
index 000000000..a7867373b
--- /dev/null
+++ b/examples/bazel/java/example/hilt/AndroidManifest.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="example.hilt">
+
+ <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="28"/>
+
+ <application android:name=".CoffeeApp">
+ </application>
+</manifest>
diff --git a/examples/bazel/java/example/hilt/BUILD b/examples/bazel/java/example/hilt/BUILD
new file mode 100644
index 000000000..e890401a4
--- /dev/null
+++ b/examples/bazel/java/example/hilt/BUILD
@@ -0,0 +1,48 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+android_binary(
+ name = "hilt",
+ srcs = ["CoffeeApp.java"],
+ manifest = "AndroidManifest.xml",
+ resource_files = glob(["res/**"]),
+ deps = [
+ ":heater_module",
+ ":pump_module",
+ "//:hilt-android",
+ "//java/example/common",
+ ],
+)
+
+android_library(
+ name = "heater_module",
+ srcs = ["HeaterModule.java"],
+ manifest = "AndroidManifest.xml",
+ visibility = ["//javatests/example/hilt:__pkg__"],
+ deps = [
+ "//:hilt-android",
+ "//java/example/common",
+ ],
+)
+
+android_library(
+ name = "pump_module",
+ srcs = ["PumpModule.java"],
+ manifest = "AndroidManifest.xml",
+ visibility = ["//javatests/example/hilt:__pkg__"],
+ deps = [
+ "//:hilt-android",
+ "//java/example/common",
+ ],
+)
diff --git a/examples/bazel/java/example/hilt/CoffeeApp.java b/examples/bazel/java/example/hilt/CoffeeApp.java
new file mode 100644
index 000000000..0ff721255
--- /dev/null
+++ b/examples/bazel/java/example/hilt/CoffeeApp.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.hilt;
+
+import android.app.Application;
+import dagger.hilt.android.HiltAndroidApp;
+import example.common.CoffeeLogger;
+import example.common.CoffeeMaker;
+import javax.inject.Inject;
+
+/** The main app responsible for brewing the coffee and printing the logs. */
+@HiltAndroidApp(Application.class)
+public class CoffeeApp extends Hilt_CoffeeApp {
+
+ @Inject CoffeeMaker maker;
+ @Inject CoffeeLogger logger;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ maker.brew();
+ logger.logs().forEach(log -> System.out.println(log));
+ }
+}
diff --git a/examples/bazel/java/example/hilt/HeaterModule.java b/examples/bazel/java/example/hilt/HeaterModule.java
new file mode 100644
index 000000000..122bec0b4
--- /dev/null
+++ b/examples/bazel/java/example/hilt/HeaterModule.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.hilt;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.hilt.InstallIn;
+import dagger.hilt.components.SingletonComponent;
+import example.common.ElectricHeater;
+import example.common.Heater;
+import javax.inject.Singleton;
+
+@Module
+@InstallIn(SingletonComponent.class)
+interface HeaterModule {
+ @Binds
+ @Singleton
+ Heater bindHeater(ElectricHeater impl);
+}
diff --git a/examples/bazel/java/example/hilt/PumpModule.java b/examples/bazel/java/example/hilt/PumpModule.java
new file mode 100644
index 000000000..0ad390311
--- /dev/null
+++ b/examples/bazel/java/example/hilt/PumpModule.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.hilt;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.hilt.InstallIn;
+import dagger.hilt.components.SingletonComponent;
+import example.common.Pump;
+import example.common.Thermosiphon;
+
+@Module
+@InstallIn(SingletonComponent.class)
+abstract class PumpModule {
+ @Binds
+ abstract Pump providePump(Thermosiphon pump);
+}
diff --git a/examples/bazel/javatests/example/hilt/BUILD b/examples/bazel/javatests/example/hilt/BUILD
new file mode 100644
index 000000000..9982fbd09
--- /dev/null
+++ b/examples/bazel/javatests/example/hilt/BUILD
@@ -0,0 +1,55 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+android_local_test(
+ name = "CoffeeAppFakePumpTest",
+ srcs = [
+ "CoffeeAppFakePumpTest.java",
+ ],
+ manifest_values = {
+ "minSdkVersion": "15",
+ },
+ deps = [
+ "//:hilt-android-testing",
+ "//java/example/common",
+ "//java/example/hilt:heater_module",
+ "@maven//:androidx_test_ext_junit",
+ "@maven//:androidx_test_runner",
+ "@maven//:com_google_truth_truth",
+ "@maven//:org_robolectric_annotations",
+ "@maven//:org_robolectric_robolectric",
+ "@robolectric//bazel:android-all",
+ ],
+)
+
+android_local_test(
+ name = "CoffeeAppFakeHeaterTest",
+ srcs = [
+ "CoffeeAppFakeHeaterTest.java",
+ ],
+ manifest_values = {
+ "minSdkVersion": "15",
+ },
+ deps = [
+ "//:hilt-android-testing",
+ "//java/example/common",
+ "//java/example/hilt:pump_module",
+ "@maven//:androidx_test_ext_junit",
+ "@maven//:androidx_test_runner",
+ "@maven//:com_google_truth_truth",
+ "@maven//:org_robolectric_annotations",
+ "@maven//:org_robolectric_robolectric",
+ "@robolectric//bazel:android-all",
+ ],
+)
diff --git a/examples/bazel/javatests/example/hilt/CoffeeAppFakeHeaterTest.java b/examples/bazel/javatests/example/hilt/CoffeeAppFakeHeaterTest.java
new file mode 100644
index 000000000..5f4e162dc
--- /dev/null
+++ b/examples/bazel/javatests/example/hilt/CoffeeAppFakeHeaterTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.hilt;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.Build;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.hilt.android.testing.BindValue;
+import dagger.hilt.android.testing.HiltAndroidRule;
+import dagger.hilt.android.testing.HiltAndroidTest;
+import dagger.hilt.android.testing.HiltTestApplication;
+import example.common.CoffeeLogger;
+import example.common.CoffeeMaker;
+import example.common.Heater;
+import javax.inject.Inject;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+/** Tests using a fake heater. */
+@HiltAndroidTest
+@Config(sdk = {Build.VERSION_CODES.P}, application = HiltTestApplication.class)
+@RunWith(AndroidJUnit4.class)
+public final class CoffeeAppFakeHeaterTest {
+ public @Rule HiltAndroidRule rule = new HiltAndroidRule(this);
+
+ final class FakeHeater implements Heater {
+ private boolean heating;
+
+ @Override
+ public void on() {
+ this.heating = true;
+ logger.log("~ ~ ~ fake heating ~ ~ ~");
+ }
+
+ @Override
+ public void off() {
+ this.heating = false;
+ }
+
+ @Override
+ public boolean isHot() {
+ return heating;
+ }
+ }
+
+ @Inject CoffeeMaker maker;
+ @Inject CoffeeLogger logger;
+
+ @BindValue Heater fakeHeater = new FakeHeater();
+
+ @Test
+ public void testApplicationClass() throws Exception {
+ assertThat((Context) ApplicationProvider.getApplicationContext())
+ .isInstanceOf(HiltTestApplication.class);
+ }
+
+ @Test
+ public void testLogs() throws Exception {
+ rule.inject();
+ assertThat(logger.logs()).isEmpty();
+ maker.brew();
+ assertThat(logger.logs())
+ .containsExactly(
+ "~ ~ ~ fake heating ~ ~ ~",
+ "=> => pumping => =>",
+ " [_]P coffee! [_]P ")
+ .inOrder();
+ }
+}
diff --git a/examples/bazel/javatests/example/hilt/CoffeeAppFakePumpTest.java b/examples/bazel/javatests/example/hilt/CoffeeAppFakePumpTest.java
new file mode 100644
index 000000000..6d8927074
--- /dev/null
+++ b/examples/bazel/javatests/example/hilt/CoffeeAppFakePumpTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.hilt;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.Build;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.hilt.android.testing.BindValue;
+import dagger.hilt.android.testing.HiltAndroidRule;
+import dagger.hilt.android.testing.HiltAndroidTest;
+import dagger.hilt.android.testing.HiltTestApplication;
+import example.common.CoffeeLogger;
+import example.common.CoffeeMaker;
+import example.common.Pump;
+import javax.inject.Inject;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+/** Tests using a fake pump. */
+@HiltAndroidTest
+@Config(sdk = {Build.VERSION_CODES.P}, application = HiltTestApplication.class)
+@RunWith(AndroidJUnit4.class)
+public final class CoffeeAppFakePumpTest {
+ public @Rule HiltAndroidRule rule = new HiltAndroidRule(this);
+
+ @Inject CoffeeMaker maker;
+ @Inject CoffeeLogger logger;
+
+ @BindValue Pump fakePump = () -> logger.log("=> => fake pumping => =>");
+
+ @Test
+ public void testApplicationClass() throws Exception {
+ assertThat((Context) ApplicationProvider.getApplicationContext())
+ .isInstanceOf(HiltTestApplication.class);
+ }
+
+ @Test
+ public void testLogs() throws Exception {
+ rule.inject();
+ assertThat(logger.logs()).isEmpty();
+ maker.brew();
+ assertThat(logger.logs())
+ .containsExactly(
+ "~ ~ ~ heating ~ ~ ~",
+ "=> => fake pumping => =>",
+ " [_]P coffee! [_]P ")
+ .inOrder();
+ }
+}
diff --git a/examples/maven/coffee/pom.xml b/examples/maven/coffee/pom.xml
new file mode 100644
index 000000000..a196d997d
--- /dev/null
+++ b/examples/maven/coffee/pom.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2012 The Dagger Authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.google.dagger.examples</groupId>
+ <artifactId>parent</artifactId>
+ <version>LOCAL-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>coffee</artifactId>
+ <name>Examples: Coffee</name>
+
+ <dependencies>
+ <dependency>
+ <!-- Force the correct version of Guava to be on the classpath. -->
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.dagger</groupId>
+ <artifactId>dagger</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.6.1</version>
+ <configuration>
+ <annotationProcessorPaths>
+ <path>
+ <groupId>com.google.dagger</groupId>
+ <artifactId>dagger-compiler</artifactId>
+ <version>${project.version}</version>
+ </path>
+ </annotationProcessorPaths>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/examples/maven/coffee/src/main/java/example/dagger/CoffeeApp.java b/examples/maven/coffee/src/main/java/example/dagger/CoffeeApp.java
new file mode 100644
index 000000000..723ab919c
--- /dev/null
+++ b/examples/maven/coffee/src/main/java/example/dagger/CoffeeApp.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.dagger;
+
+import dagger.Component;
+import javax.inject.Singleton;
+
+/** The main app responsible for brewing the coffee and printing the logs. */
+public class CoffeeApp {
+ @Singleton
+ @Component(
+ modules = {
+ HeaterModule.class,
+ PumpModule.class
+ }
+ )
+ public interface CoffeeShop {
+ CoffeeMaker maker();
+ CoffeeLogger logger();
+ }
+
+ public static void main(String[] args) {
+ CoffeeShop coffeeShop = DaggerCoffeeApp_CoffeeShop.builder().build();
+ coffeeShop.maker().brew();
+ coffeeShop.logger().logs().forEach(log -> System.out.println(log));
+ }
+}
diff --git a/examples/maven/coffee/src/main/java/example/dagger/CoffeeLogger.java b/examples/maven/coffee/src/main/java/example/dagger/CoffeeLogger.java
new file mode 100644
index 000000000..16d2bdbc6
--- /dev/null
+++ b/examples/maven/coffee/src/main/java/example/dagger/CoffeeLogger.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.dagger;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** A logger to logs steps while brewing coffee. */
+@Singleton
+public final class CoffeeLogger {
+ private final List<String> logs = new ArrayList<>();
+
+ @Inject
+ CoffeeLogger() {}
+
+ public void log(String msg) {
+ logs.add(msg);
+ }
+
+ public List<String> logs() {
+ return new ArrayList<>(logs);
+ }
+}
diff --git a/examples/maven/coffee/src/main/java/example/dagger/CoffeeMaker.java b/examples/maven/coffee/src/main/java/example/dagger/CoffeeMaker.java
new file mode 100644
index 000000000..20c1f27a8
--- /dev/null
+++ b/examples/maven/coffee/src/main/java/example/dagger/CoffeeMaker.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.dagger;
+
+import dagger.Lazy;
+import javax.inject.Inject;
+
+/** A coffee maker to brew the coffee. */
+public class CoffeeMaker {
+ private final CoffeeLogger logger;
+ private final Lazy<Heater> heater; // Create a possibly costly heater only when we use it.
+ private final Pump pump;
+
+ @Inject
+ CoffeeMaker(CoffeeLogger logger, Lazy<Heater> heater, Pump pump) {
+ this.logger = logger;
+ this.heater = heater;
+ this.pump = pump;
+ }
+
+ public void brew() {
+ heater.get().on();
+ pump.pump();
+ logger.log(" [_]P coffee! [_]P ");
+ heater.get().off();
+ }
+}
diff --git a/examples/maven/coffee/src/main/java/example/dagger/ElectricHeater.java b/examples/maven/coffee/src/main/java/example/dagger/ElectricHeater.java
new file mode 100644
index 000000000..567c7aa6b
--- /dev/null
+++ b/examples/maven/coffee/src/main/java/example/dagger/ElectricHeater.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.dagger;
+
+import javax.inject.Inject;
+
+/** An electric heater to heat the coffee. */
+public class ElectricHeater implements Heater {
+
+ private final CoffeeLogger logger;
+ private boolean heating;
+
+ @Inject
+ ElectricHeater(CoffeeLogger logger) {
+ this.logger = logger;
+ }
+
+ @Override
+ public void on() {
+ this.heating = true;
+ logger.log("~ ~ ~ heating ~ ~ ~");
+ }
+
+ @Override
+ public void off() {
+ this.heating = false;
+ }
+
+ @Override
+ public boolean isHot() {
+ return heating;
+ }
+}
diff --git a/examples/maven/coffee/src/main/java/example/dagger/Heater.java b/examples/maven/coffee/src/main/java/example/dagger/Heater.java
new file mode 100644
index 000000000..3d6d1f2b4
--- /dev/null
+++ b/examples/maven/coffee/src/main/java/example/dagger/Heater.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.dagger;
+
+/** A heater to heat the coffee. */
+public interface Heater {
+ void on();
+ void off();
+ boolean isHot();
+}
diff --git a/examples/maven/coffee/src/main/java/example/dagger/HeaterModule.java b/examples/maven/coffee/src/main/java/example/dagger/HeaterModule.java
new file mode 100644
index 000000000..fb8c9691b
--- /dev/null
+++ b/examples/maven/coffee/src/main/java/example/dagger/HeaterModule.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.dagger;
+
+import dagger.Binds;
+import dagger.Module;
+import javax.inject.Singleton;
+
+@Module
+interface HeaterModule {
+ @Binds
+ @Singleton
+ Heater bindHeater(ElectricHeater impl);
+}
diff --git a/examples/maven/coffee/src/main/java/example/dagger/Pump.java b/examples/maven/coffee/src/main/java/example/dagger/Pump.java
new file mode 100644
index 000000000..712b21c43
--- /dev/null
+++ b/examples/maven/coffee/src/main/java/example/dagger/Pump.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.dagger;
+
+/** A pump to pump the coffee. */
+public interface Pump {
+ void pump();
+}
diff --git a/examples/maven/coffee/src/main/java/example/dagger/PumpModule.java b/examples/maven/coffee/src/main/java/example/dagger/PumpModule.java
new file mode 100644
index 000000000..202fed3b6
--- /dev/null
+++ b/examples/maven/coffee/src/main/java/example/dagger/PumpModule.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.dagger;
+
+import dagger.Binds;
+import dagger.Module;
+
+@Module
+abstract class PumpModule {
+ @Binds
+ abstract Pump providePump(Thermosiphon pump);
+}
diff --git a/examples/maven/coffee/src/main/java/example/dagger/Thermosiphon.java b/examples/maven/coffee/src/main/java/example/dagger/Thermosiphon.java
new file mode 100644
index 000000000..d94c33f22
--- /dev/null
+++ b/examples/maven/coffee/src/main/java/example/dagger/Thermosiphon.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package example.dagger;
+
+import javax.inject.Inject;
+
+/** A thermosiphon to pump the coffee. */
+public class Thermosiphon implements Pump {
+ private final CoffeeLogger logger;
+ private final Heater heater;
+
+ @Inject
+ Thermosiphon(CoffeeLogger logger, Heater heater) {
+ this.logger = logger;
+ this.heater = heater;
+ }
+
+ @Override
+ public void pump() {
+ if (heater.isHot()) {
+ logger.log("=> => pumping => =>");
+ }
+ }
+}
diff --git a/examples/maven/pom.xml b/examples/maven/pom.xml
new file mode 100644
index 000000000..be21b9649
--- /dev/null
+++ b/examples/maven/pom.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2013 The Dagger Authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.sonatype.oss</groupId>
+ <artifactId>oss-parent</artifactId>
+ <version>9</version>
+ </parent>
+
+ <groupId>com.google.dagger.examples</groupId>
+ <artifactId>parent</artifactId>
+ <packaging>pom</packaging>
+ <name>Examples</name>
+ <version>LOCAL-SNAPSHOT</version>
+
+ <modules>
+ <module>coffee</module>
+ </modules>
+
+ <!-- Example-only dependencies. -->
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>com.google.dagger</groupId>
+ <artifactId>dagger</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.dagger</groupId>
+ <artifactId>dagger-compiler</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>26.0-jre</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+</project>
diff --git a/examples/pom.xml b/examples/pom.xml
deleted file mode 100644
index c12050d10..000000000
--- a/examples/pom.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2013 The Dagger Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>org.sonatype.oss</groupId>
- <artifactId>oss-parent</artifactId>
- <version>9</version>
- </parent>
-
- <groupId>com.google.dagger.example</groupId>
- <artifactId>dagger-example-parent</artifactId>
- <packaging>pom</packaging>
- <name>Examples</name>
- <version>2.17</version>
-
- <modules>
- <module>simple</module>
- </modules>
-
- <!-- Example-only dependencies. -->
- <dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger-compiler</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- <version>26.0-jre</version>
- </dependency>
- </dependencies>
- </dependencyManagement>
-
- <build>
- <pluginManagement>
- <plugins>
- <plugin>
- <artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <source>1.6</source>
- <target>1.6</target>
- </configuration>
- </plugin>
- </plugins>
- </pluginManagement>
- </build>
-</project>
diff --git a/examples/simple/pom.xml b/examples/simple/pom.xml
deleted file mode 100644
index ffc99dc15..000000000
--- a/examples/simple/pom.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2012 The Dagger Authors.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>com.google.dagger.example</groupId>
- <artifactId>dagger-example-parent</artifactId>
- <version>2.17</version>
- </parent>
-
- <artifactId>simple</artifactId>
- <name>Examples: Simple</name>
-
- <dependencies>
- <dependency>
- <!-- Force the correct version of Guava to be on the classpath. -->
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger</artifactId>
- </dependency>
- </dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>3.6.1</version>
- <configuration>
- <annotationProcessorPaths>
- <path>
- <groupId>com.google.dagger</groupId>
- <artifactId>dagger-compiler</artifactId>
- <version>${project.version}</version>
- </path>
- </annotationProcessorPaths>
- </configuration>
- </plugin>
- </plugins>
- </build>
-</project>
diff --git a/examples/simple/src/main/java/coffee/CoffeeApp.java b/examples/simple/src/main/java/coffee/CoffeeApp.java
deleted file mode 100644
index 7a2b9a6c3..000000000
--- a/examples/simple/src/main/java/coffee/CoffeeApp.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package coffee;
-
-import dagger.Component;
-import javax.inject.Singleton;
-
-public class CoffeeApp {
- @Singleton
- @Component(modules = { DripCoffeeModule.class })
- public interface CoffeeShop {
- CoffeeMaker maker();
- }
-
- public static void main(String[] args) {
- CoffeeShop coffeeShop = DaggerCoffeeApp_CoffeeShop.builder().build();
- coffeeShop.maker().brew();
- }
-}
diff --git a/examples/simple/src/main/java/coffee/CoffeeMaker.java b/examples/simple/src/main/java/coffee/CoffeeMaker.java
deleted file mode 100644
index 6410336d0..000000000
--- a/examples/simple/src/main/java/coffee/CoffeeMaker.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package coffee;
-
-import dagger.Lazy;
-import javax.inject.Inject;
-
-class CoffeeMaker {
- private final Lazy<Heater> heater; // Create a possibly costly heater only when we use it.
- private final Pump pump;
-
- @Inject CoffeeMaker(Lazy<Heater> heater, Pump pump) {
- this.heater = heater;
- this.pump = pump;
- }
-
- public void brew() {
- heater.get().on();
- pump.pump();
- System.out.println(" [_]P coffee! [_]P ");
- heater.get().off();
- }
-}
diff --git a/examples/simple/src/main/java/coffee/DripCoffeeModule.java b/examples/simple/src/main/java/coffee/DripCoffeeModule.java
deleted file mode 100644
index e50d249e1..000000000
--- a/examples/simple/src/main/java/coffee/DripCoffeeModule.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package coffee;
-
-import dagger.Module;
-import dagger.Provides;
-import javax.inject.Singleton;
-
-@Module(includes = PumpModule.class)
-class DripCoffeeModule {
- @Provides @Singleton Heater provideHeater() {
- return new ElectricHeater();
- }
-}
diff --git a/examples/simple/src/main/java/coffee/ElectricHeater.java b/examples/simple/src/main/java/coffee/ElectricHeater.java
deleted file mode 100644
index fbab399f2..000000000
--- a/examples/simple/src/main/java/coffee/ElectricHeater.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package coffee;
-
-class ElectricHeater implements Heater {
- boolean heating;
-
- @Override public void on() {
- System.out.println("~ ~ ~ heating ~ ~ ~");
- this.heating = true;
- }
-
- @Override public void off() {
- this.heating = false;
- }
-
- @Override public boolean isHot() {
- return heating;
- }
-}
diff --git a/examples/simple/src/main/java/coffee/Heater.java b/examples/simple/src/main/java/coffee/Heater.java
deleted file mode 100644
index b5ddb6b88..000000000
--- a/examples/simple/src/main/java/coffee/Heater.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package coffee;
-
-interface Heater {
- void on();
- void off();
- boolean isHot();
-}
diff --git a/examples/simple/src/main/java/coffee/Pump.java b/examples/simple/src/main/java/coffee/Pump.java
deleted file mode 100644
index e39434913..000000000
--- a/examples/simple/src/main/java/coffee/Pump.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package coffee;
-
-interface Pump {
- void pump();
-}
diff --git a/examples/simple/src/main/java/coffee/PumpModule.java b/examples/simple/src/main/java/coffee/PumpModule.java
deleted file mode 100644
index df00b8624..000000000
--- a/examples/simple/src/main/java/coffee/PumpModule.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package coffee;
-
-import dagger.Binds;
-import dagger.Module;
-
-@Module
-abstract class PumpModule {
- @Binds
- abstract Pump providePump(Thermosiphon pump);
-}
diff --git a/examples/simple/src/main/java/coffee/Thermosiphon.java b/examples/simple/src/main/java/coffee/Thermosiphon.java
deleted file mode 100644
index c9f9828b0..000000000
--- a/examples/simple/src/main/java/coffee/Thermosiphon.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package coffee;
-
-import javax.inject.Inject;
-
-class Thermosiphon implements Pump {
- private final Heater heater;
-
- @Inject
- Thermosiphon(Heater heater) {
- this.heater = heater;
- }
-
- @Override public void pump() {
- if (heater.isHot()) {
- System.out.println("=> => pumping => =>");
- }
- }
-}
diff --git a/gwt/BUILD b/gwt/BUILD
index da730138f..5aa9110ff 100644
--- a/gwt/BUILD
+++ b/gwt/BUILD
@@ -15,9 +15,11 @@
# Description:
# GWT-specific files for Dagger
-package(default_visibility = ["//:src"])
+load("@rules_java//java:defs.bzl", "java_library")
+load("//tools:maven.bzl", "pom_file")
+load("//:build_defs.bzl", "POM_VERSION")
-load("//tools:maven.bzl", "pom_file", "POM_VERSION")
+package(default_visibility = ["//:src"])
java_library(
name = "gwt",
diff --git a/java/dagger/BUILD b/java/dagger/BUILD
index 3485431d4..aaebadee0 100644
--- a/java/dagger/BUILD
+++ b/java/dagger/BUILD
@@ -15,14 +15,17 @@
# Description:
# A JSR-330 compliant dependency injection system for android and java
-package(default_visibility = ["//:src"])
-
+load("@rules_java//java:defs.bzl", "java_library")
load(
"//:build_defs.bzl",
"DOCLINT_HTML_AND_SYNTAX",
+ "POM_VERSION",
"SOURCE_7_TARGET_7",
)
-load("//tools:maven.bzl", "POM_VERSION", "pom_file")
+load("//tools:maven.bzl", "pom_file")
+load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
+
+package(default_visibility = ["//:src"])
java_library(
name = "core",
@@ -47,8 +50,6 @@ filegroup(
srcs = glob(["**/*"]),
)
-load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
-
javadoc_library(
name = "core-javadoc",
srcs = [":javadoc-srcs"],
diff --git a/java/dagger/Provides.java b/java/dagger/Provides.java
index a60be3b64..5d7827be7 100644
--- a/java/dagger/Provides.java
+++ b/java/dagger/Provides.java
@@ -30,11 +30,11 @@ import java.lang.annotation.Target;
*
* <h3>Nullability</h3>
*
- * <p>Dagger forbids injecting {@code null} by default. Component implemenations that invoke
+ * <p>Dagger forbids injecting {@code null} by default. Component implementations that invoke
* {@code @Provides} methods that return {@code null} will throw a {@link NullPointerException}
* immediately thereafter. {@code @Provides} methods may opt into allowing {@code null} by
* annotating the method with any {@code @Nullable} annotation like
- * {@code javax.annotation.Nullable} or {@code android.support.annotation.Nullable}.
+ * {@code javax.annotation.Nullable} or {@code androidx.annotation.Nullable}.
*
* <p>If a {@code @Provides} method is marked {@code @Nullable}, Dagger will <em>only</em>
* allow injection into sites that are marked {@code @Nullable} as well. A component that
diff --git a/java/dagger/android/AndroidInjection.java b/java/dagger/android/AndroidInjection.java
index 2f4fb4ca4..f3296c6cb 100644
--- a/java/dagger/android/AndroidInjection.java
+++ b/java/dagger/android/AndroidInjection.java
@@ -1,4 +1,4 @@
- /*
+/*
* Copyright (C) 2017 The Dagger Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -39,99 +39,71 @@ public final class AndroidInjection {
* otherwise throws an {@link IllegalArgumentException}.
*
* @throws RuntimeException if the {@link Application} doesn't implement {@link
- * HasAndroidInjector} or {@link HasActivityInjector}.
+ * HasAndroidInjector}.
*/
public static void inject(Activity activity) {
checkNotNull(activity, "activity");
Application application = activity.getApplication();
- AndroidInjector<? super Activity> injector;
- if (application instanceof HasAndroidInjector) {
- injector = ((HasAndroidInjector) application).androidInjector();
- checkNotNull(injector, "%s.androidInjector() returned null", application.getClass());
- } else if (application instanceof HasActivityInjector) {
- injector = ((HasActivityInjector) application).activityInjector();
- checkNotNull(injector, "%s.activityInjector() returned null", application.getClass());
- } else {
+ if (!(application instanceof HasAndroidInjector)) {
throw new RuntimeException(
String.format(
- "%s does not implement %s or %s",
+ "%s does not implement %s",
application.getClass().getCanonicalName(),
- HasAndroidInjector.class.getCanonicalName(),
- HasActivityInjector.class.getCanonicalName()));
+ HasAndroidInjector.class.getCanonicalName()));
}
- injector.inject(activity);
+ inject(activity, (HasAndroidInjector) application);
}
/**
* Injects {@code fragment} if an associated {@link AndroidInjector} implementation can be found,
* otherwise throws an {@link IllegalArgumentException}.
*
- * <p>Uses the following algorithm to find the appropriate {@link AndroidInjector} to use to
- * inject {@code fragment}:
+ * <p>Uses the following algorithm to find the appropriate {@code AndroidInjector<Fragment>} to
+ * use to inject {@code fragment}:
*
* <ol>
- * <li>Walks the parent-fragment hierarchy to find a fragment that implements {@link
- * HasAndroidInjector} or {@link HasFragmentInjector}, and if none do
+ * <li>Walks the parent-fragment hierarchy to find the a fragment that implements {@link
+ * HasAndroidInjector}, and if none do
* <li>Uses the {@code fragment}'s {@link Fragment#getActivity() activity} if it implements
- * {@link HasAndroidInjector} or {@link HasFragmentInjector}, and if not
- * <li>Uses the {@link android.app.Application} if it implements {@link HasAndroidInjector}
- * {@link HasFragmentInjector}.
+ * {@link HasAndroidInjector}, and if not
+ * <li>Uses the {@link android.app.Application} if it implements {@link HasAndroidInjector}.
* </ol>
*
- * If none of them implement {@link HasAndroidInjector} or {@link HasFragmentInjector}, a {@link
- * IllegalArgumentException} is thrown.
+ * If none of them implement {@link HasAndroidInjector}, a {@link IllegalArgumentException} is
+ * thrown.
*
* @throws IllegalArgumentException if no parent fragment, activity, or application implements
- * {@link HasAndroidInjector} or {@link HasFragmentInjector}.
+ * {@link HasAndroidInjector}.
*/
public static void inject(Fragment fragment) {
checkNotNull(fragment, "fragment");
-
- Object hasInjector = findHasFragmentInjector(fragment);
- AndroidInjector<? super Fragment> injector;
- if (hasInjector instanceof HasAndroidInjector) {
- injector = ((HasAndroidInjector) hasInjector).androidInjector();
- checkNotNull(injector, "%s.androidInjector() returned null", hasInjector.getClass());
- } else if (hasInjector instanceof HasFragmentInjector) {
- injector = ((HasFragmentInjector) hasInjector).fragmentInjector();
- checkNotNull(injector, "%s.fragmentInjector() returned null", hasInjector.getClass());
- } else {
- throw new RuntimeException(
- String.format(
- "%s does not implement %s or %s",
- hasInjector.getClass().getCanonicalName(),
- HasAndroidInjector.class.getCanonicalName(),
- HasFragmentInjector.class.getCanonicalName()));
- }
-
+ HasAndroidInjector hasAndroidInjector = findHasAndroidInjectorForFragment(fragment);
if (Log.isLoggable(TAG, DEBUG)) {
Log.d(
TAG,
String.format(
"An injector for %s was found in %s",
fragment.getClass().getCanonicalName(),
- hasInjector.getClass().getCanonicalName()));
+ hasAndroidInjector.getClass().getCanonicalName()));
}
- injector.inject(fragment);
+ inject(fragment, hasAndroidInjector);
}
- private static Object findHasFragmentInjector(Fragment fragment) {
+ private static HasAndroidInjector findHasAndroidInjectorForFragment(Fragment fragment) {
Fragment parentFragment = fragment;
while ((parentFragment = parentFragment.getParentFragment()) != null) {
- if (parentFragment instanceof HasAndroidInjector
- || parentFragment instanceof HasFragmentInjector) {
- return parentFragment;
+ if (parentFragment instanceof HasAndroidInjector) {
+ return (HasAndroidInjector) parentFragment;
}
}
Activity activity = fragment.getActivity();
- if (activity instanceof HasAndroidInjector || activity instanceof HasFragmentInjector) {
- return activity;
+ if (activity instanceof HasAndroidInjector) {
+ return (HasAndroidInjector) activity;
}
- Application application = activity.getApplication();
- if (application instanceof HasAndroidInjector || application instanceof HasFragmentInjector) {
- return application;
+ if (activity.getApplication() instanceof HasAndroidInjector) {
+ return (HasAndroidInjector) activity.getApplication();
}
throw new IllegalArgumentException(
String.format("No injector was found for %s", fragment.getClass().getCanonicalName()));
@@ -142,28 +114,20 @@ public final class AndroidInjection {
* otherwise throws an {@link IllegalArgumentException}.
*
* @throws RuntimeException if the {@link Application} doesn't implement {@link
- * HasAndroidInjector} or {@link HasServiceInjector}.
+ * HasAndroidInjector}.
*/
public static void inject(Service service) {
checkNotNull(service, "service");
Application application = service.getApplication();
- AndroidInjector<? super Service> injector;
- if (application instanceof HasAndroidInjector) {
- injector = ((HasAndroidInjector) application).androidInjector();
- checkNotNull(injector, "%s.androidInjector() returned null", application.getClass());
- } else if (application instanceof HasServiceInjector) {
- injector = ((HasServiceInjector) application).serviceInjector();
- checkNotNull(injector, "%s.serviceInjector() returned null", application.getClass());
- } else {
+ if (!(application instanceof HasAndroidInjector)) {
throw new RuntimeException(
String.format(
- "%s does not implement %s or %s",
+ "%s does not implement %s",
application.getClass().getCanonicalName(),
- HasAndroidInjector.class.getCanonicalName(),
- HasServiceInjector.class.getCanonicalName()));
+ HasAndroidInjector.class.getCanonicalName()));
}
- injector.inject(service);
+ inject(service, (HasAndroidInjector) application);
}
/**
@@ -171,32 +135,21 @@ public final class AndroidInjection {
* be found, otherwise throws an {@link IllegalArgumentException}.
*
* @throws RuntimeException if the {@link Application} from {@link
- * Context#getApplicationContext()} doesn't implement {@link HasAndroidInjector} or {@link
- * HasBroadcastReceiverInjector}.
+ * Context#getApplicationContext()} doesn't implement {@link HasAndroidInjector}.
*/
public static void inject(BroadcastReceiver broadcastReceiver, Context context) {
checkNotNull(broadcastReceiver, "broadcastReceiver");
checkNotNull(context, "context");
-
Application application = (Application) context.getApplicationContext();
- AndroidInjector<? super BroadcastReceiver> injector;
- if (application instanceof HasAndroidInjector) {
- injector = ((HasAndroidInjector) application).androidInjector();
- checkNotNull(injector, "%s.androidInjector() returned null", application.getClass());
- } else if (application instanceof HasBroadcastReceiverInjector) {
- injector = ((HasBroadcastReceiverInjector) application).broadcastReceiverInjector();
- checkNotNull(
- injector, "%s.broadcastReceiverInjector() returned null", application.getClass());
- } else {
+ if (!(application instanceof HasAndroidInjector)) {
throw new RuntimeException(
String.format(
- "%s does not implement %s or %s",
+ "%s does not implement %s",
application.getClass().getCanonicalName(),
- HasAndroidInjector.class.getCanonicalName(),
- HasBroadcastReceiverInjector.class.getCanonicalName()));
+ HasAndroidInjector.class.getCanonicalName()));
}
- injector.inject(broadcastReceiver);
+ inject(broadcastReceiver, (HasAndroidInjector) application);
}
/**
@@ -204,29 +157,28 @@ public final class AndroidInjection {
* found, otherwise throws an {@link IllegalArgumentException}.
*
* @throws RuntimeException if the {@link Application} doesn't implement {@link
- * HasAndroidInjector} or {@link HasContentProviderInjector}.
+ * HasAndroidInjector}.
*/
public static void inject(ContentProvider contentProvider) {
checkNotNull(contentProvider, "contentProvider");
Application application = (Application) contentProvider.getContext().getApplicationContext();
-
- AndroidInjector<? super ContentProvider> injector;
- if (application instanceof HasAndroidInjector) {
- injector = ((HasAndroidInjector) application).androidInjector();
- checkNotNull(injector, "%s.androidInjector() returned null", application.getClass());
- } else if (application instanceof HasContentProviderInjector) {
- injector = ((HasContentProviderInjector) application).contentProviderInjector();
- checkNotNull(injector, "%s.contentProviderInjector() returned null", application.getClass());
- } else {
+ if (!(application instanceof HasAndroidInjector)) {
throw new RuntimeException(
String.format(
- "%s does not implement %s or %s",
+ "%s does not implement %s",
application.getClass().getCanonicalName(),
- HasAndroidInjector.class.getCanonicalName(),
- HasBroadcastReceiverInjector.class.getCanonicalName()));
+ HasAndroidInjector.class.getCanonicalName()));
}
- injector.inject(contentProvider);
+ inject(contentProvider, (HasAndroidInjector) application);
+ }
+
+ private static void inject(Object target, HasAndroidInjector hasAndroidInjector) {
+ AndroidInjector<Object> androidInjector = hasAndroidInjector.androidInjector();
+ checkNotNull(
+ androidInjector, "%s.androidInjector() returned null", hasAndroidInjector.getClass());
+
+ androidInjector.inject(target);
}
private AndroidInjection() {}
diff --git a/java/dagger/android/AndroidInjectionKey.java b/java/dagger/android/AndroidInjectionKey.java
index d4a5d72c2..ff4a1f6f2 100644
--- a/java/dagger/android/AndroidInjectionKey.java
+++ b/java/dagger/android/AndroidInjectionKey.java
@@ -28,11 +28,7 @@ import java.lang.annotation.Target;
* #value() value} of the annotation is the canonical name of the class that will be passed to
* {@link AndroidInjector#inject(Object)}.
*
- * <p>All key strings will be obfuscated by ProGuard/R8/AppReduce if the named class is obfuscated.
- *
- * <p>
- * You should only use this annotation if you are using a version of ProGuard/R8/AppReduce that
- * supports the {@code -identifiernamestring} flag.
+ * <p>All key strings will be obfuscated by ProGuard/R8 if the named class is obfuscated.
*/
@Beta
@MapKey
diff --git a/java/dagger/android/BUILD b/java/dagger/android/BUILD
index a88d16e3e..f0cea4149 100644
--- a/java/dagger/android/BUILD
+++ b/java/dagger/android/BUILD
@@ -15,15 +15,18 @@
# Description:
# Public Dagger API for Android
-package(default_visibility = ["//:src"])
-
load(
"//:build_defs.bzl",
"DOCLINT_HTML_AND_SYNTAX",
"DOCLINT_REFERENCES",
+ "POM_VERSION",
"SOURCE_7_TARGET_7",
)
-load("//tools:maven.bzl", "POM_VERSION", "pom_file")
+load("//tools:dejetify.bzl", "dejetified_library")
+load("//tools:maven.bzl", "pom_file")
+load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
+
+package(default_visibility = ["//:src"])
# Work around b/70476182 which prevents Kythe from connecting :producers to the .java files it
# contains.
@@ -42,24 +45,18 @@ android_library(
srcs = SRCS,
javacopts = SOURCE_7_TARGET_7 + DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
manifest = "AndroidManifest.xml",
- proguard_specs = ["proguard.cfg"],
+ plugins = [
+ "//java/dagger/android/internal/proguard:plugin",
+ ],
tags = ["maven_coordinates=com.google.dagger:dagger-android:" + POM_VERSION],
+ exports = [
+ "//java/dagger/lint:lint-android-artifact-lib",
+ ],
deps = [
- ":manual-maven-deps",
"//:dagger_with_compiler",
"@google_bazel_common//third_party/java/auto:value",
"@google_bazel_common//third_party/java/error_prone:annotations",
- ],
-)
-
-# Our pom.xml generator does not have a way to add manual maven deps. This target exports the
-# targets that don't have the necessary maven_coordinates tags.
-android_library(
- name = "manual-maven-deps",
- tags = ["maven_coordinates=com.android.support:support-annotations:25.0.0"],
- visibility = ["//visibility:private"],
- exports = [
- "@androidsdk//com.android.support:support-annotations-25.0.0",
+ "@maven//:androidx_annotation_annotation",
],
)
@@ -79,12 +76,35 @@ pom_file(
targets = [":android"],
)
-load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
+dejetified_library(
+ name = "dejetified-android",
+ input = ":android.aar",
+ output = "android-legacy.aar",
+)
+
+android_library(
+ name = "legacy-deps",
+ tags = ["maven_coordinates=com.google.dagger:dagger-android-legacy:" + POM_VERSION],
+ exports = [
+ "//:dagger_with_compiler",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/error_prone:annotations",
+ "@maven//:com_android_support_support_annotations",
+ ],
+)
+
+pom_file(
+ name = "legacy-pom",
+ artifact_id = "dagger-android-legacy",
+ artifact_name = "Dagger Android Legacy",
+ packaging = "aar",
+ targets = [":legacy-deps"],
+)
javadoc_library(
name = "android-javadoc",
srcs = [":android-srcs"],
- android_api_level = 26,
+ android_api_level = 30,
exclude_packages = ["dagger.android.internal"],
root_packages = ["dagger.android"],
deps = [":android"],
diff --git a/java/dagger/android/DaggerActivity.java b/java/dagger/android/DaggerActivity.java
index 43708f3c3..9f3722324 100644
--- a/java/dagger/android/DaggerActivity.java
+++ b/java/dagger/android/DaggerActivity.java
@@ -19,7 +19,7 @@ package dagger.android;
import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
-import android.support.annotation.Nullable;
+import androidx.annotation.Nullable;
import dagger.internal.Beta;
import javax.inject.Inject;
diff --git a/java/dagger/android/DaggerBroadcastReceiver.java b/java/dagger/android/DaggerBroadcastReceiver.java
index d39aa8633..2efd724f1 100644
--- a/java/dagger/android/DaggerBroadcastReceiver.java
+++ b/java/dagger/android/DaggerBroadcastReceiver.java
@@ -19,7 +19,7 @@ package dagger.android;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.support.annotation.CallSuper;
+import androidx.annotation.CallSuper;
import dagger.internal.Beta;
/**
diff --git a/java/dagger/android/DaggerContentProvider.java b/java/dagger/android/DaggerContentProvider.java
index 4aad4859a..e1d3f5495 100644
--- a/java/dagger/android/DaggerContentProvider.java
+++ b/java/dagger/android/DaggerContentProvider.java
@@ -17,7 +17,7 @@
package dagger.android;
import android.content.ContentProvider;
-import android.support.annotation.CallSuper;
+import androidx.annotation.CallSuper;
import dagger.internal.Beta;
/** A {@link ContentProvider} that injects its members in {@link #onCreate()}. */
diff --git a/java/dagger/android/HasActivityInjector.java b/java/dagger/android/HasActivityInjector.java
deleted file mode 100644
index 136bbad68..000000000
--- a/java/dagger/android/HasActivityInjector.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.android;
-
-import android.app.Activity;
-import dagger.internal.Beta;
-
-/** Provides an {@link AndroidInjector} of {@link Activity}s. */
-@Beta
-public interface HasActivityInjector {
-
- /** Returns an {@link AndroidInjector} of {@link Activity}s. */
- AndroidInjector<Activity> activityInjector();
-}
diff --git a/java/dagger/android/HasBroadcastReceiverInjector.java b/java/dagger/android/HasBroadcastReceiverInjector.java
deleted file mode 100644
index b2aa99211..000000000
--- a/java/dagger/android/HasBroadcastReceiverInjector.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.android;
-
-import android.content.BroadcastReceiver;
-import dagger.internal.Beta;
-
-/** Provides an {@link AndroidInjector} of {@link BroadcastReceiver}s. */
-@Beta
-public interface HasBroadcastReceiverInjector {
-
- /** Returns an {@link AndroidInjector} of {@link BroadcastReceiver}s. */
- AndroidInjector<BroadcastReceiver> broadcastReceiverInjector();
-}
diff --git a/java/dagger/android/HasContentProviderInjector.java b/java/dagger/android/HasContentProviderInjector.java
deleted file mode 100644
index 997ddb88c..000000000
--- a/java/dagger/android/HasContentProviderInjector.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.android;
-
-import android.content.ContentProvider;
-import dagger.internal.Beta;
-
-/** Provides an {@link AndroidInjector} of {@link ContentProvider}s. */
-@Beta
-public interface HasContentProviderInjector {
-
- /** Returns an {@link AndroidInjector} of {@link ContentProvider}s. */
- AndroidInjector<ContentProvider> contentProviderInjector();
-}
diff --git a/java/dagger/android/HasFragmentInjector.java b/java/dagger/android/HasFragmentInjector.java
deleted file mode 100644
index 564f32d5d..000000000
--- a/java/dagger/android/HasFragmentInjector.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.android;
-
-import android.app.Fragment;
-import dagger.internal.Beta;
-
-/** Provides an {@link AndroidInjector} of {@link Fragment}s. */
-@Beta
-public interface HasFragmentInjector {
-
- /** Returns an {@link AndroidInjector} of {@link Fragment}s. */
- AndroidInjector<Fragment> fragmentInjector();
-}
diff --git a/java/dagger/android/HasServiceInjector.java b/java/dagger/android/HasServiceInjector.java
deleted file mode 100644
index d1c6a6c2a..000000000
--- a/java/dagger/android/HasServiceInjector.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.android;
-
-import android.app.Service;
-import dagger.internal.Beta;
-
-/** Provides an {@link AndroidInjector} of {@link Service}s. */
-@Beta
-public interface HasServiceInjector {
-
- /** Returns an {@link AndroidInjector} of {@link Service}s. */
- AndroidInjector<Service> serviceInjector();
-}
diff --git a/java/dagger/android/internal/AndroidInjectionKeys.java b/java/dagger/android/internal/AndroidInjectionKeys.java
index f30b92cdd..14253501e 100644
--- a/java/dagger/android/internal/AndroidInjectionKeys.java
+++ b/java/dagger/android/internal/AndroidInjectionKeys.java
@@ -20,6 +20,7 @@ package dagger.android.internal;
* An internal implementation detail of Dagger's generated code. This is not guaranteed to remain
* consistent from version to version.
*/
+@GenerateAndroidInjectionProguardRules
public final class AndroidInjectionKeys {
/**
* Accepts the fully qualified name of a class that is injected with {@code dagger.android}.
diff --git a/java/dagger/android/internal/GenerateAndroidInjectionProguardRules.java b/java/dagger/android/internal/GenerateAndroidInjectionProguardRules.java
new file mode 100644
index 000000000..46bbb6656
--- /dev/null
+++ b/java/dagger/android/internal/GenerateAndroidInjectionProguardRules.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.internal;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Our proguard rules generator needs one annotation to hook into for it to run, so we use this
+ * internally on {@link dagger.android.internal.AndroidInjectionKeys} as a throwaway for it to run.
+ * It has no other purpose.
+ */
+@Target(TYPE)
+@Retention(SOURCE)
+@interface GenerateAndroidInjectionProguardRules {}
diff --git a/java/dagger/android/internal/proguard/BUILD b/java/dagger/android/internal/proguard/BUILD
new file mode 100644
index 000000000..a11d0c07e
--- /dev/null
+++ b/java/dagger/android/internal/proguard/BUILD
@@ -0,0 +1,37 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Internal Proguard Processor
+
+load("@rules_java//java:defs.bzl", "java_library", "java_plugin")
+load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX", "DOCLINT_REFERENCES")
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "proguard-processor",
+ srcs = ["ProguardProcessor.java"],
+ javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
+ deps = [
+ "@google_bazel_common//third_party/java/auto:service",
+ ],
+)
+
+java_plugin(
+ name = "plugin",
+ generates_api = 1,
+ processor_class = "dagger.android.internal.proguard.ProguardProcessor",
+ deps = [":proguard-processor"],
+)
diff --git a/java/dagger/android/internal/proguard/ProguardProcessor.java b/java/dagger/android/internal/proguard/ProguardProcessor.java
new file mode 100644
index 000000000..49274e94f
--- /dev/null
+++ b/java/dagger/android/internal/proguard/ProguardProcessor.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.internal.proguard;
+
+import static javax.tools.StandardLocation.CLASS_OUTPUT;
+
+import com.google.auto.service.AutoService;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Set;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.Processor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * An {@linkplain Processor annotation processor} to generate dagger-android's specific proguard
+ * needs. This is only intended to run over the dagger-android project itself, as the alternative is
+ * to create an intermediary java_library for proguard rules to be consumed by the project.
+ *
+ * <p>Basic structure looks like this:
+ *
+ * <pre><code>
+ * resources/META-INF/com.android.tools/proguard/dagger-android.pro
+ * resources/META-INF/com.android.tools/r8/dagger-android.pro
+ * resources/META-INF/proguard/dagger-android.pro
+ * </code></pre>
+ */
+@AutoService(Processor.class)
+@SupportedAnnotationTypes(ProguardProcessor.GENERATE_RULES_ANNOTATION_NAME)
+public final class ProguardProcessor extends AbstractProcessor {
+
+ static final String GENERATE_RULES_ANNOTATION_NAME =
+ "dagger.android.internal.GenerateAndroidInjectionProguardRules";
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ roundEnv
+ .getElementsAnnotatedWith(
+ processingEnv.getElementUtils().getTypeElement(GENERATE_RULES_ANNOTATION_NAME))
+ .forEach(element -> generate());
+
+ return false;
+ }
+
+ private void generate() {
+ Filer filer = processingEnv.getFiler();
+
+ String errorProneRule = "-dontwarn com.google.errorprone.annotations.**\n";
+ String androidInjectionKeysRule =
+ "-identifiernamestring class dagger.android.internal.AndroidInjectionKeys {\n"
+ + " java.lang.String of(java.lang.String);\n"
+ + "}\n";
+
+ writeFile(filer, "com.android.tools/proguard", errorProneRule);
+ writeFile(filer, "com.android.tools/r8", errorProneRule + androidInjectionKeysRule);
+ writeFile(filer, "proguard", errorProneRule);
+ }
+
+ private static void writeFile(Filer filer, String intermediatePath, String contents) {
+ try (Writer writer =
+ filer
+ .createResource(
+ CLASS_OUTPUT, "", "META-INF/" + intermediatePath + "/dagger-android.pro")
+ .openWriter()) {
+ writer.write(contents);
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+}
diff --git a/java/dagger/android/processor/AndroidProcessor.java b/java/dagger/android/processor/AndroidProcessor.java
index 5c173413f..ad7f08ef0 100644
--- a/java/dagger/android/processor/AndroidProcessor.java
+++ b/java/dagger/android/processor/AndroidProcessor.java
@@ -17,23 +17,18 @@
package dagger.android.processor;
import static javax.tools.Diagnostic.Kind.ERROR;
-import static javax.tools.StandardLocation.CLASS_OUTPUT;
import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING;
import com.google.auto.common.BasicAnnotationProcessor;
import com.google.auto.service.AutoService;
import com.google.common.base.Ascii;
-import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.googlejavaformat.java.filer.FormattingFiler;
-import java.io.IOException;
-import java.io.Writer;
import java.util.Set;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.Processor;
-import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
@@ -98,29 +93,6 @@ public final class AndroidProcessor extends BasicAnnotationProcessor {
}
@Override
- protected void postRound(RoundEnvironment roundEnv) {
- if (roundEnv.processingOver() && useStringKeys()) {
- try (Writer writer = createProguardFile()){
- writer.write(
- Joiner.on("\n")
- .join(
- "-identifiernamestring class dagger.android.internal.AndroidInjectionKeys {",
- " java.lang.String of(java.lang.String);",
- "}"));
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
-
- private Writer createProguardFile() throws IOException {
- return processingEnv
- .getFiler()
- .createResource(CLASS_OUTPUT, "", "META-INF/proguard/dagger.android.AndroidInjectionKeys")
- .openWriter();
- }
-
- @Override
public Set<String> getSupportedOptions() {
return ImmutableSet.of(FLAG_EXPERIMENTAL_USE_STRING_KEYS);
}
diff --git a/java/dagger/android/processor/BUILD b/java/dagger/android/processor/BUILD
index 5754143a3..7e63c7633 100644
--- a/java/dagger/android/processor/BUILD
+++ b/java/dagger/android/processor/BUILD
@@ -15,14 +15,17 @@
# Description:
# Public Dagger API for Android
-package(default_visibility = ["//:src"])
-
+load("@rules_java//java:defs.bzl", "java_import", "java_library", "java_plugin")
load(
"//:build_defs.bzl",
"DOCLINT_HTML_AND_SYNTAX",
"DOCLINT_REFERENCES",
+ "POM_VERSION",
)
-load("//tools:maven.bzl", "POM_VERSION", "pom_file")
+load("//tools:maven.bzl", "pom_file")
+load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
+
+package(default_visibility = ["//:src"])
filegroup(
name = "srcs",
@@ -35,15 +38,15 @@ java_library(
javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
tags = ["maven_coordinates=com.google.dagger:dagger-android-processor:" + POM_VERSION],
deps = [
- "@google_bazel_common//third_party/java/guava",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
"@google_bazel_common//third_party/java/auto:service",
"@google_bazel_common//third_party/java/auto:value",
- "@google_bazel_common//third_party/java/auto:common",
+ "@maven//:com_google_auto_auto_common",
"@google_bazel_common//third_party/java/incap",
"@google_bazel_common//third_party/java/javapoet",
"@google_bazel_common//third_party/java/google_java_format",
"//java/dagger:core",
- "//java/dagger/model",
"//java/dagger/spi",
# https://github.com/bazelbuild/bazel/issues/2517
":dagger-android-jar",
@@ -78,8 +81,6 @@ java_plugin(
deps = [":processor"],
)
-load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
-
javadoc_library(
name = "processor-javadoc",
srcs = [":srcs"],
diff --git a/java/dagger/android/proguard.cfg b/java/dagger/android/proguard.cfg
deleted file mode 100644
index bd8ffbf73..000000000
--- a/java/dagger/android/proguard.cfg
+++ /dev/null
@@ -1 +0,0 @@
--dontwarn com.google.errorprone.annotations.**
diff --git a/java/dagger/android/support/AndroidSupportInjection.java b/java/dagger/android/support/AndroidSupportInjection.java
index 1624345a1..c77e29738 100644
--- a/java/dagger/android/support/AndroidSupportInjection.java
+++ b/java/dagger/android/support/AndroidSupportInjection.java
@@ -20,8 +20,7 @@ import static android.util.Log.DEBUG;
import static dagger.internal.Preconditions.checkNotNull;
import android.app.Activity;
-import android.app.Application;
-import android.support.v4.app.Fragment;
+import androidx.fragment.app.Fragment;
import android.util.Log;
import dagger.android.AndroidInjector;
import dagger.android.HasAndroidInjector;
@@ -36,72 +35,59 @@ public final class AndroidSupportInjection {
* Injects {@code fragment} if an associated {@link AndroidInjector} implementation can be found,
* otherwise throws an {@link IllegalArgumentException}.
*
- * <p>Uses the following algorithm to find the appropriate {@link AndroidInjector} to use to
- * inject {@code fragment}:
+ * <p>Uses the following algorithm to find the appropriate {@code AndroidInjector<Fragment>} to
+ * use to inject {@code fragment}:
*
* <ol>
- * <li>Walks the parent-fragment hierarchy to find a fragment that implements {@link
- * HasAndroidInjector} or {@link HasSupportFragmentInjector}, and if none do
+ * <li>Walks the parent-fragment hierarchy to find the a fragment that implements {@link
+ * HasAndroidInjector}, and if none do
* <li>Uses the {@code fragment}'s {@link Fragment#getActivity() activity} if it implements
- * {@link HasAndroidInjector} or {@link HasSupportFragmentInjector}, and if not
- * <li>Uses the {@link android.app.Application} if it implements {@link HasAndroidInjector}
- * {@link HasSupportFragmentInjector}.
+ * {@link HasAndroidInjector}, and if not
+ * <li>Uses the {@link android.app.Application} if it implements {@link HasAndroidInjector}.
* </ol>
*
- * If none of them implement {@link HasAndroidInjector} or {@link HasSupportFragmentInjector}, a
- * {@link IllegalArgumentException} is thrown.
+ * If none of them implement {@link HasAndroidInjector}, a {@link IllegalArgumentException} is
+ * thrown.
*
* @throws IllegalArgumentException if no parent fragment, activity, or application implements
- * {@link HasAndroidInjector} or {@link HasSupportFragmentInjector}.
+ * {@link HasAndroidInjector}.
*/
public static void inject(Fragment fragment) {
checkNotNull(fragment, "fragment");
-
- Object hasInjector = findHasSupportFragmentInjector(fragment);
- AndroidInjector<? super Fragment> injector;
- if (hasInjector instanceof HasAndroidInjector) {
- injector = ((HasAndroidInjector) hasInjector).androidInjector();
- checkNotNull(injector, "%s.androidInjector() returned null", hasInjector.getClass());
- } else if (hasInjector instanceof HasSupportFragmentInjector) {
- injector = ((HasSupportFragmentInjector) hasInjector).supportFragmentInjector();
- checkNotNull(injector, "%s.supportFragmentInjector() returned null", hasInjector.getClass());
- } else {
- throw new RuntimeException(
- String.format(
- "%s does not implement %s or %s",
- hasInjector.getClass().getCanonicalName(),
- HasAndroidInjector.class.getCanonicalName(),
- HasSupportFragmentInjector.class.getCanonicalName()));
- }
-
+ HasAndroidInjector hasAndroidInjector = findHasAndroidInjectorForFragment(fragment);
if (Log.isLoggable(TAG, DEBUG)) {
Log.d(
TAG,
String.format(
"An injector for %s was found in %s",
fragment.getClass().getCanonicalName(),
- hasInjector.getClass().getCanonicalName()));
+ hasAndroidInjector.getClass().getCanonicalName()));
}
- injector.inject(fragment);
+ inject(fragment, hasAndroidInjector);
+ }
+
+ private static void inject(Object target, HasAndroidInjector hasAndroidInjector) {
+ AndroidInjector<Object> androidInjector = hasAndroidInjector.androidInjector();
+ checkNotNull(
+ androidInjector, "%s.androidInjector() returned null", hasAndroidInjector.getClass());
+
+ androidInjector.inject(target);
}
- private static Object findHasSupportFragmentInjector(Fragment fragment) {
+ private static HasAndroidInjector findHasAndroidInjectorForFragment(Fragment fragment) {
Fragment parentFragment = fragment;
while ((parentFragment = parentFragment.getParentFragment()) != null) {
- if (parentFragment instanceof HasAndroidInjector
- || parentFragment instanceof HasSupportFragmentInjector) {
- return parentFragment;
+ if (parentFragment instanceof HasAndroidInjector) {
+ return (HasAndroidInjector) parentFragment;
}
}
Activity activity = fragment.getActivity();
- if (activity instanceof HasAndroidInjector || activity instanceof HasSupportFragmentInjector) {
- return activity;
+ if (activity instanceof HasAndroidInjector) {
+ return (HasAndroidInjector) activity;
}
- Application application = activity.getApplication();
- if (application instanceof HasAndroidInjector
- || application instanceof HasSupportFragmentInjector) {
- return application;
+ if (activity.getApplication() instanceof HasAndroidInjector) {
+ return (HasAndroidInjector) activity.getApplication();
}
throw new IllegalArgumentException(
String.format("No injector was found for %s", fragment.getClass().getCanonicalName()));
diff --git a/java/dagger/android/support/BUILD b/java/dagger/android/support/BUILD
index 749d541fe..03846b577 100644
--- a/java/dagger/android/support/BUILD
+++ b/java/dagger/android/support/BUILD
@@ -15,10 +15,16 @@
# Description:
# Public Dagger API for Android that interacts with the Android support libraries
-package(default_visibility = ["//:src"])
+load(
+ "//:build_defs.bzl",
+ "POM_VERSION",
+ "SOURCE_7_TARGET_7",
+)
+load("//tools:dejetify.bzl", "dejetified_library")
+load("//tools:maven.bzl", "pom_file")
+load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
-load("//:build_defs.bzl", "SOURCE_7_TARGET_7")
-load("//tools:maven.bzl", "pom_file", "POM_VERSION")
+package(default_visibility = ["//:src"])
filegroup(
name = "support-srcs",
@@ -32,27 +38,12 @@ android_library(
manifest = "AndroidManifest.xml",
tags = ["maven_coordinates=com.google.dagger:dagger-android-support:" + POM_VERSION],
deps = [
- ":manual-maven-deps",
"//:dagger_with_compiler",
"//java/dagger/android",
"@google_bazel_common//third_party/java/error_prone:annotations",
- ],
-)
-
-# Our pom.xml generator does not have a way to add manual maven deps. This target exports the
-# targets that don't have the necessary maven_coordinates tags.
-android_library(
- name = "manual-maven-deps",
- tags = [
- "maven_coordinates=com.android.support:appcompat-v7:25.0.0",
- "maven_coordinates=com.android.support:support-annotations:25.0.0",
- "maven_coordinates=com.android.support:support-fragment:25.0.0",
- ],
- visibility = ["//visibility:private"],
- exports = [
- "@androidsdk//com.android.support:appcompat-v7-25.0.0",
- "@androidsdk//com.android.support:support-annotations-25.0.0",
- "@androidsdk//com.android.support:support-fragment-25.0.0",
+ "@maven//:androidx_annotation_annotation",
+ "@maven//:androidx_appcompat_appcompat",
+ "@maven//:androidx_fragment_fragment",
],
)
@@ -64,12 +55,37 @@ pom_file(
targets = [":support"],
)
-load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
+dejetified_library(
+ name = "dejetified-support",
+ input = ":support.aar",
+ output = "support-legacy.aar",
+)
+
+android_library(
+ name = "legacy-deps",
+ tags = ["maven_coordinates=com.google.dagger:dagger-android-support-legacy:" + POM_VERSION],
+ exports = [
+ "//:dagger_with_compiler",
+ "//java/dagger/android:legacy-deps",
+ "@google_bazel_common//third_party/java/error_prone:annotations",
+ "@maven//:com_android_support_appcompat_v7",
+ "@maven//:com_android_support_support_annotations",
+ "@maven//:com_android_support_support_fragment",
+ ],
+)
+
+pom_file(
+ name = "legacy-pom",
+ artifact_id = "dagger-android-support-legacy",
+ artifact_name = "Dagger Android Legacy Support",
+ packaging = "aar",
+ targets = [":legacy-deps"],
+)
javadoc_library(
name = "support-javadoc",
srcs = [":support-srcs"],
- android_api_level = 26,
+ android_api_level = 30,
root_packages = ["dagger.android.support"],
deps = [":support"],
)
diff --git a/java/dagger/android/support/DaggerAppCompatActivity.java b/java/dagger/android/support/DaggerAppCompatActivity.java
index ccc4faab2..da5b17313 100644
--- a/java/dagger/android/support/DaggerAppCompatActivity.java
+++ b/java/dagger/android/support/DaggerAppCompatActivity.java
@@ -17,8 +17,10 @@
package dagger.android.support;
import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.v7.app.AppCompatActivity;
+import androidx.annotation.ContentView;
+import androidx.annotation.LayoutRes;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
import dagger.android.AndroidInjection;
import dagger.android.AndroidInjector;
import dagger.android.DispatchingAndroidInjector;
@@ -36,6 +38,15 @@ public abstract class DaggerAppCompatActivity extends AppCompatActivity
@Inject DispatchingAndroidInjector<Object> androidInjector;
+ public DaggerAppCompatActivity() {
+ super();
+ }
+
+ @ContentView
+ public DaggerAppCompatActivity(@LayoutRes int contentLayoutId) {
+ super(contentLayoutId);
+ }
+
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
AndroidInjection.inject(this);
diff --git a/java/dagger/android/support/DaggerAppCompatDialogFragment.java b/java/dagger/android/support/DaggerAppCompatDialogFragment.java
index 1efaeec80..7a5a76d72 100644
--- a/java/dagger/android/support/DaggerAppCompatDialogFragment.java
+++ b/java/dagger/android/support/DaggerAppCompatDialogFragment.java
@@ -17,8 +17,8 @@
package dagger.android.support;
import android.content.Context;
-import android.support.v4.app.Fragment;
-import android.support.v7.app.AppCompatDialogFragment;
+import androidx.fragment.app.Fragment;
+import androidx.appcompat.app.AppCompatDialogFragment;
import dagger.android.AndroidInjector;
import dagger.android.DispatchingAndroidInjector;
import dagger.android.HasAndroidInjector;
diff --git a/java/dagger/android/support/DaggerDialogFragment.java b/java/dagger/android/support/DaggerDialogFragment.java
index 69b90bc0c..8b0bf23f5 100644
--- a/java/dagger/android/support/DaggerDialogFragment.java
+++ b/java/dagger/android/support/DaggerDialogFragment.java
@@ -17,8 +17,8 @@
package dagger.android.support;
import android.content.Context;
-import android.support.v4.app.DialogFragment;
-import android.support.v4.app.Fragment;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.Fragment;
import dagger.android.AndroidInjector;
import dagger.android.DispatchingAndroidInjector;
import dagger.android.HasAndroidInjector;
diff --git a/java/dagger/android/support/DaggerFragment.java b/java/dagger/android/support/DaggerFragment.java
index 332cdaaa1..887451154 100644
--- a/java/dagger/android/support/DaggerFragment.java
+++ b/java/dagger/android/support/DaggerFragment.java
@@ -17,7 +17,9 @@
package dagger.android.support;
import android.content.Context;
-import android.support.v4.app.Fragment;
+import androidx.annotation.ContentView;
+import androidx.annotation.LayoutRes;
+import androidx.fragment.app.Fragment;
import dagger.android.AndroidInjector;
import dagger.android.DispatchingAndroidInjector;
import dagger.android.HasAndroidInjector;
@@ -34,6 +36,15 @@ public abstract class DaggerFragment extends Fragment implements HasAndroidInjec
@Inject DispatchingAndroidInjector<Object> androidInjector;
+ public DaggerFragment() {
+ super();
+ }
+
+ @ContentView
+ public DaggerFragment(@LayoutRes int contentLayoutId) {
+ super(contentLayoutId);
+ }
+
@Override
public void onAttach(Context context) {
AndroidSupportInjection.inject(this);
diff --git a/java/dagger/android/support/HasSupportFragmentInjector.java b/java/dagger/android/support/HasSupportFragmentInjector.java
deleted file mode 100644
index e80609ec5..000000000
--- a/java/dagger/android/support/HasSupportFragmentInjector.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.android.support;
-
-import android.support.v4.app.Fragment;
-import dagger.android.AndroidInjector;
-import dagger.internal.Beta;
-
-/** Provides an {@link AndroidInjector} of {@link Fragment}s. */
-@Beta
-public interface HasSupportFragmentInjector {
-
- /** Returns an {@link AndroidInjector} of {@link Fragment}s. */
- AndroidInjector<Fragment> supportFragmentInjector();
-}
diff --git a/java/dagger/assisted/Assisted.java b/java/dagger/assisted/Assisted.java
new file mode 100644
index 000000000..59d8eeccf
--- /dev/null
+++ b/java/dagger/assisted/Assisted.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.assisted;
+
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a parameter within an {@link AssistedInject}-annotated constructor.
+ *
+ * <p>See {@link AssistedInject}.
+ */
+@Documented
+@Retention(RUNTIME)
+@Target(PARAMETER)
+public @interface Assisted {
+
+ /**
+ * Returns an identifier for an {@link Assisted} parameter.
+ *
+ * <p>Within an {@link AssistedInject} constructor, each {@link Assisted} parameter must be
+ * uniquely defined by the combination of its identifier and type. If no identifier is specified,
+ * the default identifier is an empty string. Thus, the following parameters are equivalent within
+ * an {@link AssistedInject} constructor:
+ *
+ * <ul>
+ * <li> {@code @Assisted Foo foo}
+ * <li> {@code @Assisted("") Foo foo}
+ * </ul>
+ *
+ * <p>Within an {@link AssistedFactory} method, each parameter must match an {@link Assisted}
+ * parameter in the associated {@link AssistedInject} constructor (i.e. identifier + type).
+ * A parameter with no {@code @Assisted} annotation will be assigned the default identifier. Thus,
+ * the following parameters are equivalent within an {@link AssistedFactory} method:
+ *
+ * <ul>
+ * <li> {@code Foo foo}
+ * <li> {@code @Assisted Foo foo}
+ * <li> {@code @Assisted("") Foo foo}
+ * </ul>
+ *
+ * <p>Example:
+ *
+ * <pre><code>
+ * final class DataService {
+ * {@literal @}AssistedInject
+ * DataService(
+ * BindingFromDagger bindingFromDagger,
+ * {@literal @}Assisted String name,
+ * {@literal @}Assisted("id") String id,
+ * {@literal @}Assisted("repo") String repo) {}
+ * }
+ *
+ * {@literal @}AssistedFactory
+ * interface DataServiceFactory {
+ * DataService create(
+ * String name,
+ * {@literal @}Assisted("id") String id,
+ * {@literal @}Assisted("repo") String repo);
+ * }
+ * </code></pre>
+ */
+ String value() default "";
+}
diff --git a/java/dagger/assisted/AssistedFactory.java b/java/dagger/assisted/AssistedFactory.java
new file mode 100644
index 000000000..4eba9a7e2
--- /dev/null
+++ b/java/dagger/assisted/AssistedFactory.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.assisted;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates an abstract class or interface used to create an instance of a type via an {@link
+ * AssistedInject} constructor.
+ *
+ * <p>An {@link AssistedFactory}-annotated type must obey the following constraints:
+ *
+ * <ul>
+ * <li>The type must be an abstract class or interface,
+ * <li>The type must contain exactly one abstract, non-default method whose
+ * <ul>
+ * <li>return type must exactly match the type of an assisted injection type, and
+ * <li>parameters must match the exact list of {@link Assisted} parameters in the assisted
+ * injection type's constructor (and in the same order).
+ * </ul>
+ * </ul>
+ *
+ * See {@link AssistedInject}
+ */
+@Documented
+@Retention(RUNTIME)
+@Target(TYPE)
+public @interface AssistedFactory {}
diff --git a/java/dagger/assisted/AssistedInject.java b/java/dagger/assisted/AssistedInject.java
new file mode 100644
index 000000000..fadd46d0a
--- /dev/null
+++ b/java/dagger/assisted/AssistedInject.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.assisted;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates the constuctor of a type that will be created via assisted injection.
+ *
+ * <p>Note that an assisted injection type cannot be scoped. In addition, assisted injection
+ * requires the use of a factory annotated with {@link AssistedFactory} (see the example below).
+ *
+ * <p>Example usage:
+ *
+ * <p>Suppose we have a type, {@code DataService}, that has two dependencies: {@code DataFetcher}
+ * and {@code Config}. When creating {@code DataService}, we would like to pass in an instance of
+ * {@code Config} manually rather than having Dagger create it for us. This can be done using
+ * assisted injection.
+ *
+ * <p>To start, we annotate the {@code DataService} constructor with {@link AssistedInject} and we
+ * annotate the {@code Config} parameter with {@link Assisted}, as shown below:
+ *
+ * <pre><code>
+ * final class DataService {
+ * private final DataFetcher dataFetcher;
+ * private final Config config;
+ *
+ * {@literal @}AssistedInject
+ * DataService(DataFetcher dataFetcher, {@literal @}Assisted Config config) {
+ * this.dataFetcher = dataFetcher;
+ * this.config = config;
+ * }
+ * }
+ * </code></pre>
+ *
+ * <p>Next, we define a factory for the assisted type, {@code DataService}, and annotate it with
+ * {@link AssistedFactory}. The factory must contain a single abstract, non-default method which
+ * takes in all of the assisted parameters (in order) and returns the assisted type.
+ *
+ * <pre><code>
+ * {@literal @}AssistedFactory
+ * interface DataServiceFactory {
+ * DataService create(Config config);
+ * }
+ * </code></pre>
+ *
+ * <p>Dagger will generate an implementation of the factory and bind it to the factory type. The
+ * factory can then be used to create an instance of the assisted type:
+ *
+ * <pre><code>
+ * class MyApplication {
+ * {@literal @}Inject DataServiceFactory dataServiceFactory;
+ *
+ * dataService = dataServiceFactory.create(new Config(...));
+ * }
+ * </code></pre>
+ */
+@Documented
+@Retention(RUNTIME)
+@Target(CONSTRUCTOR)
+public @interface AssistedInject {}
diff --git a/java/dagger/assisted/package-info.java b/java/dagger/assisted/package-info.java
new file mode 100644
index 000000000..9b8b7f0fe
--- /dev/null
+++ b/java/dagger/assisted/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package contains the API for Dagger's assisted injection.
+ *
+ * TODO(bcorso): Link to dagger.dev documentation.
+ */
+package dagger.assisted;
diff --git a/java/dagger/errorprone/AndroidSupportInjectionModuleMigrator.java b/java/dagger/errorprone/AndroidSupportInjectionModuleMigrator.java
index e98fe9b11..bc0be48e9 100644
--- a/java/dagger/errorprone/AndroidSupportInjectionModuleMigrator.java
+++ b/java/dagger/errorprone/AndroidSupportInjectionModuleMigrator.java
@@ -19,7 +19,6 @@ package dagger.errorprone;
import static com.google.errorprone.BugPattern.SeverityLevel.SUGGESTION;
import com.google.errorprone.BugPattern;
-import com.google.errorprone.BugPattern.ProvidesFix;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MemberSelectTreeMatcher;
@@ -35,7 +34,6 @@ import com.sun.tools.javac.code.Symbol;
/** A refactoring to update AndroidInjector bindings to their new form. */
@BugPattern(
name = "AndroidSupportInjectionModuleMigrator",
- providesFix = ProvidesFix.REQUIRES_HUMAN_ATTENTION,
summary = "Inlines usages of AndroidSupportInjectionModule to AndroidInjectionModule",
explanation =
"AndroidSupportInjectionModule is now an empty module and acts as an alias for "
diff --git a/java/dagger/errorprone/BUILD b/java/dagger/errorprone/BUILD
index 408925aa2..9c3707f09 100644
--- a/java/dagger/errorprone/BUILD
+++ b/java/dagger/errorprone/BUILD
@@ -1,6 +1,8 @@
# Description:
# ErrorProne refactorings and static analysis for Dagger
+load("@rules_java//java:defs.bzl", "java_library")
+
package(default_visibility = ["//:src"])
java_library(
@@ -8,8 +10,8 @@ java_library(
srcs = glob(["*.java"]),
deps = [
"//java/dagger:core",
+ "//java/dagger/internal/guava:collect",
"@bazel_tools//tools/jdk:langtools-neverlink",
"@google_bazel_common//third_party/java/error_prone:check_api",
- "@google_bazel_common//third_party/java/guava",
],
)
diff --git a/java/dagger/example/BUILD b/java/dagger/example/BUILD
new file mode 100644
index 000000000..1973229b1
--- /dev/null
+++ b/java/dagger/example/BUILD
@@ -0,0 +1,18 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Description:
+# Dagger examples.
+
+package(default_visibility = ["//:src"])
diff --git a/java/dagger/example/android/simple/AndroidManifest.xml b/java/dagger/example/android/simple/AndroidManifest.xml
deleted file mode 100644
index 711fb1e7c..000000000
--- a/java/dagger/example/android/simple/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
- ~ Copyright (C) 2017 The Dagger Authors.
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="dagger.example.android.simple">
-
- <uses-sdk
- android:minSdkVersion="14"
- android:targetSdkVersion="24"/>
-
- <application android:name=".SimpleApplication" android:label="@string/appName">
- <activity android:name=".MainActivity" android:theme="@style/Theme.AppCompat.Light.NoActionBar">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/java/dagger/example/android/simple/BUILD b/java/dagger/example/android/simple/BUILD
deleted file mode 100644
index 7396744f9..000000000
--- a/java/dagger/example/android/simple/BUILD
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright (C) 2017 The Dagger Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Description:
-# A skeletal application that demonstates wiring for an injected Application and Actiity.
-
-package(default_visibility = ["//:src"])
-
-android_library(
- name = "simple_lib",
- srcs = glob(["*.java"]),
- manifest = "AndroidManifest.xml",
- resource_files = glob(["res/**"]),
- deps = [
- "//:android",
- "//:android-support",
- "//:dagger_with_compiler",
- "@androidsdk//com.android.support:appcompat-v7-25.0.0",
- "@androidsdk//com.android.support:support-annotations-25.0.0",
- "@androidsdk//com.android.support:support-fragment-25.0.0",
- ],
-)
-
-android_binary(
- name = "simple",
- aapt_version = "aapt",
- manifest = "AndroidManifest.xml",
- deps = [
- ":simple_lib",
- ],
-)
diff --git a/java/dagger/example/android/simple/BuildModule.java b/java/dagger/example/android/simple/BuildModule.java
deleted file mode 100644
index 40ac8ee3f..000000000
--- a/java/dagger/example/android/simple/BuildModule.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.example.android.simple;
-
-import static android.os.Build.MODEL;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-final class BuildModule {
- @Provides
- @Model
- static String provideModel() {
- return MODEL;
- }
-}
diff --git a/java/dagger/example/android/simple/MainActivity.java b/java/dagger/example/android/simple/MainActivity.java
deleted file mode 100644
index f2aab2dfa..000000000
--- a/java/dagger/example/android/simple/MainActivity.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.example.android.simple;
-
-import android.os.Bundle;
-import android.util.Log;
-import android.widget.TextView;
-import dagger.Binds;
-import dagger.android.AndroidInjector;
-import dagger.android.support.DaggerAppCompatActivity;
-import dagger.multibindings.ClassKey;
-import dagger.multibindings.IntoMap;
-import javax.inject.Inject;
-
-/**
- * The main activity application. It can be injected with any binding from both {@link Component}
- * and {@link dagger.example.android.simple.SimpleApplication.Component}.
- */
-public class MainActivity extends DaggerAppCompatActivity {
- @dagger.Subcomponent
- interface Component extends AndroidInjector<MainActivity> {
-
- @dagger.Subcomponent.Builder
- abstract class Builder extends AndroidInjector.Builder<MainActivity> {}
- }
-
- @dagger.Module(subcomponents = Component.class)
- abstract class Module {
-
- @Binds
- @IntoMap
- @ClassKey(MainActivity.class)
- abstract AndroidInjector.Factory<?> bind(Component.Builder builder);
- }
-
- private static final String TAG = MainActivity.class.getSimpleName();
-
- @Inject @Model String model;
-
- @Inject
- void logInjection() {
- Log.i(TAG, "Injecting " + MainActivity.class.getSimpleName());
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.activity_main);
-
- TextView greeting = (TextView) findViewById(R.id.greeting);
- String text = getResources().getString(R.string.welcome, model);
- greeting.setText(text);
- }
-}
diff --git a/java/dagger/example/android/simple/Model.java b/java/dagger/example/android/simple/Model.java
deleted file mode 100644
index c52bb9852..000000000
--- a/java/dagger/example/android/simple/Model.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.example.android.simple;
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import javax.inject.Qualifier;
-
-/** Qualifies bindings relating to {@link android.os.Build#MODEL}. */
-@Qualifier
-@Retention(RUNTIME)
-@Documented
-@interface Model {}
diff --git a/java/dagger/example/android/simple/SimpleApplication.java b/java/dagger/example/android/simple/SimpleApplication.java
deleted file mode 100644
index ae3d42db8..000000000
--- a/java/dagger/example/android/simple/SimpleApplication.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.example.android.simple;
-
-import android.util.Log;
-import dagger.android.AndroidInjectionModule;
-import dagger.android.AndroidInjector;
-import dagger.android.DaggerApplication;
-import javax.inject.Inject;
-
-/**
- * A simple, skeletal application that demonstrates a dependency-injected application using the
- * utilities in {@code dagger.android}.
- */
-public class SimpleApplication extends DaggerApplication {
- private static final String TAG = SimpleApplication.class.getSimpleName();
-
- @dagger.Component(
- modules = {AndroidInjectionModule.class, MainActivity.Module.class, BuildModule.class})
- /* @ApplicationScoped and/or @Singleton */
- interface Component extends AndroidInjector<SimpleApplication> {
- @dagger.Component.Builder
- abstract class Builder extends AndroidInjector.Builder<SimpleApplication> {}
- }
-
- @Inject
- void logInjection() {
- Log.i(TAG, "Injecting " + SimpleApplication.class.getSimpleName());
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- }
-
- @Override
- protected AndroidInjector<SimpleApplication> applicationInjector() {
- return DaggerSimpleApplication_Component.builder().create(this);
- }
-}
diff --git a/java/dagger/example/android/simple/res/layout/activity_main.xml b/java/dagger/example/android/simple/res/layout/activity_main.xml
deleted file mode 100644
index 37add1f0a..000000000
--- a/java/dagger/example/android/simple/res/layout/activity_main.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2017 The Dagger Authors.
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@android:color/background_light">
-
- <TextView
- android:id="@+id/greeting"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_alignParentTop="true"
- android:textColor="@android:color/primary_text_light"
- style="@style/TextAppearance.AppCompat.Display4"
- />
-</RelativeLayout>
diff --git a/java/dagger/example/android/simple/res/values/strings.xml b/java/dagger/example/android/simple/res/values/strings.xml
deleted file mode 100644
index c4ba1fd27..000000000
--- a/java/dagger/example/android/simple/res/values/strings.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <string name="appName">Simple Dagger</string>
- <string name="welcome">Hello, %s!</string>
-</resources> \ No newline at end of file
diff --git a/java/dagger/example/atm/AccountModule.java b/java/dagger/example/atm/AccountModule.java
new file mode 100644
index 000000000..3ff5a7595
--- /dev/null
+++ b/java/dagger/example/atm/AccountModule.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.example.atm.Database.Account;
+
+/** Bindings for the {@link Account} of the currently signed-in user. */
+@Module
+interface AccountModule {
+ @Provides
+ static Account account(Database database, @Username String username) {
+ return database.getAccount(username);
+ }
+}
diff --git a/java/dagger/example/atm/AmountsModule.java b/java/dagger/example/atm/AmountsModule.java
new file mode 100644
index 000000000..87e5e017c
--- /dev/null
+++ b/java/dagger/example/atm/AmountsModule.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import dagger.Module;
+import dagger.Provides;
+import java.math.BigDecimal;
+
+/** Configures various amounts of money the application uses to control transactions. */
+@Module
+abstract class AmountsModule {
+
+ @Provides
+ @MinimumBalance
+ static BigDecimal minimumBalance() {
+ return BigDecimal.ZERO;
+ }
+
+ @Provides
+ @MaximumWithdrawal
+ static BigDecimal maximumWithdrawal() {
+ return new BigDecimal(1000);
+ }
+}
diff --git a/java/dagger/example/atm/BUILD b/java/dagger/example/atm/BUILD
new file mode 100644
index 000000000..d1ff50501
--- /dev/null
+++ b/java/dagger/example/atm/BUILD
@@ -0,0 +1,33 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# An example of using dagger in a computerized fake ATM. The User's Guide (https://dagger.dev/users-guide)
+# is a walkthrough that ultimately builds this example.
+
+load("@rules_java//java:defs.bzl", "java_binary", "java_library")
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "atm",
+ srcs = glob(["*.java"]),
+ deps = ["//:dagger_with_compiler"],
+)
+
+java_binary(
+ name = "CommandLineAtm",
+ main_class = "dagger.example.atm.CommandLineAtm",
+ runtime_deps = [":atm"],
+)
diff --git a/java/dagger/example/atm/BigDecimalCommand.java b/java/dagger/example/atm/BigDecimalCommand.java
new file mode 100644
index 000000000..29bd6f540
--- /dev/null
+++ b/java/dagger/example/atm/BigDecimalCommand.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import java.math.BigDecimal;
+
+/**
+ * Abstract {@link Command} that expects a single argument that can be converted to {@link
+ * BigDecimal}.
+ */
+abstract class BigDecimalCommand extends SingleArgCommand {
+
+ private final Outputter outputter;
+
+ protected BigDecimalCommand(Outputter outputter) {
+ this.outputter = outputter;
+ }
+
+ @Override
+ protected final Result handleArg(String arg) {
+ BigDecimal amount = tryParse(arg);
+ if (amount == null) {
+ outputter.output(arg + " is not a valid number");
+ } else if (amount.signum() <= 0) {
+ outputter.output("amount must be positive");
+ } else {
+ handleAmount(amount);
+ }
+ return Result.handled();
+ }
+
+ private static BigDecimal tryParse(String arg) {
+ try {
+ return new BigDecimal(arg);
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ }
+
+ /** Handles the given (positive) {@code amount} of money. */
+ protected abstract void handleAmount(BigDecimal amount);
+}
diff --git a/java/dagger/example/atm/Command.java b/java/dagger/example/atm/Command.java
new file mode 100644
index 000000000..a3ff0d05e
--- /dev/null
+++ b/java/dagger/example/atm/Command.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import java.util.List;
+import java.util.Optional;
+
+/** A text-based command handler. */
+interface Command {
+ /**
+ * Processes and optionally acts upon the given {@code input}.
+ *
+ * @return a {@link Result} indicating how the input was handled
+ */
+ Result handleInput(List<String> input);
+
+ /**
+ * A command result, which has a {@link Status} and optionally a new {@link CommandRouter} that
+ * will handle subsequent commands.
+ */
+ final class Result {
+ private final Status status;
+ private final Optional<CommandRouter> nestedCommandRouter;
+
+ private Result(Status status, Optional<CommandRouter> nestedCommandRouter) {
+ this.status = status;
+ this.nestedCommandRouter = nestedCommandRouter;
+ }
+
+ static Result invalid() {
+ return new Result(Status.INVALID, Optional.empty());
+ }
+
+ static Result handled() {
+ return new Result(Status.HANDLED, Optional.empty());
+ }
+
+ static Result inputCompleted() {
+ return new Result(Status.INPUT_COMPLETED, Optional.empty());
+ }
+
+ static Result enterNestedCommandSet(CommandRouter nestedCommandRouter) {
+ return new Result(Status.HANDLED, Optional.of(nestedCommandRouter));
+ }
+
+ Status status() {
+ return status;
+ }
+
+ Optional<CommandRouter> nestedCommandRouter() {
+ return nestedCommandRouter;
+ }
+ }
+
+ enum Status {
+ /** The command or its arguments were invalid. */
+ INVALID,
+
+ /** The command handled the input and no other commands should attempt to handle it. */
+ HANDLED,
+
+ // TODO(ronshapiro): maybe call this TERMINATED? If so, maybe this should be called
+ // ContinueStatus?
+ /** The command handled the input and no further inputs should be submitted. */
+ INPUT_COMPLETED,
+ ;
+ }
+}
diff --git a/java/dagger/example/atm/CommandLineAtm.java b/java/dagger/example/atm/CommandLineAtm.java
new file mode 100644
index 000000000..be5cbe6e6
--- /dev/null
+++ b/java/dagger/example/atm/CommandLineAtm.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.util.Scanner;
+
+/** Main class for the command-line ATM. */
+class CommandLineAtm {
+ public static void main(String[] args) {
+ Scanner scanner = new Scanner(System.in, UTF_8.name());
+ CommandProcessor commandProcessor = CommandProcessorFactory.create().commandProcessor();
+
+ while (scanner.hasNextLine()) {
+ Command.Status commandStatus = commandProcessor.process(scanner.nextLine());
+ if (commandStatus.equals(Command.Status.INPUT_COMPLETED)) {
+ break;
+ }
+ }
+ }
+}
diff --git a/java/dagger/example/atm/CommandProcessor.java b/java/dagger/example/atm/CommandProcessor.java
new file mode 100644
index 000000000..3b52d736e
--- /dev/null
+++ b/java/dagger/example/atm/CommandProcessor.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import dagger.example.atm.Command.Result;
+import dagger.example.atm.Command.Status;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Processes successive commands by delegating to a {@link CommandRouter}.
+ *
+ * <p>Whereas {@link CommandRouter} routes an input string to a particular {@link Command}, this
+ * class maintains inter-command state to determine which {@link CommandRouter} should route
+ * successive commands.
+ *
+ * <p>This class is {@link Singleton} scoped because it has mutable state ({@code
+ * commandRouterStack}), and all users of {@link CommandProcessor} must use the same instance.
+ */
+@Singleton
+final class CommandProcessor {
+ private final Deque<CommandRouter> commandRouterStack = new ArrayDeque<>();
+
+ @Inject
+ CommandProcessor(CommandRouter firstCommandRouter) {
+ commandRouterStack.push(firstCommandRouter);
+ }
+
+ Status process(String input) {
+ if (commandRouterStack.isEmpty()) {
+ throw new IllegalStateException("No command router is available!");
+ }
+
+ Result result = commandRouterStack.peek().route(input);
+ switch (result.status()) {
+ case INPUT_COMPLETED:
+ commandRouterStack.pop();
+ return commandRouterStack.isEmpty() ? Status.INPUT_COMPLETED : Status.HANDLED;
+ case HANDLED:
+ // TODO(ronshapiro): We currently have a case of using a subcomponent for nested commands,
+ // which requires maintaining a binding indicating whether we are in the subcomponent are
+ // not. We can include another example where there's a CommandRouter that is created from an
+ // entirely different component, that way there are no inherited commands.
+ result.nestedCommandRouter().ifPresent(commandRouterStack::push);
+ // fall through
+ case INVALID:
+ return result.status();
+ }
+ throw new AssertionError(result.status());
+ }
+}
diff --git a/java/dagger/example/atm/CommandProcessorFactory.java b/java/dagger/example/atm/CommandProcessorFactory.java
new file mode 100644
index 000000000..a4497036d
--- /dev/null
+++ b/java/dagger/example/atm/CommandProcessorFactory.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import dagger.Component;
+import javax.inject.Singleton;
+
+/**
+ * Hand-written API for interfacing with Dagger. The command-line ATM needs a single class to
+ * execute: {@link CommandProcessor}.
+ *
+ * <p>The list of {@code modules} declares where Dagger should look, besides {@link
+ * javax.inject.Inject}-annotated constructors, to help instantiate {@link CommandProcessor} and its
+ * dependencies.
+ */
+@Singleton
+@Component(
+ modules = {
+ CommandsModule.class,
+ InMemoryDatabaseModule.class,
+ UserCommandsRouter.InstallationModule.class,
+ SystemOutModule.class,
+ })
+interface CommandProcessorFactory {
+ CommandProcessor commandProcessor();
+
+ static CommandProcessorFactory create() {
+ return DaggerCommandProcessorFactory.create();
+ }
+}
diff --git a/java/dagger/example/atm/CommandRouter.java b/java/dagger/example/atm/CommandRouter.java
new file mode 100644
index 000000000..ef752206a
--- /dev/null
+++ b/java/dagger/example/atm/CommandRouter.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import dagger.example.atm.Command.Result;
+import dagger.example.atm.Command.Status;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import javax.inject.Inject;
+
+/** Routes individual text commands to the appropriate {@link Command}(s). */
+final class CommandRouter {
+ private final Map<String, Command> commands;
+ private final Outputter outputter;
+
+ @Inject
+ CommandRouter(Map<String, Command> commands, Outputter outputter) {
+ this.commands = commands;
+ this.outputter = outputter;
+ }
+
+ /**
+ * Calls {@link Command#handleInput(String) command.handleInput(input)} on this router's
+ * {@linkplain #commands commands}.
+ */
+ Result route(String input) {
+ List<String> splitInput = split(input);
+ if (splitInput.isEmpty()) {
+ return invalidCommand(input);
+ }
+
+ String commandKey = splitInput.get(0);
+ Command command = commands.get(commandKey);
+ if (command == null) {
+ return invalidCommand(input);
+ }
+
+ List<String> args = splitInput.subList(1, splitInput.size());
+ Result result = command.handleInput(args);
+ return result.status().equals(Status.INVALID) ? invalidCommand(input) : result;
+ }
+
+ private Result invalidCommand(String input) {
+ outputter.output(String.format("couldn't understand \"%s\". please try again.", input));
+ return Result.invalid();
+ }
+
+ private static List<String> split(String input) {
+ return Arrays.asList(input.trim().split("\\s+"));
+ }
+}
diff --git a/java/dagger/example/atm/CommandsModule.java b/java/dagger/example/atm/CommandsModule.java
new file mode 100644
index 000000000..266af129d
--- /dev/null
+++ b/java/dagger/example/atm/CommandsModule.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import dagger.Binds;
+import dagger.BindsOptionalOf;
+import dagger.Module;
+import dagger.example.atm.Database.Account;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.StringKey;
+
+/** Installs basic commands. */
+@Module
+interface CommandsModule {
+ @Binds
+ @IntoMap
+ @StringKey("hello")
+ Command helloWorld(HelloWorldCommand command);
+
+ @Binds
+ @IntoMap
+ @StringKey("login")
+ Command login(LoginCommand command);
+
+ /**
+ * Declare an optional binding for {@link Account}. This allows other bindings to change their
+ * behavior depending on whether an {@link Account} is bound in the current (sub)component.
+ */
+ @BindsOptionalOf
+ Account loggedInAccount();
+}
diff --git a/java/dagger/example/atm/Database.java b/java/dagger/example/atm/Database.java
new file mode 100644
index 000000000..f6c0eda43
--- /dev/null
+++ b/java/dagger/example/atm/Database.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import java.math.BigDecimal;
+
+/** An ATM database. */
+interface Database {
+ Account getAccount(String username);
+
+ /** An individual user's account. */
+ interface Account {
+ String username();
+
+ void deposit(BigDecimal amount);
+
+ void withdraw(BigDecimal amount);
+
+ BigDecimal balance();
+ }
+}
diff --git a/java/dagger/example/atm/DepositCommand.java b/java/dagger/example/atm/DepositCommand.java
new file mode 100644
index 000000000..9a5e2e205
--- /dev/null
+++ b/java/dagger/example/atm/DepositCommand.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import dagger.example.atm.Database.Account;
+import java.math.BigDecimal;
+import javax.inject.Inject;
+
+/** Deposits money to the ATM. */
+final class DepositCommand extends BigDecimalCommand {
+ private final Outputter outputter;
+ private final Account account;
+ private final WithdrawalLimiter withdrawalLimiter;
+
+ @Inject
+ DepositCommand(Outputter outputter, Account account, WithdrawalLimiter withdrawalLimiter) {
+ super(outputter);
+ this.outputter = outputter;
+ this.account = account;
+ this.withdrawalLimiter = withdrawalLimiter;
+ }
+
+ @Override
+ protected void handleAmount(BigDecimal amount) {
+ account.deposit(amount);
+ withdrawalLimiter.recordDeposit(amount);
+ outputter.output("your new balance is: " + account.balance());
+ }
+}
diff --git a/java/dagger/example/atm/HelloWorldCommand.java b/java/dagger/example/atm/HelloWorldCommand.java
new file mode 100644
index 000000000..f39d58553
--- /dev/null
+++ b/java/dagger/example/atm/HelloWorldCommand.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import java.util.List;
+import javax.inject.Inject;
+
+final class HelloWorldCommand implements Command {
+ private final Outputter outputter;
+
+ @Inject
+ HelloWorldCommand(Outputter outputter) {
+ this.outputter = outputter;
+ }
+
+ @Override
+ public Result handleInput(List<String> args) {
+ if (!args.isEmpty()) {
+ return Result.invalid();
+ }
+ outputter.output("howdy!");
+ return Result.handled();
+ }
+}
diff --git a/java/dagger/example/atm/InMemoryDatabase.java b/java/dagger/example/atm/InMemoryDatabase.java
new file mode 100644
index 000000000..801d8270a
--- /dev/null
+++ b/java/dagger/example/atm/InMemoryDatabase.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.Map;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** A database that stores all of its data in memory. */
+@Singleton
+final class InMemoryDatabase implements Database {
+ private final Map<String, Account> accounts = new HashMap<>();
+
+ @Inject
+ InMemoryDatabase() {}
+
+ @Override
+ public Account getAccount(String username) {
+ return accounts.computeIfAbsent(username, InMemoryAccount::new);
+ }
+
+ private static final class InMemoryAccount implements Account {
+ private final String username;
+ private BigDecimal balance = BigDecimal.ZERO;
+
+ InMemoryAccount(String username) {
+ this.username = username;
+ }
+
+ @Override
+ public String username() {
+ return username;
+ }
+
+ @Override
+ public void deposit(BigDecimal amount) {
+ checkNonNegative(amount, "deposit");
+ balance = balance.add(amount);
+ }
+
+ @Override
+ public void withdraw(BigDecimal amount) {
+ checkNonNegative(amount, "withdraw");
+ balance = balance.subtract(amount);
+ }
+
+ private void checkNonNegative(BigDecimal amount, String action) {
+ if (amount.signum() == -1) {
+ throw new IllegalArgumentException(
+ String.format("Cannot %s negative amounts: %s", action, amount));
+ }
+ }
+
+ @Override
+ public BigDecimal balance() {
+ return balance;
+ }
+ }
+}
diff --git a/java/dagger/example/atm/InMemoryDatabaseModule.java b/java/dagger/example/atm/InMemoryDatabaseModule.java
new file mode 100644
index 000000000..bf4be6794
--- /dev/null
+++ b/java/dagger/example/atm/InMemoryDatabaseModule.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import dagger.Binds;
+import dagger.Module;
+
+@Module
+interface InMemoryDatabaseModule {
+ @Binds
+ Database inMemory(InMemoryDatabase database);
+}
diff --git a/java/dagger/example/atm/LoginCommand.java b/java/dagger/example/atm/LoginCommand.java
new file mode 100644
index 000000000..4978cb524
--- /dev/null
+++ b/java/dagger/example/atm/LoginCommand.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import dagger.example.atm.Database.Account;
+import java.util.Optional;
+import javax.inject.Inject;
+
+/** Logs in a user, allowing them to interact with the ATM. */
+final class LoginCommand extends SingleArgCommand {
+ private final Outputter outputter;
+ private final Optional<Account> account;
+ private final UserCommandsRouter.Factory userCommandsFactory;
+
+ @Inject
+ LoginCommand(
+ Outputter outputter,
+ Optional<Account> account,
+ UserCommandsRouter.Factory userCommandsFactory) {
+ this.outputter = outputter;
+ this.account = account;
+ this.userCommandsFactory = userCommandsFactory;
+ }
+
+ @Override
+ public Result handleArg(String username) {
+ // If an Account binding exists, that means there is a user logged in. Don't allow a login
+ // command if we already have someone logged in!
+ if (account.isPresent()) {
+ String loggedInUser = account.get().username();
+ outputter.output(loggedInUser + " is already logged in");
+ if (!loggedInUser.equals(username)) {
+ outputter.output("run `logout` first before trying to log in another user");
+ }
+ return Result.handled();
+ } else {
+ UserCommandsRouter userCommands = userCommandsFactory.create(username);
+ return Result.enterNestedCommandSet(userCommands.router());
+ }
+ }
+}
diff --git a/java/dagger/example/atm/LogoutCommand.java b/java/dagger/example/atm/LogoutCommand.java
new file mode 100644
index 000000000..811d4b3a0
--- /dev/null
+++ b/java/dagger/example/atm/LogoutCommand.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import dagger.example.atm.Database.Account;
+import java.util.List;
+import javax.inject.Inject;
+
+/** Logs out the current user. */
+final class LogoutCommand implements Command {
+
+ private final Outputter outputter;
+ private final Account account;
+
+ @Inject
+ LogoutCommand(Outputter outputter, Account account) {
+ this.outputter = outputter;
+ this.account = account;
+ }
+
+ @Override
+ public Result handleInput(List<String> input) {
+ if (!input.isEmpty()) {
+ return Result.invalid();
+ }
+ outputter.output("logged out " + account.username());
+ return Result.inputCompleted();
+ }
+}
diff --git a/java/dagger/example/atm/MaximumWithdrawal.java b/java/dagger/example/atm/MaximumWithdrawal.java
new file mode 100644
index 000000000..d41c55932
--- /dev/null
+++ b/java/dagger/example/atm/MaximumWithdrawal.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/**
+ * Qualifier for the maximum amount that can be withdrawn from an account in a single transaction.
+ */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+@interface MaximumWithdrawal {}
diff --git a/java/dagger/example/atm/MinimumBalance.java b/java/dagger/example/atm/MinimumBalance.java
new file mode 100644
index 000000000..bbb0ed1e4
--- /dev/null
+++ b/java/dagger/example/atm/MinimumBalance.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/** Qualifier for the minimum balance an account may have. */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+@interface MinimumBalance {}
diff --git a/java/dagger/example/atm/Outputter.java b/java/dagger/example/atm/Outputter.java
new file mode 100644
index 000000000..3d8de8af3
--- /dev/null
+++ b/java/dagger/example/atm/Outputter.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+/** Writes to a user interface. */
+interface Outputter {
+ void output(String output);
+}
diff --git a/java/dagger/example/atm/PerSession.java b/java/dagger/example/atm/PerSession.java
new file mode 100644
index 000000000..22faba540
--- /dev/null
+++ b/java/dagger/example/atm/PerSession.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Scope;
+
+/** A scope for instances that should be retained within a user session. */
+@Retention(RUNTIME)
+@Scope
+@Documented
+@interface PerSession {}
diff --git a/java/dagger/example/atm/SingleArgCommand.java b/java/dagger/example/atm/SingleArgCommand.java
new file mode 100644
index 000000000..81e5f65f8
--- /dev/null
+++ b/java/dagger/example/atm/SingleArgCommand.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import java.util.List;
+
+/** Abstract command that accepts a single argument. */
+abstract class SingleArgCommand implements Command {
+
+ @Override
+ public final Result handleInput(List<String> input) {
+ return input.size() == 1 ? handleArg(input.get(0)) : Result.invalid();
+ }
+
+ /** Handles the single argument to the command. */
+ protected abstract Result handleArg(String arg);
+}
diff --git a/java/dagger/example/atm/SystemOutModule.java b/java/dagger/example/atm/SystemOutModule.java
new file mode 100644
index 000000000..3c2294107
--- /dev/null
+++ b/java/dagger/example/atm/SystemOutModule.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+final class SystemOutModule {
+ @Provides
+ static Outputter textOutputter() {
+ return System.out::println;
+ }
+}
diff --git a/java/dagger/example/atm/UserCommandsModule.java b/java/dagger/example/atm/UserCommandsModule.java
new file mode 100644
index 000000000..bee4450ec
--- /dev/null
+++ b/java/dagger/example/atm/UserCommandsModule.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.StringKey;
+
+/** Commands that are only applicable when a user is logged in. */
+@Module
+interface UserCommandsModule {
+ @Binds
+ @IntoMap
+ @StringKey("deposit")
+ Command deposit(DepositCommand command);
+
+ @Binds
+ @IntoMap
+ @StringKey("withdraw")
+ Command withdraw(WithdrawCommand command);
+
+ @Binds
+ @IntoMap
+ @StringKey("logout")
+ Command logout(LogoutCommand command);
+}
diff --git a/java/dagger/example/atm/UserCommandsRouter.java b/java/dagger/example/atm/UserCommandsRouter.java
new file mode 100644
index 000000000..7d6598931
--- /dev/null
+++ b/java/dagger/example/atm/UserCommandsRouter.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import dagger.BindsInstance;
+import dagger.Module;
+import dagger.Subcomponent;
+
+@PerSession
+@Subcomponent(modules = {AccountModule.class, AmountsModule.class, UserCommandsModule.class})
+interface UserCommandsRouter {
+ CommandRouter router();
+
+ @Subcomponent.Factory
+ interface Factory {
+ UserCommandsRouter create(@BindsInstance @Username String username);
+ }
+
+ @Module(subcomponents = UserCommandsRouter.class)
+ interface InstallationModule {}
+}
diff --git a/java/dagger/example/atm/Username.java b/java/dagger/example/atm/Username.java
new file mode 100644
index 000000000..79728448d
--- /dev/null
+++ b/java/dagger/example/atm/Username.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/** Qualifier for the currently logged-in user. */
+@Retention(RUNTIME)
+@Qualifier
+@Documented
+@interface Username {}
diff --git a/java/dagger/example/atm/WithdrawCommand.java b/java/dagger/example/atm/WithdrawCommand.java
new file mode 100644
index 000000000..7e0cf04d8
--- /dev/null
+++ b/java/dagger/example/atm/WithdrawCommand.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import dagger.example.atm.Database.Account;
+import java.math.BigDecimal;
+import javax.inject.Inject;
+
+/** Withdraws money from the ATM. */
+final class WithdrawCommand extends BigDecimalCommand {
+ private final Outputter outputter;
+ private final Account account;
+ private final BigDecimal minimumBalance;
+ private final WithdrawalLimiter withdrawalLimiter;
+
+ @Inject
+ WithdrawCommand(
+ Outputter outputter,
+ Account account,
+ @MinimumBalance BigDecimal minimumBalance,
+ WithdrawalLimiter withdrawalLimiter) {
+ super(outputter);
+ this.outputter = outputter;
+ this.account = account;
+ this.minimumBalance = minimumBalance;
+ this.withdrawalLimiter = withdrawalLimiter;
+ }
+
+ @Override
+ protected void handleAmount(BigDecimal amount) {
+ BigDecimal remainingWithdrawalLimit = withdrawalLimiter.remainingWithdrawalLimit();
+ if (amount.compareTo(remainingWithdrawalLimit) > 0) {
+ outputter.output(
+ String.format(
+ "you may not withdraw %s; you may withdraw %s more in this session",
+ amount, remainingWithdrawalLimit));
+ return;
+ }
+
+ BigDecimal newBalance = account.balance().subtract(amount);
+ if (newBalance.compareTo(minimumBalance) < 0) {
+ outputter.output(
+ String.format(
+ "you don't have sufficient funds to withdraw %s. "
+ + "your balance is %s and the minimum balance is %s",
+ amount, account.balance(), minimumBalance));
+ } else {
+ account.withdraw(amount);
+ withdrawalLimiter.recordWithdrawal(amount);
+ outputter.output("your new balance is: " + account.balance());
+ }
+ }
+}
diff --git a/java/dagger/example/atm/WithdrawalLimiter.java b/java/dagger/example/atm/WithdrawalLimiter.java
new file mode 100644
index 000000000..c473b683e
--- /dev/null
+++ b/java/dagger/example/atm/WithdrawalLimiter.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.atm;
+
+import java.math.BigDecimal;
+import javax.inject.Inject;
+
+/** Maintains the withdrawal amount available within a user session. */
+@PerSession
+final class WithdrawalLimiter {
+ private BigDecimal remainingWithdrawalLimit;
+
+ @Inject
+ WithdrawalLimiter(@MaximumWithdrawal BigDecimal maximumWithdrawal) {
+ this.remainingWithdrawalLimit = maximumWithdrawal;
+ }
+
+ void recordDeposit(BigDecimal amount) {
+ remainingWithdrawalLimit = remainingWithdrawalLimit.add(amount);
+ }
+
+ void recordWithdrawal(BigDecimal amount) {
+ remainingWithdrawalLimit = remainingWithdrawalLimit.subtract(amount);
+ }
+
+ BigDecimal remainingWithdrawalLimit() {
+ return remainingWithdrawalLimit;
+ }
+}
diff --git a/java/dagger/example/atm/build.gradle b/java/dagger/example/atm/build.gradle
new file mode 100644
index 000000000..2e6d3f425
--- /dev/null
+++ b/java/dagger/example/atm/build.gradle
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ id 'java'
+ id 'application'
+}
+
+repositories {
+ mavenCentral()
+}
+
+sourceSets {
+ main {
+ java {
+ srcDir '.'
+ }
+ }
+}
+
+dependencies {
+ implementation 'com.google.dagger:dagger:2.23.2'
+ annotationProcessor 'com.google.dagger:dagger-compiler:2.23.2'
+}
+
+mainClassName = 'dagger.example.atm.CommandLineAtm'
+
+// Run with: `gradle run -q --console=plain`
+run {
+ standardInput = System.in
+}
diff --git a/java/dagger/example/atm/gradlew b/java/dagger/example/atm/gradlew
new file mode 100755
index 000000000..b0d6d0ab5
--- /dev/null
+++ b/java/dagger/example/atm/gradlew
@@ -0,0 +1,188 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/java/dagger/example/atm/gradlew.bat b/java/dagger/example/atm/gradlew.bat
new file mode 100644
index 000000000..9991c5032
--- /dev/null
+++ b/java/dagger/example/atm/gradlew.bat
@@ -0,0 +1,100 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem http://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/java/dagger/example/atm/settings.gradle b/java/dagger/example/atm/settings.gradle
new file mode 100644
index 000000000..a6f2e014c
--- /dev/null
+++ b/java/dagger/example/atm/settings.gradle
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+rootProject.name = 'dagger-tutorial-atm'
diff --git a/java/dagger/example/gradle/android/simple/app/build.gradle b/java/dagger/example/gradle/android/simple/app/build.gradle
new file mode 100644
index 000000000..d8f6346cb
--- /dev/null
+++ b/java/dagger/example/gradle/android/simple/app/build.gradle
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.2"
+
+ defaultConfig {
+ applicationId "dagger.example.gradle.android.simple"
+ minSdkVersion 15
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+ }
+}
+
+dependencies {
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'com.google.dagger:dagger:LOCAL-SNAPSHOT'
+ implementation 'com.google.dagger:dagger-android-support:LOCAL-SNAPSHOT'
+ annotationProcessor 'com.google.dagger:dagger-compiler:LOCAL-SNAPSHOT'
+ annotationProcessor 'com.google.dagger:dagger-android-processor:LOCAL-SNAPSHOT'
+
+ // To help us catch usages of Guava APIs for Java 8 in the '-jre' variant.
+ annotationProcessor'com.google.guava:guava:28.1-android'
+}
diff --git a/java/dagger/example/gradle/android/simple/app/src/main/AndroidManifest.xml b/java/dagger/example/gradle/android/simple/app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..cc666d2f2
--- /dev/null
+++ b/java/dagger/example/gradle/android/simple/app/src/main/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<!--
+ ~ Copyright (C) 2017 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.example.gradle.android.simple">
+
+ <application
+ android:name=".SimpleApplication"
+ android:label="@string/appName"
+ android:theme="@style/Theme.AppCompat.Light">
+ <activity android:name=".SimpleActivity" android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/BuildModule.java b/java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/BuildModule.java
new file mode 100644
index 000000000..f6317c24d
--- /dev/null
+++ b/java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/BuildModule.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.gradle.android.simple;
+
+import static android.os.Build.MODEL;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+final class BuildModule {
+ @Provides
+ @Model
+ static String provideModel() {
+ return MODEL;
+ }
+}
diff --git a/java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/Model.java b/java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/Model.java
new file mode 100644
index 000000000..30a973e03
--- /dev/null
+++ b/java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/Model.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.gradle.android.simple;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/** Qualifies bindings relating to {@link android.os.Build#MODEL}. */
+@Qualifier
+@Retention(RUNTIME)
+@Documented
+@interface Model {}
diff --git a/java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/SimpleActivity.java b/java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/SimpleActivity.java
new file mode 100644
index 000000000..734590afd
--- /dev/null
+++ b/java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/SimpleActivity.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.gradle.android.simple;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.TextView;
+import dagger.Binds;
+import dagger.Module;
+import dagger.Subcomponent;
+import dagger.android.AndroidInjector;
+import dagger.android.support.DaggerAppCompatActivity;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+import javax.inject.Inject;
+
+/**
+ * The main activity of the application.
+ *
+ * <p>It can be injected with any binding from both {@link SimpleActivityComponent} and {@link
+ * SimpleApplication.SimpleComponent}.
+ */
+public class SimpleActivity extends DaggerAppCompatActivity {
+ @Subcomponent
+ interface SimpleActivityComponent extends AndroidInjector<SimpleActivity> {
+
+ @Subcomponent.Factory
+ interface Factory extends AndroidInjector.Factory<SimpleActivity> {}
+ }
+
+ @Module(subcomponents = SimpleActivityComponent.class)
+ abstract static class InjectorModule {
+
+ @Binds
+ @IntoMap
+ @ClassKey(SimpleActivity.class)
+ abstract AndroidInjector.Factory<?> bind(SimpleActivityComponent.Factory factory);
+ }
+
+ private static final String TAG = SimpleActivity.class.getSimpleName();
+
+ @Inject @Model String model;
+
+ @Inject
+ void logInjection() {
+ Log.i(TAG, "Injecting");
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_main);
+
+ TextView greeting = (TextView) findViewById(R.id.greeting);
+ String text = getResources().getString(R.string.welcome, model);
+ greeting.setText(text);
+ }
+}
diff --git a/java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/SimpleApplication.java b/java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/SimpleApplication.java
new file mode 100644
index 000000000..e2e34ea50
--- /dev/null
+++ b/java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/SimpleApplication.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.gradle.android.simple;
+
+import android.util.Log;
+import dagger.Component;
+import dagger.android.AndroidInjectionModule;
+import dagger.android.AndroidInjector;
+import dagger.android.DaggerApplication;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * A simple, skeletal application that demonstrates a dependency-injected application using the
+ * utilities in {@code dagger.android}.
+ */
+public class SimpleApplication extends DaggerApplication {
+ private static final String TAG = SimpleApplication.class.getSimpleName();
+
+ @Singleton
+ @Component(
+ modules = {
+ AndroidInjectionModule.class,
+ SimpleActivity.InjectorModule.class,
+ BuildModule.class
+ }
+ )
+ interface SimpleComponent extends AndroidInjector<SimpleApplication> {
+ @Component.Factory
+ interface Factory extends AndroidInjector.Factory<SimpleApplication> {}
+ }
+
+ @Inject
+ void logInjection() {
+ Log.i(TAG, "Injecting " + SimpleApplication.class.getSimpleName());
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ }
+
+ @Override
+ protected AndroidInjector<SimpleApplication> applicationInjector() {
+ return DaggerSimpleApplication_SimpleComponent.factory().create(this);
+ }
+}
diff --git a/java/dagger/example/gradle/android/simple/app/src/main/res/layout/activity_main.xml b/java/dagger/example/gradle/android/simple/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 000000000..18a547bbd
--- /dev/null
+++ b/java/dagger/example/gradle/android/simple/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/background_light">
+
+ <TextView
+ android:id="@+id/greeting"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentTop="true"
+ android:textColor="@android:color/primary_text_light"
+ />
+</RelativeLayout>
diff --git a/java/dagger/example/gradle/android/simple/app/src/main/res/values/strings.xml b/java/dagger/example/gradle/android/simple/app/src/main/res/values/strings.xml
new file mode 100644
index 000000000..f45fd411c
--- /dev/null
+++ b/java/dagger/example/gradle/android/simple/app/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="appName">Simple Dagger Android</string>
+ <string name="welcome">Hello, %s!</string>
+</resources>
diff --git a/java/dagger/example/gradle/android/simple/build.gradle b/java/dagger/example/gradle/android/simple/build.gradle
new file mode 100644
index 000000000..50f8f0baa
--- /dev/null
+++ b/java/dagger/example/gradle/android/simple/build.gradle
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+buildscript {
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:4.1.1'
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ mavenCentral()
+ mavenLocal()
+ }
+}
+
diff --git a/java/dagger/example/gradle/android/simple/gradle.properties b/java/dagger/example/gradle/android/simple/gradle.properties
new file mode 100644
index 000000000..2d8d1e4dd
--- /dev/null
+++ b/java/dagger/example/gradle/android/simple/gradle.properties
@@ -0,0 +1 @@
+android.useAndroidX=true \ No newline at end of file
diff --git a/java/dagger/example/gradle/android/simple/gradle/wrapper/gradle-wrapper.jar b/java/dagger/example/gradle/android/simple/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..5c2d1cf01
--- /dev/null
+++ b/java/dagger/example/gradle/android/simple/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/java/dagger/example/gradle/android/simple/gradle/wrapper/gradle-wrapper.properties b/java/dagger/example/gradle/android/simple/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..4d9ca1649
--- /dev/null
+++ b/java/dagger/example/gradle/android/simple/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/java/dagger/example/gradle/android/simple/gradlew b/java/dagger/example/gradle/android/simple/gradlew
new file mode 100755
index 000000000..b0d6d0ab5
--- /dev/null
+++ b/java/dagger/example/gradle/android/simple/gradlew
@@ -0,0 +1,188 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/java/dagger/example/gradle/android/simple/settings.gradle b/java/dagger/example/gradle/android/simple/settings.gradle
new file mode 100644
index 000000000..c5a07bc20
--- /dev/null
+++ b/java/dagger/example/gradle/android/simple/settings.gradle
@@ -0,0 +1,2 @@
+include ':app'
+rootProject.name='Simple Dagger Android' \ No newline at end of file
diff --git a/java/dagger/example/gradle/simple/SimpleApplication.java b/java/dagger/example/gradle/simple/SimpleApplication.java
new file mode 100644
index 000000000..11552f869
--- /dev/null
+++ b/java/dagger/example/gradle/simple/SimpleApplication.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.example.gradle.simple;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** A simple, skeletal application that defines a simple component. */
+public class SimpleApplication {
+ static final class Foo {
+ @Inject Foo() {}
+ }
+
+ @Module
+ static final class SimpleModule {
+ @Provides
+ static Foo provideFoo() {
+ return new Foo();
+ }
+ }
+
+ @Singleton
+ @Component(modules = { SimpleModule.class })
+ interface SimpleComponent {
+ Foo foo();
+ }
+
+ public static void main(String[] args) {
+ Foo foo = DaggerSimpleApplication_SimpleComponent.create().foo();
+ }
+}
diff --git a/java/dagger/example/gradle/simple/build.gradle b/java/dagger/example/gradle/simple/build.gradle
new file mode 100644
index 000000000..c9e59ef23
--- /dev/null
+++ b/java/dagger/example/gradle/simple/build.gradle
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ id 'java'
+ id 'application'
+}
+
+repositories {
+ mavenCentral()
+ mavenLocal()
+}
+
+sourceSets {
+ main {
+ java {
+ srcDir '.'
+ }
+ }
+}
+
+dependencies {
+ implementation 'com.google.dagger:dagger:LOCAL-SNAPSHOT'
+ annotationProcessor 'com.google.dagger:dagger-compiler:LOCAL-SNAPSHOT'
+}
+
+mainClassName = 'dagger.example.gradle.simple.SimpleApplication' \ No newline at end of file
diff --git a/java/dagger/example/gradle/simple/gradle/wrapper/gradle-wrapper.jar b/java/dagger/example/gradle/simple/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..5c2d1cf01
--- /dev/null
+++ b/java/dagger/example/gradle/simple/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/java/dagger/example/gradle/simple/gradle/wrapper/gradle-wrapper.properties b/java/dagger/example/gradle/simple/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..4d9ca1649
--- /dev/null
+++ b/java/dagger/example/gradle/simple/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/java/dagger/example/gradle/simple/gradlew b/java/dagger/example/gradle/simple/gradlew
new file mode 100755
index 000000000..b0d6d0ab5
--- /dev/null
+++ b/java/dagger/example/gradle/simple/gradlew
@@ -0,0 +1,188 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/java/dagger/example/spi/BUILD b/java/dagger/example/spi/BUILD
index 84b4a87d8..bd889c350 100644
--- a/java/dagger/example/spi/BUILD
+++ b/java/dagger/example/spi/BUILD
@@ -15,17 +15,20 @@
# Description:
# An example of the dagger.spi.BindingGraphPlugin usage
+load("@rules_java//java:defs.bzl", "java_plugin")
+
package(default_visibility = ["//:src"])
java_plugin(
name = "binding-graph-visualizer",
srcs = glob(["*.java"]),
deps = [
- "//java/dagger/model",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:graph",
"//java/dagger/spi",
"@google_bazel_common//third_party/java/auto:service",
"@google_bazel_common//third_party/java/error_prone:annotations",
- "@google_bazel_common//third_party/java/guava",
"@google_bazel_common//third_party/java/javapoet",
],
)
diff --git a/java/dagger/grpc/server/BUILD b/java/dagger/grpc/server/BUILD
index 1c57807c7..d9a2f9789 100644
--- a/java/dagger/grpc/server/BUILD
+++ b/java/dagger/grpc/server/BUILD
@@ -1,9 +1,16 @@
# A framework supporting Dagger-injected gRPC servers.
-package(default_visibility = ["//:src"])
+load("@rules_java//java:defs.bzl", "java_library")
+load(
+ "//:build_defs.bzl",
+ "DOCLINT_HTML_AND_SYNTAX",
+ "DOCLINT_REFERENCES",
+ "POM_VERSION",
+)
+load("//tools:maven.bzl", "pom_file")
+load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
-load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX", "DOCLINT_REFERENCES")
-load("//tools:maven.bzl", "pom_file", "POM_VERSION")
+package(default_visibility = ["//:src"])
ANNOTATIONS_SRCS = [
"CallScoped.java",
@@ -34,12 +41,13 @@ java_library(
exports = [":annotations"],
deps = [
"//:dagger_with_compiler",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
"@google_bazel_common//third_party/java/auto:value",
"@google_bazel_common//third_party/java/grpc:context",
"@google_bazel_common//third_party/java/grpc:core",
"@google_bazel_common//third_party/java/grpc:netty",
"@google_bazel_common//third_party/java/grpc:protobuf",
- "@google_bazel_common//third_party/java/guava",
"@google_bazel_common//third_party/java/jsr330_inject",
"@google_bazel_common//third_party/java/protobuf",
],
@@ -64,8 +72,6 @@ filegroup(
srcs = glob(["*.java"]),
)
-load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
-
javadoc_library(
name = "javadoc",
srcs = [":javadoc-srcs"],
diff --git a/java/dagger/grpc/server/README.md b/java/dagger/grpc/server/README.md
index 5fe8d5c2f..a864f36ac 100644
--- a/java/dagger/grpc/server/README.md
+++ b/java/dagger/grpc/server/README.md
@@ -7,4 +7,4 @@ It is maintained by the Dagger team.
It is in development, and is planned for open-source release as part of Dagger.
-See user documentation at https://dagger.dev/grpc.
+See user documentation at https://dagger.dev/dev-guide/grpc.
diff --git a/java/dagger/grpc/server/processor/BUILD b/java/dagger/grpc/server/processor/BUILD
index ce02b0635..dd3365166 100644
--- a/java/dagger/grpc/server/processor/BUILD
+++ b/java/dagger/grpc/server/processor/BUILD
@@ -1,7 +1,13 @@
-package(default_visibility = ["//:src"])
+load("@rules_java//java:defs.bzl", "java_library", "java_plugin")
+load(
+ "//:build_defs.bzl",
+ "DOCLINT_HTML_AND_SYNTAX",
+ "POM_VERSION",
+)
+load("//tools:maven.bzl", "pom_file")
+load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
-load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
-load("//tools:maven.bzl", "pom_file", "POM_VERSION")
+package(default_visibility = ["//:src"])
java_library(
name = "processor",
@@ -11,12 +17,14 @@ java_library(
deps = [
"//:dagger_with_compiler",
"//java/dagger/grpc/server:annotations",
- "@google_bazel_common//third_party/java/auto:common",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:io",
"@google_bazel_common//third_party/java/auto:service",
"@google_bazel_common//third_party/java/google_java_format",
- "@google_bazel_common//third_party/java/guava",
"@google_bazel_common//third_party/java/javapoet",
"@google_bazel_common//third_party/java/jsr250_annotations",
+ "@maven//:com_google_auto_auto_common",
],
)
@@ -39,8 +47,6 @@ filegroup(
srcs = glob(["*.java"]),
)
-load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
-
javadoc_library(
name = "javadoc",
srcs = [":javadoc-srcs"],
diff --git a/java/dagger/grpc/server/processor/GrpcServiceProcessor.java b/java/dagger/grpc/server/processor/GrpcServiceProcessor.java
index e361fdbde..9b74454bf 100644
--- a/java/dagger/grpc/server/processor/GrpcServiceProcessor.java
+++ b/java/dagger/grpc/server/processor/GrpcServiceProcessor.java
@@ -40,7 +40,7 @@ import javax.tools.Diagnostic.Kind;
/**
* Generates code from types annotated with {@link GrpcService @GrpcService}.
*
- * @see <a href="https://dagger.dev/grpc">https://dagger.dev/grpc</a>
+ * @see <a href="https://dagger.dev/dev-guide/grpc">https://dagger.dev/dev-guide/grpc</a>
*/
@AutoService(Processor.class)
public class GrpcServiceProcessor extends BasicAnnotationProcessor implements ProcessingStep {
diff --git a/java/dagger/hilt/BUILD b/java/dagger/hilt/BUILD
new file mode 100644
index 000000000..94af18a91
--- /dev/null
+++ b/java/dagger/hilt/BUILD
@@ -0,0 +1,240 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+load("//tools:maven.bzl", "gen_maven_artifact")
+load("//:build_defs.bzl", "POM_VERSION_ALPHA")
+
+# Description:
+# A library that wraps the Dagger API to make DI usage and testing easier.
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "install_in",
+ srcs = ["InstallIn.java"],
+ exported_plugins = [
+ "//java/dagger/hilt/processor/internal/aggregateddeps:plugin",
+ ],
+ exports = [
+ "//java/dagger/hilt/processor/internal/aggregateddeps:annotation",
+ ],
+ deps = [
+ ":generates_root_input",
+ ":package_info",
+ ],
+)
+
+java_library(
+ name = "entry_point",
+ srcs = [
+ "EntryPoint.java",
+ "EntryPoints.java",
+ ],
+ exported_plugins = [
+ # This is required so that we can fail if @InstallIn is missing.
+ # TODO(bcorso): Consider using a separate processor to validate @EntryPoint.
+ "//java/dagger/hilt/processor/internal/aggregateddeps:plugin",
+ ],
+ deps = [
+ ":generates_root_input",
+ ":package_info",
+ "//java/dagger/hilt/internal:component_manager",
+ "//java/dagger/hilt/internal:generated_component",
+ "@google_bazel_common//third_party/java/jsr305_annotations",
+ ],
+)
+
+java_library(
+ name = "generates_root_input",
+ srcs = [
+ "GeneratesRootInput.java",
+ ],
+ exported_plugins = [
+ "//java/dagger/hilt/processor/internal/generatesrootinput:processor",
+ ],
+ exports = [
+ "//java/dagger/hilt/internal/generatesrootinput",
+ ],
+ deps = [
+ ":package_info",
+ ],
+)
+
+java_library(
+ name = "define_component",
+ srcs = [
+ "DefineComponent.java",
+ ],
+ exported_plugins = [
+ "//java/dagger/hilt/processor/internal/definecomponent:processor",
+ ],
+ exports = [
+ "//java/dagger/hilt/internal/definecomponent",
+ ],
+ deps = [
+ ":generates_root_input",
+ ":package_info",
+ "//java/dagger/hilt/internal/definecomponent",
+ ],
+)
+
+java_library(
+ name = "package_info",
+ srcs = ["package-info.java"],
+ deps = [
+ "@google_bazel_common//third_party/java/jsr305_annotations",
+ ],
+)
+
+filegroup(
+ name = "javadoc-srcs",
+ srcs = [
+ ":hilt_android_filegroup",
+ ":hilt_android_testing_filegroup",
+ ":hilt_filegroup",
+ ":hilt_testing_filegroup",
+ ],
+)
+
+filegroup(
+ name = "hilt_filegroup",
+ srcs = glob(["*"]) + [
+ "//java/dagger/hilt/codegen:srcs_filegroup",
+ "//java/dagger/hilt/components:srcs_filegroup",
+ "//java/dagger/hilt/migration:srcs_filegroup",
+ "//java/dagger/hilt/internal:srcs_filegroup",
+ "//java/dagger/hilt/internal/aliasof:srcs_filegroup",
+ "//java/dagger/hilt/internal/definecomponent:srcs_filegroup",
+ "//java/dagger/hilt/internal/generatesrootinput:srcs_filegroup",
+ ],
+)
+
+filegroup(
+ name = "hilt_testing_filegroup",
+ srcs = [
+ "//java/dagger/hilt/testing:srcs_filegroup",
+ ],
+)
+
+filegroup(
+ name = "hilt_android_filegroup",
+ srcs = [
+ "//java/dagger/hilt/android:srcs_filegroup",
+ "//java/dagger/hilt/android/components:srcs_filegroup",
+ "//java/dagger/hilt/android/internal:srcs_filegroup",
+ "//java/dagger/hilt/android/internal/builders:srcs_filegroup",
+ "//java/dagger/hilt/android/internal/lifecycle:srcs_filegroup",
+ "//java/dagger/hilt/android/internal/managers:srcs_filegroup",
+ "//java/dagger/hilt/android/internal/migration:srcs_filegroup",
+ "//java/dagger/hilt/android/internal/modules:srcs_filegroup",
+ "//java/dagger/hilt/android/lifecycle:srcs_filegroup",
+ "//java/dagger/hilt/android/migration:srcs_filegroup",
+ "//java/dagger/hilt/android/plugin:srcs_filegroup",
+ "//java/dagger/hilt/android/qualifiers:srcs_filegroup",
+ "//java/dagger/hilt/android/scopes:srcs_filegroup",
+ ],
+)
+
+filegroup(
+ name = "hilt_android_testing_filegroup",
+ srcs = [
+ "//java/dagger/hilt/android/internal/testing:srcs_filegroup",
+ "//java/dagger/hilt/android/testing:srcs_filegroup",
+ ],
+)
+
+filegroup(
+ name = "hilt_processing_filegroup",
+ srcs = [
+ "//java/dagger/hilt/android/processor:srcs_filegroup",
+ "//java/dagger/hilt/android/processor/internal:srcs_filegroup",
+ "//java/dagger/hilt/android/processor/internal/androidentrypoint:srcs_filegroup",
+ "//java/dagger/hilt/android/processor/internal/bindvalue:srcs_filegroup",
+ "//java/dagger/hilt/android/processor/internal/customtestapplication:srcs_filegroup",
+ "//java/dagger/hilt/android/processor/internal/uninstallmodules:srcs_filegroup",
+ "//java/dagger/hilt/android/processor/internal/viewmodel:srcs_filegroup",
+ "//java/dagger/hilt/processor:srcs_filegroup",
+ "//java/dagger/hilt/processor/internal:srcs_filegroup",
+ "//java/dagger/hilt/processor/internal/aggregateddeps:srcs_filegroup",
+ "//java/dagger/hilt/processor/internal/aliasof:srcs_filegroup",
+ "//java/dagger/hilt/processor/internal/definecomponent:srcs_filegroup",
+ "//java/dagger/hilt/processor/internal/disableinstallincheck:srcs_filegroup",
+ "//java/dagger/hilt/processor/internal/generatesrootinput:srcs_filegroup",
+ "//java/dagger/hilt/processor/internal/originatingelement:srcs_filegroup",
+ "//java/dagger/hilt/processor/internal/root:srcs_filegroup",
+ ],
+)
+
+java_library(
+ name = "artifact-core-lib",
+ tags = ["maven_coordinates=com.google.dagger:hilt-core:" + POM_VERSION_ALPHA],
+ exports = [
+ ":define_component",
+ ":entry_point",
+ ":generates_root_input",
+ ":install_in",
+ ":package_info",
+ "//java/dagger:core",
+ "//java/dagger/hilt/components",
+ "//java/dagger/hilt/migration:alias_of",
+ "//java/dagger/hilt/migration:disable_install_in_check",
+ ],
+)
+
+gen_maven_artifact(
+ name = "artifact-core",
+ artifact_coordinates = "com.google.dagger:hilt-core:" + POM_VERSION_ALPHA,
+ artifact_name = "Hilt Core",
+ artifact_target = ":artifact-core-lib",
+ artifact_target_libs = [
+ "//java/dagger/hilt:define_component",
+ "//java/dagger/hilt:entry_point",
+ "//java/dagger/hilt:generates_root_input",
+ "//java/dagger/hilt:install_in",
+ "//java/dagger/hilt:package_info",
+ "//java/dagger/hilt/codegen:originating_element",
+ "//java/dagger/hilt/codegen:package_info",
+ "//java/dagger/hilt/components",
+ "//java/dagger/hilt/components:package_info",
+ "//java/dagger/hilt/internal:component_manager",
+ "//java/dagger/hilt/internal:generated_component",
+ "//java/dagger/hilt/internal:preconditions",
+ "//java/dagger/hilt/internal:unsafe_casts",
+ "//java/dagger/hilt/internal/aliasof",
+ "//java/dagger/hilt/internal/definecomponent",
+ "//java/dagger/hilt/internal/generatesrootinput",
+ "//java/dagger/hilt/migration:alias_of",
+ "//java/dagger/hilt/migration:disable_install_in_check",
+ "//java/dagger/hilt/migration:package_info",
+ "//java/dagger/hilt/processor/internal/aggregateddeps:annotation",
+ ],
+ artifact_target_maven_deps = [
+ "com.google.code.findbugs:jsr305",
+ "com.google.dagger:dagger",
+ "javax.inject:javax.inject",
+ ],
+ artifact_target_maven_deps_banned = [
+ "com.google.guava:guava",
+ "javax.annotation:jsr250-api",
+ ],
+ javadoc_exclude_packages = [
+ "dagger.hilt.internal",
+ ],
+ javadoc_root_packages = [
+ "dagger.hilt",
+ ],
+ javadoc_srcs = [
+ "//java/dagger/hilt:hilt_filegroup",
+ ],
+)
diff --git a/java/dagger/hilt/DefineComponent.java b/java/dagger/hilt/DefineComponent.java
new file mode 100644
index 000000000..fcd572cab
--- /dev/null
+++ b/java/dagger/hilt/DefineComponent.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import dagger.hilt.internal.definecomponent.DefineComponentNoParent;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Defines a Hilt component.
+ *
+ * <p>Example defining a root component, {@code ParentComponent}:
+ *
+ * <pre><code>
+ * {@literal @}ParentScoped
+ * {@literal @}DefineComponent
+ * interface ParentComponent {}
+ * </code></pre>
+ *
+ * <p>Example defining a child component, {@code ChildComponent}:
+ *
+ * <pre><code>
+ * {@literal @}ChildScoped
+ * {@literal @}DefineComponent(parent = ParentComponent.class)
+ * interface ChildComponent {}
+ * </code></pre>
+ */
+@Retention(CLASS)
+@Target(TYPE)
+@GeneratesRootInput
+public @interface DefineComponent {
+ /** Returns the parent of this component, if it exists. */
+ Class<?> parent() default DefineComponentNoParent.class;
+
+ /**
+ * Defines a builder for a Hilt component.
+ *
+ * <pre><code>
+ * {@literal @}DefineComponent.Builder
+ * interface ParentComponentBuilder {
+ * ParentComponentBuilder seedData(SeedData seed);
+ * ParentComponent build();
+ * }
+ * </code></pre>
+ */
+ // TODO(bcorso): Consider making this a top-level class to hint that it doesn't need to be nested.
+ @Retention(CLASS)
+ @Target(TYPE)
+ @GeneratesRootInput
+ public @interface Builder {}
+}
diff --git a/java/dagger/hilt/EntryPoint.java b/java/dagger/hilt/EntryPoint.java
new file mode 100644
index 000000000..e216e77c0
--- /dev/null
+++ b/java/dagger/hilt/EntryPoint.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation for marking an interface as an entry point into a generated component. This annotation
+ * must be used with {@link dagger.hilt.InstallIn} to indicate which component(s) should have this
+ * entry point. When assembling components, Hilt will make the indicated components extend the
+ * interface marked with this annotation.
+ *
+ * <p>To use the annotated interface to access Dagger objects, use {@link dagger.hilt.EntryPoints}.
+ *
+ * <p>Example usage:
+ *
+ * <pre><code>
+ * {@literal @}EntryPoint
+ * {@literal @}InstallIn(SingletonComponent.class)
+ * public interface FooEntryPoint {
+ * Foo getFoo();
+ * }
+ *
+ * Foo foo = EntryPoints.get(component, FooEntryPoint.class).getFoo();
+ * </code></pre>
+ *
+ * @see <a href="https://dagger.dev/hilt/entry-points">Entry points</a>
+ */
+@Retention(CLASS)
+@Target(ElementType.TYPE)
+@GeneratesRootInput
+public @interface EntryPoint {}
diff --git a/java/dagger/hilt/EntryPoints.java b/java/dagger/hilt/EntryPoints.java
new file mode 100644
index 000000000..32e231212
--- /dev/null
+++ b/java/dagger/hilt/EntryPoints.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt;
+
+import dagger.hilt.internal.GeneratedComponent;
+import dagger.hilt.internal.GeneratedComponentManager;
+import javax.annotation.Nonnull;
+
+/** Static utility methods for accessing objects through entry points. */
+public final class EntryPoints {
+
+ /**
+ * Returns the entry point interface given a component or component manager. Note that this
+ * performs an unsafe cast and so callers should be sure that the given component/component
+ * manager matches the entry point interface that is given.
+ *
+ * @param component The Hilt-generated component instance. For convenience, also takes component
+ * manager instances as well.
+ * @param entryPoint The interface marked with {@link dagger.hilt.EntryPoint}. The {@link
+ * dagger.hilt.InstallIn} annotation on this entry point should match the component argument
+ * above.
+ */
+ // Note that the input is not statically declared to be a Component or ComponentManager to make
+ // this method easier to use, since most code will use this with an Application or Activity type.
+ @Nonnull
+ public static <T> T get(Object component, Class<T> entryPoint) {
+ if (component instanceof GeneratedComponent) {
+ // Unsafe cast. There is no way for this method to know that the correct component was used.
+ return entryPoint.cast(component);
+ } else if (component instanceof GeneratedComponentManager) {
+ // Unsafe cast. There is no way for this method to know that the correct component was used.
+ return entryPoint.cast(((GeneratedComponentManager<?>) component).generatedComponent());
+ } else {
+ throw new IllegalStateException(
+ String.format(
+ "Given component holder %s does not implement %s or %s",
+ component.getClass(), GeneratedComponent.class, GeneratedComponentManager.class));
+ }
+ }
+
+ private EntryPoints() {}
+}
diff --git a/java/dagger/hilt/GeneratesRootInput.java b/java/dagger/hilt/GeneratesRootInput.java
new file mode 100644
index 000000000..ffd786f73
--- /dev/null
+++ b/java/dagger/hilt/GeneratesRootInput.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** For annotating annotations that generate input for the {@link GenerateComponents}. */
+// TODO(danysantiago): Rename to GenerateComponentsInput
+@Target(ElementType.ANNOTATION_TYPE)
+@Retention(RetentionPolicy.CLASS)
+public @interface GeneratesRootInput {}
diff --git a/java/dagger/hilt/InstallIn.java b/java/dagger/hilt/InstallIn.java
new file mode 100644
index 000000000..9148c82f3
--- /dev/null
+++ b/java/dagger/hilt/InstallIn.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package dagger.hilt;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that declares which component(s) the annotated class should be included in when
+ * Hilt generates the components. This may only be used with classes annotated with
+ * {@literal @}{@link dagger.Module} or {@literal @}{@link dagger.hilt.EntryPoint}.
+ *
+ * <p>Example usage for installing a module in the generated {@code ApplicationComponent}:
+ *
+ * <pre><code>
+ * {@literal @}Module
+ * {@literal @}InstallIn(SingletonComponent.class)
+ * public final class FooModule {
+ * {@literal @}Provides
+ * static Foo provideFoo() {
+ * return new Foo();
+ * }
+ * }
+ * </code></pre>
+ *
+ * @see <a href="https://dagger.dev/hilt/modules">Hilt Modules</a>
+ */
+@Retention(CLASS)
+@Target({ElementType.TYPE})
+@GeneratesRootInput
+public @interface InstallIn {
+ Class<?>[] value();
+}
diff --git a/java/dagger/hilt/README.md b/java/dagger/hilt/README.md
new file mode 100644
index 000000000..911e46696
--- /dev/null
+++ b/java/dagger/hilt/README.md
@@ -0,0 +1,4 @@
+# Hilt
+
+Hilt provides a standard way to incorporate Dagger dependency injection into an
+Android application. For more information, see https://dagger.dev/hilt.
diff --git a/java/dagger/hilt/android/ActivityRetainedLifecycle.java b/java/dagger/hilt/android/ActivityRetainedLifecycle.java
new file mode 100644
index 000000000..5e2d7cdcb
--- /dev/null
+++ b/java/dagger/hilt/android/ActivityRetainedLifecycle.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android;
+
+import android.app.Activity;
+import androidx.annotation.MainThread;
+import androidx.annotation.NonNull;
+
+/**
+ * A <code>ActivityRetainedLifecycle</code> class is associated with the lifecycle of the {@link
+ * dagger.hilt.android.components.ActivityRetainedComponent}.
+ */
+public interface ActivityRetainedLifecycle {
+
+ /**
+ * Adds a new {@link OnClearedListener} for receiving a callback when the activity retained
+ * instances will no longer be needed and destroyed.
+ *
+ * @param listener The listener that should be added.
+ */
+ @MainThread
+ void addOnClearedListener(@NonNull OnClearedListener listener);
+
+ /**
+ * Removes a {@link OnClearedListener} previously added via {@link
+ * #addOnClearedListener(OnClearedListener)}.
+ *
+ * @param listener The listener that should be removed.
+ */
+ @MainThread
+ void removeOnClearedListener(@NonNull OnClearedListener listener);
+
+ /**
+ * Listener for receiving a callback for when the {@link
+ * dagger.hilt.android.components.ActivityRetainedComponent} will no longer be used and destroyed.
+ */
+ interface OnClearedListener {
+
+ /**
+ * Called when the activity retained instances will no longer be used and destroyed.
+ *
+ * <p>Specifically this will be invoked during {@link Activity#onDestroy()} when {@link
+ * Activity#isChangingConfigurations} is false.
+ */
+ void onCleared();
+ }
+}
diff --git a/java/dagger/hilt/android/AndroidEntryPoint.java b/java/dagger/hilt/android/AndroidEntryPoint.java
new file mode 100644
index 000000000..8c561161d
--- /dev/null
+++ b/java/dagger/hilt/android/AndroidEntryPoint.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android;
+
+import dagger.hilt.GeneratesRootInput;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Marks an Android component class to be setup for injection with the standard Hilt Dagger Android
+ * components. Currently, this supports activities, fragments, views, services, and broadcast
+ * receivers.
+ *
+ * <p>This annotation will generate a base class that the annotated class should extend, either
+ * directly or via the Hilt Gradle Plugin. This base class will take care of injecting members into
+ * the Android class as well as handling instantiating the proper Hilt components at the right point
+ * in the lifecycle. The name of the base class will be "Hilt_<annotated class name>".
+ *
+ * <p>Example usage (with the Hilt Gradle Plugin):
+ *
+ * <pre><code>
+ * {@literal @}AndroidEntryPoint
+ * public final class FooActivity extends FragmentActivity {
+ * {@literal @}Inject Foo foo;
+ *
+ * {@literal @}Override
+ * public void onCreate(Bundle savedInstanceState) {
+ * super.onCreate(savedInstanceState); // The foo field is injected in super.onCreate()
+ * }
+ * }
+ * </code></pre>
+ *
+ * <p>Example usage (without the Hilt Gradle Plugin):
+ *
+ * <pre><code>
+ * {@literal @}AndroidEntryPoint(FragmentActivity.class)
+ * public final class FooActivity extends Hilt_FooActivity {
+ * {@literal @}Inject Foo foo;
+ *
+ * {@literal @}Override
+ * public void onCreate(Bundle savedInstanceState) {
+ * super.onCreate(savedInstanceState); // The foo field is injected in super.onCreate()
+ * }
+ * }
+ * </code></pre>
+ *
+ * @see HiltAndroidApp
+ */
+@Target({ElementType.TYPE})
+@GeneratesRootInput
+public @interface AndroidEntryPoint {
+
+ /**
+ * The base class for the generated Hilt class. When applying the Hilt Gradle Plugin this value
+ * is not necessary and will be inferred from the current superclass.
+ */
+ Class<?> value() default Void.class;
+}
diff --git a/java/dagger/hilt/android/AndroidManifest.xml b/java/dagger/hilt/android/AndroidManifest.xml
new file mode 100644
index 000000000..3a7b087fd
--- /dev/null
+++ b/java/dagger/hilt/android/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2020 The Dagger Authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.hilt.android">
+ <uses-sdk android:minSdkVersion="14" />
+</manifest>
diff --git a/java/dagger/hilt/android/BUILD b/java/dagger/hilt/android/BUILD
new file mode 100644
index 000000000..47d7e678c
--- /dev/null
+++ b/java/dagger/hilt/android/BUILD
@@ -0,0 +1,193 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# A library based on Hilt that provides standard components and automated injection for Android.
+load("//:build_defs.bzl", "POM_VERSION_ALPHA")
+load("//tools:maven.bzl", "gen_maven_artifact")
+
+package(default_visibility = ["//:src"])
+
+android_library(
+ name = "android_entry_point",
+ srcs = [
+ "AndroidEntryPoint.java",
+ "WithFragmentBindings.java",
+ ],
+ exported_plugins = [
+ "//java/dagger/hilt/android/processor/internal/androidentrypoint:plugin",
+ ],
+ exports = [
+ "//java/dagger/hilt:install_in",
+ "//java/dagger/hilt/android/components",
+ "//java/dagger/hilt/android/internal/builders",
+ "//java/dagger/hilt/android/internal/managers",
+ "//java/dagger/hilt/android/internal/managers:component_supplier",
+ "//java/dagger/hilt/android/internal/modules",
+ "//java/dagger/hilt/android/lifecycle",
+ "//java/dagger/hilt/codegen:originating_element",
+ "//java/dagger/hilt/internal:component_entry_point",
+ "//java/dagger/hilt/internal:component_manager",
+ "//java/dagger/hilt/internal:generated_entry_point",
+ "//java/dagger/hilt/internal:preconditions",
+ "@maven//:androidx_annotation_annotation",
+ "@maven//:androidx_fragment_fragment",
+ ],
+ deps = [
+ ":package_info",
+ "//java/dagger/hilt:generates_root_input",
+ ],
+)
+
+android_library(
+ name = "hilt_android_app",
+ srcs = ["HiltAndroidApp.java"],
+ exported_plugins = [
+ "//java/dagger/hilt/android/processor/internal/androidentrypoint:plugin",
+ "//java/dagger/hilt/android/processor/internal/viewmodel:validation_plugin",
+ "//java/dagger/hilt/processor/internal/root:plugin",
+ ],
+ exports = [
+ "//:dagger_with_compiler",
+ "//java/dagger/hilt:install_in",
+ "//java/dagger/hilt/android/components",
+ "//java/dagger/hilt/android/internal/managers",
+ "//java/dagger/hilt/android/internal/managers:component_supplier",
+ "//java/dagger/hilt/android/internal/modules",
+ "//java/dagger/hilt/codegen:originating_element",
+ "//java/dagger/hilt/internal:component_manager",
+ "//java/dagger/hilt/internal:generated_component",
+ "//java/dagger/hilt/internal:generated_entry_point",
+ "//java/dagger/hilt/migration:disable_install_in_check",
+ "@maven//:androidx_annotation_annotation",
+ ],
+ deps = [
+ ":package_info",
+ "//java/dagger/hilt:generates_root_input",
+ ],
+)
+
+android_library(
+ name = "entry_point_accessors",
+ srcs = ["EntryPointAccessors.java"],
+ deps = [
+ ":package_info",
+ "//java/dagger/hilt:entry_point",
+ "@google_bazel_common//third_party/java/jsr305_annotations",
+ "@maven//:androidx_fragment_fragment",
+ ],
+)
+
+android_library(
+ name = "activity_retained_lifecycle",
+ srcs = ["ActivityRetainedLifecycle.java"],
+ deps = [
+ ":package_info",
+ "@maven//:androidx_annotation_annotation",
+ ],
+)
+
+android_library(
+ name = "artifact-lib",
+ tags = ["maven_coordinates=com.google.dagger:hilt-android:" + POM_VERSION_ALPHA],
+ exports = [
+ ":android_entry_point",
+ ":entry_point_accessors",
+ ":hilt_android_app",
+ ":package_info",
+ "//java/dagger/hilt:artifact-core-lib",
+ "//java/dagger/hilt/android/migration:optional_inject",
+ "//java/dagger/lint:lint-android-artifact-lib",
+ ],
+)
+
+java_library(
+ name = "package_info",
+ srcs = ["package-info.java"],
+ deps = [
+ "@google_bazel_common//third_party/java/jsr305_annotations",
+ ],
+)
+
+gen_maven_artifact(
+ name = "artifact",
+ artifact_coordinates = "com.google.dagger:hilt-android:" + POM_VERSION_ALPHA,
+ artifact_name = "Hilt Android",
+ artifact_target = ":artifact-lib",
+ artifact_target_libs = [
+ ":entry_point_accessors",
+ "//java/dagger/hilt/android:activity_retained_lifecycle",
+ "//java/dagger/hilt/android:android_entry_point",
+ "//java/dagger/hilt/android:hilt_android_app",
+ "//java/dagger/hilt/android:package_info",
+ "//java/dagger/hilt/android/components",
+ "//java/dagger/hilt/android/components:view_model_component",
+ "//java/dagger/hilt/android/components:package_info",
+ "//java/dagger/hilt/android/internal",
+ "//java/dagger/hilt/android/internal/builders",
+ "//java/dagger/hilt/android/internal/lifecycle",
+ "//java/dagger/hilt/android/internal/managers",
+ "//java/dagger/hilt/android/internal/managers:component_supplier",
+ "//java/dagger/hilt/android/internal/migration:injected_by_hilt",
+ "//java/dagger/hilt/android/internal/modules",
+ "//java/dagger/hilt/android/lifecycle",
+ "//java/dagger/hilt/android/migration:optional_inject",
+ "//java/dagger/hilt/android/migration:package_info",
+ "//java/dagger/hilt/android/qualifiers",
+ "//java/dagger/hilt/android/qualifiers:package_info",
+ "//java/dagger/hilt/android/scopes",
+ "//java/dagger/hilt/android/scopes:activity_retained_scoped",
+ "//java/dagger/hilt/android/scopes:view_model_scoped",
+ "//java/dagger/hilt/android/scopes:package_info",
+ "//java/dagger/hilt/internal:component_entry_point",
+ "//java/dagger/hilt/internal:generated_entry_point",
+ ],
+ artifact_target_maven_deps = [
+ "androidx.activity:activity",
+ "androidx.annotation:annotation",
+ "androidx.fragment:fragment",
+ "androidx.lifecycle:lifecycle-viewmodel",
+ "androidx.lifecycle:lifecycle-viewmodel-savedstate",
+ "androidx.savedstate:savedstate",
+ "com.google.code.findbugs:jsr305",
+ "com.google.dagger:dagger-lint-aar",
+ "com.google.dagger:dagger",
+ "com.google.dagger:hilt-core",
+ "javax.inject:javax.inject",
+ ],
+ artifact_target_maven_deps_banned = [
+ "com.google.guava:guava",
+ "javax.annotation:jsr250-api",
+ ],
+ javadoc_android_api_level = 30,
+ javadoc_exclude_packages = [
+ "dagger.hilt.android.internal",
+ ],
+ javadoc_root_packages = [
+ "dagger.hilt.android",
+ ],
+ javadoc_srcs = [
+ "//java/dagger/hilt:hilt_android_filegroup",
+ ],
+ manifest = "AndroidManifest.xml",
+ packaging = "aar",
+ proguard_specs = [
+ "//java/dagger/hilt/android/lifecycle:proguard-rules.pro",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/android/EntryPointAccessors.java b/java/dagger/hilt/android/EntryPointAccessors.java
new file mode 100644
index 000000000..6145af1bc
--- /dev/null
+++ b/java/dagger/hilt/android/EntryPointAccessors.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android;
+
+import android.app.Activity;
+import android.content.Context;
+import androidx.fragment.app.Fragment;
+import android.view.View;
+import dagger.hilt.EntryPoints;
+import javax.annotation.Nonnull;
+
+/** Static utility methods for dealing with entry points for standard Android components. */
+public final class EntryPointAccessors {
+
+ /**
+ * Returns the entry point interface from an application. The context can be any context derived
+ * from the application context. May only be used with entry point interfaces installed in the
+ * ApplicationComponent.
+ */
+ @Nonnull
+ public static <T> T fromApplication(Context context, Class<T> entryPoint) {
+ return EntryPoints.get(context.getApplicationContext(), entryPoint);
+ }
+
+ /**
+ * Returns the entry point interface from an activity. May only be used with entry point
+ * interfaces installed in the ActivityComponent.
+ */
+ @Nonnull
+ public static <T> T fromActivity(Activity activity, Class<T> entryPoint) {
+ return EntryPoints.get(activity, entryPoint);
+ }
+
+ /**
+ * Returns the entry point interface from a fragment. May only be used with entry point interfaces
+ * installed in the FragmentComponent.
+ */
+ @Nonnull
+ public static <T> T fromFragment(Fragment fragment, Class<T> entryPoint) {
+ return EntryPoints.get(fragment, entryPoint);
+ }
+
+ /**
+ * Returns the entry point interface from a view. May only be used with entry point interfaces
+ * installed in the ViewComponent or ViewNoFragmentComponent.
+ */
+ @Nonnull
+ public static <T> T fromView(View view, Class<T> entryPoint) {
+ return EntryPoints.get(view, entryPoint);
+ }
+
+ private EntryPointAccessors() {}
+}
diff --git a/java/dagger/hilt/android/HiltAndroidApp.java b/java/dagger/hilt/android/HiltAndroidApp.java
new file mode 100644
index 000000000..0c2ca0f54
--- /dev/null
+++ b/java/dagger/hilt/android/HiltAndroidApp.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android;
+
+import dagger.hilt.GeneratesRootInput;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation for marking the {@link android.app.Application} class where the Dagger components
+ * should be generated. Since all components will be built in the same compilation as the annotated
+ * application, all modules and entry points that should be installed in the component need to be
+ * transitive compilation dependencies of the annotated application.
+ *
+ * <p>Usage of this annotation is similar to {@link dagger.hilt.android.AndroidEntryPoint} with the
+ * only difference being that it only works on application classes and additionally triggers Dagger
+ * component generation.
+ *
+ * <p>This annotation will generate a base class that the annotated class should extend, either
+ * directly or via the Hilt Gradle Plugin. This base class will take care of injecting members into
+ * the Android class as well as handling instantiating the proper Hilt components at the right point
+ * in the lifecycle. The name of the base class will be "Hilt_<annotated class name>".
+ *
+ * <p>Example usage (with the Hilt Gradle Plugin):
+ *
+ * <pre><code>
+ * {@literal @}HiltAndroidApp
+ * public final class FooApplication extends Application {
+ * {@literal @}Inject Foo foo;
+ *
+ * {@literal @}Override
+ * public void onCreate() {
+ * super.onCreate(); // The foo field is injected in super.onCreate()
+ * }
+ * }
+ * </code></pre>
+ *
+ * <p>Example usage (without the Hilt Gradle Plugin):
+ *
+ * <pre><code>
+ * {@literal @}HiltAndroidApp(Application.class)
+ * public final class FooApplication extends Hilt_FooApplication {
+ * {@literal @}Inject Foo foo;
+ *
+ * {@literal @}Override
+ * public void onCreate() {
+ * super.onCreate(); // The foo field is injected in super.onCreate()
+ * }
+ * }
+ * </code></pre>
+ *
+ * @see AndroidEntryPoint
+ */
+// Set the retention to RUNTIME because we check it via reflection in the HiltAndroidRule.
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+@GeneratesRootInput
+public @interface HiltAndroidApp {
+ /**
+ * The base class for the generated Hilt application. When applying the Hilt Gradle Plugin this
+ * value is not necessary and will be inferred from the current superclass.
+ */
+ // TODO(erichang): It would be nice to make this Class<? extends Application> but then the default
+ // would have to be Application which would make the default actually valid even without the
+ // plugin. Maybe that is a good thing...but might be better to have users be explicit about the
+ // base class they want.
+ Class<?> value() default Void.class;
+}
diff --git a/java/dagger/hilt/android/WithFragmentBindings.java b/java/dagger/hilt/android/WithFragmentBindings.java
new file mode 100644
index 000000000..2b02498a3
--- /dev/null
+++ b/java/dagger/hilt/android/WithFragmentBindings.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Makes a View annotated with {@link AndroidEntryPoint} have access to fragment bindings.
+ *
+ * <p>By default, views annotated with {@literal @}AndroidEntryPoint do not have access to fragment
+ * bindings and must use this annotation if fragment bindings are required. When this annotation is
+ * used, this view must always be attached through a fragment.
+ */
+@Target({ElementType.TYPE})
+public @interface WithFragmentBindings {}
diff --git a/java/dagger/hilt/android/components/ActivityComponent.java b/java/dagger/hilt/android/components/ActivityComponent.java
new file mode 100644
index 000000000..78f48cc70
--- /dev/null
+++ b/java/dagger/hilt/android/components/ActivityComponent.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.components;
+
+import dagger.hilt.DefineComponent;
+import dagger.hilt.android.scopes.ActivityScoped;
+
+/** A Hilt component that has the lifetime of the activity. */
+@ActivityScoped
+@DefineComponent(parent = ActivityRetainedComponent.class)
+public interface ActivityComponent {}
diff --git a/java/dagger/hilt/android/components/ActivityRetainedComponent.java b/java/dagger/hilt/android/components/ActivityRetainedComponent.java
new file mode 100644
index 000000000..b3c340e48
--- /dev/null
+++ b/java/dagger/hilt/android/components/ActivityRetainedComponent.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.components;
+
+import dagger.hilt.DefineComponent;
+import dagger.hilt.android.scopes.ActivityRetainedScoped;
+import dagger.hilt.components.SingletonComponent;
+
+/** A Hilt component that has the lifetime of a configuration surviving activity. */
+@ActivityRetainedScoped
+@DefineComponent(parent = SingletonComponent.class)
+public interface ActivityRetainedComponent {}
diff --git a/java/dagger/hilt/android/components/BUILD b/java/dagger/hilt/android/components/BUILD
new file mode 100644
index 000000000..3a307d145
--- /dev/null
+++ b/java/dagger/hilt/android/components/BUILD
@@ -0,0 +1,62 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Hilt Android components
+
+package(default_visibility = ["//:src"])
+
+android_library(
+ name = "components",
+ srcs = [
+ "ActivityComponent.java",
+ "ActivityRetainedComponent.java",
+ "FragmentComponent.java",
+ "ServiceComponent.java",
+ "ViewComponent.java",
+ "ViewWithFragmentComponent.java",
+ ],
+ exports = [
+ "//java/dagger/hilt/components",
+ ],
+ deps = [
+ ":package_info",
+ "//java/dagger/hilt:define_component",
+ "//java/dagger/hilt/android/scopes",
+ "//java/dagger/hilt/android/scopes:activity_retained_scoped",
+ "//java/dagger/hilt/components",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ ],
+)
+
+android_library(
+ name = "view_model_component",
+ srcs = ["ViewModelComponent.java"],
+ deps = [
+ ":components",
+ ":package_info",
+ "//java/dagger/hilt:define_component",
+ "//java/dagger/hilt/android/scopes:view_model_scoped",
+ ],
+)
+
+java_library(
+ name = "package_info",
+ srcs = ["package-info.java"],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/android/components/FragmentComponent.java b/java/dagger/hilt/android/components/FragmentComponent.java
new file mode 100644
index 000000000..6b088e691
--- /dev/null
+++ b/java/dagger/hilt/android/components/FragmentComponent.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.components;
+
+import dagger.hilt.DefineComponent;
+import dagger.hilt.android.scopes.FragmentScoped;
+
+/** A Hilt component that has the lifetime of the fragment. */
+@FragmentScoped
+@DefineComponent(parent = ActivityComponent.class)
+public interface FragmentComponent {}
diff --git a/java/dagger/hilt/android/components/ServiceComponent.java b/java/dagger/hilt/android/components/ServiceComponent.java
new file mode 100644
index 000000000..0a3f6eaa9
--- /dev/null
+++ b/java/dagger/hilt/android/components/ServiceComponent.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.components;
+
+import dagger.hilt.DefineComponent;
+import dagger.hilt.android.scopes.ServiceScoped;
+import dagger.hilt.components.SingletonComponent;
+
+/** A Hilt component that has the lifetime of the service. */
+@ServiceScoped
+@DefineComponent(parent = SingletonComponent.class)
+public interface ServiceComponent {}
diff --git a/java/dagger/hilt/android/components/ViewComponent.java b/java/dagger/hilt/android/components/ViewComponent.java
new file mode 100644
index 000000000..1f5b420f5
--- /dev/null
+++ b/java/dagger/hilt/android/components/ViewComponent.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.components;
+
+import dagger.hilt.DefineComponent;
+import dagger.hilt.android.scopes.ViewScoped;
+
+/** A Hilt component that has the lifetime of the view. */
+@ViewScoped
+@DefineComponent(parent = ActivityComponent.class)
+public interface ViewComponent {}
diff --git a/java/dagger/hilt/android/components/ViewModelComponent.java b/java/dagger/hilt/android/components/ViewModelComponent.java
new file mode 100644
index 000000000..2ba2133f8
--- /dev/null
+++ b/java/dagger/hilt/android/components/ViewModelComponent.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.components;
+
+import dagger.hilt.DefineComponent;
+import dagger.hilt.android.scopes.ViewModelScoped;
+
+/**
+ * A Hilt component that has the lifetime of a single {@link androidx.lifecycle.ViewModel}.
+ *
+ * <p>This Hilt component is the source of {@link
+ * dagger.hilt.android.lifecycle.HiltViewModel}-annotated {@link androidx.lifecycle.ViewModel}s
+ * used by the {@link dagger.hilt.android.lifecycle.HiltViewModelFactory}. It contains a default
+ * binding for the {@link androidx.lifecycle.SavedStateHandle} associated with the {@code
+ * ViewModel} that can be used by other dependencies provided by the component.
+ *
+ * <p>Dependencies available in the {@link dagger.hilt.components.SingletonComponent} and {@link
+ * ActivityRetainedComponent} are also available in this component since it is a child of {@code
+ * ActivityRetainedComponent}.
+ *
+ * <p>Example usage:
+ *
+ * <pre>
+ * &#64;Module
+ * &#64;InstallIn(ViewModelComponent.class)
+ * public final class ViewModelMovieModule {
+ * &#64;Provides
+ * public static MovieRepository provideRepo(SavedStateHandle handle) {
+ * return new MovieRepository(handle.getString("movie-id"));
+ * }
+ * }
+ * </pre>
+ *
+ * <p>Dependencies in the {@code ViewModelComponent} can be scoped using the {@link ViewModelScoped}
+ * annotation. This allows for a single instance of a dependency to be provided across the
+ * dependencies of a single {@link dagger.hilt.android.lifecycle.HiltViewModel}-annotated {@code
+ * ViewModel}.
+ *
+ * @see dagger.hilt.android.lifecycle.HiltViewModel
+ * @see dagger.hilt.android.scopes.ViewModelScoped
+ */
+@ViewModelScoped
+@DefineComponent(parent = ActivityRetainedComponent.class)
+public interface ViewModelComponent {}
diff --git a/java/dagger/hilt/android/components/ViewWithFragmentComponent.java b/java/dagger/hilt/android/components/ViewWithFragmentComponent.java
new file mode 100644
index 000000000..be5f2e828
--- /dev/null
+++ b/java/dagger/hilt/android/components/ViewWithFragmentComponent.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.components;
+
+import dagger.hilt.DefineComponent;
+import dagger.hilt.android.scopes.ViewScoped;
+
+/** A Hilt component that has the lifetime of the view. */
+@ViewScoped
+@DefineComponent(parent = FragmentComponent.class)
+public interface ViewWithFragmentComponent {}
diff --git a/java/dagger/hilt/android/components/package-info.java b/java/dagger/hilt/android/components/package-info.java
new file mode 100644
index 000000000..9ac368647
--- /dev/null
+++ b/java/dagger/hilt/android/components/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package contains Hilt's built-in {@link dagger.Component}s for Android.
+ *
+ * @see <a href="https://dagger.dev/hilt/components.md">Hilt Components</a>
+ */
+package dagger.hilt.android.components;
diff --git a/java/dagger/hilt/android/example/BUILD b/java/dagger/hilt/android/example/BUILD
new file mode 100644
index 000000000..2a4c19450
--- /dev/null
+++ b/java/dagger/hilt/android/example/BUILD
@@ -0,0 +1,27 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Description:
+# A skeletal application that demonstrates wiring for an injected Application and Activity.
+
+package(default_visibility = ["//:src"])
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(
+ ["**/*"],
+ # Exclude Gradle build folder to enable working along side Bazel
+ exclude = ["**/build/**"],
+ ),
+)
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/build.gradle b/java/dagger/hilt/android/example/gradle/simple/app/build.gradle
new file mode 100644
index 000000000..b3687ee63
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/build.gradle
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+apply plugin: 'com.android.application'
+apply plugin: 'dagger.hilt.android.plugin'
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.2"
+
+ defaultConfig {
+ applicationId "dagger.hilt.android.example.gradle.simple"
+ minSdkVersion 15
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "dagger.hilt.android.example.gradle.simple.SimpleEmulatorTestRunner"
+ }
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+ testOptions {
+ unitTests.includeAndroidResources = true
+ }
+ sourceSets {
+ String sharedTestDir = 'src/sharedTest/java'
+ test {
+ java.srcDirs += sharedTestDir
+ }
+ androidTest {
+ java.srcDirs += sharedTestDir
+ }
+ }
+}
+
+dependencies {
+ implementation project(':feature')
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'
+ annotationProcessor 'com.google.dagger:hilt-android-compiler:LOCAL-SNAPSHOT'
+
+ testImplementation 'com.google.truth:truth:1.0.1'
+ testImplementation 'junit:junit:4.13'
+ testImplementation 'org.robolectric:robolectric:4.5-alpha-3'
+ testImplementation 'androidx.core:core:1.3.2'
+ testImplementation 'androidx.test.ext:junit:1.1.2'
+ testImplementation 'androidx.test:runner:1.3.0'
+ testImplementation 'com.google.dagger:hilt-android-testing:LOCAL-SNAPSHOT'
+ testAnnotationProcessor 'com.google.dagger:hilt-android-compiler:LOCAL-SNAPSHOT'
+
+ androidTestImplementation 'com.google.truth:truth:1.0.1'
+ androidTestImplementation 'junit:junit:4.13'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+ androidTestImplementation 'androidx.test:runner:1.3.0'
+ androidTestImplementation 'com.google.dagger:hilt-android-testing:LOCAL-SNAPSHOT'
+ androidTestAnnotationProcessor 'com.google.dagger:hilt-android-compiler:LOCAL-SNAPSHOT'
+
+ // To help us catch usages of Guava APIs for Java 8 in the '-jre' variant.
+ annotationProcessor'com.google.guava:guava:28.1-android'
+ testAnnotationProcessor'com.google.guava:guava:28.1-android'
+ androidTestAnnotationProcessor'com.google.guava:guava:28.1-android'
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/androidTest/java/dagger/hilt/android/example/gradle/simple/SettingsActivityEmulatorTest.java b/java/dagger/hilt/android/example/gradle/simple/app/src/androidTest/java/dagger/hilt/android/example/gradle/simple/SettingsActivityEmulatorTest.java
new file mode 100644
index 000000000..28a722302
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/androidTest/java/dagger/hilt/android/example/gradle/simple/SettingsActivityEmulatorTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.hilt.android.testing.BindValue;
+import dagger.hilt.android.testing.HiltAndroidRule;
+import dagger.hilt.android.testing.HiltAndroidTest;
+import dagger.hilt.android.testing.UninstallModules;
+import javax.inject.Inject;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** A simple test using Hilt. */
+@UninstallModules(ModelModule.class)
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+public final class SettingsActivityEmulatorTest {
+ @Rule public HiltAndroidRule rule = new HiltAndroidRule(this);
+
+ @BindValue @Model String fakeModel = "FakeModel";
+
+ @Inject @Model String injectedModel;
+
+ @Test
+ public void testInjectedModel() throws Exception {
+ assertThat(injectedModel).isNull();
+ rule.inject();
+ assertThat(injectedModel).isEqualTo("FakeModel");
+ }
+
+ @Test
+ public void testActivityInject() throws Exception {
+ try (ActivityScenario<SettingsActivity> scenario =
+ ActivityScenario.launch(SettingsActivity.class)) {
+ scenario.onActivity(
+ activity ->
+ assertThat(activity.greeter.greet())
+ .isEqualTo("ProdUser, you are on build FakeModel."));
+ }
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/androidTest/java/dagger/hilt/android/example/gradle/simple/SimpleActivityEmulatorTest.java b/java/dagger/hilt/android/example/gradle/simple/app/src/androidTest/java/dagger/hilt/android/example/gradle/simple/SimpleActivityEmulatorTest.java
new file mode 100644
index 000000000..cbf8f7f72
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/androidTest/java/dagger/hilt/android/example/gradle/simple/SimpleActivityEmulatorTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.hilt.android.testing.BindValue;
+import dagger.hilt.android.testing.HiltAndroidRule;
+import dagger.hilt.android.testing.HiltAndroidTest;
+import dagger.hilt.android.testing.UninstallModules;
+import javax.inject.Inject;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** A simple test using Hilt. */
+@UninstallModules(UserNameModule.class)
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+public final class SimpleActivityEmulatorTest {
+
+ @Rule public HiltAndroidRule rule = new HiltAndroidRule(this);
+
+ @BindValue @UserName String fakeUserName = "FakeUser";
+
+ @Inject @UserName String injectedUserName;
+
+ @Test
+ public void testInjectedUserName() throws Exception {
+ assertThat(injectedUserName).isNull();
+ rule.inject();
+ assertThat(injectedUserName).isEqualTo("FakeUser");
+ }
+
+ @Test
+ public void testActivityInject() throws Exception {
+ try (ActivityScenario<SimpleActivity> scenario =
+ ActivityScenario.launch(SimpleActivity.class)) {
+ scenario.onActivity(
+ activity -> assertThat(activity.greeter.greet()).isEqualTo("Hello, FakeUser!"));
+ }
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/androidTest/java/dagger/hilt/android/example/gradle/simple/SimpleEmulatorTestRunner.java b/java/dagger/hilt/android/example/gradle/simple/app/src/androidTest/java/dagger/hilt/android/example/gradle/simple/SimpleEmulatorTestRunner.java
new file mode 100644
index 000000000..daf046b2e
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/androidTest/java/dagger/hilt/android/example/gradle/simple/SimpleEmulatorTestRunner.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple;
+
+import android.app.Application;
+import android.content.Context;
+import androidx.test.runner.AndroidJUnitRunner;
+import dagger.hilt.android.testing.HiltTestApplication;
+
+/** A custom runner to setup the emulator application class for tests. */
+public final class SimpleEmulatorTestRunner extends AndroidJUnitRunner {
+
+ @Override
+ public Application newApplication(ClassLoader cl, String className, Context context)
+ throws ClassNotFoundException, IllegalAccessException, InstantiationException {
+ return super.newApplication(cl, HiltTestApplication.class.getName(), context);
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/debug/AndroidManifest.xml b/java/dagger/hilt/android/example/gradle/simple/app/src/debug/AndroidManifest.xml
new file mode 100644
index 000000000..86460d834
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<!--
+ ~ Copyright (C) 2017 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.hilt.android.example.gradle.simple">
+
+ <application>
+ <activity
+ android:name=".Injection1Test$TestActivity"
+ android:theme="@style/Theme.AppCompat.Light"
+ android:exported="false" />
+ <activity
+ android:name=".Injection2Test$TestActivity"
+ android:theme="@style/Theme.AppCompat.Light"
+ android:exported="false"/>
+ <activity
+ android:name=".ActivityScenarioRuleTest$TestActivity"
+ android:theme="@style/Theme.AppCompat.Light"
+ android:exported="false"/>
+ </application>
+</manifest>
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/main/AndroidManifest.xml b/java/dagger/hilt/android/example/gradle/simple/app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..662c5b4c2
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/main/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<!--
+ ~ Copyright (C) 2017 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.hilt.android.example.gradle.simple">
+
+ <application android:name=".SimpleApplication" android:label="@string/appName">
+ <activity
+ android:name=".SimpleActivity"
+ android:theme="@style/Theme.AppCompat.Light"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name=".SettingsActivity"
+ android:theme="@style/Theme.AppCompat.Light"
+ android:exported="false">
+ </activity>
+ </application>
+</manifest>
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/Model.java b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/Model.java
new file mode 100644
index 000000000..b4aec95d4
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/Model.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/** Qualifies bindings relating to {@link android.os.Build#MODEL}. */
+@Qualifier
+@Retention(RUNTIME)
+@Documented
+@interface Model {}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/ModelModule.java b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/ModelModule.java
new file mode 100644
index 000000000..d933c813e
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/ModelModule.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple;
+
+import static android.os.Build.MODEL;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.hilt.InstallIn;
+import dagger.hilt.components.SingletonComponent;
+
+@Module
+@InstallIn(SingletonComponent.class)
+final class ModelModule {
+ @Provides
+ @Model
+ static String provideModel() {
+ return MODEL;
+ }
+
+ private ModelModule() {}
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SettingsActivity.java b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SettingsActivity.java
new file mode 100644
index 000000000..f508e48a0
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SettingsActivity.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple;
+
+import android.os.Bundle;
+import android.widget.TextView;
+import androidx.appcompat.app.AppCompatActivity;
+import dagger.hilt.android.AndroidEntryPoint;
+import javax.inject.Inject;
+
+/** The settings activity of the application. */
+@AndroidEntryPoint
+public class SettingsActivity extends AppCompatActivity {
+ private static final String TAG = SettingsActivity.class.getSimpleName();
+
+ @Inject SettingsGreeter greeter;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_settings);
+
+ ((TextView) findViewById(R.id.settings_greeting)).setText(greeter.greet());
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SettingsGreeter.java b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SettingsGreeter.java
new file mode 100644
index 000000000..4f8ab14cd
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SettingsGreeter.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple;
+
+import android.app.Activity;
+import javax.inject.Inject;
+
+/** A class that returns a greeting for {@link SettingsActivity}. */
+final class SettingsGreeter {
+ private final Activity activity;
+ private final String userName;
+ private final String model;
+
+ @Inject
+ SettingsGreeter(Activity activity, @UserName String userName, @Model String model) {
+ this.activity = activity;
+ this.userName = userName;
+ this.model = model;
+ }
+
+ public String greet() {
+ return activity.getResources().getString(R.string.settings_welcome, userName, model);
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleActivity.java b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleActivity.java
new file mode 100644
index 000000000..70e2e5775
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleActivity.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.TextView;
+import androidx.appcompat.app.AppCompatActivity;
+import dagger.hilt.android.AndroidEntryPoint;
+import dagger.hilt.android.example.gradle.simple.feature.FeatureActivity;
+import javax.inject.Inject;
+
+/** The main activity of the application. */
+@AndroidEntryPoint
+public class SimpleActivity extends AppCompatActivity {
+ private static final String TAG = SimpleActivity.class.getSimpleName();
+
+ @Inject SimpleGreeter greeter;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_main);
+
+ ((TextView) findViewById(R.id.greeting)).setText(greeter.greet());
+
+ Button featureButton = (Button) findViewById(R.id.goto_feature);
+ featureButton.setOnClickListener(
+ view -> startActivity(new Intent(this, FeatureActivity.class)));
+
+ Button settingsButton = (Button) findViewById(R.id.goto_settings);
+ settingsButton.setOnClickListener(
+ view -> startActivity(new Intent(this, SettingsActivity.class)));
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleApplication.java b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleApplication.java
new file mode 100644
index 000000000..a163d8c5e
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleApplication.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple;
+
+import android.app.Application;
+import dagger.hilt.android.HiltAndroidApp;
+import javax.inject.Inject;
+
+/**
+ * A simple, skeletal application that demonstrates a dependency-injected application using the
+ * utilities in {@code Hilt} in Android.
+ */
+@HiltAndroidApp
+public class SimpleApplication extends Application {
+
+ // Shows that we can inject SingletonComponent bindings into an application.
+ @Inject @Model String model;
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleGreeter.java b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleGreeter.java
new file mode 100644
index 000000000..6c35600b3
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleGreeter.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple;
+
+import android.app.Activity;
+import javax.inject.Inject;
+
+/** A class that returns a greeting for {@link SimpleActivity}. */
+final class SimpleGreeter {
+ private final Activity activity;
+ private final String userName;
+
+ @Inject
+ SimpleGreeter(Activity activity, @UserName String userName) {
+ this.activity = activity;
+ this.userName = userName;
+ }
+
+ public String greet() {
+ return activity.getResources().getString(R.string.welcome, userName);
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/UserName.java b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/UserName.java
new file mode 100644
index 000000000..173b163b9
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/UserName.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/** Qualifies bindings relating to the user name. */
+@Qualifier
+@Retention(RUNTIME)
+@Documented
+@interface UserName {}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/UserNameModule.java b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/UserNameModule.java
new file mode 100644
index 000000000..3969b7335
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/UserNameModule.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.components.ActivityComponent;
+
+@Module
+@InstallIn(ActivityComponent.class)
+final class UserNameModule {
+ @UserName
+ @Provides
+ static String provideUserName() {
+ return "ProdUser";
+ }
+
+ private UserNameModule() {}
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/main/res/layout/activity_main.xml b/java/dagger/hilt/android/example/gradle/simple/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 000000000..8a23af595
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/background_light">
+
+ <TextView
+ android:id="@+id/greeting"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:textColor="@android:color/primary_text_light"
+ />
+
+ <Button
+ android:id="@+id/goto_settings"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/greeting"
+ android:text="@string/navigateToSettings"
+ />
+
+ <Button
+ android:id="@+id/goto_feature"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/goto_settings"
+ android:text="@string/navigateToFeature"
+ />
+</RelativeLayout>
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/main/res/layout/activity_settings.xml b/java/dagger/hilt/android/example/gradle/simple/app/src/main/res/layout/activity_settings.xml
new file mode 100644
index 000000000..466856c26
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/main/res/layout/activity_settings.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/settings_greeting"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/settings_welcome" />
+</FrameLayout>
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/main/res/values/strings.xml b/java/dagger/hilt/android/example/gradle/simple/app/src/main/res/values/strings.xml
new file mode 100644
index 000000000..f1b550cf9
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/main/res/values/strings.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!--The app name [CHAR_LIMIT=20]-->
+ <string name="appName">Simple Hilt Android</string>
+
+ <!--The greeting message [CHAR_LIMIT=100]-->
+ <string name="welcome">Hello, %1$s!</string>
+
+ <!--The feature button message [CHAR_LIMIT=100]-->
+ <string name="navigateToFeature">Navigate to a feature!</string>
+
+ <!--The settings button message [CHAR_LIMIT=100]-->
+ <string name="navigateToSettings">Navigate to settings!</string>
+
+ <!--The feature button message [CHAR_LIMIT=100]-->
+ <string name="settings_welcome">%1$s, you are on build %2$s.</string>
+</resources>
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/ActivityScenarioRuleTest.java b/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/ActivityScenarioRuleTest.java
new file mode 100644
index 000000000..7f1fcfcea
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/ActivityScenarioRuleTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple;
+
+import static androidx.lifecycle.Lifecycle.State.RESUMED;
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.hilt.android.AndroidEntryPoint;
+import dagger.hilt.android.testing.BindValue;
+import dagger.hilt.android.testing.HiltAndroidRule;
+import dagger.hilt.android.testing.HiltAndroidTest;
+import javax.inject.Inject;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests that {@link ActivityScenarioRule} works with Hilt tests. */
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+public final class ActivityScenarioRuleTest {
+ private static final String STR_VALUE = "STR_VALUE";
+
+ /** An activity to test injection. */
+ @AndroidEntryPoint
+ public static final class TestActivity extends AppCompatActivity {
+ @Inject String str;
+ }
+
+ @Rule(order = 0) public HiltAndroidRule hiltRule = new HiltAndroidRule(this);
+
+ @Rule(order = 1)
+ public ActivityScenarioRule<TestActivity> scenarioRule =
+ new ActivityScenarioRule<>(TestActivity.class);
+
+ @BindValue String str = STR_VALUE;
+
+ @Test
+ public void testState() {
+ assertThat(scenarioRule.getScenario().getState()).isEqualTo(RESUMED);
+ }
+
+ @Test
+ public void testInjection() {
+ scenarioRule
+ .getScenario()
+ .onActivity(activity -> assertThat(activity.str).isEqualTo(STR_VALUE));
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/BindValueTest.java b/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/BindValueTest.java
new file mode 100644
index 000000000..7abe3c2ff
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/BindValueTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.google.common.collect.ImmutableSet;
+import dagger.MapKey;
+import dagger.hilt.EntryPoint;
+import dagger.hilt.EntryPoints;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.testing.BindElementsIntoSet;
+import dagger.hilt.android.testing.BindValue;
+import dagger.hilt.android.testing.BindValueIntoMap;
+import dagger.hilt.android.testing.BindValueIntoSet;
+import dagger.hilt.android.testing.HiltAndroidRule;
+import dagger.hilt.android.testing.HiltAndroidTest;
+import dagger.hilt.components.SingletonComponent;
+import java.util.Map;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** A simple test using Hilt. */
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+public final class BindValueTest {
+ private static final String BIND_VALUE_STRING = "BIND_VALUE_STRING";
+ private static final String TEST_QUALIFIER = "TEST_QUALIFIER";
+
+ private static final String SET_STRING_1 = "SetString1";
+ private static final String SET_STRING_2 = "SetString2";
+ private static final String SET_STRING_3 = "SetString3";
+
+ private static final String KEY_1 = "Key1";
+ private static final String KEY_2 = "Key2";
+ private static final String VALUE_1 = "Value1";
+ private static final String VALUE_2 = "Value2";
+ private static final String VALUE_3 = "Value3";
+
+ private static final Integer SET_INT_1 = 1;
+ private static final Integer SET_INT_2 = 2;
+ private static final Integer SET_INT_3 = 3;
+
+ @EntryPoint
+ @InstallIn(SingletonComponent.class)
+ interface BindValueEntryPoint {
+ @Named(TEST_QUALIFIER)
+ String bindValueString();
+
+ Set<String> getStringSet();
+ }
+
+ @Rule public HiltAndroidRule rule = new HiltAndroidRule(this);
+
+ @BindValue
+ @Named(TEST_QUALIFIER)
+ String bindValueString = BIND_VALUE_STRING;
+
+ @BindElementsIntoSet Set<String> bindElementsSet1 = ImmutableSet.of(SET_STRING_1);
+ @BindElementsIntoSet Set<String> bindElementsSet2 = ImmutableSet.of(SET_STRING_2);
+
+ @BindValueIntoMap
+ @MyMapKey(KEY_1)
+ String boundValue1 = VALUE_1;
+
+ @BindValueIntoMap
+ @MyMapKey(KEY_2)
+ String boundValue2 = VALUE_2;
+
+ @BindValueIntoSet Integer bindValueSetInt1 = SET_INT_1;
+ @BindValueIntoSet Integer bindValueSetInt2 = SET_INT_2;
+
+ @Inject Set<String> stringSet;
+ @Inject Provider<Set<String>> providedStringSet;
+ @Inject Provider<Map<String, String>> mapProvider;
+ @Inject Set<Integer> intSet;
+ @Inject Provider<Set<Integer>> providedIntSet;
+
+ @Test
+ public void testBindValueFieldIsProvided() throws Exception {
+ assertThat(bindValueString).isEqualTo(BIND_VALUE_STRING);
+ assertThat(getBinding()).isEqualTo(BIND_VALUE_STRING);
+ }
+
+ @Test
+ public void testBindValueIsMutable() throws Exception {
+ bindValueString = "newValue";
+ assertThat(getBinding()).isEqualTo("newValue");
+ }
+
+ @Test
+ public void testElementsIntoSet() throws Exception {
+ rule.inject();
+ // basic check that initial/default values are properly injected
+ assertThat(providedStringSet.get()).containsExactly(SET_STRING_1, SET_STRING_2);
+ // Test empty sets (something that cannot be done with @BindValueIntoSet)
+ bindElementsSet1 = ImmutableSet.of();
+ bindElementsSet2 = ImmutableSet.of();
+ assertThat(providedStringSet.get()).isEmpty();
+ // Test multiple elements in set.
+ bindElementsSet1 = ImmutableSet.of(SET_STRING_1, SET_STRING_2, SET_STRING_3);
+ assertThat(providedStringSet.get()).containsExactly(SET_STRING_1, SET_STRING_2, SET_STRING_3);
+ }
+
+ @Test
+ public void testBindValueIntoMap() throws Exception {
+ rule.inject();
+ Map<String, String> oldMap = mapProvider.get();
+ assertThat(oldMap).containsExactly(KEY_1, VALUE_1, KEY_2, VALUE_2);
+ boundValue1 = VALUE_3;
+ Map<String, String> newMap = mapProvider.get();
+ assertThat(oldMap).containsExactly(KEY_1, VALUE_1, KEY_2, VALUE_2);
+ assertThat(newMap).containsExactly(KEY_1, VALUE_3, KEY_2, VALUE_2);
+ }
+
+ @Test
+ public void testBindValueIntoSet() throws Exception {
+ rule.inject();
+ // basic check that initial/default values are properly injected
+ assertThat(providedIntSet.get()).containsExactly(SET_INT_1, SET_INT_2);
+ bindValueSetInt1 = SET_INT_3;
+ // change the value for bindValueSetString from 1 to 3
+ assertThat(providedIntSet.get()).containsExactly(SET_INT_2, SET_INT_3);
+ }
+
+ @MapKey
+ @interface MyMapKey {
+ String value();
+ }
+
+ private static String getBinding() {
+ return EntryPoints.get(getApplicationContext(), BindValueEntryPoint.class).bindValueString();
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/BroadcastReceiverTest.java b/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/BroadcastReceiverTest.java
new file mode 100644
index 000000000..7585bbb1d
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/BroadcastReceiverTest.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+import dagger.hilt.android.AndroidEntryPoint;
+import dagger.hilt.android.testing.BindValue;
+import dagger.hilt.android.testing.HiltAndroidRule;
+import dagger.hilt.android.testing.HiltAndroidTest;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import javax.inject.Inject;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** A injection test of Broadcast receivers */
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+public class BroadcastReceiverTest {
+
+ @Rule public HiltAndroidRule rule = new HiltAndroidRule(this);
+
+ @BindValue final String valueToInject = "test value";
+
+ @Test
+ public void verifyReceiverInjectedValue() throws InterruptedException {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+ TestReceiverOne receiver = new TestReceiverOne();
+ IntentFilter intentFilter = new IntentFilter("test-action");
+ context.registerReceiver(receiver, intentFilter);
+
+ Intent intent = new Intent();
+ intent.setAction("test-action");
+ context.sendBroadcast(intent);
+
+ receiver.latch.await(2, TimeUnit.SECONDS);
+ assertThat(receiver.injectedValue).isNotEmpty();
+ }
+
+ @Test
+ public void verifyBaseReceiverInjectedValue() throws InterruptedException {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+ TestReceiverTwo receiver = new TestReceiverTwo();
+ IntentFilter intentFilter = new IntentFilter("test-action");
+ context.registerReceiver(receiver, intentFilter);
+
+ Intent intent = new Intent();
+ intent.setAction("test-action");
+ context.sendBroadcast(intent);
+
+ receiver.latch.await(2, TimeUnit.SECONDS);
+ assertThat(receiver.injectedValue).isNotEmpty();
+ }
+
+ @Test
+ public void verifyBaseReceiverIsNotDoubleInjected() throws InterruptedException {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+ TestReceiverThree receiver = new TestReceiverThree();
+ IntentFilter intentFilter = new IntentFilter("test-action");
+ context.registerReceiver(receiver, intentFilter);
+
+ Intent intent = new Intent();
+ intent.setAction("test-action");
+ context.sendBroadcast(intent);
+
+ receiver.latch.await(2, TimeUnit.SECONDS);
+ assertThat(receiver.injectedValue).isNotEmpty();
+ assertThat(receiver.onReceiveCalled).isEqualTo(1);
+ }
+
+ @Test
+ public void verifyComplexReceiverInjectedValue() throws InterruptedException {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+ TestReceiverFour receiver = new TestReceiverFour();
+ IntentFilter intentFilter = new IntentFilter("test-action");
+ context.registerReceiver(receiver, intentFilter);
+
+ Intent intent = new Intent();
+ intent.setAction("test-action");
+ context.sendBroadcast(intent);
+
+ receiver.latch.await(2, TimeUnit.SECONDS);
+ assertThat(receiver.injectedValue).isNotEmpty();
+ }
+
+ /** Test receiver */
+ @AndroidEntryPoint
+ static class TestReceiverOne extends BroadcastReceiver {
+
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ @Inject
+ String injectedValue;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ latch.countDown();
+ }
+ }
+
+ /** Test receiver */
+ @AndroidEntryPoint
+ static class TestReceiverTwo extends BaseReceiverAbstractMethod {
+
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ @Inject
+ String injectedValue;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ latch.countDown();
+ }
+ }
+
+ /** Test receiver */
+ @AndroidEntryPoint
+ static class TestReceiverThree extends BaseReceiverConcreteMethod {
+
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ @Inject
+ String injectedValue;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ super.onReceive(context, intent);
+ latch.countDown();
+ }
+ }
+
+ /** Complex-ish test receiver */
+ @AndroidEntryPoint
+ static class TestReceiverFour extends BroadcastReceiver {
+
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ @Inject String injectedValue;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Weird code, but it tests that the exception table and stack table frames are correctly
+ // updated in a transformation.
+ boolean var0;
+ if (context != null) {
+ var0 = false;
+ Object var1 = context.getClass();
+ try {
+ throw new IllegalStateException();
+ } catch (IllegalStateException ex) {
+ var0 = true;
+ }
+ } else {
+ BroadcastReceiver myself = this;
+ var0 = false;
+ }
+ latch.countDown();
+ }
+ }
+
+ /** Base test receiver */
+ abstract static class BaseReceiverAbstractMethod extends BroadcastReceiver {
+
+ }
+
+ /** Base test receiver */
+ abstract static class BaseReceiverConcreteMethod extends BroadcastReceiver {
+
+ int onReceiveCalled = 0;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onReceiveCalled++;
+ }
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/Injection1Test.java b/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/Injection1Test.java
new file mode 100644
index 000000000..0ebd1506c
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/Injection1Test.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.Module;
+import dagger.Provides;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.AndroidEntryPoint;
+import dagger.hilt.android.components.ActivityComponent;
+import dagger.hilt.android.testing.HiltAndroidRule;
+import dagger.hilt.android.testing.HiltAndroidTest;
+import dagger.hilt.components.SingletonComponent;
+import javax.inject.Inject;
+import javax.inject.Named;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests basic injection APIs, and that bindings don't conflict with {@link Injection2Test}. */
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+public final class Injection1Test {
+ private static final String APPLICATION_QUALIFIER = "APPLICATION_QUALIFIER";
+ private static final String ACTIVITY_QUALIFIER = "ACTIVITY_QUALIFIER";
+ private static final String APPLICATION_VALUE = "Injection1Test_ApplicationValue";
+ private static final String ACTIVITY_VALUE = "Injection1Test_ActivityValue";
+
+ @Module
+ @InstallIn(SingletonComponent.class)
+ interface TestApplicationModule {
+ @Provides
+ @Named(APPLICATION_QUALIFIER)
+ static String provideString() {
+ return APPLICATION_VALUE;
+ }
+ }
+
+ @Module
+ @InstallIn(ActivityComponent.class)
+ interface TestActivityModule {
+ @Provides
+ @Named(ACTIVITY_QUALIFIER)
+ static String provideString() {
+ return ACTIVITY_VALUE;
+ }
+ }
+
+ /** Test activity used to test activity injection */
+ @AndroidEntryPoint
+ public static final class TestActivity extends AppCompatActivity {
+ @Inject @Named(ACTIVITY_QUALIFIER) String activityValue;
+ }
+
+ @Rule public HiltAndroidRule rule = new HiltAndroidRule(this);
+
+ @Inject @Named(APPLICATION_QUALIFIER) String applicationValue;
+
+ @Test
+ public void testApplicationInjection() throws Exception {
+ assertThat(applicationValue).isNull();
+ rule.inject();
+ assertThat(applicationValue).isEqualTo(APPLICATION_VALUE);
+ }
+
+ @Test
+ public void testActivityInjection() throws Exception {
+ try (ActivityScenario<TestActivity> scenario = ActivityScenario.launch(TestActivity.class)) {
+ scenario.onActivity(activity -> assertThat(activity.activityValue).isEqualTo(ACTIVITY_VALUE));
+ }
+ }
+
+ @Test
+ public void testSuperClassTransformation() {
+ try (ActivityScenario<TestActivity> scenario = ActivityScenario.launch(TestActivity.class)) {
+ scenario.onActivity(
+ activity ->
+ assertThat(activity.getClass().getSuperclass().getSimpleName())
+ .isEqualTo("Hilt_Injection1Test_TestActivity"));
+ }
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/Injection2Test.java b/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/Injection2Test.java
new file mode 100644
index 000000000..51d60b239
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/Injection2Test.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.Module;
+import dagger.Provides;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.AndroidEntryPoint;
+import dagger.hilt.android.components.ActivityComponent;
+import dagger.hilt.android.testing.HiltAndroidRule;
+import dagger.hilt.android.testing.HiltAndroidTest;
+import dagger.hilt.components.SingletonComponent;
+import javax.inject.Inject;
+import javax.inject.Named;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests basic injection APIs, and that bindings don't conflict with {@link Injection1Test}. */
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+public final class Injection2Test {
+ private static final String APPLICATION_QUALIFIER = "APPLICATION_QUALIFIER";
+ private static final String ACTIVITY_QUALIFIER = "ACTIVITY_QUALIFIER";
+ private static final String APPLICATION_VALUE = "Injection2Test_ApplicationValue";
+ private static final String ACTIVITY_VALUE = "Injection2Test_ActivityValue";
+
+ @Module
+ @InstallIn(SingletonComponent.class)
+ interface TestApplicationModule {
+ @Provides
+ @Named(APPLICATION_QUALIFIER)
+ static String provideString() {
+ return APPLICATION_VALUE;
+ }
+ }
+
+ @Module
+ @InstallIn(ActivityComponent.class)
+ interface TestActivityModule {
+ @Provides
+ @Named(ACTIVITY_QUALIFIER)
+ static String provideString() {
+ return ACTIVITY_VALUE;
+ }
+ }
+
+ /** Test activity used to test activity injection */
+ @AndroidEntryPoint
+ public static final class TestActivity extends AppCompatActivity {
+ @Inject @Named(ACTIVITY_QUALIFIER) String activityValue;
+ }
+
+ @Rule public HiltAndroidRule rule = new HiltAndroidRule(this);
+
+ @Inject @Named(APPLICATION_QUALIFIER) String applicationValue;
+
+ @Test
+ public void testApplicationInjection() throws Exception {
+ assertThat(applicationValue).isNull();
+ rule.inject();
+ assertThat(applicationValue).isEqualTo(APPLICATION_VALUE);
+ }
+
+ @Test
+ public void testActivityInjection() throws Exception {
+ try (ActivityScenario<TestActivity> scenario = ActivityScenario.launch(TestActivity.class)) {
+ scenario.onActivity(activity -> assertThat(activity.activityValue).isEqualTo(ACTIVITY_VALUE));
+ }
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/ModuleTest.java b/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/ModuleTest.java
new file mode 100644
index 000000000..73daf8fac
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/ModuleTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.testing.HiltAndroidRule;
+import dagger.hilt.android.testing.HiltAndroidTest;
+import dagger.hilt.components.SingletonComponent;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+import javax.inject.Inject;
+import javax.inject.Qualifier;
+import javax.inject.Singleton;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests basic functionality of using modules in test. */
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+public final class ModuleTest {
+ @Rule public final HiltAndroidRule rules = new HiltAndroidRule(this);
+
+ /** Qualifier for distinguishing test Strings, */
+ @Qualifier
+ @Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
+ public @interface TestQualifier {
+ int value();
+ }
+
+ @Inject
+ @TestQualifier(1)
+ String testString1;
+
+ @Inject
+ @TestQualifier(2)
+ String testString2;
+
+ @Inject
+ @TestQualifier(3)
+ String testString3;
+
+ @Inject
+ @TestQualifier(4)
+ String testString4;
+
+ @Inject FooImpl fooImpl;
+ @Inject Foo foo;
+
+ /**
+ * Module which is used to test if non-static test modules get registered in the component
+ * correctly.
+ */
+ @Module
+ @InstallIn(SingletonComponent.class)
+ public final class NonStaticModuleNonStaticProvides {
+ @Provides
+ @TestQualifier(1)
+ String provideString() {
+ return "1";
+ }
+ }
+
+ /**
+ * Module which is used to test if static test modules get registered in the component correctly.
+ */
+ @Module
+ @InstallIn(SingletonComponent.class)
+ public static final class StaticModuleStaticProvides {
+ @Provides
+ @TestQualifier(2)
+ static String provideString() {
+ return "2";
+ }
+
+ private StaticModuleStaticProvides() {}
+ }
+
+ /**
+ * Module which is used to test if static test modules with a non-static methods get registered in
+ * the component correctly.
+ */
+ @Module
+ @InstallIn(SingletonComponent.class)
+ public static final class StaticModuleNonStaticProvidesDefaultConstructor {
+ @Provides
+ @TestQualifier(3)
+ String provideString() {
+ return "3";
+ }
+ }
+
+ /**
+ * Module which is used to test if abstract test modules get registered in the component
+ * correctly.
+ */
+ @Module
+ @InstallIn(SingletonComponent.class)
+ public abstract static class AbstractModuleStaticProvides {
+ @Provides
+ @TestQualifier(4)
+ static String provideString() {
+ return "4";
+ }
+
+ private AbstractModuleStaticProvides() {}
+ }
+
+ /**
+ * Module which is used to test if abstract test modules with a binds method get registered in the
+ * component correctly.
+ */
+ @Module
+ @InstallIn(SingletonComponent.class)
+ public abstract static class AbstractModuleBindsMethod {
+ @Binds
+ abstract Foo foo(FooImpl fooImpl);
+ }
+
+ interface Foo {}
+
+ @Singleton
+ static final class FooImpl implements Foo {
+ @Inject
+ FooImpl() {}
+ }
+
+ @Test
+ public void testInjection() throws Exception {
+ rules.inject();
+ assertEquals("1", testString1);
+ assertEquals("2", testString2);
+ assertEquals("3", testString3);
+ assertEquals("4", testString4);
+ assertEquals(fooImpl, foo);
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/SettingsActivityTest.java b/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/SettingsActivityTest.java
new file mode 100644
index 000000000..f1e5f278e
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/SettingsActivityTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Build;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.hilt.android.testing.BindValue;
+import dagger.hilt.android.testing.HiltAndroidRule;
+import dagger.hilt.android.testing.HiltAndroidTest;
+import dagger.hilt.android.testing.HiltTestApplication;
+import dagger.hilt.android.testing.UninstallModules;
+import javax.inject.Inject;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+/** A simple test using Hilt. */
+@UninstallModules(ModelModule.class)
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+// Robolectric requires Java9 to run API 29 and above, so use API 28 instead
+@Config(sdk = Build.VERSION_CODES.P, application = HiltTestApplication.class)
+public final class SettingsActivityTest {
+ @Rule public HiltAndroidRule rule = new HiltAndroidRule(this);
+
+ @BindValue @Model String fakeModel = "FakeModel";
+
+ @Inject @Model String injectedModel;
+
+ @Test
+ public void testInjectedModel() throws Exception {
+ assertThat(injectedModel).isNull();
+ rule.inject();
+ assertThat(injectedModel).isEqualTo("FakeModel");
+ }
+
+ @Test
+ public void testActivityInject() throws Exception {
+ try (ActivityScenario<SettingsActivity> scenario =
+ ActivityScenario.launch(SettingsActivity.class)) {
+ scenario.onActivity(
+ activity ->
+ assertThat(activity.greeter.greet())
+ .isEqualTo("ProdUser, you are on build FakeModel."));
+ }
+ }
+
+ @Test
+ public void testSuperClassTransformation() {
+ try (ActivityScenario<SettingsActivity> scenario =
+ ActivityScenario.launch(SettingsActivity.class)) {
+ scenario.onActivity(
+ activity ->
+ assertThat(activity.getClass().getSuperclass().getSimpleName())
+ .isEqualTo("Hilt_SettingsActivity"));
+ }
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/SimpleActivityTest.java b/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/SimpleActivityTest.java
new file mode 100644
index 000000000..29b5f70ff
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/SimpleActivityTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Build;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.hilt.android.testing.BindValue;
+import dagger.hilt.android.testing.HiltAndroidRule;
+import dagger.hilt.android.testing.HiltAndroidTest;
+import dagger.hilt.android.testing.HiltTestApplication;
+import dagger.hilt.android.testing.UninstallModules;
+import javax.inject.Inject;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+/** A simple test using Hilt. */
+@UninstallModules(UserNameModule.class)
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+// Robolectric requires Java9 to run API 29 and above, so use API 28 instead
+@Config(sdk = Build.VERSION_CODES.P, application = HiltTestApplication.class)
+public final class SimpleActivityTest {
+ @Rule public HiltAndroidRule rule = new HiltAndroidRule(this);
+
+ @BindValue @UserName String fakeUserName = "FakeUser";
+
+ @Inject @UserName String injectedUserName;
+
+ @Test
+ public void testInjectedUserName() throws Exception {
+ assertThat(injectedUserName).isNull();
+ rule.inject();
+ assertThat(injectedUserName).isEqualTo("FakeUser");
+ }
+
+ @Test
+ public void testActivityInject() throws Exception {
+ try (ActivityScenario<SimpleActivity> scenario =
+ ActivityScenario.launch(SimpleActivity.class)) {
+ scenario.onActivity(
+ activity -> assertThat(activity.greeter.greet()).isEqualTo("Hello, FakeUser!"));
+ }
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/test/resources/dagger/hilt/android/example/gradle/simple/robolectric.properties b/java/dagger/hilt/android/example/gradle/simple/app/src/test/resources/dagger/hilt/android/example/gradle/simple/robolectric.properties
new file mode 100644
index 000000000..0234ffe6f
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/test/resources/dagger/hilt/android/example/gradle/simple/robolectric.properties
@@ -0,0 +1,2 @@
+sdk=28
+application=dagger.hilt.android.testing.HiltTestApplication \ No newline at end of file
diff --git a/java/dagger/hilt/android/example/gradle/simple/build.gradle b/java/dagger/hilt/android/example/gradle/simple/build.gradle
new file mode 100644
index 000000000..c0916a4d2
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/build.gradle
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+buildscript {
+ ext {
+ kotlin_version = '1.3.61'
+ agp_version = "4.2.0-beta04"
+ }
+ repositories {
+ google()
+ jcenter()
+ mavenLocal()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:$agp_version"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath 'com.google.dagger:hilt-android-gradle-plugin:LOCAL-SNAPSHOT'
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ mavenCentral()
+ mavenLocal()
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/feature/build.gradle b/java/dagger/hilt/android/example/gradle/simple/feature/build.gradle
new file mode 100644
index 000000000..462aefc1e
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/feature/build.gradle
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-kapt'
+apply plugin: 'dagger.hilt.android.plugin'
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.2"
+
+ defaultConfig {
+ minSdkVersion 15
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+ }
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+}
+
+kapt {
+ correctErrorTypes true
+}
+
+dependencies {
+ // This is api instead of implementation since Kotlin modules here consumed
+ // by the app need to expose @kotlin.Metadata
+ api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'
+ kapt 'com.google.dagger:hilt-android-compiler:LOCAL-SNAPSHOT'
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/AndroidManifest.xml b/java/dagger/hilt/android/example/gradle/simple/feature/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..f7919a92f
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/feature/src/main/AndroidManifest.xml
@@ -0,0 +1,9 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.hilt.android.example.gradle.simple.feature">
+ <application>
+ <activity
+ android:name=".FeatureActivity"
+ android:theme="@style/Theme.AppCompat.Light"
+ android:exported="true"/>
+ </application>
+</manifest>
diff --git a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureActivity.kt b/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureActivity.kt
new file mode 100644
index 000000000..03b9ae9f7
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureActivity.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple.feature
+
+import android.os.Bundle
+import android.widget.Button
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import dagger.hilt.android.AndroidEntryPoint
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class FeatureActivity : AppCompatActivity() {
+ @Inject lateinit var counter: FeatureCounter
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.activity_feature)
+
+ findViewById<Button>(R.id.feature_button).setOnClickListener {
+ counter.count++
+ updateCountText()
+ }
+
+ updateCountText()
+ }
+
+ private fun updateCountText() {
+ findViewById<TextView>(R.id.feature_count).text = "The count: ${counter.count}"
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt b/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
new file mode 100644
index 000000000..0fd3db369
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple.feature
+
+class FeatureCounter(var count: Int)
diff --git a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureModule.kt b/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureModule.kt
new file mode 100644
index 000000000..84ca99a84
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureModule.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simple.feature
+
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.ActivityRetainedComponent
+import dagger.hilt.android.scopes.ActivityRetainedScoped
+
+@Module
+@InstallIn(ActivityRetainedComponent::class)
+object FeatureModule {
+ @Provides
+ @ActivityRetainedScoped
+ fun provideData() = FeatureCounter(0)
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/res/layout/activity_feature.xml b/java/dagger/hilt/android/example/gradle/simple/feature/src/main/res/layout/activity_feature.xml
new file mode 100644
index 000000000..bf524311d
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/feature/src/main/res/layout/activity_feature.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/background_light">
+
+ <TextView
+ android:id="@+id/feature_count"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:textColor="@android:color/primary_text_light"
+ />
+
+ <Button
+ android:id="@+id/feature_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/feature_count"
+ android:text="Count++"
+ />
+</RelativeLayout>
diff --git a/java/dagger/hilt/android/example/gradle/simple/gradle.properties b/java/dagger/hilt/android/example/gradle/simple/gradle.properties
new file mode 100644
index 000000000..646c51b97
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/gradle.properties
@@ -0,0 +1,2 @@
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/java/dagger/hilt/android/example/gradle/simple/gradle/wrapper/gradle-wrapper.jar b/java/dagger/hilt/android/example/gradle/simple/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..5c2d1cf01
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/java/dagger/hilt/android/example/gradle/simple/gradle/wrapper/gradle-wrapper.properties b/java/dagger/hilt/android/example/gradle/simple/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..4d9ca1649
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/java/dagger/hilt/android/example/gradle/simple/gradlew b/java/dagger/hilt/android/example/gradle/simple/gradlew
new file mode 100755
index 000000000..b0d6d0ab5
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/gradlew
@@ -0,0 +1,188 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/java/dagger/hilt/android/example/gradle/simple/settings.gradle b/java/dagger/hilt/android/example/gradle/simple/settings.gradle
new file mode 100644
index 000000000..2ae0b0a70
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simple/settings.gradle
@@ -0,0 +1,3 @@
+rootProject.name='Simple Hilt Android'
+include ':app'
+include ':feature'
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/build.gradle b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/build.gradle
new file mode 100644
index 000000000..bff479799
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/build.gradle
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'dagger.hilt.android.plugin'
+apply plugin: 'kotlin-kapt'
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.2"
+
+ defaultConfig {
+ applicationId "dagger.hilt.android.example.gradle.simpleKotlin"
+ minSdkVersion 15
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+ }
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+ testOptions {
+ unitTests.includeAndroidResources = true
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: "libs", include: ["*.jar"])
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+
+ implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'
+ kapt 'com.google.dagger:hilt-android-compiler:LOCAL-SNAPSHOT'
+
+ testImplementation 'com.google.truth:truth:1.0.1'
+ testImplementation 'junit:junit:4.13'
+ testImplementation 'org.robolectric:robolectric:4.5-alpha-3'
+ testImplementation 'androidx.core:core:1.3.2'
+ // TODO(bcorso): This multidex dep shouldn't be required -- it's a dep for the generated code.
+ testImplementation 'androidx.multidex:multidex:2.0.0'
+ testImplementation 'com.google.dagger:hilt-android-testing:LOCAL-SNAPSHOT'
+ kaptTest 'com.google.dagger:hilt-android-compiler:LOCAL-SNAPSHOT'
+}
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/debug/AndroidManifest.xml b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/debug/AndroidManifest.xml
new file mode 100644
index 000000000..081c7ade7
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.hilt.android.example.gradle.simpleKotlin">
+
+ <application>
+ <activity android:name=".SimpleTest$TestActivity" android:exported="false"/>
+ </application>
+</manifest>
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/AndroidManifest.xml b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..2e274ab0b
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.hilt.android.example.gradle.simpleKotlin">
+
+ <application
+ android:name=".KotlinApplication"
+ android:allowBackup="true"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.AppCompat.Light">
+ <activity android:name=".MainActivity" android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/ActivityModule.kt b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/ActivityModule.kt
new file mode 100644
index 000000000..8a85040ab
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/ActivityModule.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.hilt.android.example.gradle.simpleKotlin
+
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.ActivityComponent
+
+@Module
+@InstallIn(ActivityComponent::class)
+object ActivityModule {
+ @UserName
+ @Provides
+ fun provideUserName(): String {
+ return "Android User"
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/ApplicationModule.kt b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/ApplicationModule.kt
new file mode 100644
index 000000000..9ee904d52
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/ApplicationModule.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simpleKotlin
+
+import android.os.Build
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+
+@Module
+@InstallIn(SingletonComponent::class)
+object ApplicationModule {
+ @Provides
+ @Model
+ fun provideModel(): String {
+ return Build.MODEL
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/KotlinApplication.kt b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/KotlinApplication.kt
new file mode 100644
index 000000000..765fb3614
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/KotlinApplication.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simpleKotlin
+
+import android.app.Application
+import dagger.hilt.android.HiltAndroidApp
+import javax.inject.Inject
+
+@HiltAndroidApp
+class KotlinApplication : Application() {
+ // Shows that we can inject SingletonComponent bindings into an application.
+ @Inject
+ @Model
+ @JvmField
+ var model: String? = null
+}
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/MainActivity.kt b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/MainActivity.kt
new file mode 100644
index 000000000..564f05ab1
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/MainActivity.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simpleKotlin
+
+import android.os.Bundle
+import android.view.View
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import dagger.hilt.android.AndroidEntryPoint
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class MainActivity : AppCompatActivity() {
+ // Shows that we can inject Application/Activity bindings into an activity.
+ @JvmField
+ @Model
+ @Inject
+ var model: String? = null
+
+ @JvmField
+ @UserName
+ @Inject
+ var name: String? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+ val greeting = findViewById<View>(R.id.greeting) as TextView
+ val text = resources.getString(R.string.welcome, name, model)
+ greeting.text = text
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/Model.kt b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/Model.kt
new file mode 100644
index 000000000..4fe168db8
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/Model.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simpleKotlin
+
+import javax.inject.Qualifier
+
+/** Qualifies bindings relating to [android.os.Build.MODEL]. */
+@Qualifier
+@Retention(AnnotationRetention.RUNTIME)
+internal annotation class Model
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/UserName.kt b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/UserName.kt
new file mode 100644
index 000000000..a061ab587
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/UserName.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simpleKotlin
+
+import javax.inject.Qualifier
+
+/** Qualifies bindings relating to the user name. */
+@Qualifier
+@Retention(AnnotationRetention.RUNTIME)
+internal annotation class UserName
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/layout/activity_main.xml b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 000000000..0d7637d42
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/background_light">
+
+ <TextView
+ android:id="@+id/greeting"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:textColor="@android:color/primary_text_light"
+ />
+</RelativeLayout>
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/values/strings.xml b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/values/strings.xml
new file mode 100644
index 000000000..c1e4f272f
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/values/strings.xml
@@ -0,0 +1,23 @@
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <!--The app name [CHAR_LIMIT=40]-->
+ <string name="app_name">Simple Hilt Kotlin Android App</string>
+
+ <!--The greeting message [CHAR_LIMIT=100]-->
+ <string name="welcome">Hello, %1$s! You are on build %2$s.</string>
+</resources>
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/test/java/dagger/hilt/android/example/gradle/simpleKotlin/SimpleTest.kt b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/test/java/dagger/hilt/android/example/gradle/simpleKotlin/SimpleTest.kt
new file mode 100644
index 000000000..d52ff716d
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/test/java/dagger/hilt/android/example/gradle/simpleKotlin/SimpleTest.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simpleKotlin
+
+import android.os.Build
+import androidx.appcompat.app.AppCompatActivity
+import androidx.test.core.app.ActivityScenario
+import com.google.common.truth.Truth.assertThat
+import dagger.hilt.android.AndroidEntryPoint
+import dagger.hilt.android.testing.HiltAndroidRule
+import dagger.hilt.android.testing.HiltAndroidTest
+import dagger.hilt.android.testing.HiltTestApplication
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@HiltAndroidTest
+@RunWith(RobolectricTestRunner::class)
+// Robolectric requires Java9 to run API 29 and above, so use API 28 instead
+@Config(sdk = [Build.VERSION_CODES.P], application = HiltTestApplication::class)
+class SimpleTest {
+ @Rule
+ @JvmField
+ var rule = HiltAndroidRule(this)
+
+ @AndroidEntryPoint
+ class TestActivity : AppCompatActivity()
+
+ @Test
+ fun verifyMainActivity() {
+ ActivityScenario.launch(MainActivity::class.java).use { scenario ->
+ scenario.onActivity { activity ->
+ assertThat(activity::class.java.getSuperclass()?.getSimpleName())
+ .isEqualTo("Hilt_MainActivity")
+ assertThat(activity.model).isNotNull()
+ assertThat(activity.name).isNotNull()
+ }
+ }
+ }
+
+ @Test
+ fun verifyTestActivity() {
+ ActivityScenario.launch(TestActivity::class.java).use { scenario ->
+ scenario.onActivity { activity ->
+ assertThat(activity::class.java.getSuperclass()?.getSimpleName())
+ .isEqualTo("Hilt_SimpleTest_TestActivity")
+ }
+ }
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/build.gradle b/java/dagger/hilt/android/example/gradle/simpleKotlin/build.gradle
new file mode 100644
index 000000000..7a02482df
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/build.gradle
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+buildscript {
+ ext {
+ kotlin_version = '1.3.61'
+ agp_version = "4.2.0-beta04"
+ }
+ repositories {
+ google()
+ jcenter()
+ mavenLocal()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:$agp_version"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath 'com.google.dagger:hilt-android-gradle-plugin:LOCAL-SNAPSHOT'
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ mavenCentral()
+ mavenLocal()
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle.properties b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle.properties
new file mode 100644
index 000000000..646c51b97
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle.properties
@@ -0,0 +1,2 @@
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle/wrapper/gradle-wrapper.jar b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..5c2d1cf01
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle/wrapper/gradle-wrapper.properties b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..4d9ca1649
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/gradlew b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradlew
new file mode 100755
index 000000000..b0d6d0ab5
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradlew
@@ -0,0 +1,188 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/settings.gradle b/java/dagger/hilt/android/example/gradle/simpleKotlin/settings.gradle
new file mode 100644
index 000000000..e42f9d1ea
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name='Simple Kotlin Hilt Android'
+include ':app'
diff --git a/java/dagger/hilt/android/internal/BUILD b/java/dagger/hilt/android/internal/BUILD
new file mode 100644
index 000000000..57c84e3eb
--- /dev/null
+++ b/java/dagger/hilt/android/internal/BUILD
@@ -0,0 +1,28 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Internal Hilt Android utitlies
+
+package(default_visibility = ["//:src"])
+
+android_library(
+ name = "internal",
+ srcs = ["ThreadUtil.java"],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/android/internal/ThreadUtil.java b/java/dagger/hilt/android/internal/ThreadUtil.java
new file mode 100644
index 000000000..d5b11f58c
--- /dev/null
+++ b/java/dagger/hilt/android/internal/ThreadUtil.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal;
+
+import android.os.Looper;
+
+/** Thread utility methods. */
+public final class ThreadUtil {
+
+ private static Thread mainThread;
+
+ private ThreadUtil() {}
+
+ /** Returns true if the current thread is the Main thread. */
+ public static boolean isMainThread() {
+ if (mainThread == null) {
+ mainThread = Looper.getMainLooper().getThread();
+ }
+ return Thread.currentThread() == mainThread;
+ }
+
+ /** Checks that the current thread is the Main thread. Otherwise throws an exception. */
+ public static void ensureMainThread() {
+ if (!isMainThread()) {
+ throw new IllegalStateException("Must be called on the Main thread.");
+ }
+ }
+}
diff --git a/java/dagger/hilt/android/internal/builders/ActivityComponentBuilder.java b/java/dagger/hilt/android/internal/builders/ActivityComponentBuilder.java
new file mode 100644
index 000000000..5ea09db14
--- /dev/null
+++ b/java/dagger/hilt/android/internal/builders/ActivityComponentBuilder.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.builders;
+
+import android.app.Activity;
+import dagger.BindsInstance;
+import dagger.hilt.DefineComponent;
+import dagger.hilt.android.components.ActivityComponent;
+
+/** Interface for creating an {@link ActivityComponent}. */
+@DefineComponent.Builder
+public interface ActivityComponentBuilder {
+ ActivityComponentBuilder activity(
+ @BindsInstance
+ Activity activity);
+
+ ActivityComponent build();
+}
diff --git a/java/dagger/hilt/android/internal/builders/ActivityRetainedComponentBuilder.java b/java/dagger/hilt/android/internal/builders/ActivityRetainedComponentBuilder.java
new file mode 100644
index 000000000..110b3fef1
--- /dev/null
+++ b/java/dagger/hilt/android/internal/builders/ActivityRetainedComponentBuilder.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.builders;
+
+import dagger.hilt.DefineComponent;
+import dagger.hilt.android.components.ActivityRetainedComponent;
+
+/** Interface for creating a {@link ActivityRetainedComponent}. */
+@DefineComponent.Builder
+public interface ActivityRetainedComponentBuilder {
+ ActivityRetainedComponent build();
+}
diff --git a/java/dagger/hilt/android/internal/builders/BUILD b/java/dagger/hilt/android/internal/builders/BUILD
new file mode 100644
index 000000000..0e282be5f
--- /dev/null
+++ b/java/dagger/hilt/android/internal/builders/BUILD
@@ -0,0 +1,37 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Hilt Android component builders
+
+package(default_visibility = ["//:src"])
+
+android_library(
+ name = "builders",
+ srcs = glob(["*.java"]),
+ deps = [
+ "//:dagger_with_compiler",
+ "//java/dagger/hilt:define_component",
+ "//java/dagger/hilt/android/components",
+ "//java/dagger/hilt/android/components:view_model_component",
+ "@maven//:androidx_activity_activity",
+ "@maven//:androidx_fragment_fragment",
+ "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/android/internal/builders/FragmentComponentBuilder.java b/java/dagger/hilt/android/internal/builders/FragmentComponentBuilder.java
new file mode 100644
index 000000000..11cc89ff7
--- /dev/null
+++ b/java/dagger/hilt/android/internal/builders/FragmentComponentBuilder.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.builders;
+
+import androidx.fragment.app.Fragment;
+import dagger.BindsInstance;
+import dagger.hilt.DefineComponent;
+import dagger.hilt.android.components.FragmentComponent;
+
+/** Interface for creating a {@link FragmentComponent}. */
+@DefineComponent.Builder
+public interface FragmentComponentBuilder {
+ FragmentComponentBuilder fragment(@BindsInstance Fragment fragment);
+ FragmentComponent build();
+}
diff --git a/java/dagger/hilt/android/internal/builders/ServiceComponentBuilder.java b/java/dagger/hilt/android/internal/builders/ServiceComponentBuilder.java
new file mode 100644
index 000000000..9b95d12dd
--- /dev/null
+++ b/java/dagger/hilt/android/internal/builders/ServiceComponentBuilder.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.builders;
+
+import android.app.Service;
+import dagger.BindsInstance;
+import dagger.hilt.DefineComponent;
+import dagger.hilt.android.components.ServiceComponent;
+
+/** Interface for creating a {@link ServiceComponent}. */
+@DefineComponent.Builder
+public interface ServiceComponentBuilder {
+ ServiceComponentBuilder service(@BindsInstance Service service);
+ ServiceComponent build();
+}
diff --git a/java/dagger/hilt/android/internal/builders/ViewComponentBuilder.java b/java/dagger/hilt/android/internal/builders/ViewComponentBuilder.java
new file mode 100644
index 000000000..f2e37b61b
--- /dev/null
+++ b/java/dagger/hilt/android/internal/builders/ViewComponentBuilder.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.builders;
+
+import android.view.View;
+import dagger.BindsInstance;
+import dagger.hilt.DefineComponent;
+import dagger.hilt.android.components.ViewComponent;
+
+/** Interface for creating a {@link ViewComponent}. */
+@DefineComponent.Builder
+public interface ViewComponentBuilder {
+ ViewComponentBuilder view(@BindsInstance View view);
+
+ ViewComponent build();
+}
diff --git a/java/dagger/hilt/android/internal/builders/ViewModelComponentBuilder.java b/java/dagger/hilt/android/internal/builders/ViewModelComponentBuilder.java
new file mode 100644
index 000000000..e9acbb9a4
--- /dev/null
+++ b/java/dagger/hilt/android/internal/builders/ViewModelComponentBuilder.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.builders;
+
+import androidx.lifecycle.SavedStateHandle;
+import dagger.BindsInstance;
+import dagger.hilt.DefineComponent;
+import dagger.hilt.android.components.ViewModelComponent;
+
+/** Interface for creating a {@link ViewModelComponent}. */
+@DefineComponent.Builder
+public interface ViewModelComponentBuilder {
+ ViewModelComponentBuilder savedStateHandle(@BindsInstance SavedStateHandle handle);
+
+ ViewModelComponent build();
+}
diff --git a/java/dagger/hilt/android/internal/builders/ViewWithFragmentComponentBuilder.java b/java/dagger/hilt/android/internal/builders/ViewWithFragmentComponentBuilder.java
new file mode 100644
index 000000000..b15a8cc97
--- /dev/null
+++ b/java/dagger/hilt/android/internal/builders/ViewWithFragmentComponentBuilder.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.builders;
+
+import android.view.View;
+import dagger.BindsInstance;
+import dagger.hilt.DefineComponent;
+import dagger.hilt.android.components.ViewWithFragmentComponent;
+
+/** Interface for creating a {@link ViewWithFragmentComponent}. */
+@DefineComponent.Builder
+public interface ViewWithFragmentComponentBuilder {
+ ViewWithFragmentComponentBuilder view(@BindsInstance View view);
+
+ ViewWithFragmentComponent build();
+}
diff --git a/java/dagger/hilt/android/internal/lifecycle/BUILD b/java/dagger/hilt/android/internal/lifecycle/BUILD
new file mode 100644
index 000000000..b3ecfc22e
--- /dev/null
+++ b/java/dagger/hilt/android/internal/lifecycle/BUILD
@@ -0,0 +1,43 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Hilt lifecycle ViewModel hooks.
+
+package(default_visibility = ["//:src"])
+
+android_library(
+ name = "lifecycle",
+ srcs = glob(["*.java"]),
+ deps = [
+ "//:dagger_with_compiler",
+ "//java/dagger/hilt:entry_point",
+ "//java/dagger/hilt:install_in",
+ "//java/dagger/hilt/android/components",
+ "//java/dagger/hilt/android/components:view_model_component",
+ "//java/dagger/hilt/android/internal/builders",
+ "//java/dagger/hilt/android/qualifiers",
+ "@maven//:androidx_activity_activity",
+ "@maven//:androidx_annotation_annotation",
+ "@maven//:androidx_fragment_fragment",
+ "@maven//:androidx_lifecycle_lifecycle_viewmodel",
+ "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate",
+ "@maven//:androidx_savedstate_savedstate",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/android/internal/lifecycle/DefaultActivityViewModelFactory.java b/java/dagger/hilt/android/internal/lifecycle/DefaultActivityViewModelFactory.java
new file mode 100644
index 000000000..becce8cdb
--- /dev/null
+++ b/java/dagger/hilt/android/internal/lifecycle/DefaultActivityViewModelFactory.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.lifecycle;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.inject.Qualifier;
+
+/** Qualifier for the default view model factory used by @AndroidEntryPoint annotated activities. */
+@Qualifier
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
+public @interface DefaultActivityViewModelFactory {}
diff --git a/java/dagger/hilt/android/internal/lifecycle/DefaultFragmentViewModelFactory.java b/java/dagger/hilt/android/internal/lifecycle/DefaultFragmentViewModelFactory.java
new file mode 100644
index 000000000..996705376
--- /dev/null
+++ b/java/dagger/hilt/android/internal/lifecycle/DefaultFragmentViewModelFactory.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.lifecycle;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.inject.Qualifier;
+
+/** Qualifier for the default view model factory used by @AndroidEntryPoint annotated fragments. */
+@Qualifier
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
+public @interface DefaultFragmentViewModelFactory {}
diff --git a/java/dagger/hilt/android/internal/lifecycle/DefaultViewModelFactories.java b/java/dagger/hilt/android/internal/lifecycle/DefaultViewModelFactories.java
new file mode 100644
index 000000000..427822dbb
--- /dev/null
+++ b/java/dagger/hilt/android/internal/lifecycle/DefaultViewModelFactories.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.lifecycle;
+
+import android.app.Application;
+import androidx.lifecycle.SavedStateViewModelFactory;
+import androidx.lifecycle.ViewModelProvider;
+import android.os.Bundle;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.activity.ComponentActivity;
+import androidx.savedstate.SavedStateRegistryOwner;
+import dagger.Module;
+import dagger.hilt.EntryPoint;
+import dagger.hilt.EntryPoints;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.components.ActivityComponent;
+import dagger.hilt.android.components.FragmentComponent;
+import dagger.hilt.android.internal.builders.ViewModelComponentBuilder;
+import dagger.multibindings.Multibinds;
+import java.util.Set;
+import javax.inject.Inject;
+
+/**
+ * Modules and entry points for the default view model factory used by activities and fragments
+ * annotated with @AndroidEntryPoint.
+ *
+ * <p>Entry points are used to acquire the factory because injected fields in the generated
+ * activities and fragments are ignored by Dagger when using the transform due to the generated
+ * class not being part of the hierarchy during compile time.
+ */
+public final class DefaultViewModelFactories {
+
+ /**
+ * Retrieves the default view model factory for the activity.
+ *
+ * <p>Do not use except in Hilt generated code!
+ */
+ public static ViewModelProvider.Factory getActivityFactory(ComponentActivity activity) {
+ return EntryPoints.get(activity, ActivityEntryPoint.class)
+ .getHiltInternalFactoryFactory()
+ .fromActivity(activity);
+ }
+
+ /**
+ * Retrieves the default view model factory for the activity.
+ *
+ * <p>Do not use except in Hilt generated code!
+ */
+ public static ViewModelProvider.Factory getFragmentFactory(Fragment fragment) {
+ return EntryPoints.get(fragment, FragmentEntryPoint.class)
+ .getHiltInternalFactoryFactory()
+ .fromFragment(fragment);
+ }
+
+ /** Internal factory for the Hilt ViewModel Factory. */
+ public static final class InternalFactoryFactory {
+
+ private final Application application;
+ private final Set<String> keySet;
+ private final ViewModelComponentBuilder viewModelComponentBuilder;
+ @Nullable private final ViewModelProvider.Factory defaultActivityFactory;
+ @Nullable private final ViewModelProvider.Factory defaultFragmentFactory;
+
+ @Inject
+ InternalFactoryFactory(
+ Application application,
+ @HiltViewModelMap.KeySet Set<String> keySet,
+ ViewModelComponentBuilder viewModelComponentBuilder,
+ // These default factory bindings are temporary for the transition of deprecating
+ // the Hilt ViewModel extension for the built-in support
+ @DefaultActivityViewModelFactory Set<ViewModelProvider.Factory> defaultActivityFactorySet,
+ @DefaultFragmentViewModelFactory Set<ViewModelProvider.Factory> defaultFragmentFactorySet) {
+ this.application = application;
+ this.keySet = keySet;
+ this.viewModelComponentBuilder = viewModelComponentBuilder;
+ this.defaultActivityFactory = getFactoryFromSet(defaultActivityFactorySet);
+ this.defaultFragmentFactory = getFactoryFromSet(defaultFragmentFactorySet);
+ }
+
+ ViewModelProvider.Factory fromActivity(ComponentActivity activity) {
+ return getHiltViewModelFactory(activity,
+ activity.getIntent() != null ? activity.getIntent().getExtras() : null,
+ defaultActivityFactory);
+ }
+
+ ViewModelProvider.Factory fromFragment(Fragment fragment) {
+ return getHiltViewModelFactory(fragment, fragment.getArguments(), defaultFragmentFactory);
+ }
+
+ private ViewModelProvider.Factory getHiltViewModelFactory(
+ SavedStateRegistryOwner owner,
+ @Nullable Bundle defaultArgs,
+ @Nullable ViewModelProvider.Factory extensionDelegate) {
+ ViewModelProvider.Factory delegate = extensionDelegate == null
+ ? new SavedStateViewModelFactory(application, owner, defaultArgs)
+ : extensionDelegate;
+ return new HiltViewModelFactory(
+ owner, defaultArgs, keySet, delegate, viewModelComponentBuilder);
+ }
+
+ @Nullable
+ private static ViewModelProvider.Factory getFactoryFromSet(Set<ViewModelProvider.Factory> set) {
+ // A multibinding set is used instead of BindsOptionalOf because Optional is not available in
+ // Android until API 24 and we don't want to have Guava as a transitive dependency.
+ if (set.isEmpty()) {
+ return null;
+ }
+ if (set.size() > 1) {
+ throw new IllegalStateException(
+ "At most one default view model factory is expected. Found " + set);
+ }
+ ViewModelProvider.Factory factory = set.iterator().next();
+ if (factory == null) {
+ throw new IllegalStateException("Default view model factory must not be null.");
+ }
+ return factory;
+ }
+ }
+
+ /** The activity module to declare the optional factories. */
+ @Module
+ @InstallIn(ActivityComponent.class)
+ interface ActivityModule {
+ @Multibinds
+ @HiltViewModelMap.KeySet
+ abstract Set<String> viewModelKeys();
+
+ @Multibinds
+ @DefaultActivityViewModelFactory
+ Set<ViewModelProvider.Factory> defaultActivityViewModelFactory();
+
+ @Multibinds
+ @DefaultFragmentViewModelFactory
+ Set<ViewModelProvider.Factory> defaultFragmentViewModelFactory();
+ }
+
+ /** The activity entry point to retrieve the factory. */
+ @EntryPoint
+ @InstallIn(ActivityComponent.class)
+ public interface ActivityEntryPoint {
+ InternalFactoryFactory getHiltInternalFactoryFactory();
+ }
+
+ /** The fragment entry point to retrieve the factory. */
+ @EntryPoint
+ @InstallIn(FragmentComponent.class)
+ public interface FragmentEntryPoint {
+ InternalFactoryFactory getHiltInternalFactoryFactory();
+ }
+
+ private DefaultViewModelFactories() {}
+}
diff --git a/java/dagger/hilt/android/internal/lifecycle/HiltViewModelFactory.java b/java/dagger/hilt/android/internal/lifecycle/HiltViewModelFactory.java
new file mode 100644
index 000000000..3b0d3bc43
--- /dev/null
+++ b/java/dagger/hilt/android/internal/lifecycle/HiltViewModelFactory.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.lifecycle;
+
+import androidx.lifecycle.AbstractSavedStateViewModelFactory;
+import androidx.lifecycle.SavedStateHandle;
+import androidx.lifecycle.ViewModel;
+import androidx.lifecycle.ViewModelProvider;
+import android.os.Bundle;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.savedstate.SavedStateRegistryOwner;
+import dagger.Module;
+import dagger.hilt.EntryPoint;
+import dagger.hilt.EntryPoints;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.components.ViewModelComponent;
+import dagger.hilt.android.internal.builders.ViewModelComponentBuilder;
+import dagger.multibindings.Multibinds;
+import java.util.Map;
+import java.util.Set;
+import javax.inject.Provider;
+
+/**
+ * View Model Provider Factory for the Hilt Extension.
+ *
+ * <p>A provider for this factory will be installed in the {@link
+ * dagger.hilt.android.components.ActivityComponent} and {@link
+ * dagger.hilt.android.components.FragmentComponent}. An instance of this factory will also be the
+ * default factory by activities and fragments annotated with {@link
+ * dagger.hilt.android.AndroidEntryPoint}.
+ */
+public final class HiltViewModelFactory implements ViewModelProvider.Factory {
+
+ /** Hilt entry point for getting the multi-binding map of ViewModels. */
+ @EntryPoint
+ @InstallIn(ViewModelComponent.class)
+ public interface ViewModelFactoriesEntryPoint {
+ @HiltViewModelMap
+ Map<String, Provider<ViewModel>> getHiltViewModelMap();
+ }
+
+ /** Hilt module for providing the empty multi-binding map of ViewModels. */
+ @Module
+ @InstallIn(ViewModelComponent.class)
+ interface ViewModelModule {
+ @Multibinds
+ @HiltViewModelMap
+ Map<String, ViewModel> hiltViewModelMap();
+ }
+
+ private final Set<String> hiltViewModelKeys;
+ private final ViewModelProvider.Factory delegateFactory;
+ private final AbstractSavedStateViewModelFactory hiltViewModelFactory;
+
+ public HiltViewModelFactory(
+ @NonNull SavedStateRegistryOwner owner,
+ @Nullable Bundle defaultArgs,
+ @NonNull Set<String> hiltViewModelKeys,
+ @NonNull ViewModelProvider.Factory delegateFactory,
+ @NonNull ViewModelComponentBuilder viewModelComponentBuilder) {
+ this.hiltViewModelKeys = hiltViewModelKeys;
+ this.delegateFactory = delegateFactory;
+ this.hiltViewModelFactory =
+ new AbstractSavedStateViewModelFactory(owner, defaultArgs) {
+ @NonNull
+ @Override
+ @SuppressWarnings("unchecked")
+ protected <T extends ViewModel> T create(
+ @NonNull String key, @NonNull Class<T> modelClass, @NonNull SavedStateHandle handle) {
+ ViewModelComponent component =
+ viewModelComponentBuilder.savedStateHandle(handle).build();
+ Provider<? extends ViewModel> provider =
+ EntryPoints.get(component, ViewModelFactoriesEntryPoint.class)
+ .getHiltViewModelMap()
+ .get(modelClass.getName());
+ if (provider == null) {
+ throw new IllegalStateException(
+ "Expected the @HiltViewModel-annotated class '"
+ + modelClass.getName()
+ + "' to be available in the multi-binding of "
+ + "@HiltViewModelMap but none was found.");
+ }
+ return (T) provider.get();
+ }
+ };
+ }
+
+ @NonNull
+ @Override
+ public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
+ if (hiltViewModelKeys.contains(modelClass.getName())) {
+ return hiltViewModelFactory.create(modelClass);
+ } else {
+ return delegateFactory.create(modelClass);
+ }
+ }
+}
diff --git a/java/dagger/hilt/android/internal/lifecycle/HiltViewModelMap.java b/java/dagger/hilt/android/internal/lifecycle/HiltViewModelMap.java
new file mode 100644
index 000000000..5d54e9ca3
--- /dev/null
+++ b/java/dagger/hilt/android/internal/lifecycle/HiltViewModelMap.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.lifecycle;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.inject.Qualifier;
+
+/**
+ * Internal qualifier for the multibinding map of ViewModels used by the {@link
+ * dagger.hilt.android.lifecycle.HiltViewModelFactory}.
+ */
+@Qualifier
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.METHOD, ElementType.PARAMETER})
+public @interface HiltViewModelMap {
+
+ /** Internal qualifier for the multibinding set of class names annotated with @ViewModelInject. */
+ @Qualifier
+ @Retention(RetentionPolicy.CLASS)
+ @Target({ElementType.METHOD, ElementType.PARAMETER})
+ @interface KeySet {}
+}
diff --git a/java/dagger/hilt/android/internal/managers/ActivityComponentManager.java b/java/dagger/hilt/android/internal/managers/ActivityComponentManager.java
new file mode 100644
index 000000000..ef7aa3b19
--- /dev/null
+++ b/java/dagger/hilt/android/internal/managers/ActivityComponentManager.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.managers;
+
+import android.app.Activity;
+import android.app.Application;
+import androidx.activity.ComponentActivity;
+import dagger.hilt.EntryPoint;
+import dagger.hilt.EntryPoints;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.components.ActivityRetainedComponent;
+import dagger.hilt.android.internal.builders.ActivityComponentBuilder;
+import dagger.hilt.internal.GeneratedComponentManager;
+
+/**
+ * Do not use except in Hilt generated code!
+ *
+ * <p>A manager for the creation of components that live in the Activity.
+ *
+ * <p>Note: This class is not typed since its type in generated code is always <?> or <Object>. This
+ * is mainly due to the fact that we don't know the components at the time of generation, and
+ * because even the injector interface type is not a valid type if we have a hilt base class.
+ *
+ */
+public class ActivityComponentManager implements GeneratedComponentManager<Object> {
+ /** Entrypoint for {@link ActivityComponentBuilder}. */
+ @EntryPoint
+ @InstallIn(ActivityRetainedComponent.class)
+ public interface ActivityComponentBuilderEntryPoint {
+ ActivityComponentBuilder activityComponentBuilder();
+ }
+
+ private volatile Object component;
+ private final Object componentLock = new Object();
+
+ protected final Activity activity;
+
+ private final GeneratedComponentManager<ActivityRetainedComponent>
+ activityRetainedComponentManager;
+
+ public ActivityComponentManager(Activity activity) {
+ this.activity = activity;
+ this.activityRetainedComponentManager =
+ new ActivityRetainedComponentManager((ComponentActivity) activity);
+ }
+
+ @Override
+ public Object generatedComponent() {
+ if (component == null) {
+ synchronized (componentLock) {
+ if (component == null) {
+ component = createComponent();
+ }
+ }
+ }
+ return component;
+ }
+
+ protected Object createComponent() {
+ if (!(activity.getApplication() instanceof GeneratedComponentManager)) {
+ if (Application.class.equals(activity.getApplication().getClass())) {
+ throw new IllegalStateException(
+ "Hilt Activity must be attached to an @HiltAndroidApp Application. "
+ + "Did you forget to specify your Application's class name in your manifest's "
+ + "<application />'s android:name attribute?");
+ }
+ throw new IllegalStateException(
+ "Hilt Activity must be attached to an @AndroidEntryPoint Application. Found: "
+ + activity.getApplication().getClass());
+ }
+
+ return EntryPoints.get(
+ activityRetainedComponentManager, ActivityComponentBuilderEntryPoint.class)
+ .activityComponentBuilder()
+ .activity(activity)
+ .build();
+ }
+}
diff --git a/java/dagger/hilt/android/internal/managers/ActivityRetainedComponentManager.java b/java/dagger/hilt/android/internal/managers/ActivityRetainedComponentManager.java
new file mode 100644
index 000000000..2d4a72bdd
--- /dev/null
+++ b/java/dagger/hilt/android/internal/managers/ActivityRetainedComponentManager.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.managers;
+
+import androidx.lifecycle.ViewModel;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.activity.ComponentActivity;
+import dagger.Binds;
+import dagger.Module;
+import dagger.hilt.EntryPoint;
+import dagger.hilt.EntryPoints;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.ActivityRetainedLifecycle;
+import dagger.hilt.android.components.ActivityRetainedComponent;
+import dagger.hilt.android.internal.ThreadUtil;
+import dagger.hilt.android.internal.builders.ActivityRetainedComponentBuilder;
+import dagger.hilt.android.scopes.ActivityRetainedScoped;
+import dagger.hilt.components.SingletonComponent;
+import dagger.hilt.internal.GeneratedComponentManager;
+import java.util.HashSet;
+import java.util.Set;
+import javax.inject.Inject;
+
+/** A manager for the creation of components that survives activity configuration changes. */
+final class ActivityRetainedComponentManager
+ implements GeneratedComponentManager<ActivityRetainedComponent> {
+
+ /** Entry point for {@link ActivityRetainedComponentBuilder}. */
+ @EntryPoint
+ @InstallIn(SingletonComponent.class)
+ public interface ActivityRetainedComponentBuilderEntryPoint {
+ ActivityRetainedComponentBuilder retainedComponentBuilder();
+ }
+
+ /** Entry point for {@link Lifecycle}. */
+ @EntryPoint
+ @InstallIn(ActivityRetainedComponent.class)
+ public interface ActivityRetainedLifecycleEntryPoint {
+ ActivityRetainedLifecycle getActivityRetainedLifecycle();
+ }
+
+ static final class ActivityRetainedComponentViewModel extends ViewModel {
+ private final ActivityRetainedComponent component;
+
+ ActivityRetainedComponentViewModel(ActivityRetainedComponent component) {
+ this.component = component;
+ }
+
+ ActivityRetainedComponent getComponent() {
+ return component;
+ }
+
+ @Override
+ protected void onCleared() {
+ super.onCleared();
+ ActivityRetainedLifecycle lifecycle =
+ EntryPoints.get(component, ActivityRetainedLifecycleEntryPoint.class)
+ .getActivityRetainedLifecycle();
+ ((ActivityRetainedComponentManager.Lifecycle) lifecycle).dispatchOnCleared();
+ }
+ }
+
+ private final ViewModelProvider viewModelProvider;
+
+ @Nullable private volatile ActivityRetainedComponent component;
+ private final Object componentLock = new Object();
+
+ ActivityRetainedComponentManager(ComponentActivity activity) {
+ this.viewModelProvider =
+ new ViewModelProvider(
+ activity,
+ new ViewModelProvider.Factory() {
+ @NonNull
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T extends ViewModel> T create(@NonNull Class<T> aClass) {
+ ActivityRetainedComponent component =
+ EntryPoints.get(
+ activity.getApplication(),
+ ActivityRetainedComponentBuilderEntryPoint.class)
+ .retainedComponentBuilder()
+ .build();
+ return (T) new ActivityRetainedComponentViewModel(component);
+ }
+ });
+ }
+
+ @Override
+ public ActivityRetainedComponent generatedComponent() {
+ if (component == null) {
+ synchronized (componentLock) {
+ if (component == null) {
+ component = createComponent();
+ }
+ }
+ }
+ return component;
+ }
+
+ private ActivityRetainedComponent createComponent() {
+ return viewModelProvider.get(ActivityRetainedComponentViewModel.class).getComponent();
+ }
+
+ /** The default implementation of {@link ActivityRetainedLifecycle}. */
+ @ActivityRetainedScoped
+ static final class Lifecycle implements ActivityRetainedLifecycle {
+
+ private final Set<OnClearedListener> listeners = new HashSet<>();
+ private boolean onClearedDispatched = false;
+
+ @Inject
+ Lifecycle() {}
+
+ @Override
+ public void addOnClearedListener(@NonNull OnClearedListener listener) {
+ ThreadUtil.ensureMainThread();
+ throwIfOnClearedDispatched();
+ listeners.add(listener);
+ }
+
+ @Override
+ public void removeOnClearedListener(@NonNull OnClearedListener listener) {
+ ThreadUtil.ensureMainThread();
+ throwIfOnClearedDispatched();
+ listeners.remove(listener);
+ }
+
+ void dispatchOnCleared() {
+ ThreadUtil.ensureMainThread();
+ onClearedDispatched = true;
+ for (OnClearedListener listener : listeners) {
+ listener.onCleared();
+ }
+ }
+
+ private void throwIfOnClearedDispatched() {
+ if (onClearedDispatched) {
+ throw new IllegalStateException(
+ "There was a race between the call to add/remove an OnClearedListener and onCleared(). "
+ + "This can happen when posting to the Main thread from a background thread, "
+ + "which is not supported.");
+ }
+ }
+ }
+
+ @Module
+ @InstallIn(ActivityRetainedComponent.class)
+ abstract static class LifecycleModule {
+ @Binds
+ abstract ActivityRetainedLifecycle bind(Lifecycle impl);
+ }
+}
diff --git a/java/dagger/hilt/android/internal/managers/ApplicationComponentManager.java b/java/dagger/hilt/android/internal/managers/ApplicationComponentManager.java
new file mode 100644
index 000000000..bb645dbc7
--- /dev/null
+++ b/java/dagger/hilt/android/internal/managers/ApplicationComponentManager.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.managers;
+
+import dagger.hilt.internal.GeneratedComponentManager;
+
+/**
+ * Do not use except in Hilt generated code!
+ *
+ * <p>A manager for the creation of components that live in the Application.
+ */
+public final class ApplicationComponentManager implements GeneratedComponentManager<Object> {
+ private volatile Object component;
+ private final Object componentLock = new Object();
+ private final ComponentSupplier componentCreator;
+
+ public ApplicationComponentManager(ComponentSupplier componentCreator) {
+ this.componentCreator = componentCreator;
+ }
+
+ @Override
+ public Object generatedComponent() {
+ if (component == null) {
+ synchronized (componentLock) {
+ if (component == null) {
+ component = componentCreator.get();
+ }
+ }
+ }
+ return component;
+ }
+}
diff --git a/java/dagger/hilt/android/internal/managers/BUILD b/java/dagger/hilt/android/internal/managers/BUILD
new file mode 100644
index 000000000..3bc8df144
--- /dev/null
+++ b/java/dagger/hilt/android/internal/managers/BUILD
@@ -0,0 +1,60 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Internal Hilt Android managers
+
+package(default_visibility = ["//:src"])
+
+android_library(
+ name = "component_supplier",
+ srcs = ["ComponentSupplier.java"],
+)
+
+android_library(
+ name = "managers",
+ srcs = [
+ "ActivityComponentManager.java",
+ "ActivityRetainedComponentManager.java",
+ "ApplicationComponentManager.java",
+ "BroadcastReceiverComponentManager.java",
+ "FragmentComponentManager.java",
+ "ServiceComponentManager.java",
+ "ViewComponentManager.java",
+ ],
+ deps = [
+ ":component_supplier",
+ "//:dagger_with_compiler",
+ "//java/dagger/hilt:entry_point",
+ "//java/dagger/hilt:install_in",
+ "//java/dagger/hilt/android:activity_retained_lifecycle",
+ "//java/dagger/hilt/android/components",
+ "//java/dagger/hilt/android/components:view_model_component",
+ "//java/dagger/hilt/android/internal",
+ "//java/dagger/hilt/android/internal/builders",
+ "//java/dagger/hilt/android/scopes:activity_retained_scoped",
+ "//java/dagger/hilt/android/scopes:view_model_scoped",
+ "//java/dagger/hilt/internal:component_manager",
+ "//java/dagger/hilt/internal:preconditions",
+ "@maven//:androidx_activity_activity",
+ "@maven//:androidx_annotation_annotation",
+ "@maven//:androidx_fragment_fragment",
+ "@maven//:androidx_lifecycle_lifecycle_viewmodel",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/android/internal/managers/BroadcastReceiverComponentManager.java b/java/dagger/hilt/android/internal/managers/BroadcastReceiverComponentManager.java
new file mode 100644
index 000000000..471c31c91
--- /dev/null
+++ b/java/dagger/hilt/android/internal/managers/BroadcastReceiverComponentManager.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.managers;
+
+import android.app.Application;
+import android.content.Context;
+import dagger.hilt.internal.GeneratedComponentManager;
+import dagger.hilt.internal.Preconditions;
+
+/**
+ * Do not use except in Hilt generated code!
+ *
+ * <p>A manager for the creation of components that live in the BroadcastReceiver.
+ */
+public final class BroadcastReceiverComponentManager {
+ @SuppressWarnings("unchecked")
+ public static Object generatedComponent(Context context) {
+ Application application = (Application) context.getApplicationContext();
+
+ Preconditions.checkArgument(
+ application instanceof GeneratedComponentManager,
+ "Hilt BroadcastReceiver must be attached to an @AndroidEntryPoint Application. "
+ + "Found: %s",
+ application.getClass());
+
+ return ((GeneratedComponentManager<?>) application).generatedComponent();
+ }
+
+ private BroadcastReceiverComponentManager() {}
+}
diff --git a/java/dagger/hilt/android/internal/managers/ComponentSupplier.java b/java/dagger/hilt/android/internal/managers/ComponentSupplier.java
new file mode 100644
index 000000000..de702a993
--- /dev/null
+++ b/java/dagger/hilt/android/internal/managers/ComponentSupplier.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.managers;
+
+/**
+ * Interface for supplying a component. This is separate from the Supplier interface so that
+ * optimizers can strip this method (and therefore all the Dagger code) from the main dex even if a
+ * Supplier is referenced in code kept in the main dex.
+ */
+public interface ComponentSupplier {
+ Object get();
+}
diff --git a/java/dagger/hilt/android/internal/managers/FragmentComponentManager.java b/java/dagger/hilt/android/internal/managers/FragmentComponentManager.java
new file mode 100644
index 000000000..40dd60a64
--- /dev/null
+++ b/java/dagger/hilt/android/internal/managers/FragmentComponentManager.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.managers;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.os.Bundle;
+import androidx.fragment.app.Fragment;
+import android.view.LayoutInflater;
+import dagger.hilt.EntryPoint;
+import dagger.hilt.EntryPoints;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.components.ActivityComponent;
+import dagger.hilt.android.internal.builders.FragmentComponentBuilder;
+import dagger.hilt.internal.GeneratedComponentManager;
+import dagger.hilt.internal.Preconditions;
+
+/**
+ * Do not use except in Hilt generated code!
+ *
+ * <p>A manager for the creation of components that live in the Fragment.
+ *
+ * <p>Note: This class is not typed since its type in generated code is always <?> or <Object>. This
+ * is mainly due to the fact that we don't know the components at the time of generation, and
+ * because even the injector interface type is not a valid type if we have a hilt base class.
+ *
+ */
+public class FragmentComponentManager implements GeneratedComponentManager<Object> {
+ /** Entrypoint for {@link FragmentComponentBuilder}. */
+ @EntryPoint
+ @InstallIn(ActivityComponent.class)
+ public interface FragmentComponentBuilderEntryPoint {
+ FragmentComponentBuilder fragmentComponentBuilder();
+ }
+
+ private volatile Object component;
+ private final Object componentLock = new Object();
+ private final Fragment fragment;
+
+ public FragmentComponentManager(Fragment fragment) {
+ this.fragment = fragment;
+ }
+
+ @Override
+ public Object generatedComponent() {
+ if (component == null) {
+ synchronized (componentLock) {
+ if (component == null) {
+ component = createComponent();
+ }
+ }
+ }
+ return component;
+ }
+
+ private Object createComponent() {
+ Preconditions.checkNotNull(
+ fragment.getHost(),
+ "Hilt Fragments must be attached before creating the component.");
+ Preconditions.checkState(
+ fragment.getHost() instanceof GeneratedComponentManager,
+ "Hilt Fragments must be attached to an @AndroidEntryPoint Activity. Found: %s",
+ fragment.getHost().getClass());
+
+ validate(fragment);
+
+ return EntryPoints.get(fragment.getHost(), FragmentComponentBuilderEntryPoint.class)
+ .fragmentComponentBuilder()
+ .fragment(fragment)
+ .build();
+ }
+
+ /** Returns the fragments bundle, creating a new one if none exists. */
+ public static final void initializeArguments(Fragment fragment) {
+ Preconditions.checkNotNull(fragment);
+ if (fragment.getArguments() == null) {
+ fragment.setArguments(new Bundle());
+ }
+ }
+
+ public static final Context findActivity(Context context) {
+ while (context instanceof ContextWrapper
+ && !(context instanceof Activity)) {
+ context = ((ContextWrapper) context).getBaseContext();
+ }
+ return context;
+ }
+
+ public static ContextWrapper createContextWrapper(Context base, Fragment fragment) {
+ return new ViewComponentManager.FragmentContextWrapper(base, fragment);
+ }
+
+ public static ContextWrapper createContextWrapper(
+ LayoutInflater baseInflater, Fragment fragment) {
+ return new ViewComponentManager.FragmentContextWrapper(baseInflater, fragment);
+ }
+
+ /** Called immediately before component creation to allow validation on the Fragment. */
+ protected void validate(Fragment fragment) {
+ }
+}
diff --git a/java/dagger/hilt/android/internal/managers/ServiceComponentManager.java b/java/dagger/hilt/android/internal/managers/ServiceComponentManager.java
new file mode 100644
index 000000000..d5e7f15c7
--- /dev/null
+++ b/java/dagger/hilt/android/internal/managers/ServiceComponentManager.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.managers;
+
+import android.app.Application;
+import android.app.Service;
+import dagger.hilt.EntryPoint;
+import dagger.hilt.EntryPoints;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.internal.builders.ServiceComponentBuilder;
+import dagger.hilt.components.SingletonComponent;
+import dagger.hilt.internal.GeneratedComponentManager;
+import dagger.hilt.internal.Preconditions;
+
+/**
+ * Do not use except in Hilt generated code!
+ *
+ * <p>A manager for the creation of components that live in the Service.
+ *
+ * <p>Note: This class is not typed since its type in generated code is always <?> or <Object>. This
+ * is mainly due to the fact that we don't know the components at the time of generation, and
+ * because even the injector interface type is not a valid type if we have a hilt base class.
+ */
+public final class ServiceComponentManager implements GeneratedComponentManager<Object> {
+ /** Entrypoint for {@link ServiceComponentBuilder}. */
+ @EntryPoint
+ @InstallIn(SingletonComponent.class)
+ public interface ServiceComponentBuilderEntryPoint {
+ ServiceComponentBuilder serviceComponentBuilder();
+ }
+
+ private final Service service;
+ private Object component;
+
+ public ServiceComponentManager(Service service) {
+ this.service = service;
+ }
+
+ // This isn't ever really publicly exposed on a service so it should be fine without
+ // synchronization.
+ @Override
+ public Object generatedComponent() {
+ if (component == null) {
+ component = createComponent();
+ }
+ return component;
+ }
+
+ private Object createComponent() {
+ Application application = service.getApplication();
+ Preconditions.checkState(
+ application instanceof GeneratedComponentManager,
+ "Hilt service must be attached to an @AndroidEntryPoint Application. Found: %s",
+ application.getClass());
+
+ return EntryPoints.get(application, ServiceComponentBuilderEntryPoint.class)
+ .serviceComponentBuilder()
+ .service(service)
+ .build();
+ }
+}
diff --git a/java/dagger/hilt/android/internal/managers/ViewComponentManager.java b/java/dagger/hilt/android/internal/managers/ViewComponentManager.java
new file mode 100644
index 000000000..cb2ece0c8
--- /dev/null
+++ b/java/dagger/hilt/android/internal/managers/ViewComponentManager.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.managers;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import androidx.fragment.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import dagger.hilt.EntryPoint;
+import dagger.hilt.EntryPoints;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.components.ActivityComponent;
+import dagger.hilt.android.components.FragmentComponent;
+import dagger.hilt.android.internal.builders.ViewComponentBuilder;
+import dagger.hilt.android.internal.builders.ViewWithFragmentComponentBuilder;
+import dagger.hilt.internal.GeneratedComponentManager;
+import dagger.hilt.internal.Preconditions;
+
+/**
+ * Do not use except in Hilt generated code!
+ *
+ * <p>A manager for the creation of components that live in the View.
+ *
+ * <p>Note: This class is not typed since its type in generated code is always <?> or <Object>. This
+ * is mainly due to the fact that we don't know the components at the time of generation, and
+ * because even the injector interface type is not a valid type if we have a hilt base class.
+ */
+public final class ViewComponentManager implements GeneratedComponentManager<Object> {
+ /** Entrypoint for {@link ViewWithFragmentComponentBuilder}. */
+ @EntryPoint
+ @InstallIn(FragmentComponent.class)
+ public interface ViewWithFragmentComponentBuilderEntryPoint {
+ ViewWithFragmentComponentBuilder viewWithFragmentComponentBuilder();
+ }
+
+ /** Entrypoint for {@link ViewComponentBuilder}. */
+ @EntryPoint
+ @InstallIn(ActivityComponent.class)
+ public interface ViewComponentBuilderEntryPoint {
+ ViewComponentBuilder viewComponentBuilder();
+ }
+
+ private volatile Object component;
+ private final Object componentLock = new Object();
+ private final boolean hasFragmentBindings;
+ private final View view;
+
+ public ViewComponentManager(View view, boolean hasFragmentBindings) {
+ this.view = view;
+ this.hasFragmentBindings = hasFragmentBindings;
+ }
+
+ @Override
+ public Object generatedComponent() {
+ if (component == null) {
+ synchronized (componentLock) {
+ if (component == null) {
+ component = createComponent();
+ }
+ }
+ }
+ return component;
+ }
+
+ private Object createComponent() {
+ GeneratedComponentManager<?> componentManager =
+ getParentComponentManager(/*allowMissing=*/ false);
+ if (hasFragmentBindings) {
+ return EntryPoints.get(componentManager, ViewWithFragmentComponentBuilderEntryPoint.class)
+ .viewWithFragmentComponentBuilder()
+ .view(view)
+ .build();
+ } else {
+ return EntryPoints.get(componentManager, ViewComponentBuilderEntryPoint.class)
+ .viewComponentBuilder()
+ .view(view)
+ .build();
+ }
+ }
+
+ /* Returns the component manager of the parent or null if not found. */
+ public GeneratedComponentManager<?> maybeGetParentComponentManager() {
+ return getParentComponentManager(/*allowMissing=*/ true);
+ }
+
+ private GeneratedComponentManager<?> getParentComponentManager(boolean allowMissing) {
+ if (hasFragmentBindings) {
+ Context context = getParentContext(FragmentContextWrapper.class, allowMissing);
+ if (context instanceof FragmentContextWrapper) {
+
+ FragmentContextWrapper fragmentContextWrapper = (FragmentContextWrapper) context;
+ return (GeneratedComponentManager<?>) fragmentContextWrapper.fragment;
+ } else if (allowMissing) {
+ // We didn't find anything, so return null if we're not supposed to fail.
+ // The rest of the logic is just about getting a good error message.
+ return null;
+ }
+
+ // Check if there was a valid parent component, just not a Fragment, to give a more
+ // specific error.
+ Context parent = getParentContext(GeneratedComponentManager.class, allowMissing);
+ Preconditions.checkState(
+ !(parent instanceof GeneratedComponentManager),
+ "%s, @WithFragmentBindings Hilt view must be attached to an "
+ + "@AndroidEntryPoint Fragment. "
+ + "Was attached to context %s",
+ view.getClass(),
+ parent.getClass().getName());
+ } else {
+ Context context = getParentContext(GeneratedComponentManager.class, allowMissing);
+ if (context instanceof GeneratedComponentManager) {
+ return (GeneratedComponentManager<?>) context;
+ } else if (allowMissing) {
+ return null;
+ }
+ }
+
+ // Couldn't find any parent components to descend from.
+ throw new IllegalStateException(
+ String.format(
+ "%s, Hilt view must be attached to an @AndroidEntryPoint Fragment or Activity.",
+ view.getClass()));
+
+ }
+
+ private Context getParentContext(Class<?> parentType, boolean allowMissing) {
+ Context context = unwrap(view.getContext(), parentType);
+ if (context == unwrap(context.getApplicationContext(), GeneratedComponentManager.class)) {
+ // If we searched for a type but ended up on the application context, just return null
+ // as this is never what we are looking for
+ Preconditions.checkState(
+ allowMissing,
+ "%s, Hilt view cannot be created using the application context. "
+ + "Use a Hilt Fragment or Activity context.",
+ view.getClass());
+ return null;
+ }
+ return context;
+ }
+
+ private static Context unwrap(Context context, Class<?> target) {
+ while (context instanceof ContextWrapper && !target.isInstance(context)) {
+ context = ((ContextWrapper) context).getBaseContext();
+ }
+ return context;
+ }
+
+ /**
+ * Do not use except in Hilt generated code!
+ *
+ * <p>A wrapper class to expose the {@link Fragment} to the views they're inflating.
+ */
+ // This is only non-final for the account override
+ public static final class FragmentContextWrapper extends ContextWrapper {
+ private LayoutInflater baseInflater;
+ private LayoutInflater inflater;
+ public final Fragment fragment;
+
+ public FragmentContextWrapper(Context base, Fragment fragment) {
+ super(Preconditions.checkNotNull(base));
+ this.baseInflater = null;
+ this.fragment = Preconditions.checkNotNull(fragment);
+ }
+
+ public FragmentContextWrapper(LayoutInflater baseInflater, Fragment fragment) {
+ super(Preconditions.checkNotNull(Preconditions.checkNotNull(baseInflater).getContext()));
+ this.baseInflater = baseInflater;
+ this.fragment = Preconditions.checkNotNull(fragment);
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ if (!LAYOUT_INFLATER_SERVICE.equals(name)) {
+ return getBaseContext().getSystemService(name);
+ }
+ if (inflater == null) {
+ if (baseInflater == null) {
+ baseInflater =
+ (LayoutInflater) getBaseContext().getSystemService(LAYOUT_INFLATER_SERVICE);
+ }
+ inflater = baseInflater.cloneInContext(this);
+ }
+ return inflater;
+ }
+ }
+}
diff --git a/java/dagger/hilt/android/internal/migration/BUILD b/java/dagger/hilt/android/internal/migration/BUILD
new file mode 100644
index 000000000..b877f6f48
--- /dev/null
+++ b/java/dagger/hilt/android/internal/migration/BUILD
@@ -0,0 +1,28 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Internal classes for migration
+
+package(default_visibility = ["//:src"])
+
+android_library(
+ name = "injected_by_hilt",
+ srcs = ["InjectedByHilt.java"],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/android/internal/migration/InjectedByHilt.java b/java/dagger/hilt/android/internal/migration/InjectedByHilt.java
new file mode 100644
index 000000000..087a7ee6f
--- /dev/null
+++ b/java/dagger/hilt/android/internal/migration/InjectedByHilt.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.migration;
+
+/**
+ * Do not use except in Hilt generated code!
+ */
+public interface InjectedByHilt {
+ /** Returns true if this class was injected by Hilt. */
+ boolean wasInjectedByHilt();
+}
diff --git a/java/dagger/hilt/android/internal/modules/ActivityModule.java b/java/dagger/hilt/android/internal/modules/ActivityModule.java
new file mode 100644
index 000000000..080e2030b
--- /dev/null
+++ b/java/dagger/hilt/android/internal/modules/ActivityModule.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.modules;
+
+import android.app.Activity;
+import android.content.Context;
+import androidx.fragment.app.FragmentActivity;
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Reusable;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.components.ActivityComponent;
+import dagger.hilt.android.qualifiers.ActivityContext;
+
+/** Provides convenience bindings for activities. */
+@Module
+@InstallIn(ActivityComponent.class)
+abstract class ActivityModule {
+ @Binds
+ @ActivityContext
+ abstract Context provideContext(Activity activity);
+
+ @Provides
+ @Reusable
+ static FragmentActivity provideFragmentActivity(Activity activity) {
+ try {
+ return (FragmentActivity) activity;
+ } catch (ClassCastException e) {
+ throw new IllegalStateException("Expected activity to be a FragmentActivity: " + activity, e);
+ }
+ }
+
+ private ActivityModule() {}
+}
diff --git a/java/dagger/hilt/android/internal/modules/ApplicationContextModule.java b/java/dagger/hilt/android/internal/modules/ApplicationContextModule.java
new file mode 100644
index 000000000..af4ebc250
--- /dev/null
+++ b/java/dagger/hilt/android/internal/modules/ApplicationContextModule.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.modules;
+
+import android.app.Application;
+import android.content.Context;
+import dagger.Module;
+import dagger.Provides;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.qualifiers.ApplicationContext;
+import dagger.hilt.components.SingletonComponent;
+
+/** Provides a binding for an Android BinderFragment Context. */
+@Module
+@InstallIn(SingletonComponent.class)
+public final class ApplicationContextModule {
+ private final Context applicationContext;
+
+ public ApplicationContextModule(Context applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+ @Provides
+ @ApplicationContext
+ Context provideContext() {
+ return applicationContext;
+ }
+
+ @Provides
+ Application provideApplication() {
+ return (Application) applicationContext.getApplicationContext();
+ }
+}
diff --git a/java/dagger/hilt/android/internal/modules/BUILD b/java/dagger/hilt/android/internal/modules/BUILD
new file mode 100644
index 000000000..a36e23746
--- /dev/null
+++ b/java/dagger/hilt/android/internal/modules/BUILD
@@ -0,0 +1,37 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Hilt android modules for standard components.
+
+package(default_visibility = ["//:src"])
+
+android_library(
+ name = "modules",
+ srcs = glob(["*.java"]),
+ deps = [
+ "//:dagger_with_compiler",
+ "//java/dagger/hilt:install_in",
+ "//java/dagger/hilt/android/components",
+ "//java/dagger/hilt/android/qualifiers",
+ "@maven//:androidx_activity_activity",
+ "@maven//:androidx_annotation_annotation",
+ "@maven//:androidx_fragment_fragment",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/android/internal/testing/BUILD b/java/dagger/hilt/android/internal/testing/BUILD
new file mode 100644
index 000000000..b0bc361c7
--- /dev/null
+++ b/java/dagger/hilt/android/internal/testing/BUILD
@@ -0,0 +1,89 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Internal Hilt android testing libraries
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "test_injector",
+ testonly = 1,
+ srcs = [
+ "TestApplicationInjector.java",
+ "TestInjector.java",
+ ],
+)
+
+android_library(
+ name = "internal_test_root",
+ srcs = [
+ "InternalTestRoot.java",
+ ],
+ deps = [
+ "//java/dagger/hilt:generates_root_input",
+ ],
+)
+
+android_library(
+ name = "test_application_component_manager",
+ testonly = 1,
+ srcs = ["TestApplicationComponentManager.java"],
+ deps = [
+ ":test_component_data",
+ ":test_injector",
+ "//java/dagger/hilt/android/testing:on_component_ready_runner",
+ "//java/dagger/hilt/internal:component_manager",
+ "//java/dagger/hilt/internal:preconditions",
+ "@maven//:junit_junit",
+ ],
+)
+
+android_library(
+ name = "test_component_data",
+ testonly = 1,
+ srcs = [
+ "TestComponentData.java",
+ "TestComponentDataSupplier.java",
+ ],
+ deps = [
+ ":test_injector",
+ "//java/dagger/hilt/internal:component_manager",
+ ],
+)
+
+android_library(
+ name = "test_application_component_manager_holder",
+ testonly = 1,
+ srcs = ["TestApplicationComponentManagerHolder.java"],
+)
+
+android_library(
+ name = "mark_that_rules_ran_rule",
+ testonly = 1,
+ srcs = ["MarkThatRulesRanRule.java"],
+ deps = [
+ ":test_application_component_manager",
+ ":test_application_component_manager_holder",
+ "//java/dagger/hilt/internal:component_manager",
+ "//java/dagger/hilt/internal:preconditions",
+ "@maven//:androidx_test_core",
+ "@maven//:junit_junit",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["**/*"]),
+)
diff --git a/java/dagger/hilt/android/internal/testing/InternalTestRoot.java b/java/dagger/hilt/android/internal/testing/InternalTestRoot.java
new file mode 100644
index 000000000..c3bbf0cf6
--- /dev/null
+++ b/java/dagger/hilt/android/internal/testing/InternalTestRoot.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.testing;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import android.app.Application;
+import dagger.hilt.GeneratesRootInput;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/** Annotation that generates a Hilt test application. */
+@Retention(CLASS)
+@Target({ElementType.TYPE})
+@GeneratesRootInput
+public @interface InternalTestRoot {
+
+ /** Returns the test class. */
+ Class<?> testClass();
+
+ /** Returns the base {@link Application} class. */
+ Class<? extends Application> applicationBaseClass();
+}
diff --git a/java/dagger/hilt/android/internal/testing/MarkThatRulesRanRule.java b/java/dagger/hilt/android/internal/testing/MarkThatRulesRanRule.java
new file mode 100644
index 000000000..17c40b308
--- /dev/null
+++ b/java/dagger/hilt/android/internal/testing/MarkThatRulesRanRule.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.testing;
+
+import static dagger.hilt.internal.Preconditions.checkNotNull;
+import static dagger.hilt.internal.Preconditions.checkState;
+
+import android.content.Context;
+import androidx.test.core.app.ApplicationProvider;
+import dagger.hilt.internal.GeneratedComponentManager;
+import java.lang.annotation.Annotation;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * A Junit {@code TestRule} that's installed in all Hilt tests.
+ *
+ * <p>This rule enforces that a Hilt TestRule has run. The Dagger component will not be created
+ * without this test rule.
+ */
+public final class MarkThatRulesRanRule implements TestRule {
+ private static final String HILT_ANDROID_APP = "dagger.hilt.android.HiltAndroidApp";
+ private static final String HILT_ANDROID_TEST = "dagger.hilt.android.testing.HiltAndroidTest";
+
+ private final Context context = ApplicationProvider.getApplicationContext();
+ private final Object testInstance;
+ private final boolean autoAddModule;
+
+ private final AtomicBoolean started = new AtomicBoolean(false);
+
+ public MarkThatRulesRanRule(Object testInstance) {
+ this.autoAddModule = true;
+ this.testInstance = checkNotNull(testInstance);
+ checkState(
+ hasAnnotation(testInstance, HILT_ANDROID_TEST),
+ "Expected %s to be annotated with @HiltAndroidTest.",
+ testInstance.getClass().getName());
+ checkState(
+ context instanceof GeneratedComponentManager,
+ "Hilt test, %s, must use a Hilt test application but found %s. To fix, configure the test "
+ + "to use HiltTestApplication or a custom Hilt test application generated with "
+ + "@CustomTestApplication.",
+ testInstance.getClass().getName(),
+ context.getClass().getName());
+ checkState(
+ !hasAnnotation(context, HILT_ANDROID_APP),
+ "Hilt test, %s, cannot use a @HiltAndroidApp application but found %s. To fix, configure "
+ + "the test to use HiltTestApplication or a custom Hilt test application generated "
+ + "with @CustomTestApplication.",
+ testInstance.getClass().getName(),
+ context.getClass().getName());
+ }
+
+ public void delayComponentReady() {
+ checkState(!started.get(), "Called delayComponentReady after test execution started");
+ getTestApplicationComponentManager().delayComponentReady();
+ }
+
+ public void componentReady() {
+ checkState(started.get(), "Called componentReady before test execution started");
+ getTestApplicationComponentManager().componentReady();
+ }
+
+ public void inject() {
+ getTestApplicationComponentManager().inject();
+ }
+
+ @Override
+ public Statement apply(final Statement base, Description description) {
+ started.set(true);
+ checkState(
+ description.getTestClass().isInstance(testInstance),
+ "HiltAndroidRule was constructed with an argument that was not an instance of the test"
+ + " class");
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+
+ TestApplicationComponentManager componentManager = getTestApplicationComponentManager();
+ try {
+ // This check is required to check that state hasn't been set before this rule runs. This
+ // prevents cases like setting state in Application.onCreate for Gradle emulator tests
+ // that will get cleared after running the first test case.
+ componentManager.checkStateIsCleared();
+ componentManager.setAutoAddModule(autoAddModule);
+ if (testInstance != null) {
+ componentManager.setTestInstance(testInstance);
+ }
+ componentManager.setHasHiltTestRule(description);
+ base.evaluate();
+ componentManager.verifyDelayedComponentWasMadeReady();
+ } finally {
+ componentManager.clearState();
+ }
+ }
+ };
+ }
+
+ private TestApplicationComponentManager getTestApplicationComponentManager() {
+ checkState(
+ context instanceof TestApplicationComponentManagerHolder,
+ "The context is not an instance of TestApplicationComponentManagerHolder: %s",
+ context);
+ Object componentManager = ((TestApplicationComponentManagerHolder) context).componentManager();
+ checkState(
+ componentManager instanceof TestApplicationComponentManager,
+ "Expected TestApplicationComponentManagerHolder to return an instance of"
+ + "TestApplicationComponentManager");
+ return (TestApplicationComponentManager) componentManager;
+ }
+
+ private static boolean hasAnnotation(Object obj, String annotationName) {
+ for (Annotation annotation : obj.getClass().getAnnotations()) {
+ if (annotation.annotationType().getName().contentEquals(annotationName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/java/dagger/hilt/android/internal/testing/TestApplicationComponentManager.java b/java/dagger/hilt/android/internal/testing/TestApplicationComponentManager.java
new file mode 100644
index 000000000..187887022
--- /dev/null
+++ b/java/dagger/hilt/android/internal/testing/TestApplicationComponentManager.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.testing;
+
+import android.app.Application;
+import dagger.hilt.android.testing.OnComponentReadyRunner;
+import dagger.hilt.android.testing.OnComponentReadyRunner.OnComponentReadyRunnerHolder;
+import dagger.hilt.internal.GeneratedComponentManager;
+import dagger.hilt.internal.Preconditions;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
+import org.junit.runner.Description;
+
+/**
+ * Do not use except in Hilt generated code!
+ *
+ * <p>A manager for the creation of components that live in the test Application.
+ */
+public final class TestApplicationComponentManager
+ implements GeneratedComponentManager<Object>, OnComponentReadyRunnerHolder {
+
+ // This is a generated class that we always generate in a known location.
+ private static final String TEST_COMPONENT_DATA_SUPPLIER_IMPL =
+ "dagger.hilt.android.internal.testing.TestComponentDataSupplierImpl";
+
+ private final Application application;
+ private final Map<Class<?>, TestComponentData> testComponentDataSupplier;
+
+ private final AtomicReference<Object> component = new AtomicReference<>();
+ private final AtomicReference<Description> hasHiltTestRule = new AtomicReference<>();
+ private final Map<Class<?>, Object> registeredModules = new ConcurrentHashMap<>();
+ private final AtomicReference<Boolean> autoAddModuleEnabled = new AtomicReference<>();
+ private final AtomicReference<DelayedComponentState> delayedComponentState =
+ new AtomicReference<>(DelayedComponentState.NOT_DELAYED);
+ private volatile Object testInstance;
+ private volatile OnComponentReadyRunner onComponentReadyRunner = new OnComponentReadyRunner();
+
+ /**
+ * Represents the state of Component readiness. There are two valid transition sequences.
+ *
+ * <ul>
+ * <li>Typical test (no HiltAndroidRule#delayComponentReady): {@code NOT_DELAYED -> INJECTED}
+ * <li>Using HiltAndroidRule#delayComponentReady: {@code NOT_DELAYED -> COMPONENT_DELAYED ->
+ * COMPONENT_READY -> INJECTED}
+ * </ul>
+ */
+ private enum DelayedComponentState {
+ // Valid transitions: COMPONENT_DELAYED, INJECTED
+ NOT_DELAYED,
+ // Valid transitions: COMPONENT_READY
+ COMPONENT_DELAYED,
+ // Valid transitions: INJECTED
+ COMPONENT_READY,
+ // Terminal state
+ INJECTED
+ }
+
+ public TestApplicationComponentManager(Application application) {
+ this.application = application;
+ try {
+ this.testComponentDataSupplier =
+ Class.forName(TEST_COMPONENT_DATA_SUPPLIER_IMPL)
+ .asSubclass(TestComponentDataSupplier.class)
+ .getDeclaredConstructor()
+ .newInstance()
+ .get();
+ } catch (ClassNotFoundException
+ | NoSuchMethodException
+ | IllegalAccessException
+ | InstantiationException
+ | InvocationTargetException e) {
+ throw new RuntimeException(
+ "Hilt classes generated from @HiltAndroidTest are missing. Check that you have annotated "
+ + "your test class with @HiltAndroidTest and that the processor is running over your "
+ + "test",
+ e);
+ }
+ }
+
+ @Override
+ public Object generatedComponent() {
+ if (component.get() == null) {
+ Preconditions.checkState(
+ hasHiltTestRule(),
+ "The component was not created. Check that you have added the HiltAndroidRule.");
+ if (!registeredModules.keySet().containsAll(requiredModules())) {
+ Set<Class<?>> difference = new HashSet<>(requiredModules());
+ difference.removeAll(registeredModules.keySet());
+ throw new IllegalStateException(
+ "The component was not created. Check that you have "
+ + "registered all test modules:\n\tUnregistered: "
+ + difference);
+ }
+ Preconditions.checkState(
+ bindValueReady(), "The test instance has not been set. Did you forget to call #bind()?");
+ throw new IllegalStateException(
+ "The component has not been created. "
+ + "Check that you have called #inject()? Otherwise, "
+ + "there is a race between injection and component creation. Make sure there is a "
+ + "happens-before edge between the HiltAndroidRule/registering"
+ + " all test modules and the first injection.");
+ }
+ return component.get();
+ }
+
+ @Override
+ public OnComponentReadyRunner getOnComponentReadyRunner() {
+ return onComponentReadyRunner;
+ }
+
+ /** For framework use only! This flag must be set before component creation. */
+ void setHasHiltTestRule(Description description) {
+ Preconditions.checkState(
+ // Some exempted tests set the test rule multiple times. Use CAS to avoid setting twice.
+ hasHiltTestRule.compareAndSet(null, description),
+ "The hasHiltTestRule flag has already been set!");
+ tryToCreateComponent();
+ }
+
+ void checkStateIsCleared() {
+ Preconditions.checkState(
+ component.get() == null,
+ "The Hilt component cannot be set before Hilt's test rule has run.");
+ Preconditions.checkState(
+ hasHiltTestRule.get() == null,
+ "The Hilt test rule cannot be set before Hilt's test rule has run.");
+ Preconditions.checkState(
+ autoAddModuleEnabled.get() == null,
+ "The Hilt autoAddModuleEnabled cannot be set before Hilt's test rule has run.");
+ Preconditions.checkState(
+ testInstance == null,
+ "The Hilt BindValue instance cannot be set before Hilt's test rule has run.");
+ Preconditions.checkState(
+ registeredModules.isEmpty(),
+ "The Hilt registered modules cannot be set before Hilt's test rule has run.");
+ Preconditions.checkState(
+ onComponentReadyRunner.isEmpty(),
+ "The Hilt onComponentReadyRunner cannot add listeners before Hilt's test rule has run.");
+ DelayedComponentState state = delayedComponentState.get();
+ switch (state) {
+ case NOT_DELAYED:
+ case COMPONENT_DELAYED:
+ // Expected
+ break;
+ case COMPONENT_READY:
+ throw new IllegalStateException("Called componentReady before test execution started");
+ case INJECTED:
+ throw new IllegalStateException("Called inject before test execution started");
+ }
+ }
+
+ void clearState() {
+ component.set(null);
+ hasHiltTestRule.set(null);
+ testInstance = null;
+ registeredModules.clear();
+ autoAddModuleEnabled.set(null);
+ delayedComponentState.set(DelayedComponentState.NOT_DELAYED);
+ onComponentReadyRunner = new OnComponentReadyRunner();
+ }
+
+ public Description getDescription() {
+ return hasHiltTestRule.get();
+ }
+
+ public Object getTestInstance() {
+ Preconditions.checkState(
+ testInstance != null,
+ "The test instance has not been set.");
+ return testInstance;
+ }
+
+ /** For framework use only! This method should be called when a required module is installed. */
+ public <T> void registerModule(Class<T> moduleClass, T module) {
+ Preconditions.checkNotNull(moduleClass);
+ Preconditions.checkState(
+ testComponentData().daggerRequiredModules().contains(moduleClass),
+ "Found unknown module class: %s",
+ moduleClass.getName());
+ if (requiredModules().contains(moduleClass)) {
+ Preconditions.checkState(
+ // Some exempted tests register modules multiple times.
+ !registeredModules.containsKey(moduleClass),
+ "Module is already registered: %s",
+ moduleClass.getName());
+
+ registeredModules.put(moduleClass, module);
+ tryToCreateComponent();
+ }
+ }
+
+ void delayComponentReady() {
+ switch (delayedComponentState.getAndSet(DelayedComponentState.COMPONENT_DELAYED)) {
+ case NOT_DELAYED:
+ // Expected
+ break;
+ case COMPONENT_DELAYED:
+ throw new IllegalStateException("Called delayComponentReady() twice");
+ case COMPONENT_READY:
+ throw new IllegalStateException("Called delayComponentReady() after componentReady()");
+ case INJECTED:
+ throw new IllegalStateException("Called delayComponentReady() after inject()");
+ }
+ }
+
+ void componentReady() {
+ switch (delayedComponentState.getAndSet(DelayedComponentState.COMPONENT_READY)) {
+ case NOT_DELAYED:
+ throw new IllegalStateException(
+ "Called componentReady(), even though delayComponentReady() was not used.");
+ case COMPONENT_DELAYED:
+ // Expected
+ break;
+ case COMPONENT_READY:
+ throw new IllegalStateException("Called componentReady() multiple times");
+ case INJECTED:
+ throw new IllegalStateException("Called componentReady() after inject()");
+ }
+ tryToCreateComponent();
+ }
+
+ void inject() {
+ switch (delayedComponentState.getAndSet(DelayedComponentState.INJECTED)) {
+ case NOT_DELAYED:
+ case COMPONENT_READY:
+ // Expected
+ break;
+ case COMPONENT_DELAYED:
+ throw new IllegalStateException("Called inject() before calling componentReady()");
+ case INJECTED:
+ throw new IllegalStateException("Called inject() multiple times");
+ }
+ Preconditions.checkNotNull(testInstance);
+ testInjector().injectTest(testInstance);
+ }
+
+ void verifyDelayedComponentWasMadeReady() {
+ Preconditions.checkState(
+ delayedComponentState.get() != DelayedComponentState.COMPONENT_DELAYED,
+ "Used delayComponentReady(), but never called componentReady()");
+ }
+
+ private void tryToCreateComponent() {
+ if (hasHiltTestRule()
+ && registeredModules.keySet().containsAll(requiredModules())
+ && bindValueReady()
+ && delayedComponentReady()) {
+ Preconditions.checkState(
+ autoAddModuleEnabled.get() != null,
+ "Component cannot be created before autoAddModuleEnabled is set.");
+ Preconditions.checkState(
+ component.compareAndSet(
+ null,
+ componentSupplier().get(registeredModules, testInstance, autoAddModuleEnabled.get())),
+ "Tried to create the component more than once! "
+ + "There is a race between registering the HiltAndroidRule and registering"
+ + " all test modules. Make sure there is a happens-before edge between the two.");
+ onComponentReadyRunner.setComponentManager((GeneratedComponentManager) application);
+ }
+ }
+
+ void setTestInstance(Object testInstance) {
+ Preconditions.checkNotNull(testInstance);
+ Preconditions.checkState(this.testInstance == null, "The test instance was already set!");
+ this.testInstance = testInstance;
+ }
+
+ void setAutoAddModule(boolean autoAddModule) {
+ Preconditions.checkState(
+ autoAddModuleEnabled.get() == null, "autoAddModuleEnabled is already set!");
+ autoAddModuleEnabled.set(autoAddModule);
+ }
+
+ private Set<Class<?>> requiredModules() {
+ return autoAddModuleEnabled.get()
+ ? testComponentData().hiltRequiredModules()
+ : testComponentData().daggerRequiredModules();
+ }
+
+ private boolean waitForBindValue() {
+ return testComponentData().waitForBindValue();
+ }
+
+ private TestInjector<Object> testInjector() {
+ return testComponentData().testInjector();
+ }
+
+ private TestComponentData.ComponentSupplier componentSupplier() {
+ return testComponentData().componentSupplier();
+ }
+
+ private TestComponentData testComponentData() {
+ return testComponentDataSupplier.get(testClass());
+ }
+
+ private Class<?> testClass() {
+ Preconditions.checkState(
+ hasHiltTestRule(),
+ "Test must have an HiltAndroidRule.");
+ return hasHiltTestRule.get().getTestClass();
+ }
+
+ private boolean bindValueReady() {
+ return !waitForBindValue() || testInstance != null;
+ }
+
+ private boolean delayedComponentReady() {
+ return delayedComponentState.get() != DelayedComponentState.COMPONENT_DELAYED;
+ }
+
+ private boolean hasHiltTestRule() {
+ return hasHiltTestRule.get() != null;
+ }
+}
diff --git a/java/dagger/hilt/android/internal/testing/TestApplicationComponentManagerHolder.java b/java/dagger/hilt/android/internal/testing/TestApplicationComponentManagerHolder.java
new file mode 100644
index 000000000..a8695c4fe
--- /dev/null
+++ b/java/dagger/hilt/android/internal/testing/TestApplicationComponentManagerHolder.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.testing;
+
+/** For use by Hilt internally only! Returns the component manager. */
+public interface TestApplicationComponentManagerHolder {
+ // Returns {@link Object} so that we do not expose {@code TestApplicationComponentManager} to
+ // clients. Framework code should explicitly cast to {@code TestApplicationComponentManager}.
+ Object componentManager();
+}
diff --git a/java/dagger/hilt/android/internal/testing/TestApplicationInjector.java b/java/dagger/hilt/android/internal/testing/TestApplicationInjector.java
new file mode 100644
index 000000000..c7ff5c9aa
--- /dev/null
+++ b/java/dagger/hilt/android/internal/testing/TestApplicationInjector.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.testing;
+
+/**
+ * Interface to expose a method for members injection for use in tests.
+ */
+public interface TestApplicationInjector<T> {
+ void injectApp(T t);
+}
diff --git a/java/dagger/hilt/android/internal/testing/TestComponentData.java b/java/dagger/hilt/android/internal/testing/TestComponentData.java
new file mode 100644
index 000000000..4eed6fb9b
--- /dev/null
+++ b/java/dagger/hilt/android/internal/testing/TestComponentData.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.testing;
+
+import dagger.hilt.internal.Preconditions;
+import java.util.Map;
+import java.util.Set;
+
+/** Contains the data needed to create a test's component. */
+public final class TestComponentData {
+ private final ComponentSupplier componentSupplier;
+ private final TestInjector<Object> testInjector;
+ private final Set<Class<?>> daggerRequiredModules;
+ private final Set<Class<?>> hiltRequiredModules;
+ private final boolean waitForBindValue;
+
+ public TestComponentData(
+ boolean waitForBindValue,
+ TestInjector<Object> testInjector,
+ Set<Class<?>> daggerRequiredModules,
+ Set<Class<?>> hiltRequiredModules,
+ ComponentSupplier componentSupplier) {
+ Preconditions.checkState(
+ daggerRequiredModules.containsAll(hiltRequiredModules),
+ "Hilt required modules should be subset of Dagger required modules.");
+ this.componentSupplier = componentSupplier;
+ this.testInjector = testInjector;
+ this.daggerRequiredModules = daggerRequiredModules;
+ this.waitForBindValue = waitForBindValue;
+ this.hiltRequiredModules = hiltRequiredModules;
+ }
+
+ /** Returns the {@link ComponentSupplier}. */
+ public ComponentSupplier componentSupplier() {
+ return componentSupplier;
+ }
+
+ /** Returns the {@link TestInjector}. */
+ public TestInjector<Object> testInjector() {
+ return testInjector;
+ }
+
+ /** Returns the set of modules that Dagger cannot create instances of itself */
+ public Set<Class<?>> daggerRequiredModules() {
+ return daggerRequiredModules;
+ }
+
+ /**
+ * Returns a subset of {@link #daggerRequiredModules} that filters out the modules Hilt can
+ * instantiate itself.
+ */
+ public Set<Class<?>> hiltRequiredModules() {
+ return hiltRequiredModules;
+ }
+
+ /** Returns true if creation of the component needs to wait for bind() to be called. */
+ public boolean waitForBindValue() {
+ return waitForBindValue;
+ }
+
+ /** Returns the component using the given registered modules. */
+ public interface ComponentSupplier {
+ Object get(Map<Class<?>, ?> registeredModules, Object testInstance, Boolean autoAddModule);
+ }
+}
diff --git a/java/dagger/hilt/android/internal/testing/TestComponentDataSupplier.java b/java/dagger/hilt/android/internal/testing/TestComponentDataSupplier.java
new file mode 100644
index 000000000..e39073f9a
--- /dev/null
+++ b/java/dagger/hilt/android/internal/testing/TestComponentDataSupplier.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.testing;
+
+import java.util.Map;
+
+/** Stores the {@link TestComponentData} for all Hilt test classes. */
+public abstract class TestComponentDataSupplier {
+
+ /** Returns a map of {@link TestComponentData} keyed by test class. */
+ protected abstract Map<Class<?>, TestComponentData> get();
+}
diff --git a/java/dagger/hilt/android/internal/testing/TestInjector.java b/java/dagger/hilt/android/internal/testing/TestInjector.java
new file mode 100644
index 000000000..7055f91ac
--- /dev/null
+++ b/java/dagger/hilt/android/internal/testing/TestInjector.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.internal.testing;
+
+/**
+ * Interface to expose a method for members injection for use in tests.
+ */
+public interface TestInjector<T> {
+ void injectTest(T t);
+}
diff --git a/java/dagger/hilt/android/lifecycle/BUILD b/java/dagger/hilt/android/lifecycle/BUILD
new file mode 100644
index 000000000..fd805102c
--- /dev/null
+++ b/java/dagger/hilt/android/lifecycle/BUILD
@@ -0,0 +1,39 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Hilt ViewModel integration.
+
+package(default_visibility = ["//:src"])
+
+android_library(
+ name = "lifecycle",
+ srcs = glob(["*.java"]),
+ exported_plugins = [
+ "//java/dagger/hilt/android/processor/internal/viewmodel:processor",
+ ],
+ proguard_specs = ["proguard-rules.pro"],
+ exports = [
+ "//java/dagger/hilt/android/components:view_model_component",
+ "//java/dagger/hilt/android/internal/lifecycle",
+ ],
+ deps = [
+ "//java/dagger/hilt:generates_root_input",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/android/lifecycle/HiltViewModel.java b/java/dagger/hilt/android/lifecycle/HiltViewModel.java
new file mode 100644
index 000000000..198ec8abe
--- /dev/null
+++ b/java/dagger/hilt/android/lifecycle/HiltViewModel.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.lifecycle;
+
+import dagger.hilt.GeneratesRootInput;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Identifies a {@link androidx.lifecycle.ViewModel} for construction injection.
+ *
+ * <p>The {@code ViewModel} annotated with {@link HiltViewModel} will be available for creation by
+ * the {@link dagger.hilt.android.lifecycle.HiltViewModelFactory} and can be retrieved by default in
+ * an {@code Activity} or {@code Fragment} annotated with {@link
+ * dagger.hilt.android.AndroidEntryPoint}. The {@code HiltViewModel} containing a constructor
+ * annotated with {@link javax.inject.Inject} will have its dependencies defined in the constructor
+ * parameters injected by Dagger's Hilt.
+ *
+ * <p>Example:
+ *
+ * <pre>
+ * &#64;HiltViewModel
+ * public class DonutViewModel extends ViewModel {
+ * &#64;Inject
+ * public DonutViewModel(SavedStateHandle handle, RecipeRepository repository) {
+ * // ...
+ * }
+ * }
+ * </pre>
+ *
+ * <pre>
+ * &#64;AndroidEntryPoint
+ * public class CookingActivity extends AppCompatActivity {
+ * public void onCreate(Bundle savedInstanceState) {
+ * DonutViewModel vm = new ViewModelProvider(this).get(DonutViewModel.class);
+ * }
+ * }
+ * </pre>
+ *
+ * <p>Exactly one constructor in the {@code ViewModel} must be annotated with {@code Inject}.
+ *
+ * <p>Only dependencies available in the {@link dagger.hilt.android.components.ViewModelComponent}
+ * can be injected into the {@code ViewModel}.
+ *
+ * <p>
+ *
+ * @see dagger.hilt.android.components.ViewModelComponent
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.CLASS)
+@GeneratesRootInput
+public @interface HiltViewModel {}
diff --git a/java/dagger/hilt/android/lifecycle/proguard-rules.pro b/java/dagger/hilt/android/lifecycle/proguard-rules.pro
new file mode 100644
index 000000000..edd3d3d79
--- /dev/null
+++ b/java/dagger/hilt/android/lifecycle/proguard-rules.pro
@@ -0,0 +1,2 @@
+# Keep class names of Hilt injected ViewModels since their name are used as a multibinding map key.
+-keepnames @dagger.hilt.android.lifecycle.HiltViewModel class * extends androidx.lifecycle.ViewModel
diff --git a/java/dagger/hilt/android/migration/BUILD b/java/dagger/hilt/android/migration/BUILD
new file mode 100644
index 000000000..ade47c37f
--- /dev/null
+++ b/java/dagger/hilt/android/migration/BUILD
@@ -0,0 +1,50 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Helpers for migrating to Hilt.
+
+package(default_visibility = ["//:src"])
+
+android_library(
+ name = "optional_inject",
+ srcs = [
+ "OptionalInject.java",
+ "OptionalInjectCheck.java",
+ ],
+ exports = [
+ "//java/dagger/hilt/android/internal/migration:injected_by_hilt",
+ ],
+ deps = [
+ ":package_info",
+ "//java/dagger/hilt/android/internal/migration:injected_by_hilt",
+ "//java/dagger/hilt/internal:preconditions",
+ "@maven//:androidx_activity_activity",
+ "@maven//:androidx_annotation_annotation",
+ "@maven//:androidx_fragment_fragment",
+ ],
+)
+
+java_library(
+ name = "package_info",
+ srcs = ["package-info.java"],
+ deps = [
+ "@google_bazel_common//third_party/java/jsr305_annotations",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["**/*"]),
+)
diff --git a/java/dagger/hilt/android/migration/OptionalInject.java b/java/dagger/hilt/android/migration/OptionalInject.java
new file mode 100644
index 000000000..14c1387a2
--- /dev/null
+++ b/java/dagger/hilt/android/migration/OptionalInject.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.migration;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * When placed on an {@link dagger.hilt.android.AndroidEntryPoint}-annotated activity / fragment /
+ * view / etc, allows injection to occur optionally based on whether or not the application is using
+ * Hilt.
+ *
+ * <p>When using this annotation, you can use {@link OptionalInjectCheck#wasInjectedByHilt} to check
+ * at runtime if the annotated class was injected by Hilt. Additionally, this annotation will also
+ * cause a method, {@code wasInjectedByHilt} to be generated in the Hilt base class as well, that
+ * behaves the same as {@link OptionalInjectCheck#wasInjectedByHilt}. The method is available to
+ * users that extend the Hilt base class directly and don't use the Gradle plugin.
+ *
+ * <p>Example usage:
+ *
+ * <pre><code>
+ * {@literal @}OptionalInject
+ * {@literal @}AndroidEntryPoint
+ * public final class MyFragment extends Fragment {
+ *
+ * {@literal @}Inject Foo foo;
+ *
+ * {@literal @}Override
+ * public void onAttach(Activity activity) {
+ * // Injection will happen here, but only if the Activity and the Application are also
+ * // AndroidEntryPoints and were injected by Hilt.
+ * super.onAttach(activity);
+ * if (!OptionalInjectCheck.wasInjectedByHilt(this)) {
+ * // Get Dagger components the previous way and inject.
+ * }
+ * }
+ * }
+ * </code></pre>
+ *
+ * <p>This is useful for libraries that have to support Hilt users as well as non-Hilt users.
+ * Injection will happen if the parent type (e.g. the activity of a fragment) is an {@link
+ * dagger.hilt.android.AndroidEntryPoint} annotated class and if that parent was also injected via
+ * Hilt.
+ *
+ * @see OptionalInjectCheck
+ * @see <a href="https://dagger.dev/hilt/optional-inject">Optional injection</a>
+ */
+@Target(ElementType.TYPE)
+public @interface OptionalInject {}
diff --git a/java/dagger/hilt/android/migration/OptionalInjectCheck.java b/java/dagger/hilt/android/migration/OptionalInjectCheck.java
new file mode 100644
index 000000000..f9d0ad8f2
--- /dev/null
+++ b/java/dagger/hilt/android/migration/OptionalInjectCheck.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.migration;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import android.view.View;
+import androidx.activity.ComponentActivity;
+import dagger.hilt.android.internal.migration.InjectedByHilt;
+import dagger.hilt.internal.Preconditions;
+
+/**
+ * Utility methods for validating if an {@link dagger.hilt.android.AndroidEntryPoint}-annotated
+ * class that is also annotated with {@link OptionalInject} was injected by Hilt.
+ *
+ * @see OptionalInject
+ */
+public final class OptionalInjectCheck {
+
+ /**
+ * Returns true if the Activity was injected by Hilt.
+ *
+ * @throws IllegalArgumentException if the given instance is not an AndroidEntryPoint nor is
+ * annotated with {@link OptionalInject}.
+ */
+ public static boolean wasInjectedByHilt(@NonNull ComponentActivity activity) {
+ return check(activity);
+ }
+
+ /**
+ * Returns true if the BroadcastReceiver was injected by Hilt.
+ *
+ * @throws IllegalArgumentException if the given instance is not an AndroidEntryPoint nor is
+ * annotated with {@link OptionalInject}.
+ */
+ public static boolean wasInjectedByHilt(@NonNull BroadcastReceiver broadcastReceiver) {
+ return check(broadcastReceiver);
+ }
+
+ /**
+ * Returns true if the Fragment was injected by Hilt.
+ *
+ * @throws IllegalArgumentException if the given instance is not an AndroidEntryPoint nor is
+ * annotated with {@link OptionalInject}.
+ */
+ public static boolean wasInjectedByHilt(@NonNull Fragment fragment) {
+ return check(fragment);
+ }
+
+ /**
+ * Returns true if the Service was injected by Hilt.
+ *
+ * @throws IllegalArgumentException if the given instance is not an AndroidEntryPoint nor is
+ * annotated with {@link OptionalInject}.
+ */
+ public static boolean wasInjectedByHilt(@NonNull Service service) {
+ return check(service);
+ }
+
+ /**
+ * Returns true if the View was injected by Hilt.
+ *
+ * @throws IllegalArgumentException if the given instance is not an AndroidEntryPoint nor is
+ * annotated with {@link OptionalInject}.
+ */
+ public static boolean wasInjectedByHilt(@NonNull View view) {
+ return check(view);
+ }
+
+ private static boolean check(@NonNull Object obj) {
+ Preconditions.checkNotNull(obj);
+ Preconditions.checkArgument(
+ obj instanceof InjectedByHilt,
+ "'%s' is not an optionally injected android entry point. Check that you have annotated"
+ + " the class with both @AndroidEntryPoint and @OptionalInject.",
+ obj.getClass());
+ return ((InjectedByHilt) obj).wasInjectedByHilt();
+ }
+
+ private OptionalInjectCheck() {}
+}
diff --git a/java/dagger/hilt/android/migration/package-info.java b/java/dagger/hilt/android/migration/package-info.java
new file mode 100644
index 000000000..e37225a84
--- /dev/null
+++ b/java/dagger/hilt/android/migration/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package contains Android APIs to help migrating a codebase to Hilt.
+ *
+ * @see <a href="https://dagger.dev/hilt/migration">Migration to Hilt</a>
+ */
+@ParametersAreNonnullByDefault
+package dagger.hilt.android.migration;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/java/dagger/hilt/android/package-info.java b/java/dagger/hilt/android/package-info.java
new file mode 100644
index 000000000..894f7ab47
--- /dev/null
+++ b/java/dagger/hilt/android/package-info.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package contains Hilt APIs for Android applications.
+ *
+ * <p>Hilt provides a standard way to incorporate Dagger dependency injection into an Android
+ * application.
+ *
+ * @see <a href="https://dagger.dev/hilt">Hilt Developer Docs</a>
+ */
+@ParametersAreNonnullByDefault
+package dagger.hilt.android;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/java/dagger/hilt/android/plugin/BUILD b/java/dagger/hilt/android/plugin/BUILD
new file mode 100644
index 000000000..7fd2c8377
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/BUILD
@@ -0,0 +1,27 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Description:
+# A Gradle plugin that performs a transform.
+
+package(default_visibility = ["//:src"])
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(
+ ["**/*"],
+ # Exclude Gradle build folder to enable working along side Bazel
+ exclude = ["**/build/**"],
+ ),
+)
diff --git a/java/dagger/hilt/android/plugin/build.gradle b/java/dagger/hilt/android/plugin/build.gradle
new file mode 100644
index 000000000..274ecbead
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/build.gradle
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+buildscript {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+plugins {
+ id 'org.jetbrains.kotlin.jvm' version '1.4.20'
+ id 'java-gradle-plugin'
+ id 'maven-publish'
+}
+
+repositories {
+ google()
+ jcenter()
+}
+
+configurations {
+ additionalTestPlugin {
+ canBeConsumed = false
+ canBeResolved = true
+ extendsFrom implementation
+ }
+}
+
+dependencies {
+ implementation gradleApi()
+ compileOnly 'com.android.tools.build:gradle:4.2.0-beta04'
+ // TODO(danysantiago): Make compileOnly to avoid dep for non-Kotlin projects.
+ implementation 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.20'
+ implementation 'org.javassist:javassist:3.26.0-GA'
+ implementation 'org.ow2.asm:asm:9.0'
+
+ testImplementation gradleTestKit()
+ testImplementation 'junit:junit:4.12'
+ testImplementation 'com.google.truth:truth:1.0.1'
+ additionalTestPlugin 'com.android.tools.build:gradle:4.2.0-beta04'
+}
+
+// Configure the generating task of plugin-under-test-metadata.properties to
+// include additional dependencies for the injected plugin classpath that
+// are not present in the main runtime dependencies. This allows us to test
+// the desired AGP version while keeping a compileOnly dep on the main source.
+tasks.withType(PluginUnderTestMetadata.class).named("pluginUnderTestMetadata").configure {
+ it.pluginClasspath.from(configurations.additionalTestPlugin)
+}
+
+compileKotlin {
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
+}
+
+// Create sources Jar from main kotlin sources
+tasks.register("sourcesJar", Jar).configure {
+ group = JavaBasePlugin.DOCUMENTATION_GROUP
+ description = "Assembles sources JAR"
+ classifier = "sources"
+ from(sourceSets["main"].allSource)
+}
+
+// Create javadoc Jar. The jar is empty since we don't really have docs
+// for this plugin but this is required to upload to Sonatype.
+// https://central.sonatype.org/pages/requirements.html#supply-javadoc-and-sources
+tasks.register("javadocJar", Jar).configure {
+ group = JavaBasePlugin.DOCUMENTATION_GROUP
+ description = "Assembles javadoc JAR"
+ classifier = "javadoc"
+}
+
+// Disable Gradle metadata publication.
+tasks.withType(GenerateModuleMetadata) {
+ enabled = false
+}
+
+// TODO(danysantiago): Use POM template in tools/ to avoid duplicating lines.
+publishing {
+ publications {
+ plugin(MavenPublication) {
+ artifactId = 'hilt-android-gradle-plugin'
+ def publishVersion = findProperty("PublishVersion")
+ version = (publishVersion != null) ? publishVersion : "LOCAL-SNAPSHOT"
+ from components.kotlin
+ artifact(sourcesJar)
+ artifact(javadocJar)
+ pom {
+ name = 'Hilt Android Gradle Plugin'
+ description = 'A fast dependency injector for Android and Java.'
+ url = 'https://github.com/google/dagger'
+ scm {
+ url = 'https://github.com/google/dagger/'
+ connection = 'scm:git:git://github.com/google/dagger.git'
+ developerConnection = 'scm:git:ssh://git@github.com/google/dagger.git'
+ tag = 'HEAD'
+ }
+ issueManagement {
+ system = 'GitHub Issues'
+ url = 'https://github.com/google/dagger/issues'
+ }
+ licenses {
+ license {
+ name = 'Apache 2.0'
+ url = 'https://www.apache.org/licenses/LICENSE-2.0.txt'
+ }
+ }
+ organization {
+ name = 'Google, Inc.'
+ url = 'https://www.google.com'
+ }
+ withXml {
+ def projectNode = asNode()
+ // Adds:
+ // <parent>
+ // <groupId>org.sonatype.oss</groupId>
+ // <artifactId>oss-parent</artifactId>
+ // <version>7</version>
+ // </parent>
+ def parentNode = projectNode.appendNode('parent')
+ parentNode.appendNode('groupId', 'org.sonatype.oss')
+ parentNode.appendNode('artifactId', 'oss-parent')
+ parentNode.appendNode('version', '7')
+ // Adds scm->tag because for some reason the DSL API does not.
+ // <scm>
+ // <tag>HEAD</tag>
+ // </scm>
+ projectNode.get('scm').first().appendNode('tag', 'HEAD')
+ }
+ }
+ }
+ }
+ // Publish to build output repository.
+ repositories {
+ maven {
+ url = uri("$buildDir/repo")
+ }
+ }
+}
+
+group='com.google.dagger'
diff --git a/java/dagger/hilt/android/plugin/gradle/wrapper/gradle-wrapper.jar b/java/dagger/hilt/android/plugin/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..5c2d1cf01
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/java/dagger/hilt/android/plugin/gradle/wrapper/gradle-wrapper.properties b/java/dagger/hilt/android/plugin/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..4d9ca1649
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/java/dagger/hilt/android/plugin/gradlew b/java/dagger/hilt/android/plugin/gradlew
new file mode 100755
index 000000000..b0d6d0ab5
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/gradlew
@@ -0,0 +1,188 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassTransformer.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassTransformer.kt
new file mode 100644
index 000000000..e066f48a1
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassTransformer.kt
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.hilt.android.plugin
+
+import dagger.hilt.android.plugin.util.isClassFile
+import dagger.hilt.android.plugin.util.isJarFile
+import java.io.File
+import java.io.FileInputStream
+import java.util.zip.ZipInputStream
+import javassist.ClassPool
+import javassist.CtClass
+import javassist.Modifier
+import javassist.bytecode.Bytecode
+import javassist.bytecode.CodeIterator
+import javassist.bytecode.Opcode
+import org.slf4j.LoggerFactory
+
+typealias CodeArray = javassist.bytecode.ByteArray // Avoids conflict with Kotlin's stdlib ByteArray
+
+/**
+ * A helper class for performing the transform.
+ *
+ * Create it with the list of all available source directories along with the root output directory
+ * and use [AndroidEntryPointClassTransformer.transformFile] or
+ * [AndroidEntryPointClassTransformer.transformJarContents] to perform the actual transformation.
+ */
+internal class AndroidEntryPointClassTransformer(
+ val taskName: String,
+ allInputs: List<File>,
+ private val sourceRootOutputDir: File,
+ private val copyNonTransformed: Boolean
+) {
+ private val logger = LoggerFactory.getLogger(AndroidEntryPointClassTransformer::class.java)
+
+ // A ClassPool created from the given input files, this allows us to use the higher
+ // level Javaassit APIs, but requires class parsing/loading.
+ private val classPool: ClassPool = ClassPool(true).also { pool ->
+ allInputs.forEach {
+ pool.appendClassPath(it.path)
+ }
+ }
+
+ init {
+ sourceRootOutputDir.mkdirs()
+ }
+
+ /**
+ * Transforms the classes inside the jar and copies re-written class files if and only if they are
+ * transformed.
+ *
+ * @param inputFile The jar file to transform, must be a jar.
+ * @return true if at least one class within the jar was transformed.
+ */
+ fun transformJarContents(inputFile: File): Boolean {
+ require(inputFile.isJarFile()) {
+ "Invalid file, '$inputFile' is not a jar."
+ }
+ // Validate transform is not applied to a jar when copying is enabled, meaning the transformer
+ // is being used in the Android transform API pipeline which does not need to transform jars
+ // and handles copying them.
+ check(!copyNonTransformed) {
+ "Transforming a jar is not supported with 'copyNonTransformed'."
+ }
+ var transformed = false
+ ZipInputStream(FileInputStream(inputFile)).use { input ->
+ var entry = input.nextEntry
+ while (entry != null) {
+ if (entry.isClassFile()) {
+ val clazz = classPool.makeClass(input, false)
+ transformed = transformClassToOutput(clazz) || transformed
+ }
+ entry = input.nextEntry
+ }
+ }
+ return transformed
+ }
+
+ /**
+ * Transform a single class file.
+ *
+ * @param inputFile The file to transform, must be a class file.
+ * @return true if the class file was transformed.
+ */
+ fun transformFile(inputFile: File): Boolean {
+ check(inputFile.isClassFile()) {
+ "Invalid file, '$inputFile' is not a class."
+ }
+ val clazz = inputFile.inputStream().use { classPool.makeClass(it, false) }
+ return transformClassToOutput(clazz)
+ }
+
+ private fun transformClassToOutput(clazz: CtClass): Boolean {
+ val transformed = transformClass(clazz)
+ if (transformed || copyNonTransformed) {
+ clazz.writeFile(sourceRootOutputDir.path)
+ }
+ return transformed
+ }
+
+ private fun transformClass(clazz: CtClass): Boolean {
+ if (ANDROID_ENTRY_POINT_ANNOTATIONS.none { clazz.hasAnnotation(it) }) {
+ // Not a Android entry point annotated class, don't do anything.
+ return false
+ }
+
+ // TODO(danysantiago): Handle classes with '$' in their name if they do become an issue.
+ val superclassName = clazz.classFile.superclass
+ val entryPointSuperclassName =
+ clazz.packageName + ".Hilt_" + clazz.simpleName.replace("$", "_")
+ logger.info(
+ "[$taskName] Transforming ${clazz.name} to extend $entryPointSuperclassName instead of " +
+ "$superclassName."
+ )
+ val entryPointSuperclass = classPool.get(entryPointSuperclassName)
+ clazz.superclass = entryPointSuperclass
+ transformSuperMethodCalls(clazz, superclassName, entryPointSuperclassName)
+
+ // Check if Hilt generated class is a BroadcastReceiver with the marker field which means
+ // a super.onReceive invocation has to be inserted in the implementation.
+ if (entryPointSuperclass.declaredFields.any { it.name == "onReceiveBytecodeInjectionMarker" }) {
+ transformOnReceive(clazz, entryPointSuperclassName)
+ }
+
+ return true
+ }
+
+ /**
+ * Iterates over each declared method, finding in its bodies super calls. (e.g. super.onCreate())
+ * and rewrites the method reference of the invokespecial instruction to one that uses the new
+ * superclass.
+ *
+ * The invokespecial instruction is emitted for code that between other things also invokes a
+ * method of a superclass of the current class. The opcode invokespecial takes two operands, each
+ * of 8 bit, that together represent an address in the constant pool to a method reference. The
+ * method reference is computed at compile-time by looking the direct superclass declaration, but
+ * at runtime the code behaves like invokevirtual, where as the actual method invoked is looked up
+ * based on the class hierarchy.
+ *
+ * However, it has been observed that on APIs 19 to 22 the Android Runtime (ART) jumps over the
+ * direct superclass and into the method reference class, causing unexpected behaviours.
+ * Therefore, this method performs the additional transformation to rewrite direct super call
+ * invocations to use a method reference whose class in the pool is the new superclass. Note that
+ * this is not necessary for constructor calls since the Javassist library takes care of those.
+ *
+ * @see: https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-6.html#jvms-6.5.invokespecial
+ * @see: https://source.android.com/devices/tech/dalvik/dalvik-bytecode
+ */
+ private fun transformSuperMethodCalls(
+ clazz: CtClass,
+ oldSuperclassName: String,
+ newSuperclassName: String
+ ) {
+ val constantPool = clazz.classFile.constPool
+ clazz.declaredMethods
+ .filter {
+ it.methodInfo.isMethod &&
+ !Modifier.isStatic(it.modifiers) &&
+ !Modifier.isAbstract(it.modifiers) &&
+ !Modifier.isNative(it.modifiers)
+ }
+ .forEach { method ->
+ val codeAttr = method.methodInfo.codeAttribute
+ val code = codeAttr.code
+ codeAttr.iterator().forEachInstruction { index, opcode ->
+ // We are only interested in 'invokespecial' instructions.
+ if (opcode != Opcode.INVOKESPECIAL) {
+ return@forEachInstruction
+ }
+ // If the method reference of the instruction is not using the old superclass then we
+ // should not rewrite it.
+ val methodRef = CodeArray.readU16bit(code, index + 1)
+ val currentClassRef = constantPool.getMethodrefClassName(methodRef)
+ if (currentClassRef != oldSuperclassName) {
+ return@forEachInstruction
+ }
+ val nameAndTypeRef = constantPool.getMethodrefNameAndType(methodRef)
+ val newSuperclassRef = constantPool.addClassInfo(newSuperclassName)
+ val newMethodRef = constantPool.addMethodrefInfo(newSuperclassRef, nameAndTypeRef)
+ logger.info(
+ "[$taskName] Redirecting an invokespecial in " +
+ "${clazz.name}.${method.name}:${method.signature} at code index $index from " +
+ "method ref #$methodRef to #$newMethodRef."
+ )
+ CodeArray.write16bit(newMethodRef, code, index + 1)
+ }
+ }
+ }
+
+ // Iterate over each instruction in a CodeIterator.
+ private fun CodeIterator.forEachInstruction(body: CodeIterator.(Int, Int) -> Unit) {
+ while (hasNext()) {
+ val index = next()
+ this.body(index, byteAt(index))
+ }
+ }
+
+ /**
+ * For a BroadcastReceiver insert a super call in the onReceive method implementation since
+ * after the class is transformed onReceive will no longer be abstract (it is implemented by
+ * Hilt generated receiver).
+ */
+ private fun transformOnReceive(clazz: CtClass, entryPointSuperclassName: String) {
+ val method = clazz.declaredMethods.first {
+ it.name + it.signature == ON_RECEIVE_METHOD_NAME + ON_RECEIVE_METHOD_SIGNATURE
+ }
+ val constantPool = clazz.classFile.constPool
+ val newCode = Bytecode(constantPool).apply {
+ addAload(0) // Loads 'this'
+ addAload(1) // Loads method param 1 (Context)
+ addAload(2) // Loads method param 2 (Intent)
+ addInvokespecial(
+ entryPointSuperclassName, ON_RECEIVE_METHOD_NAME, ON_RECEIVE_METHOD_SIGNATURE
+ )
+ }
+ val newCodeAttribute = newCode.toCodeAttribute()
+ val currentCodeAttribute = method.methodInfo.codeAttribute
+ currentCodeAttribute.maxStack =
+ maxOf(newCodeAttribute.maxStack, currentCodeAttribute.maxStack)
+ currentCodeAttribute.maxLocals =
+ maxOf(newCodeAttribute.maxLocals, currentCodeAttribute.maxLocals)
+ val codeIterator = currentCodeAttribute.iterator()
+ val pos = codeIterator.insertEx(newCode.get()) // insert new code
+ codeIterator.insert(newCodeAttribute.exceptionTable, pos) // offset exception table
+ method.methodInfo.rebuildStackMap(clazz.classPool) // update stack table
+ }
+
+ companion object {
+ val ANDROID_ENTRY_POINT_ANNOTATIONS = setOf(
+ "dagger.hilt.android.AndroidEntryPoint",
+ "dagger.hilt.android.HiltAndroidApp"
+ )
+ val ON_RECEIVE_METHOD_NAME = "onReceive"
+ val ON_RECEIVE_METHOD_SIGNATURE =
+ "(Landroid/content/Context;Landroid/content/Intent;)V"
+ }
+}
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassVisitor.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassVisitor.kt
new file mode 100644
index 000000000..015bb7623
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassVisitor.kt
@@ -0,0 +1,197 @@
+package dagger.hilt.android.plugin
+
+import com.android.build.api.instrumentation.AsmClassVisitorFactory
+import com.android.build.api.instrumentation.ClassContext
+import com.android.build.api.instrumentation.ClassData
+import com.android.build.api.instrumentation.InstrumentationParameters
+import java.io.File
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Input
+import org.objectweb.asm.ClassReader
+import org.objectweb.asm.ClassVisitor
+import org.objectweb.asm.FieldVisitor
+import org.objectweb.asm.MethodVisitor
+import org.objectweb.asm.Opcodes
+
+/**
+ * ASM Adapter that transforms @AndroidEntryPoint-annotated classes to extend the Hilt
+ * generated android class, including the @HiltAndroidApp application class.
+ */
+@Suppress("UnstableApiUsage")
+class AndroidEntryPointClassVisitor(
+ private val apiVersion: Int,
+ nextClassVisitor: ClassVisitor,
+ private val additionalClasses: File
+) : ClassVisitor(apiVersion, nextClassVisitor) {
+
+ interface AndroidEntryPointParams : InstrumentationParameters {
+ @get:Input
+ val additionalClassesDir: Property<File>
+ }
+
+ abstract class Factory : AsmClassVisitorFactory<AndroidEntryPointParams> {
+ override fun createClassVisitor(
+ classContext: ClassContext,
+ nextClassVisitor: ClassVisitor
+ ): ClassVisitor {
+ return AndroidEntryPointClassVisitor(
+ apiVersion = instrumentationContext.apiVersion.get(),
+ nextClassVisitor = nextClassVisitor,
+ additionalClasses = parameters.get().additionalClassesDir.get()
+ )
+ }
+
+ /**
+ * Check if a class should be transformed.
+ *
+ * Only classes that are an Android entry point should be transformed.
+ */
+ override fun isInstrumentable(classData: ClassData) =
+ classData.classAnnotations.any { ANDROID_ENTRY_POINT_ANNOTATIONS.contains(it) }
+ }
+
+ // The name of the Hilt generated superclass in it internal form.
+ // e.g. "my/package/Hilt_MyActivity"
+ lateinit var newSuperclassName: String
+
+ lateinit var oldSuperclassName: String
+
+ override fun visit(
+ version: Int,
+ access: Int,
+ name: String,
+ signature: String?,
+ superName: String?,
+ interfaces: Array<out String>?
+ ) {
+ val packageName = name.substringBeforeLast('/')
+ val className = name.substringAfterLast('/')
+ newSuperclassName =
+ packageName + "/Hilt_" + className.replace("$", "_")
+ oldSuperclassName = superName ?: error { "Superclass of $name is null!" }
+ super.visit(version, access, name, signature, newSuperclassName, interfaces)
+ }
+
+ override fun visitMethod(
+ access: Int,
+ name: String,
+ descriptor: String,
+ signature: String?,
+ exceptions: Array<out String>?
+ ): MethodVisitor {
+ val nextMethodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions)
+ val invokeSpecialVisitor = InvokeSpecialAdapter(apiVersion, nextMethodVisitor)
+ if (name == ON_RECEIVE_METHOD_NAME &&
+ descriptor == ON_RECEIVE_METHOD_DESCRIPTOR &&
+ hasOnReceiveBytecodeInjectionMarker()
+ ) {
+ return OnReceiveAdapter(apiVersion, invokeSpecialVisitor)
+ }
+ return invokeSpecialVisitor
+ }
+
+ /**
+ * Adapter for super calls (e.g. super.onCreate()) that rewrites the owner reference of the
+ * invokespecial instruction to use the new superclass.
+ *
+ * The invokespecial instruction is emitted for code that between other things also invokes a
+ * method of a superclass of the current class. The opcode invokespecial takes two operands, each
+ * of 8 bit, that together represent an address in the constant pool to a method reference. The
+ * method reference is computed at compile-time by looking the direct superclass declaration, but
+ * at runtime the code behaves like invokevirtual, where as the actual method invoked is looked up
+ * based on the class hierarchy.
+ *
+ * However, it has been observed that on APIs 19 to 22 the Android Runtime (ART) jumps over the
+ * direct superclass and into the method reference class, causing unexpected behaviours.
+ * Therefore, this method performs the additional transformation to rewrite direct super call
+ * invocations to use a method reference whose class in the pool is the new superclass. Note that
+ * this is not necessary for constructor calls since the Javassist library takes care of those.
+ *
+ * @see: https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-6.html#jvms-6.5.invokespecial
+ * @see: https://source.android.com/devices/tech/dalvik/dalvik-bytecode
+ */
+ inner class InvokeSpecialAdapter(
+ apiVersion: Int,
+ nextClassVisitor: MethodVisitor
+ ) : MethodVisitor(apiVersion, nextClassVisitor) {
+ override fun visitMethodInsn(
+ opcode: Int,
+ owner: String,
+ name: String,
+ descriptor: String,
+ isInterface: Boolean
+ ) {
+ if (opcode == Opcodes.INVOKESPECIAL && owner == oldSuperclassName) {
+ // Update the owner of all INVOKESPECIAL instructions, including those found in
+ // constructors.
+ super.visitMethodInsn(opcode, newSuperclassName, name, descriptor, isInterface)
+ } else {
+ super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
+ }
+ }
+ }
+
+ /**
+ * Method adapter for a BroadcastReceiver's onReceive method to insert a super call since with
+ * its new superclass, onReceive will no longer be abstract (it is implemented by Hilt generated
+ * receiver).
+ */
+ inner class OnReceiveAdapter(
+ apiVersion: Int,
+ nextClassVisitor: MethodVisitor
+ ) : MethodVisitor(apiVersion, nextClassVisitor) {
+ override fun visitCode() {
+ super.visitCode()
+ super.visitIntInsn(Opcodes.ALOAD, 0) // Load 'this'
+ super.visitIntInsn(Opcodes.ALOAD, 1) // Load method param 1 (Context)
+ super.visitIntInsn(Opcodes.ALOAD, 2) // Load method param 2 (Intent)
+ super.visitMethodInsn(
+ Opcodes.INVOKESPECIAL,
+ newSuperclassName,
+ ON_RECEIVE_METHOD_NAME,
+ ON_RECEIVE_METHOD_DESCRIPTOR,
+ false
+ )
+ }
+ }
+
+ /**
+ * Check if Hilt generated class is a BroadcastReceiver with the marker field which means
+ * a super.onReceive invocation has to be inserted in the implementation.
+ */
+ private fun hasOnReceiveBytecodeInjectionMarker() =
+ findAdditionalClassFile(newSuperclassName).inputStream().use {
+ var hasMarker = false
+ ClassReader(it).accept(
+ object : ClassVisitor(apiVersion) {
+ override fun visitField(
+ access: Int,
+ name: String,
+ descriptor: String,
+ signature: String?,
+ value: Any?
+ ): FieldVisitor? {
+ if (name == "onReceiveBytecodeInjectionMarker") {
+ hasMarker = true
+ }
+ return null
+ }
+ },
+ ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES
+ )
+ return@use hasMarker
+ }
+
+ private fun findAdditionalClassFile(className: String) =
+ File(additionalClasses, "$className.class")
+
+ companion object {
+ val ANDROID_ENTRY_POINT_ANNOTATIONS = setOf(
+ "dagger.hilt.android.AndroidEntryPoint",
+ "dagger.hilt.android.HiltAndroidApp"
+ )
+ const val ON_RECEIVE_METHOD_NAME = "onReceive"
+ const val ON_RECEIVE_METHOD_DESCRIPTOR =
+ "(Landroid/content/Context;Landroid/content/Intent;)V"
+ }
+}
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointTransform.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointTransform.kt
new file mode 100644
index 000000000..9bb5160b6
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointTransform.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.plugin
+
+import com.android.build.api.transform.DirectoryInput
+import com.android.build.api.transform.Format
+import com.android.build.api.transform.JarInput
+import com.android.build.api.transform.QualifiedContent
+import com.android.build.api.transform.Status
+import com.android.build.api.transform.Transform
+import com.android.build.api.transform.TransformInput
+import com.android.build.api.transform.TransformInvocation
+import dagger.hilt.android.plugin.util.isClassFile
+import java.io.File
+
+/**
+ * Bytecode transformation to make @AndroidEntryPoint annotated classes extend the Hilt
+ * generated android classes, including the @HiltAndroidApp application class.
+ *
+ * A transform receives input as a collection [TransformInput], which is composed of [JarInput]s and
+ * [DirectoryInput]s. The resulting files must be placed in the
+ * [TransformInvocation.getOutputProvider]. The bytecode transformation can be done with any library
+ * (in our case Javaassit). The [QualifiedContent.Scope] defined in a transform defines the input
+ * the transform will receive and if it can be applied to only the Android application projects or
+ * Android libraries too.
+ *
+ * See: [TransformPublic Docs](https://google.github.io/android-gradle-dsl/javadoc/current/com/android/build/api/transform/Transform.html)
+ */
+class AndroidEntryPointTransform : Transform() {
+ // The name of the transform. This name appears as a gradle task.
+ override fun getName() = "AndroidEntryPointTransform"
+
+ // The type of input this transform will handle.
+ override fun getInputTypes() = setOf(QualifiedContent.DefaultContentType.CLASSES)
+
+ override fun isIncremental() = true
+
+ // The project scope this transform is applied to.
+ override fun getScopes() = mutableSetOf(QualifiedContent.Scope.PROJECT)
+
+ /**
+ * Performs the transformation of the bytecode.
+ *
+ * The inputs will be available in the [TransformInvocation] along with referenced inputs that
+ * should not be transformed. The inputs received along with the referenced inputs depend on the
+ * scope of the transform.
+ *
+ * The invocation will also indicate if an incremental transform has to be applied or not. Even
+ * though a transform might return true in its [isIncremental] function, the invocation might
+ * return false in [TransformInvocation.isIncremental], therefore both cases must be handled.
+ */
+ override fun transform(invocation: TransformInvocation) {
+ if (!invocation.isIncremental) {
+ // Remove any lingering files on a non-incremental invocation since everything has to be
+ // transformed.
+ invocation.outputProvider.deleteAll()
+ }
+
+ invocation.inputs.forEach { transformInput ->
+ transformInput.jarInputs.forEach { jarInput ->
+ val outputJar =
+ invocation.outputProvider.getContentLocation(
+ jarInput.name,
+ jarInput.contentTypes,
+ jarInput.scopes,
+ Format.JAR
+ )
+ if (invocation.isIncremental) {
+ when (jarInput.status) {
+ Status.ADDED, Status.CHANGED -> copyJar(jarInput.file, outputJar)
+ Status.REMOVED -> outputJar.delete()
+ Status.NOTCHANGED -> {
+ // No need to transform.
+ }
+ else -> {
+ error("Unknown status: ${jarInput.status}")
+ }
+ }
+ } else {
+ copyJar(jarInput.file, outputJar)
+ }
+ }
+ transformInput.directoryInputs.forEach { directoryInput ->
+ val outputDir = invocation.outputProvider.getContentLocation(
+ directoryInput.name,
+ directoryInput.contentTypes,
+ directoryInput.scopes,
+ Format.DIRECTORY
+ )
+ val classTransformer =
+ createHiltClassTransformer(invocation.inputs, invocation.referencedInputs, outputDir)
+ if (invocation.isIncremental) {
+ directoryInput.changedFiles.forEach { (file, status) ->
+ val outputFile = toOutputFile(outputDir, directoryInput.file, file)
+ when (status) {
+ Status.ADDED, Status.CHANGED ->
+ transformFile(file, outputFile.parentFile, classTransformer)
+ Status.REMOVED -> outputFile.delete()
+ Status.NOTCHANGED -> {
+ // No need to transform.
+ }
+ else -> {
+ error("Unknown status: $status")
+ }
+ }
+ }
+ } else {
+ directoryInput.file.walkTopDown().forEach { file ->
+ val outputFile = toOutputFile(outputDir, directoryInput.file, file)
+ transformFile(file, outputFile.parentFile, classTransformer)
+ }
+ }
+ }
+ }
+ }
+
+ // Create a transformer given an invocation inputs. Note that since this is a PROJECT scoped
+ // transform the actual transformation is only done on project files and not its dependencies.
+ private fun createHiltClassTransformer(
+ inputs: Collection<TransformInput>,
+ referencedInputs: Collection<TransformInput>,
+ outputDir: File
+ ): AndroidEntryPointClassTransformer {
+ val classFiles = (inputs + referencedInputs).flatMap { input ->
+ (input.directoryInputs + input.jarInputs).map { it.file }
+ }
+ return AndroidEntryPointClassTransformer(
+ taskName = name,
+ allInputs = classFiles,
+ sourceRootOutputDir = outputDir,
+ copyNonTransformed = true
+ )
+ }
+
+ // Transform a single file. If the file is not a class file it is just copied to the output dir.
+ private fun transformFile(
+ inputFile: File,
+ outputDir: File,
+ transformer: AndroidEntryPointClassTransformer
+ ) {
+ if (inputFile.isClassFile()) {
+ transformer.transformFile(inputFile)
+ } else if (inputFile.isFile) {
+ // Copy all non .class files to the output.
+ outputDir.mkdirs()
+ val outputFile = File(outputDir, inputFile.name)
+ inputFile.copyTo(target = outputFile, overwrite = true)
+ }
+ }
+
+ // We are only interested in project compiled classes but we have to copy received jars to the
+ // output.
+ private fun copyJar(inputJar: File, outputJar: File) {
+ outputJar.parentFile?.mkdirs()
+ inputJar.copyTo(target = outputJar, overwrite = true)
+ }
+
+ private fun toOutputFile(outputDir: File, inputDir: File, inputFile: File) =
+ File(outputDir, inputFile.relativeTo(inputDir).path)
+}
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltExtension.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltExtension.kt
new file mode 100644
index 000000000..7a33836dc
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltExtension.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.hilt.android.plugin
+
+/**
+ * Configuration options for the Hilt Gradle Plugin
+ */
+interface HiltExtension {
+
+ /**
+ * If set to `true`, Hilt will adjust the compile classpath such that it includes transitive
+ * dependencies, ignoring `api` or `implementation` boundaries during compilation. You should
+ * enable this option if your project has multiple level of transitive dependencies that contain
+ * injected classes or entry points.
+ *
+ * Enabling this option also requires android.lintOptions.checkReleaseBuilds to be set to 'false'
+ * if the Android Gradle Plugin version being used is less than 7.0.
+ *
+ * See https://github.com/google/dagger/issues/1991 for more context.
+ */
+ var enableExperimentalClasspathAggregation: Boolean
+
+ /**
+ * If set to `true`, Hilt will register a transform task that will rewrite `@AndroidEntryPoint`
+ * annotated classes before the host-side JVM tests run. You should enable this option if you are
+ * running Robolectric UI tests as part of your JUnit tests.
+ *
+ * This flag is not necessary if when com.android.tools.build:gradle:4.2.0+ is used and will be
+ * deprecated in a future version.
+ */
+ var enableTransformForLocalTests: Boolean
+}
+
+internal open class HiltExtensionImpl : HiltExtension {
+ override var enableExperimentalClasspathAggregation: Boolean = false
+ override var enableTransformForLocalTests: Boolean = false
+}
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt
new file mode 100644
index 000000000..e26edb5fd
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.plugin
+
+import com.android.build.api.component.Component
+import com.android.build.api.extension.AndroidComponentsExtension
+import com.android.build.api.instrumentation.FramesComputationMode
+import com.android.build.api.instrumentation.InstrumentationScope
+import com.android.build.gradle.AppExtension
+import com.android.build.gradle.BaseExtension
+import com.android.build.gradle.LibraryExtension
+import com.android.build.gradle.TestExtension
+import com.android.build.gradle.TestedExtension
+import com.android.build.gradle.api.AndroidBasePlugin
+import com.android.build.gradle.api.BaseVariant
+import com.android.build.gradle.api.TestVariant
+import com.android.build.gradle.api.UnitTestVariant
+import dagger.hilt.android.plugin.util.CopyTransform
+import dagger.hilt.android.plugin.util.SimpleAGPVersion
+import java.io.File
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.artifacts.component.ProjectComponentIdentifier
+import org.gradle.api.attributes.Attribute
+
+/**
+ * A Gradle plugin that checks if the project is an Android project and if so, registers a
+ * bytecode transformation.
+ *
+ * The plugin also passes an annotation processor option to disable superclass validation for
+ * classes annotated with `@AndroidEntryPoint` since the registered transform by this plugin will
+ * update the superclass.
+ */
+class HiltGradlePlugin : Plugin<Project> {
+ override fun apply(project: Project) {
+ var configured = false
+ project.plugins.withType(AndroidBasePlugin::class.java) {
+ configured = true
+ configureHilt(project)
+ }
+ project.afterEvaluate {
+ check(configured) {
+ // Check if configuration was applied, if not inform the developer they have applied the
+ // plugin to a non-android project.
+ "The Hilt Android Gradle plugin can only be applied to an Android project."
+ }
+ verifyDependencies(it)
+ }
+ }
+
+ private fun configureHilt(project: Project) {
+ val hiltExtension = project.extensions.create(
+ HiltExtension::class.java, "hilt", HiltExtensionImpl::class.java
+ )
+ configureCompileClasspath(project, hiltExtension)
+ if (SimpleAGPVersion.ANDROID_GRADLE_PLUGIN_VERSION < SimpleAGPVersion(4, 2)) {
+ // Configures bytecode transform using older APIs pre AGP 4.2
+ configureTransform(project, hiltExtension)
+ } else {
+ // Configures bytecode transform using AGP 4.2 ASM pipeline.
+ configureTransformASM(project, hiltExtension)
+ }
+ configureProcessorFlags(project)
+ }
+
+ private fun configureCompileClasspath(project: Project, hiltExtension: HiltExtension) {
+ val androidExtension = project.extensions.findByType(BaseExtension::class.java)
+ ?: throw error("Android BaseExtension not found.")
+ when (androidExtension) {
+ is AppExtension -> {
+ // For an app project we configure the app variant and both androidTest and test variants,
+ // Hilt components are generated in all of them.
+ androidExtension.applicationVariants.all {
+ configureVariantCompileClasspath(project, hiltExtension, androidExtension, it)
+ }
+ androidExtension.testVariants.all {
+ configureVariantCompileClasspath(project, hiltExtension, androidExtension, it)
+ }
+ androidExtension.unitTestVariants.all {
+ configureVariantCompileClasspath(project, hiltExtension, androidExtension, it)
+ }
+ }
+ is LibraryExtension -> {
+ // For a library project, only the androidTest and test variant are configured since
+ // Hilt components are not generated in a library.
+ androidExtension.testVariants.all {
+ configureVariantCompileClasspath(project, hiltExtension, androidExtension, it)
+ }
+ androidExtension.unitTestVariants.all {
+ configureVariantCompileClasspath(project, hiltExtension, androidExtension, it)
+ }
+ }
+ is TestExtension -> {
+ androidExtension.applicationVariants.all {
+ configureVariantCompileClasspath(project, hiltExtension, androidExtension, it)
+ }
+ }
+ else -> error(
+ "Hilt plugin is unable to configure the compile classpath for project with extension " +
+ "'$androidExtension'"
+ )
+ }
+
+ project.dependencies.apply {
+ registerTransform(CopyTransform::class.java) { spec ->
+ // Java/Kotlin library projects offer an artifact of type 'jar'.
+ spec.from.attribute(ARTIFACT_TYPE_ATTRIBUTE, "jar")
+ // Android library projects (with or without Kotlin) offer an artifact of type
+ // 'processed-jar', which AGP can offer as a jar.
+ spec.from.attribute(ARTIFACT_TYPE_ATTRIBUTE, "processed-jar")
+ spec.to.attribute(ARTIFACT_TYPE_ATTRIBUTE, DAGGER_ARTIFACT_TYPE_VALUE)
+ }
+ }
+ }
+
+ private fun configureVariantCompileClasspath(
+ project: Project,
+ hiltExtension: HiltExtension,
+ androidExtension: BaseExtension,
+ variant: BaseVariant
+ ) {
+ if (!hiltExtension.enableExperimentalClasspathAggregation) {
+ // Option is not enabled, don't configure compile classpath. Note that the option can't be
+ // checked earlier (before iterating over the variants) since it would have been too early for
+ // the value to be populated from the build file.
+ return
+ }
+
+ if (androidExtension.lintOptions.isCheckReleaseBuilds &&
+ SimpleAGPVersion.ANDROID_GRADLE_PLUGIN_VERSION < SimpleAGPVersion(7, 0)
+ ) {
+ // Sadly we have to ask users to disable lint when enableExperimentalClasspathAggregation is
+ // set to true and they are not in AGP 7.0+ since Lint will cause issues during the
+ // configuration phase. See b/158753935 and b/160392650
+ error(
+ "Invalid Hilt plugin configuration: When 'enableExperimentalClasspathAggregation' is " +
+ "enabled 'android.lintOptions.checkReleaseBuilds' has to be set to false unless " +
+ "com.android.tools.build:gradle:7.0.0+ is used."
+ )
+ }
+
+ if (
+ listOf(
+ "android.injected.build.model.only", // Sent by AS 1.0 only
+ "android.injected.build.model.only.advanced", // Sent by AS 1.1+
+ "android.injected.build.model.only.versioned", // Sent by AS 2.4+
+ "android.injected.build.model.feature.full.dependencies", // Sent by AS 2.4+
+ "android.injected.build.model.v2", // Sent by AS 4.2+
+ ).any { project.properties.containsKey(it) }
+ ) {
+ // Do not configure compile classpath when AndroidStudio is building the model (syncing)
+ // otherwise it will cause a freeze.
+ return
+ }
+
+ val runtimeConfiguration = if (variant is TestVariant) {
+ // For Android test variants, the tested runtime classpath is used since the test app has
+ // tested dependencies removed.
+ variant.testedVariant.runtimeConfiguration
+ } else {
+ variant.runtimeConfiguration
+ }
+ val artifactView = runtimeConfiguration.incoming.artifactView { view ->
+ view.attributes.attribute(ARTIFACT_TYPE_ATTRIBUTE, DAGGER_ARTIFACT_TYPE_VALUE)
+ view.componentFilter { identifier ->
+ // Filter out the project's classes from the aggregated view since this can cause
+ // issues with Kotlin internal members visibility. b/178230629
+ if (identifier is ProjectComponentIdentifier) {
+ identifier.projectName != project.name
+ } else {
+ true
+ }
+ }
+ }
+
+ // CompileOnly config names don't follow the usual convention:
+ // <Variant Name> -> <Config Name>
+ // debug -> debugCompileOnly
+ // debugAndroidTest -> androidTestDebugCompileOnly
+ // debugUnitTest -> testDebugCompileOnly
+ // release -> releaseCompileOnly
+ // releaseUnitTest -> testReleaseCompileOnly
+ val compileOnlyConfigName = when (variant) {
+ is TestVariant ->
+ "androidTest${variant.name.substringBeforeLast("AndroidTest").capitalize()}CompileOnly"
+ is UnitTestVariant ->
+ "test${variant.name.substringBeforeLast("UnitTest").capitalize()}CompileOnly"
+ else ->
+ "${variant.name}CompileOnly"
+ }
+ project.dependencies.add(compileOnlyConfigName, artifactView.files)
+ }
+
+ @Suppress("UnstableApiUsage")
+ private fun configureTransformASM(project: Project, hiltExtension: HiltExtension) {
+ var warnAboutLocalTestsFlag = false
+ fun registerTransform(androidComponent: Component) {
+ if (hiltExtension.enableTransformForLocalTests && !warnAboutLocalTestsFlag) {
+ project.logger.warn(
+ "The Hilt configuration option 'enableTransformForLocalTests' is no longer necessary " +
+ "when com.android.tools.build:gradle:4.2.0+ is used."
+ )
+ warnAboutLocalTestsFlag = true
+ }
+ androidComponent.transformClassesWith(
+ classVisitorFactoryImplClass = AndroidEntryPointClassVisitor.Factory::class.java,
+ scope = InstrumentationScope.PROJECT
+ ) { params ->
+ val classesDir =
+ File(project.buildDir, "intermediates/javac/${androidComponent.name}/classes")
+ params.additionalClassesDir.set(classesDir)
+ }
+ androidComponent.setAsmFramesComputationMode(
+ FramesComputationMode.COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS
+ )
+ }
+
+ val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
+ androidComponents.onVariants { registerTransform(it) }
+ androidComponents.androidTests { registerTransform(it) }
+ androidComponents.unitTests { registerTransform(it) }
+ }
+
+ private fun configureTransform(project: Project, hiltExtension: HiltExtension) {
+ val androidExtension = project.extensions.findByType(BaseExtension::class.java)
+ ?: throw error("Android BaseExtension not found.")
+ androidExtension.registerTransform(AndroidEntryPointTransform())
+
+ // Create and configure a task for applying the transform for host-side unit tests. b/37076369
+ val testedExtensions = project.extensions.findByType(TestedExtension::class.java)
+ testedExtensions?.unitTestVariants?.all { unitTestVariant ->
+ HiltTransformTestClassesTask.create(
+ project = project,
+ unitTestVariant = unitTestVariant,
+ extension = hiltExtension
+ )
+ }
+ }
+
+ private fun configureProcessorFlags(project: Project) {
+ val androidExtension = project.extensions.findByType(BaseExtension::class.java)
+ ?: throw error("Android BaseExtension not found.")
+ // Pass annotation processor flag to disable @AndroidEntryPoint superclass validation.
+ androidExtension.defaultConfig.apply {
+ javaCompileOptions.apply {
+ annotationProcessorOptions.apply {
+ PROCESSOR_OPTIONS.forEach { (key, value) -> argument(key, value) }
+ }
+ }
+ }
+ }
+
+ private fun verifyDependencies(project: Project) {
+ // If project is already failing, skip verification since dependencies might not be resolved.
+ if (project.state.failure != null) {
+ return
+ }
+ val dependencies = project.configurations.flatMap { configuration ->
+ configuration.dependencies.map { dependency -> dependency.group to dependency.name }
+ }
+ if (!dependencies.contains(LIBRARY_GROUP to "hilt-android")) {
+ error(missingDepError("$LIBRARY_GROUP:hilt-android"))
+ }
+ if (!dependencies.contains(LIBRARY_GROUP to "hilt-android-compiler") &&
+ !dependencies.contains(LIBRARY_GROUP to "hilt-compiler")
+ ) {
+ error(missingDepError("$LIBRARY_GROUP:hilt-compiler"))
+ }
+ }
+
+ companion object {
+ val ARTIFACT_TYPE_ATTRIBUTE = Attribute.of("artifactType", String::class.java)
+ const val DAGGER_ARTIFACT_TYPE_VALUE = "jar-for-dagger"
+
+ const val LIBRARY_GROUP = "com.google.dagger"
+ val PROCESSOR_OPTIONS = listOf(
+ "dagger.fastInit" to "enabled",
+ "dagger.hilt.android.internal.disableAndroidSuperclassValidation" to "true"
+ )
+ val missingDepError: (String) -> String = { depCoordinate ->
+ "The Hilt Android Gradle plugin is applied but no $depCoordinate dependency was found."
+ }
+ }
+}
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltTransformTestClassesTask.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltTransformTestClassesTask.kt
new file mode 100644
index 000000000..84b35b1c7
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltTransformTestClassesTask.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.hilt.android.plugin
+
+import com.android.build.gradle.api.UnitTestVariant
+import dagger.hilt.android.plugin.util.isClassFile
+import dagger.hilt.android.plugin.util.isJarFile
+import java.io.File
+import javax.inject.Inject
+import org.gradle.api.Action
+import org.gradle.api.DefaultTask
+import org.gradle.api.Project
+import org.gradle.api.file.ConfigurableFileCollection
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.file.FileCollection
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Classpath
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.TaskAction
+import org.gradle.api.tasks.TaskProvider
+import org.gradle.api.tasks.compile.JavaCompile
+import org.gradle.api.tasks.testing.Test
+import org.gradle.workers.WorkAction
+import org.gradle.workers.WorkParameters
+import org.gradle.workers.WorkerExecutor
+import org.jetbrains.kotlin.gradle.plugin.KotlinBasePluginWrapper
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+/**
+ * Task that transform classes used by host-side unit tests. See b/37076369
+ */
+@Suppress("UnstableApiUsage")
+abstract class HiltTransformTestClassesTask @Inject constructor(
+ private val workerExecutor: WorkerExecutor
+) : DefaultTask() {
+
+ @get:Classpath
+ abstract val compiledClasses: ConfigurableFileCollection
+
+ @get:OutputDirectory
+ abstract val outputDir: DirectoryProperty
+
+ internal interface Parameters : WorkParameters {
+ val name: Property<String>
+ val compiledClasses: ConfigurableFileCollection
+ val outputDir: DirectoryProperty
+ }
+
+ abstract class WorkerAction : WorkAction<Parameters> {
+ override fun execute() {
+ val outputDir = parameters.outputDir.asFile.get()
+ outputDir.deleteRecursively()
+ outputDir.mkdirs()
+
+ val allInputs = parameters.compiledClasses.files.toList()
+ val classTransformer = AndroidEntryPointClassTransformer(
+ taskName = parameters.name.get(),
+ allInputs = allInputs,
+ sourceRootOutputDir = outputDir,
+ copyNonTransformed = false
+ )
+ // Parse the classpath in reverse so that we respect overwrites, if it ever happens.
+ allInputs.reversed().forEach {
+ if (it.isDirectory) {
+ it.walkTopDown().forEach { file ->
+ if (file.isClassFile()) {
+ classTransformer.transformFile(file)
+ }
+ }
+ } else if (it.isJarFile()) {
+ classTransformer.transformJarContents(it)
+ }
+ }
+ }
+ }
+
+ @TaskAction
+ fun transformClasses() {
+ workerExecutor.noIsolation().submit(WorkerAction::class.java) {
+ it.compiledClasses.from(compiledClasses)
+ it.outputDir.set(outputDir)
+ it.name.set(name)
+ }
+ }
+
+ internal class ConfigAction(
+ private val outputDir: File,
+ private val inputClasspath: FileCollection
+ ) : Action<HiltTransformTestClassesTask> {
+ override fun execute(transformTask: HiltTransformTestClassesTask) {
+ transformTask.description = "Transforms AndroidEntryPoint annotated classes for JUnit tests."
+ transformTask.outputDir.set(outputDir)
+ transformTask.compiledClasses.from(inputClasspath)
+ }
+ }
+
+ companion object {
+
+ private const val TASK_PREFIX = "hiltTransformFor"
+
+ fun create(
+ project: Project,
+ unitTestVariant: UnitTestVariant,
+ extension: HiltExtension
+ ) {
+ if (!extension.enableTransformForLocalTests) {
+ // Not enabled, nothing to do here.
+ return
+ }
+
+ // TODO(danysantiago): Only use project compiled sources as input, and not all dependency jars
+ // Using 'null' key to obtain the full compile classpath since we are not using the
+ // registerPreJavacGeneratedBytecode() API that would have otherwise given us a key to get
+ // a classpath up to the generated bytecode associated with the key.
+ val inputClasspath =
+ project.objects.fileCollection().from(unitTestVariant.getCompileClasspath(null))
+
+ // Find the test sources Java compile task and add its output directory into our input
+ // classpath file collection. This also makes the transform task depend on the test compile
+ // task.
+ @Suppress("UNCHECKED_CAST")
+ val testCompileTaskProvider = project.tasks.named(
+ "compile${unitTestVariant.name.capitalize()}JavaWithJavac"
+ ) as TaskProvider<JavaCompile>
+ inputClasspath.from(testCompileTaskProvider.map { it.destinationDirectory })
+
+ // Similarly, if the Kotlin plugin is configured, find the test sources Kotlin compile task
+ // and add its output directory to our input classpath file collection.
+ project.plugins.withType(KotlinBasePluginWrapper::class.java) {
+ @Suppress("UNCHECKED_CAST")
+ val kotlinCompileTaskProvider = project.tasks.named(
+ "compile${unitTestVariant.name.capitalize()}Kotlin"
+ ) as TaskProvider<KotlinCompile>
+ inputClasspath.from(kotlinCompileTaskProvider.map { it.destinationDirectory })
+ }
+
+ // Create and configure the transform task.
+ val outputDir =
+ project.buildDir.resolve("intermediates/hilt/${unitTestVariant.dirName}Output")
+ val hiltTransformProvider = project.tasks.register(
+ "$TASK_PREFIX${unitTestVariant.name.capitalize()}",
+ HiltTransformTestClassesTask::class.java,
+ ConfigAction(outputDir, inputClasspath)
+ )
+ // Map the transform task's output to a file collection.
+ val outputFileCollection =
+ project.objects.fileCollection().from(hiltTransformProvider.map { it.outputDir })
+
+ // Configure test classpath by appending the transform output file collection to the start of
+ // the test classpath so they override the original ones. This also makes test task (the one
+ // that runs the tests) depend on the transform task.
+ @Suppress("UNCHECKED_CAST")
+ val testTaskProvider = project.tasks.named(
+ "test${unitTestVariant.name.capitalize()}"
+ ) as TaskProvider<Test>
+ testTaskProvider.configure {
+ it.classpath = outputFileCollection + it.classpath
+ }
+ }
+ }
+}
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/CopyTransform.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/CopyTransform.kt
new file mode 100644
index 000000000..39a2d3a89
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/CopyTransform.kt
@@ -0,0 +1,29 @@
+package dagger.hilt.android.plugin.util
+
+import org.gradle.api.artifacts.transform.CacheableTransform
+import org.gradle.api.artifacts.transform.InputArtifact
+import org.gradle.api.artifacts.transform.TransformAction
+import org.gradle.api.artifacts.transform.TransformOutputs
+import org.gradle.api.artifacts.transform.TransformParameters
+import org.gradle.api.file.FileSystemLocation
+import org.gradle.api.provider.Provider
+import org.gradle.api.tasks.Classpath
+
+// A transform that registers the input jar file as an output and thus changing from one artifact
+// type to another.
+// TODO: Improve to only copy classes that need to be visible by Hilt & Dagger.
+@CacheableTransform
+abstract class CopyTransform : TransformAction<TransformParameters.None> {
+ @get:Classpath
+ @get:InputArtifact
+ abstract val inputArtifactProvider: Provider<FileSystemLocation>
+
+ override fun transform(outputs: TransformOutputs) {
+ val input = inputArtifactProvider.get().asFile
+ when {
+ input.isDirectory -> outputs.dir(input)
+ input.isFile -> outputs.file(input)
+ else -> error("File/directory does not exist: ${input.absolutePath}")
+ }
+ }
+}
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/Files.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/Files.kt
new file mode 100644
index 000000000..e5a101e34
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/Files.kt
@@ -0,0 +1,14 @@
+package dagger.hilt.android.plugin.util
+
+import com.android.SdkConstants
+import java.io.File
+import java.util.zip.ZipEntry
+
+/* Checks if a file is a .class file. */
+fun File.isClassFile() = this.isFile && this.extension == SdkConstants.EXT_CLASS
+
+/* Checks if a Zip entry is a .class file. */
+fun ZipEntry.isClassFile() = !this.isDirectory && this.name.endsWith(SdkConstants.DOT_CLASS)
+
+/* CHecks if a file is a .jar file. */
+fun File.isJarFile() = this.isFile && this.extension == SdkConstants.EXT_JAR
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/SimpleAGPVersion.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/SimpleAGPVersion.kt
new file mode 100644
index 000000000..158043124
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/SimpleAGPVersion.kt
@@ -0,0 +1,41 @@
+package dagger.hilt.android.plugin.util
+
+import com.android.Version
+
+/**
+ * Simple Android Gradle Plugin version class since there is no public API one. b/175816217
+ */
+internal data class SimpleAGPVersion(
+ val major: Int,
+ val minor: Int,
+) : Comparable<SimpleAGPVersion> {
+
+ override fun compareTo(other: SimpleAGPVersion): Int {
+ return compareValuesBy(
+ this,
+ other,
+ compareBy(SimpleAGPVersion::major).thenBy(SimpleAGPVersion::minor)
+ ) { it }
+ }
+
+ companion object {
+
+ val ANDROID_GRADLE_PLUGIN_VERSION by lazy { parse(Version.ANDROID_GRADLE_PLUGIN_VERSION) }
+
+ fun parse(version: String?) =
+ tryParse(version) ?: error("Unable to parse AGP version: $version")
+
+ private fun tryParse(version: String?): SimpleAGPVersion? {
+ if (version == null) {
+ return null
+ }
+
+ val parts = version.split('.')
+ if (parts.size == 1) {
+ return SimpleAGPVersion(parts[0].toInt(), 0)
+ }
+
+ return SimpleAGPVersion(parts[0].toInt(), parts[1].toInt())
+ }
+ }
+}
diff --git a/java/dagger/hilt/android/plugin/src/main/resources/META-INF/gradle-plugins/dagger.hilt.android.plugin.properties b/java/dagger/hilt/android/plugin/src/main/resources/META-INF/gradle-plugins/dagger.hilt.android.plugin.properties
new file mode 100644
index 000000000..5d2b9dfc8
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/resources/META-INF/gradle-plugins/dagger.hilt.android.plugin.properties
@@ -0,0 +1 @@
+implementation-class=dagger.hilt.android.plugin.HiltGradlePlugin \ No newline at end of file
diff --git a/java/dagger/hilt/android/plugin/src/test/data/android-libraryA/build.gradle b/java/dagger/hilt/android/plugin/src/test/data/android-libraryA/build.gradle
new file mode 100644
index 000000000..e2d2a7b55
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/android-libraryA/build.gradle
@@ -0,0 +1,28 @@
+plugins {
+ id 'com.android.library'
+ id 'dagger.hilt.android.plugin'
+}
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.2"
+
+ defaultConfig {
+ minSdkVersion 21
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+ }
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+}
+
+dependencies {
+ implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'
+ annotationProcessor 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'
+
+ implementation project(':libraryB')
+ implementation project(':libraryC')
+} \ No newline at end of file
diff --git a/java/dagger/hilt/android/plugin/src/test/data/android-libraryA/src/main/AndroidManifest.xml b/java/dagger/hilt/android/plugin/src/test/data/android-libraryA/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..e704d4ad2
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/android-libraryA/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="liba">
+</manifest> \ No newline at end of file
diff --git a/java/dagger/hilt/android/plugin/src/test/data/android-libraryA/src/main/java/liba/LibraryA.java b/java/dagger/hilt/android/plugin/src/test/data/android-libraryA/src/main/java/liba/LibraryA.java
new file mode 100644
index 000000000..95e9356fd
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/android-libraryA/src/main/java/liba/LibraryA.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package liba;
+
+import javax.inject.Inject;
+import libb.LibraryB;
+import libc.LibraryC;
+
+/** Test LibA */
+public class LibraryA {
+ @Inject
+ public LibraryA(LibraryB b, LibraryC c) {}
+}
diff --git a/java/dagger/hilt/android/plugin/src/test/data/android-libraryC/build.gradle b/java/dagger/hilt/android/plugin/src/test/data/android-libraryC/build.gradle
new file mode 100644
index 000000000..30315286b
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/android-libraryC/build.gradle
@@ -0,0 +1,25 @@
+plugins {
+ id 'com.android.library'
+ id 'dagger.hilt.android.plugin'
+}
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.2"
+
+ defaultConfig {
+ minSdkVersion 21
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+ }
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+}
+
+dependencies {
+ implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'
+ annotationProcessor 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'
+} \ No newline at end of file
diff --git a/java/dagger/hilt/android/plugin/src/test/data/android-libraryC/src/main/AndroidManifest.xml b/java/dagger/hilt/android/plugin/src/test/data/android-libraryC/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..a4967e325
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/android-libraryC/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="libc">
+</manifest> \ No newline at end of file
diff --git a/java/dagger/hilt/android/plugin/src/test/data/android-libraryC/src/main/java/libc/LibraryC.java b/java/dagger/hilt/android/plugin/src/test/data/android-libraryC/src/main/java/libc/LibraryC.java
new file mode 100644
index 000000000..f7397fc13
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/android-libraryC/src/main/java/libc/LibraryC.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libc;
+
+import javax.inject.Inject;
+
+/** Test LibC */
+public class LibraryC {
+ @Inject
+ public LibraryC() {}
+}
diff --git a/java/dagger/hilt/android/plugin/src/test/data/java-libraryA/build.gradle b/java/dagger/hilt/android/plugin/src/test/data/java-libraryA/build.gradle
new file mode 100644
index 000000000..dde885882
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/java-libraryA/build.gradle
@@ -0,0 +1,15 @@
+plugins {
+ id 'java-library'
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+dependencies {
+ implementation 'com.google.dagger:hilt-core:LOCAL-SNAPSHOT'
+ annotationProcessor 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'
+
+ implementation project(':libraryB')
+} \ No newline at end of file
diff --git a/java/dagger/hilt/android/plugin/src/test/data/java-libraryA/src/main/java/liba/LibraryA.java b/java/dagger/hilt/android/plugin/src/test/data/java-libraryA/src/main/java/liba/LibraryA.java
new file mode 100644
index 000000000..afc0da2c5
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/java-libraryA/src/main/java/liba/LibraryA.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package liba;
+
+import javax.inject.Inject;
+import libb.LibraryBProvided;
+
+/** Test LibA */
+public class LibraryA {
+ @Inject
+ public LibraryA(LibraryBProvided b) {}
+}
diff --git a/java/dagger/hilt/android/plugin/src/test/data/java-libraryB/build.gradle b/java/dagger/hilt/android/plugin/src/test/data/java-libraryB/build.gradle
new file mode 100644
index 000000000..55780ccb0
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/java-libraryB/build.gradle
@@ -0,0 +1,13 @@
+plugins {
+ id 'java-library'
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+dependencies {
+ implementation 'com.google.dagger:hilt-core:LOCAL-SNAPSHOT'
+ annotationProcessor 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'
+} \ No newline at end of file
diff --git a/java/dagger/hilt/android/plugin/src/test/data/java-libraryB/src/main/java/libb/LibraryB.java b/java/dagger/hilt/android/plugin/src/test/data/java-libraryB/src/main/java/libb/LibraryB.java
new file mode 100644
index 000000000..285d5bab7
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/java-libraryB/src/main/java/libb/LibraryB.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libb;
+
+import javax.inject.Inject;
+
+/** Test LibB */
+public class LibraryB {
+ @Inject
+ public LibraryB() {}
+}
diff --git a/java/dagger/hilt/android/plugin/src/test/data/java-libraryB/src/main/java/libb/LibraryBModule.java b/java/dagger/hilt/android/plugin/src/test/data/java-libraryB/src/main/java/libb/LibraryBModule.java
new file mode 100644
index 000000000..d1c08f0dc
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/java-libraryB/src/main/java/libb/LibraryBModule.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libb;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.hilt.InstallIn;
+import dagger.hilt.components.SingletonComponent;
+
+/** Test LibB Module */
+@Module
+@InstallIn(SingletonComponent.class)
+public final class LibraryBModule {
+ @Provides
+ public static LibraryBProvided provideB() {
+ return new LibraryBProvided();
+ }
+}
diff --git a/java/dagger/hilt/android/plugin/src/test/data/java-libraryB/src/main/java/libb/LibraryBProvided.java b/java/dagger/hilt/android/plugin/src/test/data/java-libraryB/src/main/java/libb/LibraryBProvided.java
new file mode 100644
index 000000000..b472bfb55
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/java-libraryB/src/main/java/libb/LibraryBProvided.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libb;
+
+/** Test LibB Provided */
+public class LibraryBProvided {}
diff --git a/java/dagger/hilt/android/plugin/src/test/data/simple-project/gradle.properties b/java/dagger/hilt/android/plugin/src/test/data/simple-project/gradle.properties
new file mode 100644
index 000000000..5bac8ac50
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/simple-project/gradle.properties
@@ -0,0 +1 @@
+android.useAndroidX=true
diff --git a/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/AndroidManifest.xml b/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..29060e05c
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="simple">
+</manifest> \ No newline at end of file
diff --git a/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/java/simple/Activity1.java b/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/java/simple/Activity1.java
new file mode 100644
index 000000000..a6e5d936d
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/java/simple/Activity1.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package simple;
+
+import androidx.appcompat.app.AppCompatActivity;
+import dagger.hilt.android.AndroidEntryPoint;
+import javax.inject.Inject;
+
+/** Just an activity. */
+@AndroidEntryPoint(AppCompatActivity.class)
+public class Activity1 extends Hilt_Activity1 {
+ @Inject String data;
+
+ // Insert-change
+}
diff --git a/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/java/simple/Activity2.java b/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/java/simple/Activity2.java
new file mode 100644
index 000000000..f7793be08
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/java/simple/Activity2.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package simple;
+
+import androidx.appcompat.app.AppCompatActivity;
+import dagger.hilt.android.AndroidEntryPoint;
+import javax.inject.Inject;
+
+/** Just an activity. */
+@AndroidEntryPoint(AppCompatActivity.class)
+public class Activity2 extends Hilt_Activity2 {
+ @Inject String data;
+
+ // Insert-change
+}
diff --git a/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/java/simple/Module1.java b/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/java/simple/Module1.java
new file mode 100644
index 000000000..d3297c3ac
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/java/simple/Module1.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package simple;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.components.ActivityComponent;
+
+/** Just a module. */
+@Module
+@InstallIn(ActivityComponent.class)
+public class Module1 {
+
+ @Provides
+ static String provideData() {
+ return "010101";
+ }
+
+ // Insert-change
+
+}
diff --git a/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/java/simple/Module2.java b/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/java/simple/Module2.java
new file mode 100644
index 000000000..dd2323608
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/java/simple/Module2.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package simple;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.components.ActivityComponent;
+
+/** Just a module. */
+@Module
+@InstallIn(ActivityComponent.class)
+public class Module2 {
+
+ @Provides
+ static int provideData() {
+ return 0x010101;
+ }
+
+ // Insert-change
+
+}
diff --git a/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/java/simple/SimpleApp.java b/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/java/simple/SimpleApp.java
new file mode 100644
index 000000000..5a9224194
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/main/java/simple/SimpleApp.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package simple;
+
+import android.app.Application;
+import dagger.hilt.android.HiltAndroidApp;
+
+/** Just an application. */
+@HiltAndroidApp(Application.class)
+public class SimpleApp extends Hilt_SimpleApp {
+ // Insert-change
+}
diff --git a/java/dagger/hilt/android/plugin/src/test/kotlin/CompileClasspathTest.kt b/java/dagger/hilt/android/plugin/src/test/kotlin/CompileClasspathTest.kt
new file mode 100644
index 000000000..614125081
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/kotlin/CompileClasspathTest.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+
+class CompileClasspathTest {
+ @get:Rule
+ val testProjectDir = TemporaryFolder()
+
+ lateinit var gradleRunner: GradleTestRunner
+
+ @Before
+ fun setup() {
+ gradleRunner = GradleTestRunner(testProjectDir)
+ gradleRunner.addAndroidOption(
+ "lintOptions.checkReleaseBuilds = false"
+ )
+ gradleRunner.addHiltOption(
+ "enableExperimentalClasspathAggregation = true"
+ )
+ gradleRunner.addDependencies(
+ "implementation 'androidx.appcompat:appcompat:1.1.0'",
+ "implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'",
+ "annotationProcessor 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'",
+ "implementation project(':libraryA')",
+ )
+ gradleRunner.addSrc(
+ srcPath = "minimal/MyApp.java",
+ srcContent =
+ """
+ package minimal;
+
+ import android.app.Application;
+ import liba.LibraryA;
+
+ @dagger.hilt.android.HiltAndroidApp
+ public class MyApp extends Application {
+ @javax.inject.Inject
+ LibraryA libraryA;
+ }
+ """.trimIndent()
+ )
+ gradleRunner.setAppClassName(".MyApp")
+ }
+
+ // Verifies that library B and library C injected classes are available in the root classpath.
+ @Test
+ fun test_injectClasses() {
+ File("src/test/data/android-libraryA")
+ .copyRecursively(File(testProjectDir.root, "libraryA"))
+ File("src/test/data/java-libraryB")
+ .copyRecursively(File(testProjectDir.root, "libraryB"))
+ File("src/test/data/android-libraryC")
+ .copyRecursively(File(testProjectDir.root, "libraryC"))
+
+ testProjectDir.newFile("settings.gradle").apply {
+ writeText(
+ """
+ include ':libraryA'
+ include ':libraryB'
+ include ':libraryC'
+ """.trimIndent()
+ )
+ }
+
+ val result = gradleRunner.build()
+ val assembleTask = result.getTask(":assembleDebug")
+ assertEquals(TaskOutcome.SUCCESS, assembleTask.outcome)
+ }
+
+ // Verifies that library B Hilt module is available in the root classpath.
+ @Test
+ fun test_injectClassesFromModules() {
+ File("src/test/data/java-libraryA")
+ .copyRecursively(File(testProjectDir.root, "libraryA"))
+ File("src/test/data/java-libraryB")
+ .copyRecursively(File(testProjectDir.root, "libraryB"))
+
+ testProjectDir.newFile("settings.gradle").apply {
+ writeText(
+ """
+ include ':libraryA'
+ include ':libraryB'
+ """.trimIndent()
+ )
+ }
+
+ val result = gradleRunner.build()
+ val assembleTask = result.getTask(":assembleDebug")
+ assertEquals(TaskOutcome.SUCCESS, assembleTask.outcome)
+ }
+}
diff --git a/java/dagger/hilt/android/plugin/src/test/kotlin/GradleTestRunner.kt b/java/dagger/hilt/android/plugin/src/test/kotlin/GradleTestRunner.kt
new file mode 100644
index 000000000..2d58766d8
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/kotlin/GradleTestRunner.kt
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File
+import org.gradle.testkit.runner.BuildResult
+import org.gradle.testkit.runner.GradleRunner
+import org.junit.rules.TemporaryFolder
+
+/**
+ * Testing utility class that sets up a simple Android project that applies the Hilt plugin.
+ */
+class GradleTestRunner(val tempFolder: TemporaryFolder) {
+ private val dependencies = mutableListOf<String>()
+ private val activities = mutableListOf<String>()
+ private val additionalAndroidOptions = mutableListOf<String>()
+ private val hiltOptions = mutableListOf<String>()
+ private var appClassName: String? = null
+ private var buildFile: File? = null
+ private var gradlePropertiesFile: File? = null
+ private var manifestFile: File? = null
+
+ init {
+ tempFolder.newFolder("src", "main", "java", "minimal")
+ tempFolder.newFolder("src", "main", "res")
+ }
+
+ // Adds project dependencies, e.g. "implementation <group>:<id>:<version>"
+ fun addDependencies(vararg deps: String) {
+ dependencies.addAll(deps)
+ }
+
+ // Adds an <activity> tag in the project's Android Manifest, e.g. "<activity name=".Foo"/>
+ fun addActivities(vararg activityElements: String) {
+ activities.addAll(activityElements)
+ }
+
+ // Adds 'android' options to the project's build.gradle, e.g. "lintOptions.checkReleaseBuilds = false"
+ fun addAndroidOption(vararg options: String) {
+ additionalAndroidOptions.addAll(options)
+ }
+
+ // Adds 'hilt' options to the project's build.gradle, e.g. "enableExperimentalClasspathAggregation = true"
+ fun addHiltOption(vararg options: String) {
+ hiltOptions.addAll(options)
+ }
+
+ // Adds a source package to the project. The package path is relative to 'src/main/java'.
+ fun addSrcPackage(packagePath: String) {
+ File(tempFolder.root, "src/main/java/$packagePath").mkdirs()
+ }
+
+ // Adds a source file to the project. The source path is relative to 'src/main/java'.
+ fun addSrc(srcPath: String, srcContent: String): File {
+ File(tempFolder.root, "src/main/java/${srcPath.substringBeforeLast(File.separator)}").mkdirs()
+ return tempFolder.newFile("/src/main/java/$srcPath").apply { writeText(srcContent) }
+ }
+
+ // Adds a resource file to the project. The source path is relative to 'src/main/res'.
+ fun addRes(resPath: String, resContent: String): File {
+ File(tempFolder.root, "src/main/res/${resPath.substringBeforeLast(File.separator)}").mkdirs()
+ return tempFolder.newFile("/src/main/res/$resPath").apply { writeText(resContent) }
+ }
+
+ fun setAppClassName(name: String) {
+ appClassName = name
+ }
+
+ // Executes a Gradle builds and expects it to succeed.
+ fun build(): Result {
+ setupFiles()
+ return Result(tempFolder.root, createRunner().build())
+ }
+
+ // Executes a Gradle build and expects it to fail.
+ fun buildAndFail(): Result {
+ setupFiles()
+ return Result(tempFolder.root, createRunner().buildAndFail())
+ }
+
+ private fun setupFiles() {
+ writeBuildFile()
+ writeGradleProperties()
+ writeAndroidManifest()
+ }
+
+ private fun writeBuildFile() {
+ buildFile?.delete()
+ buildFile = tempFolder.newFile("build.gradle").apply {
+ writeText(
+ """
+ buildscript {
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:4.2.0-beta04'
+ }
+ }
+
+ plugins {
+ id 'com.android.application'
+ id 'dagger.hilt.android.plugin'
+ }
+
+ android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.2"
+
+ defaultConfig {
+ applicationId "plugin.test"
+ minSdkVersion 21
+ targetSdkVersion 30
+ }
+
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+ ${additionalAndroidOptions.joinToString(separator = "\n")}
+ }
+
+ allprojects {
+ repositories {
+ mavenLocal()
+ google()
+ jcenter()
+ }
+ }
+
+ dependencies {
+ ${dependencies.joinToString(separator = "\n")}
+ }
+
+ hilt {
+ ${hiltOptions.joinToString(separator = "\n")}
+ }
+ """.trimIndent()
+ )
+ }
+ }
+
+ private fun writeGradleProperties() {
+ gradlePropertiesFile?.delete()
+ gradlePropertiesFile = tempFolder.newFile("gradle.properties").apply {
+ writeText(
+ """
+ android.useAndroidX=true
+ """.trimIndent()
+ )
+ }
+ }
+
+ private fun writeAndroidManifest() {
+ manifestFile?.delete()
+ manifestFile = tempFolder.newFile("/src/main/AndroidManifest.xml").apply {
+ writeText(
+ """
+ <?xml version="1.0" encoding="utf-8"?>
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="minimal">
+ <application
+ android:name="${appClassName ?: "android.app.Application"}"
+ android:theme="@style/Theme.AppCompat.Light.DarkActionBar">
+ ${activities.joinToString(separator = "\n")}
+ </application>
+ </manifest>
+ """.trimIndent()
+ )
+ }
+ }
+
+ private fun createRunner() = GradleRunner.create()
+ .withProjectDir(tempFolder.root)
+ .withArguments("assembleDebug", "--stacktrace")
+ .withPluginClasspath()
+// .withDebug(true) // Add this line to enable attaching a debugger to the gradle test invocation
+ .forwardOutput()
+
+ // Data class representing a Gradle Test run result.
+ data class Result(
+ private val projectRoot: File,
+ private val buildResult: BuildResult
+ ) {
+ // Finds a task by name.
+ fun getTask(name: String) = buildResult.task(name) ?: error("Task '$name' not found.")
+
+ // Gets the full build output.
+ fun getOutput() = buildResult.output
+
+ // Finds a transformed file. The srcFilePath is relative to the app's package.
+ fun getTransformedFile(srcFilePath: String): File {
+ val parentDir =
+ File(projectRoot, "build/intermediates/asm_instrumented_project_classes/debug")
+ return File(parentDir, srcFilePath).also {
+ if (!it.exists()) {
+ error("Unable to find transformed class ${it.path}")
+ }
+ }
+ }
+ }
+}
diff --git a/java/dagger/hilt/android/plugin/src/test/kotlin/HiltGradlePluginTest.kt b/java/dagger/hilt/android/plugin/src/test/kotlin/HiltGradlePluginTest.kt
new file mode 100644
index 000000000..f2569853a
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/kotlin/HiltGradlePluginTest.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+
+/**
+ * Functional test of the plugin
+ *
+ * To run these tests first deploy artifacts to local maven via util/install-local-snapshot.sh.
+ */
+class HiltGradlePluginTest {
+
+ @get:Rule
+ val testProjectDir = TemporaryFolder()
+
+ lateinit var gradleRunner: GradleTestRunner
+
+ @Before
+ fun setup() {
+ gradleRunner = GradleTestRunner(testProjectDir)
+ }
+
+ // Verify plugin configuration fails when runtime dependency is missing but plugin is applied.
+ @Test
+ fun test_missingLibraryDep() {
+ gradleRunner.addDependencies(
+ "implementation 'androidx.appcompat:appcompat:1.1.0'"
+ )
+
+ val result = gradleRunner.buildAndFail()
+ assertThat(result.getOutput()).contains(
+ "The Hilt Android Gradle plugin is applied but no " +
+ "com.google.dagger:hilt-android dependency was found."
+ )
+ }
+
+ // Verify plugin configuration fails when compiler dependency is missing but plugin is applied.
+ @Test
+ fun test_missingCompilerDep() {
+ gradleRunner.addDependencies(
+ "implementation 'androidx.appcompat:appcompat:1.1.0'",
+ "implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'"
+ )
+
+ val result = gradleRunner.buildAndFail()
+ assertThat(result.getOutput()).contains(
+ "The Hilt Android Gradle plugin is applied but no " +
+ "com.google.dagger:hilt-compiler dependency was found."
+ )
+ }
+}
diff --git a/java/dagger/hilt/android/plugin/src/test/kotlin/IncrementalProcessorTest.kt b/java/dagger/hilt/android/plugin/src/test/kotlin/IncrementalProcessorTest.kt
new file mode 100644
index 000000000..a003ab516
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/kotlin/IncrementalProcessorTest.kt
@@ -0,0 +1,615 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.google.common.truth.Expect
+import java.io.File
+import org.gradle.testkit.runner.BuildResult
+import org.gradle.testkit.runner.GradleRunner
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+
+/**
+ * Tests to verify Gradle annotation processor incremental compilation.
+ *
+ * To run these tests first deploy artifacts to local maven via util/install-local-snapshot.sh.
+ */
+class IncrementalProcessorTest {
+
+ @get:Rule
+ val testProjectDir = TemporaryFolder()
+
+ @get:Rule
+ val expect: Expect = Expect.create()
+
+ // Original source files
+ private lateinit var srcApp: File
+ private lateinit var srcActivity1: File
+ private lateinit var srcActivity2: File
+ private lateinit var srcModule1: File
+ private lateinit var srcModule2: File
+
+ // Generated source files
+ private lateinit var genHiltApp: File
+ private lateinit var genHiltActivity1: File
+ private lateinit var genHiltActivity2: File
+ private lateinit var genAppInjector: File
+ private lateinit var genActivityInjector1: File
+ private lateinit var genActivityInjector2: File
+ private lateinit var genAppInjectorDeps: File
+ private lateinit var genActivityInjectorDeps1: File
+ private lateinit var genActivityInjectorDeps2: File
+ private lateinit var genModuleDeps1: File
+ private lateinit var genModuleDeps2: File
+ private lateinit var genHiltComponents: File
+ private lateinit var genDaggerHiltApplicationComponent: File
+
+ // Compiled classes
+ private lateinit var classSrcApp: File
+ private lateinit var classSrcActivity1: File
+ private lateinit var classSrcActivity2: File
+ private lateinit var classSrcModule1: File
+ private lateinit var classSrcModule2: File
+ private lateinit var classGenHiltApp: File
+ private lateinit var classGenHiltActivity1: File
+ private lateinit var classGenHiltActivity2: File
+ private lateinit var classGenAppInjector: File
+ private lateinit var classGenActivityInjector1: File
+ private lateinit var classGenActivityInjector2: File
+ private lateinit var classGenAppInjectorDeps: File
+ private lateinit var classGenActivityInjectorDeps1: File
+ private lateinit var classGenActivityInjectorDeps2: File
+ private lateinit var classGenModuleDeps1: File
+ private lateinit var classGenModuleDeps2: File
+ private lateinit var classGenHiltComponents: File
+ private lateinit var classGenDaggerHiltApplicationComponent: File
+
+ // Timestamps of files
+ private lateinit var fileToTimestampMap: Map<File, Long>
+
+ // Sets of files that have changed/not changed/deleted
+ private lateinit var changedFiles: Set<File>
+ private lateinit var unchangedFiles: Set<File>
+ private lateinit var deletedFiles: Set<File>
+
+ @Before
+ fun setup() {
+ val projectRoot = testProjectDir.root
+ // copy test project
+ File("src/test/data/simple-project").copyRecursively(projectRoot)
+
+ // set up build file
+ File(projectRoot, "build.gradle").writeText(
+ """
+ buildscript {
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.5.3'
+ }
+ }
+
+ plugins {
+ id 'com.android.application'
+ }
+
+ android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.2"
+
+ defaultConfig {
+ applicationId "hilt.simple"
+ minSdkVersion 21
+ targetSdkVersion 30
+ }
+
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+ }
+
+ repositories {
+ mavenLocal()
+ google()
+ jcenter()
+ }
+
+ dependencies {
+ implementation 'androidx.appcompat:appcompat:1.1.0'
+ implementation 'com.google.dagger:dagger:LOCAL-SNAPSHOT'
+ annotationProcessor 'com.google.dagger:dagger-compiler:LOCAL-SNAPSHOT'
+ implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'
+ annotationProcessor 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'
+ }
+ """.trimIndent()
+ )
+
+ // Compute file paths
+ srcApp = File(projectRoot, "$SRC_DIR/simple/SimpleApp.java")
+ srcActivity1 = File(projectRoot, "$SRC_DIR/simple/Activity1.java")
+ srcActivity2 = File(projectRoot, "$SRC_DIR/simple/Activity2.java")
+ srcModule1 = File(projectRoot, "$SRC_DIR/simple/Module1.java")
+ srcModule2 = File(projectRoot, "$SRC_DIR/simple/Module2.java")
+
+ genHiltApp = File(projectRoot, "$GEN_SRC_DIR/simple/Hilt_SimpleApp.java")
+ genHiltActivity1 = File(projectRoot, "$GEN_SRC_DIR/simple/Hilt_Activity1.java")
+ genHiltActivity2 = File(projectRoot, "$GEN_SRC_DIR/simple/Hilt_Activity2.java")
+ genAppInjector = File(projectRoot, "$GEN_SRC_DIR/simple/SimpleApp_GeneratedInjector.java")
+ genActivityInjector1 = File(projectRoot, "$GEN_SRC_DIR/simple/Activity1_GeneratedInjector.java")
+ genActivityInjector2 = File(projectRoot, "$GEN_SRC_DIR/simple/Activity2_GeneratedInjector.java")
+ genAppInjectorDeps = File(
+ projectRoot,
+ "$GEN_SRC_DIR/hilt_aggregated_deps/simple_SimpleApp_GeneratedInjectorModuleDeps.java"
+ )
+ genActivityInjectorDeps1 = File(
+ projectRoot,
+ "$GEN_SRC_DIR/hilt_aggregated_deps/simple_Activity1_GeneratedInjectorModuleDeps.java"
+ )
+ genActivityInjectorDeps2 = File(
+ projectRoot,
+ "$GEN_SRC_DIR/hilt_aggregated_deps/simple_Activity2_GeneratedInjectorModuleDeps.java"
+ )
+ genModuleDeps1 = File(
+ projectRoot,
+ "$GEN_SRC_DIR/hilt_aggregated_deps/simple_Module1ModuleDeps.java"
+ )
+ genModuleDeps2 = File(
+ projectRoot,
+ "$GEN_SRC_DIR/hilt_aggregated_deps/simple_Module2ModuleDeps.java"
+ )
+ genHiltComponents = File(
+ projectRoot,
+ "$GEN_SRC_DIR/simple/SimpleApp_HiltComponents.java"
+ )
+ genDaggerHiltApplicationComponent = File(
+ projectRoot,
+ "$GEN_SRC_DIR/simple/DaggerSimpleApp_HiltComponents_SingletonC.java"
+ )
+
+ classSrcApp = File(projectRoot, "$CLASS_DIR/simple/SimpleApp.class")
+ classSrcActivity1 = File(projectRoot, "$CLASS_DIR/simple/Activity1.class")
+ classSrcActivity2 = File(projectRoot, "$CLASS_DIR/simple/Activity2.class")
+ classSrcModule1 = File(projectRoot, "$CLASS_DIR/simple/Module1.class")
+ classSrcModule2 = File(projectRoot, "$CLASS_DIR/simple/Module2.class")
+ classGenHiltApp = File(projectRoot, "$CLASS_DIR/simple/Hilt_SimpleApp.class")
+ classGenHiltActivity1 = File(projectRoot, "$CLASS_DIR/simple/Hilt_Activity1.class")
+ classGenHiltActivity2 = File(projectRoot, "$CLASS_DIR/simple/Hilt_Activity2.class")
+ classGenAppInjector = File(projectRoot, "$CLASS_DIR/simple/SimpleApp_GeneratedInjector.class")
+ classGenActivityInjector1 = File(
+ projectRoot,
+ "$CLASS_DIR/simple/Activity1_GeneratedInjector.class"
+ )
+ classGenActivityInjector2 = File(
+ projectRoot,
+ "$CLASS_DIR/simple/Activity2_GeneratedInjector.class"
+ )
+ classGenAppInjectorDeps = File(
+ projectRoot,
+ "$CLASS_DIR/hilt_aggregated_deps/simple_SimpleApp_GeneratedInjectorModuleDeps.class"
+ )
+ classGenActivityInjectorDeps1 = File(
+ projectRoot,
+ "$CLASS_DIR/hilt_aggregated_deps/simple_Activity1_GeneratedInjectorModuleDeps.class"
+ )
+ classGenActivityInjectorDeps2 = File(
+ projectRoot,
+ "$CLASS_DIR/hilt_aggregated_deps/simple_Activity2_GeneratedInjectorModuleDeps.class"
+ )
+ classGenModuleDeps1 = File(
+ projectRoot,
+ "$CLASS_DIR/hilt_aggregated_deps/simple_Module1ModuleDeps.class"
+ )
+ classGenModuleDeps2 = File(
+ projectRoot,
+ "$CLASS_DIR/hilt_aggregated_deps/simple_Module2ModuleDeps.class"
+ )
+ classGenHiltComponents = File(
+ projectRoot,
+ "$CLASS_DIR/simple/SimpleApp_HiltComponents.class"
+ )
+ classGenDaggerHiltApplicationComponent = File(
+ projectRoot,
+ "$CLASS_DIR/simple/DaggerSimpleApp_HiltComponents_SingletonC.class"
+ )
+ }
+
+ @Test
+ fun firstFullBuild() {
+ // This test verifies the results of the first full (non-incremental) build. The other tests
+ // verify the results of the second incremental build based on different change scenarios.
+ val result = runFullBuild()
+ expect.that(result.task(COMPILE_TASK)!!.outcome).isEqualTo(TaskOutcome.SUCCESS)
+
+ // Check annotation processing outputs
+ assertFilesExist(
+ genHiltApp,
+ genHiltActivity1,
+ genHiltActivity2,
+ genAppInjector,
+ genActivityInjector1,
+ genActivityInjector2,
+ genAppInjectorDeps,
+ genActivityInjectorDeps1,
+ genActivityInjectorDeps2,
+ genModuleDeps1,
+ genModuleDeps2,
+ genHiltComponents,
+ genDaggerHiltApplicationComponent
+ )
+
+ // Check compilation outputs
+ assertFilesExist(
+ classSrcApp,
+ classSrcActivity1,
+ classSrcActivity2,
+ classSrcModule1,
+ classSrcModule2,
+ classGenHiltApp,
+ classGenHiltActivity1,
+ classGenHiltActivity2,
+ classGenAppInjector,
+ classGenActivityInjector1,
+ classGenActivityInjector2,
+ classGenAppInjectorDeps,
+ classGenActivityInjectorDeps1,
+ classGenActivityInjectorDeps2,
+ classGenModuleDeps1,
+ classGenModuleDeps2,
+ classGenHiltComponents,
+ classGenDaggerHiltApplicationComponent
+ )
+ }
+
+ @Test
+ fun changeActivitySource() {
+ runFullBuild()
+
+ // Change Activity 1 source
+ searchAndReplace(
+ srcActivity1, "// Insert-change",
+ """
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+ """.trimIndent()
+ )
+
+ val result = runIncrementalBuild()
+ expect.that(result.task(COMPILE_TASK)!!.outcome).isEqualTo(TaskOutcome.SUCCESS)
+
+ // Check annotation processing outputs
+ // * Only activity 1 sources are re-generated, isolation in modules and from other activities
+ // * Root classes along with components are always re-generated (aggregated processor)
+ assertChangedFiles(
+ FileType.JAVA,
+ genHiltApp,
+ genHiltActivity1,
+ genAppInjector,
+ genActivityInjector1,
+ genAppInjectorDeps,
+ genActivityInjectorDeps1,
+ genHiltComponents,
+ genDaggerHiltApplicationComponent
+ )
+
+ // Check compilation outputs
+ // * Gen sources from activity 1 are re-compiled
+ // * All aggregating processor gen sources are re-compiled
+ assertChangedFiles(
+ FileType.CLASS,
+ classSrcActivity1,
+ classGenHiltApp,
+ classGenHiltActivity1,
+ classGenAppInjector,
+ classGenActivityInjector1,
+ classGenAppInjectorDeps,
+ classGenActivityInjectorDeps1,
+ classGenHiltComponents,
+ classGenDaggerHiltApplicationComponent
+ )
+ }
+
+ @Test
+ fun changeModuleSource() {
+ runFullBuild()
+
+ // Change Module 1 source
+ searchAndReplace(
+ srcModule1, "// Insert-change",
+ """
+ @Provides
+ static double provideDouble() {
+ return 10.10;
+ }
+ """.trimIndent()
+ )
+
+ val result = runIncrementalBuild()
+ expect.that(result.task(COMPILE_TASK)!!.outcome).isEqualTo(TaskOutcome.SUCCESS)
+
+ // Check annotation processing outputs
+ // * Only module 1 sources are re-generated, isolation from other modules
+ // * Root classes along with components are always re-generated (aggregated processor)
+ assertChangedFiles(
+ FileType.JAVA,
+ genHiltApp,
+ genAppInjector,
+ genAppInjectorDeps,
+ genModuleDeps1,
+ genHiltComponents,
+ genDaggerHiltApplicationComponent
+ )
+
+ // Check compilation outputs
+ // * Gen sources from module 1 are re-compiled
+ // * All aggregating processor gen sources are re-compiled
+ assertChangedFiles(
+ FileType.CLASS,
+ classSrcModule1,
+ classGenHiltApp,
+ classGenAppInjector,
+ classGenAppInjectorDeps,
+ classGenModuleDeps1,
+ classGenHiltComponents,
+ classGenDaggerHiltApplicationComponent
+ )
+ }
+
+ @Test
+ fun changeAppSource() {
+ runFullBuild()
+
+ // Change Application source
+ searchAndReplace(
+ srcApp, "// Insert-change",
+ """
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ }
+ """.trimIndent()
+ )
+
+ val result = runIncrementalBuild()
+ expect.that(result.task(COMPILE_TASK)!!.outcome).isEqualTo(TaskOutcome.SUCCESS)
+
+ // Check annotation processing outputs
+ // * No modules or activities (or any other non-root) classes should be generated
+ // * Root classes along with components are always re-generated (aggregated processor)
+ assertChangedFiles(
+ FileType.JAVA,
+ genHiltApp,
+ genAppInjector,
+ genAppInjectorDeps,
+ genHiltComponents,
+ genDaggerHiltApplicationComponent
+ )
+
+ // Check compilation outputs
+ // * All aggregating processor gen sources are re-compiled
+ assertChangedFiles(
+ FileType.CLASS,
+ classSrcApp, // re-compiles because superclass re-compiled
+ classGenHiltApp,
+ classGenAppInjector,
+ classGenAppInjectorDeps,
+ classGenHiltComponents,
+ classGenDaggerHiltApplicationComponent
+ )
+ }
+
+ @Test
+ fun deleteActivitySource() {
+ runFullBuild()
+
+ srcActivity2.delete()
+
+ val result = runIncrementalBuild()
+ expect.that(result.task(COMPILE_TASK)!!.outcome).isEqualTo(TaskOutcome.SUCCESS)
+
+ // Check annotation processing outputs
+ // * All related gen classes from activity 2 should be deleted
+ // * Unrelated activities and modules are in isolation and should be unchanged
+ // * Root classes along with components are always re-generated (aggregated processor)
+ assertDeletedFiles(
+ genHiltActivity2,
+ genActivityInjector2,
+ genActivityInjectorDeps2
+ )
+ assertChangedFiles(
+ FileType.JAVA,
+ genHiltApp,
+ genAppInjector,
+ genAppInjectorDeps,
+ genHiltComponents,
+ genDaggerHiltApplicationComponent
+ )
+
+ // Check compilation outputs
+ // * All compiled classes from activity 2 should be deleted
+ // * Unrelated activities and modules are in isolation and should be unchanged
+ assertDeletedFiles(
+ classSrcActivity2,
+ classGenHiltActivity2,
+ classGenActivityInjector2,
+ classGenActivityInjectorDeps2
+ )
+ assertChangedFiles(
+ FileType.CLASS,
+ classGenHiltApp,
+ classGenAppInjector,
+ classGenAppInjectorDeps,
+ classGenHiltComponents,
+ classGenDaggerHiltApplicationComponent
+ )
+ }
+
+ @Test
+ fun deleteModuleSource() {
+ runFullBuild()
+
+ srcModule2.delete()
+
+ val result = runIncrementalBuild()
+ expect.that(result.task(COMPILE_TASK)!!.outcome).isEqualTo(TaskOutcome.SUCCESS)
+
+ // Check annotation processing outputs
+ // * All related gen classes from module 2 should be deleted
+ // * Unrelated activities and modules are in isolation and should be unchanged
+ // * Root classes along with components are always re-generated (aggregated processor)
+ assertDeletedFiles(
+ genModuleDeps2
+ )
+ assertChangedFiles(
+ FileType.JAVA,
+ genHiltApp,
+ genAppInjector,
+ genAppInjectorDeps,
+ genHiltComponents,
+ genDaggerHiltApplicationComponent
+ )
+
+ // Check compilation outputs
+ // * All compiled classes from module 2 should be deleted
+ // * Unrelated activities and modules are in isolation and should be unchanged
+ assertDeletedFiles(
+ classSrcModule2,
+ classGenModuleDeps2
+ )
+ assertChangedFiles(
+ FileType.CLASS,
+ classGenHiltApp,
+ classGenAppInjector,
+ classGenAppInjectorDeps,
+ classGenHiltComponents,
+ classGenDaggerHiltApplicationComponent
+ )
+ }
+
+ private fun runGradleTasks(vararg args: String): BuildResult {
+ return GradleRunner.create()
+ .withProjectDir(testProjectDir.root)
+ .withArguments(*args)
+ .withPluginClasspath()
+ .forwardOutput()
+ .build()
+ }
+
+ private fun runFullBuild(): BuildResult {
+ val result = runGradleTasks(CLEAN_TASK, COMPILE_TASK)
+ recordTimestamps()
+ return result
+ }
+
+ private fun runIncrementalBuild(): BuildResult {
+ val result = runGradleTasks(COMPILE_TASK)
+ recordFileChanges()
+ return result
+ }
+ private fun recordTimestamps() {
+ val files = listOf(
+ genHiltApp,
+ genHiltActivity1,
+ genHiltActivity2,
+ genAppInjector,
+ genActivityInjector1,
+ genActivityInjector2,
+ genAppInjectorDeps,
+ genActivityInjectorDeps1,
+ genActivityInjectorDeps2,
+ genModuleDeps1,
+ genModuleDeps2,
+ genHiltComponents,
+ genDaggerHiltApplicationComponent,
+ classSrcApp,
+ classSrcActivity1,
+ classSrcActivity2,
+ classSrcModule1,
+ classSrcModule2,
+ classGenHiltApp,
+ classGenHiltActivity1,
+ classGenHiltActivity2,
+ classGenAppInjector,
+ classGenActivityInjector1,
+ classGenActivityInjector2,
+ classGenAppInjectorDeps,
+ classGenActivityInjectorDeps1,
+ classGenActivityInjectorDeps2,
+ classGenModuleDeps1,
+ classGenModuleDeps2,
+ classGenHiltComponents,
+ classGenDaggerHiltApplicationComponent
+ )
+
+ fileToTimestampMap = mutableMapOf<File, Long>().apply {
+ for (file in files) {
+ this[file] = file.lastModified()
+ }
+ }
+ }
+
+ private fun recordFileChanges() {
+ changedFiles = fileToTimestampMap.filter { (file, previousTimestamp) ->
+ file.exists() && file.lastModified() != previousTimestamp
+ }.keys
+
+ unchangedFiles = fileToTimestampMap.filter { (file, previousTimestamp) ->
+ file.exists() && file.lastModified() == previousTimestamp
+ }.keys
+
+ deletedFiles = fileToTimestampMap.filter { (file, _) -> !file.exists() }.keys
+ }
+
+ private fun assertFilesExist(vararg files: File) {
+ expect.withMessage("Existing files")
+ .that(files.filter { it.exists() })
+ .containsExactlyElementsIn(files)
+ }
+
+ private fun assertChangedFiles(type: FileType, vararg files: File) {
+ expect.withMessage("Changed files")
+ .that(changedFiles.filter { it.name.endsWith(type.extension) })
+ .containsExactlyElementsIn(files)
+ }
+
+ private fun assertDeletedFiles(vararg files: File) {
+ expect.withMessage("Deleted files").that(deletedFiles).containsAtLeastElementsIn(files)
+ }
+
+ private fun searchAndReplace(file: File, search: String, replace: String) {
+ file.writeText(file.readText().replace(search, replace))
+ }
+
+ enum class FileType(val extension: String) {
+ JAVA(".java"),
+ CLASS(".class"),
+ }
+
+ companion object {
+ private const val SRC_DIR = "src/main/java"
+ private const val GEN_SRC_DIR = "build/generated/ap_generated_sources/debug/out/"
+ private const val CLASS_DIR = "build/intermediates/javac/debug/classes"
+
+ private const val CLEAN_TASK = ":clean"
+ private const val COMPILE_TASK = ":compileDebugJavaWithJavac"
+ }
+}
diff --git a/java/dagger/hilt/android/plugin/src/test/kotlin/TransformTest.kt b/java/dagger/hilt/android/plugin/src/test/kotlin/TransformTest.kt
new file mode 100644
index 000000000..0b8400214
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/kotlin/TransformTest.kt
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.DataInputStream
+import java.io.FileInputStream
+import javassist.bytecode.ByteArray
+import javassist.bytecode.ClassFile
+import junit.framework.Assert.assertEquals
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+
+class TransformTest {
+
+ @get:Rule
+ val testProjectDir = TemporaryFolder()
+
+ lateinit var gradleRunner: GradleTestRunner
+
+ @Before
+ fun setup() {
+ gradleRunner = GradleTestRunner(testProjectDir)
+ gradleRunner.addSrc(
+ srcPath = "minimal/MainActivity.java",
+ srcContent =
+ """
+ package minimal;
+
+ import android.os.Bundle;
+ import androidx.appcompat.app.AppCompatActivity;
+
+ @dagger.hilt.android.AndroidEntryPoint
+ public class MainActivity extends AppCompatActivity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+ }
+ """.trimIndent()
+ )
+ }
+
+ // Simple functional test to verify transformation.
+ @Test
+ fun testAssemble() {
+ gradleRunner.addDependencies(
+ "implementation 'androidx.appcompat:appcompat:1.1.0'",
+ "implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'",
+ "annotationProcessor 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'"
+ )
+ gradleRunner.addActivities(
+ "<activity android:name=\".MainActivity\"/>"
+ )
+
+ val result = gradleRunner.build()
+ val assembleTask = result.getTask(":assembleDebug")
+ Assert.assertEquals(TaskOutcome.SUCCESS, assembleTask.outcome)
+
+ val transformedClass = result.getTransformedFile("minimal/MainActivity.class")
+ FileInputStream(transformedClass).use { fileInput ->
+ ClassFile(DataInputStream(fileInput)).let { classFile ->
+ // Verify superclass is updated
+ Assert.assertEquals("minimal.Hilt_MainActivity", classFile.superclass)
+ // Verify super call is also updated
+ val constPool = classFile.constPool
+ classFile.methods.first { it.name == "onCreate" }.let { methodInfo ->
+ // bytecode of MainActivity.onCreate() is:
+ // 0 - aload_0
+ // 1 - aload_1
+ // 2 - invokespecial
+ // 5 - return
+ val invokeIndex = 2
+ val methodRef = ByteArray.readU16bit(methodInfo.codeAttribute.code, invokeIndex + 1)
+ val classRef = constPool.getMethodrefClassName(methodRef)
+ Assert.assertEquals("minimal.Hilt_MainActivity", classRef)
+ }
+ }
+ }
+ }
+
+ // Verify correct transformation is done on nested classes.
+ @Test
+ fun testAssemble_nestedClass() {
+ gradleRunner.addDependencies(
+ "implementation 'androidx.appcompat:appcompat:1.1.0'",
+ "implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'",
+ "annotationProcessor 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'"
+ )
+
+ gradleRunner.addSrc(
+ srcPath = "minimal/TopClass.java",
+ srcContent =
+ """
+ package minimal;
+
+ import androidx.appcompat.app.AppCompatActivity;
+
+ public class TopClass {
+ @dagger.hilt.android.AndroidEntryPoint
+ public static class NestedActivity extends AppCompatActivity { }
+ }
+ """.trimIndent()
+ )
+
+ val result = gradleRunner.build()
+ val assembleTask = result.getTask(":assembleDebug")
+ Assert.assertEquals(TaskOutcome.SUCCESS, assembleTask.outcome)
+
+ val transformedClass = result.getTransformedFile("minimal/TopClass\$NestedActivity.class")
+ FileInputStream(transformedClass).use { fileInput ->
+ ClassFile(DataInputStream(fileInput)).let { classFile ->
+ Assert.assertEquals("minimal.Hilt_TopClass_NestedActivity", classFile.superclass)
+ }
+ }
+ }
+
+ // Verify transformation ignores abstract methods.
+ @Test
+ fun testAssemble_abstractMethod() {
+ gradleRunner.addDependencies(
+ "implementation 'androidx.appcompat:appcompat:1.1.0'",
+ "implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'",
+ "annotationProcessor 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'"
+ )
+
+ gradleRunner.addSrc(
+ srcPath = "minimal/AbstractActivity.java",
+ srcContent =
+ """
+ package minimal;
+
+ import androidx.appcompat.app.AppCompatActivity;
+
+ @dagger.hilt.android.AndroidEntryPoint
+ public abstract class AbstractActivity extends AppCompatActivity {
+ public abstract void method();
+ }
+ """.trimIndent()
+ )
+
+ val result = gradleRunner.build()
+ val assembleTask = result.getTask(":assembleDebug")
+ Assert.assertEquals(TaskOutcome.SUCCESS, assembleTask.outcome)
+
+ val transformedClass = result.getTransformedFile("minimal/AbstractActivity.class")
+ FileInputStream(transformedClass).use { fileInput ->
+ ClassFile(DataInputStream(fileInput)).let { classFile ->
+ Assert.assertEquals("minimal.Hilt_AbstractActivity", classFile.superclass)
+ }
+ }
+ }
+
+ // Verify transformation ignores native methods.
+ @Test
+ fun testAssemble_nativeMethod() {
+ gradleRunner.addDependencies(
+ "implementation 'androidx.appcompat:appcompat:1.1.0'",
+ "implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'",
+ "annotationProcessor 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'"
+ )
+
+ gradleRunner.addSrc(
+ srcPath = "minimal/SimpleActivity.java",
+ srcContent =
+ """
+ package minimal;
+
+ import androidx.appcompat.app.AppCompatActivity;
+
+ @dagger.hilt.android.AndroidEntryPoint
+ public class SimpleActivity extends AppCompatActivity {
+ public native void method();
+ }
+ """.trimIndent()
+ )
+
+ val result = gradleRunner.build()
+ val assembleTask = result.getTask(":assembleDebug")
+ Assert.assertEquals(TaskOutcome.SUCCESS, assembleTask.outcome)
+
+ val transformedClass = result.getTransformedFile("minimal/SimpleActivity.class")
+ FileInputStream(transformedClass).use { fileInput ->
+ ClassFile(DataInputStream(fileInput)).let { classFile ->
+ Assert.assertEquals("minimal.Hilt_SimpleActivity", classFile.superclass)
+ }
+ }
+ }
+
+ // Verifies the transformation is applied incrementally when a class to be transformed is updated.
+ @Test
+ fun testTransform_incrementalClass() {
+ gradleRunner.addDependencies(
+ "implementation 'androidx.appcompat:appcompat:1.1.0'",
+ "implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'",
+ "annotationProcessor 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'"
+ )
+
+ val srcFile = gradleRunner.addSrc(
+ srcPath = "minimal/OtherActivity.java",
+ srcContent =
+ """
+ package minimal;
+
+ import androidx.appcompat.app.AppCompatActivity;
+
+ @dagger.hilt.android.AndroidEntryPoint
+ public class OtherActivity extends AppCompatActivity {
+
+ }
+ """.trimIndent()
+ )
+
+ gradleRunner.build().let {
+ val assembleTask = it.getTask(TRANSFORM_TASK_NAME)
+ Assert.assertEquals(TaskOutcome.SUCCESS, assembleTask.outcome)
+ }
+
+ gradleRunner.build().let {
+ val assembleTask = it.getTask(TRANSFORM_TASK_NAME)
+ Assert.assertEquals(TaskOutcome.UP_TO_DATE, assembleTask.outcome)
+ }
+
+ srcFile.delete()
+ gradleRunner.addSrc(
+ srcPath = "minimal/OtherActivity.java",
+ srcContent =
+ """
+ package minimal;
+
+ import androidx.fragment.app.FragmentActivity;
+
+ @dagger.hilt.android.AndroidEntryPoint
+ public class OtherActivity extends FragmentActivity {
+
+ }
+ """.trimIndent()
+ )
+
+ val result = gradleRunner.build()
+ val assembleTask = result.getTask(TRANSFORM_TASK_NAME)
+ Assert.assertEquals(TaskOutcome.SUCCESS, assembleTask.outcome)
+
+ val transformedClass = result.getTransformedFile("minimal/OtherActivity.class")
+ FileInputStream(transformedClass).use { fileInput ->
+ ClassFile(DataInputStream(fileInput)).let { classFile ->
+ Assert.assertEquals("minimal.Hilt_OtherActivity", classFile.superclass)
+ }
+ }
+ }
+
+ // Verifies the transformation is applied incrementally when a new class is added to an existing
+ // directory.
+ @Test
+ fun testTransform_incrementalDir() {
+ gradleRunner.addDependencies(
+ "implementation 'androidx.appcompat:appcompat:1.1.0'",
+ "implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'",
+ "annotationProcessor 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'"
+ )
+
+ gradleRunner.addSrcPackage("ui/")
+
+ gradleRunner.build().let {
+ val assembleTask = it.getTask(TRANSFORM_TASK_NAME)
+ assertEquals(TaskOutcome.SUCCESS, assembleTask.outcome)
+ }
+
+ gradleRunner.build().let {
+ val assembleTask = it.getTask(TRANSFORM_TASK_NAME)
+ assertEquals(TaskOutcome.UP_TO_DATE, assembleTask.outcome)
+ }
+
+ gradleRunner.addSrc(
+ srcPath = "ui/OtherActivity.java",
+ srcContent =
+ """
+ package ui;
+
+ import androidx.appcompat.app.AppCompatActivity;
+
+ @dagger.hilt.android.AndroidEntryPoint
+ public class OtherActivity extends AppCompatActivity {
+
+ }
+ """.trimIndent()
+ )
+
+ val result = gradleRunner.build()
+ val assembleTask = result.getTask(TRANSFORM_TASK_NAME)
+ assertEquals(TaskOutcome.SUCCESS, assembleTask.outcome)
+ }
+
+ companion object {
+ const val TRANSFORM_TASK_NAME =
+ ":transformDebugClassesWithAsm"
+ }
+}
diff --git a/java/dagger/hilt/android/plugin/src/test/kotlin/util/SimpleAGPVersionTest.kt b/java/dagger/hilt/android/plugin/src/test/kotlin/util/SimpleAGPVersionTest.kt
new file mode 100644
index 000000000..75292b83c
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/kotlin/util/SimpleAGPVersionTest.kt
@@ -0,0 +1,40 @@
+package util
+
+import com.google.common.truth.Truth.assertThat
+import dagger.hilt.android.plugin.util.SimpleAGPVersion
+import org.junit.Test
+
+class SimpleAGPVersionTest {
+
+ @Test
+ fun parsing() {
+ assertThat(SimpleAGPVersion.parse("4.2"))
+ .isEqualTo(SimpleAGPVersion(4, 2))
+ assertThat(SimpleAGPVersion.parse("4.2.1"))
+ .isEqualTo(SimpleAGPVersion(4, 2))
+ assertThat(SimpleAGPVersion.parse("7.0.0-alpha01"))
+ .isEqualTo(SimpleAGPVersion(7, 0))
+ }
+
+ @Test
+ fun comparing() {
+ assertThat(SimpleAGPVersion(4, 2))
+ .isEqualTo(SimpleAGPVersion(4, 2))
+ assertThat(SimpleAGPVersion(4, 2))
+ .isGreaterThan(SimpleAGPVersion(3, 4))
+ assertThat(SimpleAGPVersion(4, 2))
+ .isLessThan(SimpleAGPVersion(7, 0))
+
+ assertThat(SimpleAGPVersion.parse("4.2.1"))
+ .isEqualTo(SimpleAGPVersion.parse("4.2.2"))
+ assertThat(SimpleAGPVersion.parse("4.2.1"))
+ .isGreaterThan(SimpleAGPVersion.parse("3.4.1"))
+ assertThat(SimpleAGPVersion.parse("4.2.1"))
+ .isLessThan(SimpleAGPVersion.parse("7.0.1"))
+
+ assertThat(SimpleAGPVersion.parse("4.2.1"))
+ .isLessThan(SimpleAGPVersion.parse("7.0.0-alpha01"))
+ assertThat(SimpleAGPVersion.parse("7.0.0-alpha01"))
+ .isEqualTo(SimpleAGPVersion.parse("7.0.0-alpha02"))
+ }
+}
diff --git a/java/dagger/hilt/android/processor/BUILD b/java/dagger/hilt/android/processor/BUILD
new file mode 100644
index 000000000..e116c4443
--- /dev/null
+++ b/java/dagger/hilt/android/processor/BUILD
@@ -0,0 +1,100 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Hilt android processors.
+
+load("//:build_defs.bzl", "POM_VERSION_ALPHA")
+load("//tools:maven.bzl", "gen_maven_artifact")
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "artifact-lib",
+ tags = ["maven_coordinates=com.google.dagger:hilt-android-compiler:" + POM_VERSION_ALPHA],
+ visibility = ["//visibility:private"],
+ exports = [
+ "//java/dagger/hilt/processor:artifact-lib-shared",
+ ],
+)
+
+gen_maven_artifact(
+ name = "artifact",
+ artifact_coordinates = "com.google.dagger:hilt-android-compiler:" + POM_VERSION_ALPHA,
+ artifact_name = "Hilt Android Processor",
+ artifact_target = ":artifact-lib",
+ artifact_target_libs = [
+ "//java/dagger/hilt/android/processor/internal:android_classnames",
+ "//java/dagger/hilt/android/processor/internal:utils",
+ "//java/dagger/hilt/android/processor/internal/androidentrypoint:android_generators",
+ "//java/dagger/hilt/android/processor/internal/androidentrypoint:compiler_options",
+ "//java/dagger/hilt/android/processor/internal/androidentrypoint:metadata",
+ "//java/dagger/hilt/android/processor/internal/androidentrypoint:processor_lib",
+ "//java/dagger/hilt/android/processor/internal/bindvalue:bind_value_processor_lib",
+ "//java/dagger/hilt/android/processor/internal/customtestapplication:processor_lib",
+ "//java/dagger/hilt/android/processor/internal/viewmodel:validation_plugin_lib",
+ "//java/dagger/hilt/android/processor/internal/uninstallmodules:processor_lib",
+ "//java/dagger/hilt/android/processor/internal/viewmodel:processor_lib",
+ "//java/dagger/hilt/processor/internal:base_processor",
+ "//java/dagger/hilt/processor/internal:classnames",
+ "//java/dagger/hilt/processor/internal:component_descriptor",
+ "//java/dagger/hilt/processor/internal:component_names",
+ "//java/dagger/hilt/processor/internal:components",
+ "//java/dagger/hilt/processor/internal:kotlin",
+ "//java/dagger/hilt/processor/internal:processor_errors",
+ "//java/dagger/hilt/processor/internal:processors",
+ "//java/dagger/hilt/processor/internal/aggregateddeps:component_dependencies",
+ "//java/dagger/hilt/processor/internal/aggregateddeps:processor_lib",
+ "//java/dagger/hilt/processor/internal/aliasof:alias_ofs",
+ "//java/dagger/hilt/processor/internal/aliasof:processor_lib",
+ "//java/dagger/hilt/processor/internal/definecomponent:define_components",
+ "//java/dagger/hilt/processor/internal/definecomponent:processor_lib",
+ "//java/dagger/hilt/processor/internal/generatesrootinput:generates_root_inputs",
+ "//java/dagger/hilt/processor/internal/generatesrootinput:processor_lib",
+ "//java/dagger/hilt/processor/internal/originatingelement:processor_lib",
+ "//java/dagger/hilt/processor/internal/root:processor_lib",
+ "//java/dagger/hilt/processor/internal/root:root_metadata",
+ "//java/dagger/hilt/processor/internal/root:root_type",
+ ],
+ artifact_target_maven_deps = [
+ "com.google.auto:auto-common",
+ "com.google.code.findbugs:jsr305",
+ "com.google.dagger:dagger-compiler",
+ "com.google.dagger:dagger",
+ "com.google.dagger:dagger-spi",
+ "com.google.guava:failureaccess",
+ "com.google.guava:guava",
+ "com.squareup:javapoet",
+ "javax.annotation:jsr250-api",
+ "javax.inject:javax.inject",
+ "net.ltgt.gradle.incap:incap",
+ "org.jetbrains.kotlin:kotlin-stdlib",
+ "org.jetbrains.kotlinx:kotlinx-metadata-jvm",
+ ],
+ javadoc_android_api_level = 30,
+ javadoc_root_packages = [
+ "dagger.hilt.processor",
+ "dagger.hilt.android.processor",
+ ],
+ javadoc_srcs = [
+ "//java/dagger/hilt:hilt_processing_filegroup",
+ ],
+ shaded_deps = ["@maven//:com_google_auto_auto_common"],
+ shaded_rules = ["rule com.google.auto.common.** dagger.hilt.android.shaded.auto.common.@1"],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/android/processor/internal/AndroidClassNames.java b/java/dagger/hilt/android/processor/internal/AndroidClassNames.java
new file mode 100644
index 000000000..915ae519f
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/AndroidClassNames.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal;
+
+import static com.squareup.javapoet.ClassName.get;
+
+import com.squareup.javapoet.ClassName;
+
+/** Holder for commonly used class names. */
+public final class AndroidClassNames {
+
+ public static final ClassName APPLICATION_PROVIDER =
+ get("androidx.test.core.app", "ApplicationProvider");
+ public static final ClassName ACTIVITY = get("android.app", "Activity");
+ public static final ClassName COMPONENT_ACTIVITY = get("androidx.activity", "ComponentActivity");
+ public static final ClassName APPLICATION = get("android.app", "Application");
+ public static final ClassName BROADCAST_RECEIVER = get("android.content", "BroadcastReceiver");
+ public static final ClassName SERVICE = get("android.app", "Service");
+ public static final ClassName FRAGMENT =
+ get("androidx.fragment.app", "Fragment");
+ public static final ClassName VIEW = get("android.view", "View");
+
+ public static final ClassName NULLABLE_INTERNAL = get("android.annotation", "Nullable");
+ public static final ClassName TARGET_API = get("android.annotation", "TargetApi");
+
+ public static final ClassName CONTEXT = get("android.content", "Context");
+ public static final ClassName CONTEXT_WRAPPER = get("android.content", "ContextWrapper");
+ public static final ClassName INTENT = get("android.content", "Intent");
+
+ public static final ClassName BUNDLE = get("android.os", "Bundle");
+
+ public static final ClassName CALL_SUPER = get("androidx.annotation", "CallSuper");
+ public static final ClassName MAIN_THREAD = get("androidx.annotation", "MainThread");
+ public static final ClassName NULLABLE = get("androidx.annotation", "Nullable");
+ public static final ClassName MULTI_DEX_APPLICATION =
+ get("androidx.multidex", "MultiDexApplication");
+
+ public static final ClassName ATTRIBUTE_SET = get("android.util", "AttributeSet");
+ public static final ClassName LAYOUT_INFLATER = get("android.view", "LayoutInflater");
+
+ public static final ClassName ANDROID_ENTRY_POINT =
+ get("dagger.hilt.android", "AndroidEntryPoint");
+ public static final ClassName WITH_FRAGMENT_BINDINGS =
+ get("dagger.hilt.android", "WithFragmentBindings");
+ public static final ClassName HILT_ANDROID_APP =
+ get("dagger.hilt.android", "HiltAndroidApp");
+ public static final ClassName OPTIONAL_INJECT =
+ get("dagger.hilt.android.migration", "OptionalInject");
+
+ public static final ClassName SINGLETON_COMPONENT =
+ get("dagger.hilt.components", "SingletonComponent");
+ public static final ClassName ACTIVITY_COMPONENT =
+ get("dagger.hilt.android.components", "ActivityComponent");
+ public static final ClassName ACTIVITY_RETAINED_COMPONENT =
+ get("dagger.hilt.android.components", "ActivityRetainedComponent");
+ public static final ClassName FRAGMENT_COMPONENT =
+ get("dagger.hilt.android.components", "FragmentComponent");
+ public static final ClassName VIEW_WITH_FRAGMENT_COMPONENT =
+ get("dagger.hilt.android.components", "ViewWithFragmentComponent");
+ public static final ClassName VIEW_COMPONENT =
+ get("dagger.hilt.android.components", "ViewComponent");
+ public static final ClassName SERVICE_COMPONENT =
+ get("dagger.hilt.android.components", "ServiceComponent");
+ public static final ClassName VIEW_MODEL_COMPONENT =
+ get("dagger.hilt.android.components", "ViewModelComponent");
+
+ public static final ClassName ACTIVITY_COMPONENT_MANAGER =
+ get("dagger.hilt.android.internal.managers", "ActivityComponentManager");
+ public static final ClassName APPLICATION_COMPONENT_MANAGER =
+ get("dagger.hilt.android.internal.managers", "ApplicationComponentManager");
+ public static final ClassName BROADCAST_RECEIVER_COMPONENT_MANAGER =
+ get("dagger.hilt.android.internal.managers", "BroadcastReceiverComponentManager");
+ public static final ClassName COMPONENT_SUPPLIER =
+ get("dagger.hilt.android.internal.managers", "ComponentSupplier");
+ public static final ClassName FRAGMENT_COMPONENT_MANAGER =
+ get("dagger.hilt.android.internal.managers", "FragmentComponentManager");
+ public static final ClassName SERVICE_COMPONENT_MANAGER =
+ get("dagger.hilt.android.internal.managers", "ServiceComponentManager");
+ public static final ClassName VIEW_COMPONENT_MANAGER =
+ get("dagger.hilt.android.internal.managers", "ViewComponentManager");
+
+ public static final ClassName INJECTED_BY_HILT =
+ get("dagger.hilt.android.internal.migration", "InjectedByHilt");
+
+ public static final ClassName APPLICATION_CONTEXT_MODULE =
+ get("dagger.hilt.android.internal.modules", "ApplicationContextModule");
+
+ public static final ClassName DEFAULT_VIEW_MODEL_FACTORIES =
+ get("dagger.hilt.android.internal.lifecycle", "DefaultViewModelFactories");
+ public static final ClassName HILT_VIEW_MODEL =
+ get("dagger.hilt.android.lifecycle", "HiltViewModel");
+ public static final ClassName HILT_VIEW_MODEL_MAP_QUALIFIER =
+ get("dagger.hilt.android.internal.lifecycle", "HiltViewModelMap");
+ public static final ClassName HILT_VIEW_MODEL_KEYS_QUALIFIER =
+ get("dagger.hilt.android.internal.lifecycle", "HiltViewModelMap", "KeySet");
+ public static final ClassName VIEW_MODEL = get("androidx.lifecycle", "ViewModel");
+ public static final ClassName VIEW_MODEL_PROVIDER_FACTORY =
+ get("androidx.lifecycle", "ViewModelProvider", "Factory");
+ public static final ClassName SAVED_STATE_HANDLE =
+ get("androidx.lifecycle", "SavedStateHandle");
+
+ private AndroidClassNames() {}
+}
diff --git a/java/dagger/hilt/android/processor/internal/BUILD b/java/dagger/hilt/android/processor/internal/BUILD
new file mode 100644
index 000000000..aaf8b8916
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/BUILD
@@ -0,0 +1,45 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Internal code for implementing Hilt android processors.
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "android_classnames",
+ srcs = [
+ "AndroidClassNames.java",
+ ],
+ deps = [
+ "@google_bazel_common//third_party/java/javapoet",
+ ],
+)
+
+# TODO(erichang): Merge this into other utils
+java_library(
+ name = "utils",
+ srcs = [
+ "MoreTypes.java",
+ ],
+ deps = [
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["**/*"]),
+)
diff --git a/java/dagger/hilt/android/processor/internal/MoreTypes.java b/java/dagger/hilt/android/processor/internal/MoreTypes.java
new file mode 100644
index 000000000..e5a2cdd00
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/MoreTypes.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ErrorType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.SimpleTypeVisitor7;
+import javax.lang.model.util.Types;
+
+/** More utility methods for types. */
+public final class MoreTypes {
+ private MoreTypes() {}
+
+ /**
+ * If the received mirror represents a declared type or an array of declared types, this returns
+ * the represented declared type. Otherwise throws an IllegalStateException.
+ */
+ public static DeclaredType getDeclaredType(TypeMirror type) {
+ return type.accept(
+ new SimpleTypeVisitor7<DeclaredType, Void>() {
+ @Override public DeclaredType visitArray(ArrayType type, Void unused) {
+ return getDeclaredType(type.getComponentType());
+ }
+
+ @Override public DeclaredType visitDeclared(DeclaredType type, Void unused) {
+ return type;
+ }
+
+ @Override public DeclaredType visitError(ErrorType type, Void unused) {
+ return type;
+ }
+
+ @Override public DeclaredType defaultAction(TypeMirror type, Void unused) {
+ throw new IllegalStateException("Unhandled type: " + type);
+ }
+ }, null /* the Void accumulator */);
+ }
+
+ /** Returns the TypeElement corresponding to a TypeMirror. */
+ public static TypeElement asTypeElement(TypeMirror type) {
+ return asTypeElement(getDeclaredType(type));
+ }
+
+ /** Returns the TypeElement corresponding to a DeclaredType. */
+ public static TypeElement asTypeElement(DeclaredType type) {
+ return (TypeElement) type.asElement();
+ }
+
+ /**
+ * Returns a {@link ExecutableType} if the {@link TypeMirror} represents an executable type such
+ * as a method, constructor, or initializer or throws an {@link IllegalArgumentException}.
+ */
+ public static ExecutableType asExecutable(TypeMirror maybeExecutableType) {
+ return maybeExecutableType.accept(ExecutableTypeVisitor.INSTANCE, null);
+ }
+
+ private static final class ExecutableTypeVisitor extends CastingTypeVisitor<ExecutableType> {
+ private static final ExecutableTypeVisitor INSTANCE = new ExecutableTypeVisitor();
+
+ ExecutableTypeVisitor() {
+ super("executable type");
+ }
+
+ @Override
+ public ExecutableType visitExecutable(ExecutableType type, Void ignore) {
+ return type;
+ }
+ }
+
+ private abstract static class CastingTypeVisitor<T> extends SimpleTypeVisitor7<T, Void> {
+ private final String label;
+
+ CastingTypeVisitor(String label) {
+ this.label = label;
+ }
+
+ @Override
+ protected T defaultAction(TypeMirror e, Void v) {
+ throw new IllegalArgumentException(e + " does not represent a " + label);
+ }
+ }
+
+ /**
+ * Returns the first matching method, if one exists (starting with classElement, then searching
+ * each sub classes).
+ */
+ public static Optional<ExecutableElement> findInheritedMethod(
+ Types types, TypeElement classElement, ExecutableElement method) {
+ Optional<ExecutableElement> match = Optional.empty();
+ while (!match.isPresent() && !classElement.asType().getKind().equals(TypeKind.NONE)) {
+ match = findMethod(types, classElement, method);
+ classElement = MoreTypes.asTypeElement(classElement.getSuperclass());
+ }
+ return match;
+ }
+
+ /** Returns a method with a matching signature in classElement if one exists. */
+ public static Optional<ExecutableElement> findMethod(
+ Types types, TypeElement classElement, ExecutableElement method) {
+ ExecutableType methodType = asExecutable(method.asType());
+ Set<ExecutableElement> matchingMethods =
+ findMethods(classElement, method.getSimpleName().toString())
+ .stream()
+ .filter(clsMethod -> types.isSubsignature(asExecutable(clsMethod.asType()), methodType))
+ .collect(Collectors.toSet());
+
+ Preconditions.checkState(
+ matchingMethods.size() <= 1,
+ "Found multiple methods with matching signature in class %s: %s",
+ classElement,
+ matchingMethods);
+
+ return matchingMethods.size() == 1
+ ? Optional.of(Iterables.getOnlyElement(matchingMethods))
+ : Optional.empty();
+ }
+
+ /** Returns methods with a matching name in classElement. */
+ public static Set<ExecutableElement> findMethods(TypeElement classElement, String name) {
+ return ElementFilter.methodsIn(classElement.getEnclosedElements())
+ .stream()
+ .filter(clsMethod -> clsMethod.getSimpleName().contentEquals(name))
+ .collect(Collectors.toSet());
+ }
+}
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGenerator.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGenerator.java
new file mode 100644
index 000000000..ce9ad14c5
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGenerator.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.androidentrypoint;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.TypeVariableName;
+import dagger.hilt.android.processor.internal.AndroidClassNames;
+import dagger.hilt.processor.internal.Processors;
+import java.io.IOException;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Modifier;
+
+/** Generates an Hilt Activity class for the @AndroidEntryPoint annotated class. */
+public final class ActivityGenerator {
+
+ private final ProcessingEnvironment env;
+ private final AndroidEntryPointMetadata metadata;
+ private final ClassName generatedClassName;
+
+ public ActivityGenerator(ProcessingEnvironment env, AndroidEntryPointMetadata metadata) {
+ this.env = env;
+ this.metadata = metadata;
+
+ generatedClassName = metadata.generatedClassName();
+ }
+
+ // @Generated("ActivityGenerator")
+ // abstract class Hilt_$CLASS extends $BASE implements ComponentManager<?> {
+ // ...
+ // }
+ public void generate() throws IOException {
+ TypeSpec.Builder builder =
+ TypeSpec.classBuilder(generatedClassName.simpleName())
+ .addOriginatingElement(metadata.element())
+ .superclass(metadata.baseClassName())
+ .addModifiers(metadata.generatedClassModifiers());
+
+ Generators.addGeneratedBaseClassJavadoc(builder, AndroidClassNames.ANDROID_ENTRY_POINT);
+ Processors.addGeneratedAnnotation(builder, env, getClass());
+
+ Generators.copyConstructors(metadata.baseElement(), builder);
+ builder.addMethod(onCreate());
+
+
+ metadata.baseElement().getTypeParameters().stream()
+ .map(TypeVariableName::get)
+ .forEachOrdered(builder::addTypeVariable);
+
+ Generators.addComponentOverride(metadata, builder);
+ Generators.copyLintAnnotations(metadata.element(), builder);
+
+ Generators.addInjectionMethods(metadata, builder);
+
+ if (Processors.isAssignableFrom(metadata.baseElement(), AndroidClassNames.COMPONENT_ACTIVITY)
+ && !metadata.overridesAndroidEntryPointClass()) {
+ builder.addMethod(getDefaultViewModelProviderFactory());
+ }
+
+ JavaFile.builder(generatedClassName.packageName(), builder.build())
+ .build()
+ .writeTo(env.getFiler());
+ }
+
+ // @CallSuper
+ // @Override
+ // protected void onCreate(@Nullable Bundle savedInstanceState) {
+ // inject();
+ // super.onCreate(savedInstanceState);
+ // }
+ private MethodSpec onCreate() {
+ return MethodSpec.methodBuilder("onCreate")
+ .addAnnotation(AndroidClassNames.CALL_SUPER)
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PROTECTED)
+ .addParameter(
+ ParameterSpec.builder(AndroidClassNames.BUNDLE, "savedInstanceState")
+ .addAnnotation(AndroidClassNames.NULLABLE)
+ .build())
+ .addStatement("inject()")
+ .addStatement("super.onCreate(savedInstanceState)")
+ .build();
+ }
+
+ // @Override
+ // public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
+ // return DefaultViewModelFactories.getActivityFactory(this);
+ // }
+ private MethodSpec getDefaultViewModelProviderFactory() {
+ return MethodSpec.methodBuilder("getDefaultViewModelProviderFactory")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC)
+ .returns(AndroidClassNames.VIEW_MODEL_PROVIDER_FACTORY)
+ .addStatement(
+ "return $T.getActivityFactory(this)",
+ AndroidClassNames.DEFAULT_VIEW_MODEL_FACTORIES)
+ .build();
+ }
+}
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java
new file mode 100644
index 000000000..e5868f861
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java
@@ -0,0 +1,522 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.androidentrypoint;
+
+import static dagger.hilt.android.processor.internal.androidentrypoint.HiltCompilerOptions.BooleanOption.DISABLE_ANDROID_SUPERCLASS_VALIDATION;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.hilt.android.processor.internal.AndroidClassNames;
+import dagger.hilt.processor.internal.BadInputException;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.Components;
+import dagger.hilt.processor.internal.KotlinMetadataUtils;
+import dagger.hilt.processor.internal.ProcessorErrors;
+import dagger.hilt.processor.internal.Processors;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import java.util.LinkedHashSet;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+/** Metadata class for @AndroidEntryPoint annotated classes. */
+@AutoValue
+public abstract class AndroidEntryPointMetadata {
+
+ /** The class {@link Element} annotated with @AndroidEntryPoint. */
+ public abstract TypeElement element();
+
+ /** The base class {@link Element} given to @AndroidEntryPoint. */
+ public abstract TypeElement baseElement();
+
+ /** The name of the generated base class, beginning with 'Hilt_'. */
+ public abstract ClassName generatedClassName();
+
+ /** Returns {@code true} if the class requires bytecode injection to replace the base class. */
+ public abstract boolean requiresBytecodeInjection();
+
+ /** Returns the {@link AndroidType} for the annotated element. */
+ public abstract AndroidType androidType();
+
+ /** Returns {@link Optional} of {@link AndroidEntryPointMetadata}. */
+ public abstract Optional<AndroidEntryPointMetadata> baseMetadata();
+
+ /** Returns set of scopes that the component interface should be installed in. */
+ public abstract ImmutableSet<ClassName> installInComponents();
+
+ /** Returns the component manager this generated Hilt class should use. */
+ public abstract TypeName componentManager();
+
+ /** Returns the initialization arguments for the component manager. */
+ public abstract Optional<CodeBlock> componentManagerInitArgs();
+
+ /**
+ * Returns the metadata for the root most class in the hierarchy.
+ *
+ * <p>If this is the only metadata in the class hierarchy, it returns this.
+ */
+ @Memoized
+ public AndroidEntryPointMetadata rootMetadata() {
+ return baseMetadata().map(AndroidEntryPointMetadata::rootMetadata).orElse(this);
+ }
+
+ boolean isRootMetadata() {
+ return this.equals(rootMetadata());
+ }
+
+ /** Returns true if this class allows optional injection. */
+ public boolean allowsOptionalInjection() {
+ return Processors.hasAnnotation(element(), AndroidClassNames.OPTIONAL_INJECT);
+ }
+
+ /** Returns true if any base class (transitively) allows optional injection. */
+ public boolean baseAllowsOptionalInjection() {
+ return baseMetadata().isPresent() && baseMetadata().get().allowsOptionalInjection();
+ }
+
+ /** Returns true if any base class (transitively) uses @AndroidEntryPoint. */
+ public boolean overridesAndroidEntryPointClass() {
+ return baseMetadata().isPresent();
+ }
+
+ /** The name of the class annotated with @AndroidEntryPoint */
+ public ClassName elementClassName() {
+ return ClassName.get(element());
+ }
+
+ /** The name of the base class given to @AndroidEntryPoint */
+ public TypeName baseClassName() {
+ return TypeName.get(baseElement().asType());
+ }
+
+ /** The name of the generated injector for the Hilt class. */
+ public ClassName injectorClassName() {
+ return Processors.append(
+ Processors.getEnclosedClassName(elementClassName()), "_GeneratedInjector");
+ }
+
+ /**
+ * The name of inject method for this class. The format is: inject$CLASS. If the class is nested,
+ * will return the full name deliminated with '_'. e.g. Foo.Bar.Baz -> injectFoo_Bar_Baz
+ */
+ public String injectMethodName() {
+ return "inject" + Processors.getEnclosedName(elementClassName());
+ }
+
+ /** Returns the @InstallIn annotation for the module providing this class. */
+ public final AnnotationSpec injectorInstallInAnnotation() {
+ return Components.getInstallInAnnotationSpec(installInComponents());
+ }
+
+ public ParameterSpec componentManagerParam() {
+ return ParameterSpec.builder(componentManager(), "componentManager").build();
+ }
+
+ /**
+ * Modifiers that should be applied to the generated class.
+ *
+ * <p>Note that the generated class must have public visibility if used by a
+ * public @AndroidEntryPoint-annotated kotlin class. See:
+ * https://discuss.kotlinlang.org/t/why-does-kotlin-prohibit-exposing-restricted-visibility-types/7047
+ */
+ public Modifier[] generatedClassModifiers() {
+ return isKotlinClass(element()) && element().getModifiers().contains(Modifier.PUBLIC)
+ ? new Modifier[] {Modifier.ABSTRACT, Modifier.PUBLIC}
+ : new Modifier[] {Modifier.ABSTRACT};
+ }
+
+ private static ClassName generatedClassName(TypeElement element) {
+ String prefix = "Hilt_";
+ return Processors.prepend(Processors.getEnclosedClassName(ClassName.get(element)), prefix);
+ }
+
+ private static final ImmutableSet<ClassName> HILT_ANNOTATION_NAMES =
+ ImmutableSet.of(
+ AndroidClassNames.HILT_ANDROID_APP,
+ AndroidClassNames.ANDROID_ENTRY_POINT);
+
+ private static ImmutableSet<? extends AnnotationMirror> hiltAnnotations(Element element) {
+ return element.getAnnotationMirrors().stream()
+ .filter(mirror -> HILT_ANNOTATION_NAMES.contains(ClassName.get(mirror.getAnnotationType())))
+ .collect(toImmutableSet());
+ }
+
+ /** Returns true if the given element has Android Entry Point metadata. */
+ public static boolean hasAndroidEntryPointMetadata(Element element) {
+ return !hiltAnnotations(element).isEmpty();
+ }
+
+ /** Returns the {@link AndroidEntryPointMetadata} for a @AndroidEntryPoint annotated element. */
+ public static AndroidEntryPointMetadata of(ProcessingEnvironment env, Element element) {
+ LinkedHashSet<Element> inheritanceTrace = new LinkedHashSet<>();
+ inheritanceTrace.add(element);
+ return of(env, element, inheritanceTrace);
+ }
+
+ public static AndroidEntryPointMetadata manuallyConstruct(
+ TypeElement element,
+ TypeElement baseElement,
+ ClassName generatedClassName,
+ boolean requiresBytecodeInjection,
+ AndroidType androidType,
+ Optional<AndroidEntryPointMetadata> baseMetadata,
+ ImmutableSet<ClassName> installInComponents,
+ TypeName componentManager,
+ Optional<CodeBlock> componentManagerInitArgs) {
+ return new AutoValue_AndroidEntryPointMetadata(
+ element,
+ baseElement,
+ generatedClassName,
+ requiresBytecodeInjection,
+ androidType,
+ baseMetadata,
+ installInComponents,
+ componentManager,
+ componentManagerInitArgs);
+ }
+
+ /**
+ * Internal implementation for "of" method, checking inheritance cycle utilizing inheritanceTrace
+ * along the way.
+ */
+ private static AndroidEntryPointMetadata of(
+ ProcessingEnvironment env, Element element, LinkedHashSet<Element> inheritanceTrace) {
+ ImmutableSet<? extends AnnotationMirror> hiltAnnotations = hiltAnnotations(element);
+ ProcessorErrors.checkState(
+ hiltAnnotations.size() == 1,
+ element,
+ "Expected exactly 1 of %s. Found: %s",
+ HILT_ANNOTATION_NAMES,
+ hiltAnnotations);
+ ClassName annotationClassName =
+ ClassName.get(
+ MoreTypes.asTypeElement(Iterables.getOnlyElement(hiltAnnotations).getAnnotationType()));
+
+ ProcessorErrors.checkState(
+ element.getKind() == ElementKind.CLASS,
+ element,
+ "Only classes can be annotated with @%s",
+ annotationClassName.simpleName());
+ TypeElement androidEntryPointElement = MoreElements.asType(element);
+
+ ProcessorErrors.checkState(
+ androidEntryPointElement.getTypeParameters().isEmpty(),
+ element,
+ "@%s-annotated classes cannot have type parameters.",
+ annotationClassName.simpleName());
+
+ final TypeElement androidEntryPointClassValue =
+ Processors.getAnnotationClassValue(
+ env.getElementUtils(),
+ Processors.getAnnotationMirror(androidEntryPointElement, annotationClassName),
+ "value");
+ final TypeElement baseElement;
+ final ClassName generatedClassName;
+ boolean requiresBytecodeInjection =
+ DISABLE_ANDROID_SUPERCLASS_VALIDATION.get(env)
+ && MoreTypes.isTypeOf(Void.class, androidEntryPointClassValue.asType());
+ if (requiresBytecodeInjection) {
+ baseElement = MoreElements.asType(env.getTypeUtils().asElement(androidEntryPointElement.getSuperclass()));
+ // If this AndroidEntryPoint is a Kotlin class and its base type is also Kotlin and has
+ // default values declared in its constructor then error out because for the short-form
+ // usage of @AndroidEntryPoint the bytecode transformation will be done incorrectly.
+ KotlinMetadataUtil metadataUtil = KotlinMetadataUtils.getMetadataUtil();
+ ProcessorErrors.checkState(
+ !metadataUtil.hasMetadata(androidEntryPointElement)
+ || !metadataUtil.containsConstructorWithDefaultParam(baseElement),
+ baseElement,
+ "The base class, '%s', of the @AndroidEntryPoint, '%s', contains a constructor with "
+ + "default parameters. This is currently not supported by the Gradle plugin. Either "
+ + "specify the base class as described at "
+ + "https://dagger.dev/hilt/gradle-setup#why-use-the-plugin or remove the default value "
+ + "declaration.",
+ baseElement.getQualifiedName(),
+ androidEntryPointElement.getQualifiedName());
+ generatedClassName = generatedClassName(androidEntryPointElement);
+ } else {
+ baseElement = androidEntryPointClassValue;
+ ProcessorErrors.checkState(
+ !MoreTypes.isTypeOf(Void.class, baseElement.asType()),
+ androidEntryPointElement,
+ "Expected @%s to have a value."
+ + " Did you forget to apply the Gradle Plugin? (dagger.hilt.android.plugin)\n"
+ + "See https://dagger.dev/hilt/gradle-setup.html" ,
+ annotationClassName.simpleName());
+
+ // Check that the root $CLASS extends Hilt_$CLASS
+ String extendsName =
+ env.getTypeUtils()
+ .asElement(androidEntryPointElement.getSuperclass())
+ .getSimpleName()
+ .toString();
+ generatedClassName = generatedClassName(androidEntryPointElement);
+ ProcessorErrors.checkState(
+ extendsName.contentEquals(generatedClassName.simpleName()),
+ androidEntryPointElement,
+ "@%s class expected to extend %s. Found: %s",
+ annotationClassName.simpleName(),
+ generatedClassName.simpleName(),
+ extendsName);
+ }
+
+ Optional<AndroidEntryPointMetadata> baseMetadata =
+ baseMetadata(env, androidEntryPointElement, baseElement, inheritanceTrace);
+
+ if (baseMetadata.isPresent()) {
+ return manuallyConstruct(
+ androidEntryPointElement,
+ baseElement,
+ generatedClassName,
+ requiresBytecodeInjection,
+ baseMetadata.get().androidType(),
+ baseMetadata,
+ baseMetadata.get().installInComponents(),
+ baseMetadata.get().componentManager(),
+ baseMetadata.get().componentManagerInitArgs());
+ } else {
+ Type type = Type.of(androidEntryPointElement, baseElement);
+ return manuallyConstruct(
+ androidEntryPointElement,
+ baseElement,
+ generatedClassName,
+ requiresBytecodeInjection,
+ type.androidType,
+ Optional.empty(),
+ ImmutableSet.of(type.component),
+ type.manager,
+ Optional.ofNullable(type.componentManagerInitArgs));
+ }
+ }
+
+ private static Optional<AndroidEntryPointMetadata> baseMetadata(
+ ProcessingEnvironment env,
+ TypeElement element,
+ TypeElement baseElement,
+ LinkedHashSet<Element> inheritanceTrace) {
+ ProcessorErrors.checkState(
+ inheritanceTrace.add(baseElement),
+ element,
+ cyclicInheritanceErrorMessage(inheritanceTrace, baseElement));
+ if (hasAndroidEntryPointMetadata(baseElement)) {
+ AndroidEntryPointMetadata baseMetadata =
+ AndroidEntryPointMetadata.of(env, baseElement, inheritanceTrace);
+ checkConsistentAnnotations(element, baseMetadata);
+ return Optional.of(baseMetadata);
+ }
+
+ TypeMirror superClass = baseElement.getSuperclass();
+ // None type is returned if this is an interface or Object
+ if (superClass.getKind() != TypeKind.NONE && superClass.getKind() != TypeKind.ERROR) {
+ Preconditions.checkState(superClass.getKind() == TypeKind.DECLARED);
+ return baseMetadata(env, element, MoreTypes.asTypeElement(superClass), inheritanceTrace);
+ }
+
+ return Optional.empty();
+ }
+
+ private static String cyclicInheritanceErrorMessage(
+ LinkedHashSet<Element> inheritanceTrace, TypeElement cycleEntryPoint) {
+ return String.format(
+ "Cyclic inheritance detected. Make sure the base class of @AndroidEntryPoint "
+ + "is not the annotated class itself or subclass of the annotated class.\n"
+ + "The cyclic inheritance structure: %s --> %s\n",
+ inheritanceTrace.stream()
+ .map(Element::asType)
+ .map(TypeMirror::toString)
+ .collect(Collectors.joining(" --> ")),
+ cycleEntryPoint.asType());
+ }
+
+ private static boolean isKotlinClass(TypeElement typeElement) {
+ return typeElement.getAnnotationMirrors().stream()
+ .map(mirror -> mirror.getAnnotationType())
+ .anyMatch(type -> ClassName.get(type).equals(ClassNames.KOTLIN_METADATA));
+ }
+
+ /**
+ * The Android type of the Android Entry Point element. Component splits (like with fragment
+ * bindings) are coalesced.
+ */
+ public enum AndroidType {
+ APPLICATION,
+ ACTIVITY,
+ BROADCAST_RECEIVER,
+ FRAGMENT,
+ SERVICE,
+ VIEW
+ }
+
+ /** The type of Android Entry Point element. This includes splits for different components. */
+ private static final class Type {
+ private static final Type APPLICATION =
+ new Type(
+ AndroidClassNames.SINGLETON_COMPONENT,
+ AndroidType.APPLICATION,
+ AndroidClassNames.APPLICATION_COMPONENT_MANAGER,
+ null);
+ private static final Type SERVICE =
+ new Type(
+ AndroidClassNames.SERVICE_COMPONENT,
+ AndroidType.SERVICE,
+ AndroidClassNames.SERVICE_COMPONENT_MANAGER,
+ CodeBlock.of("this"));
+ private static final Type BROADCAST_RECEIVER =
+ new Type(
+ AndroidClassNames.SINGLETON_COMPONENT,
+ AndroidType.BROADCAST_RECEIVER,
+ AndroidClassNames.BROADCAST_RECEIVER_COMPONENT_MANAGER,
+ null);
+ private static final Type ACTIVITY =
+ new Type(
+ AndroidClassNames.ACTIVITY_COMPONENT,
+ AndroidType.ACTIVITY,
+ AndroidClassNames.ACTIVITY_COMPONENT_MANAGER,
+ CodeBlock.of("this"));
+ private static final Type FRAGMENT =
+ new Type(
+ AndroidClassNames.FRAGMENT_COMPONENT,
+ AndroidType.FRAGMENT,
+ AndroidClassNames.FRAGMENT_COMPONENT_MANAGER,
+ CodeBlock.of("this"));
+ private static final Type VIEW =
+ new Type(
+ AndroidClassNames.VIEW_WITH_FRAGMENT_COMPONENT,
+ AndroidType.VIEW,
+ AndroidClassNames.VIEW_COMPONENT_MANAGER,
+ CodeBlock.of("this, true /* hasFragmentBindings */"));
+ private static final Type VIEW_NO_FRAGMENT =
+ new Type(
+ AndroidClassNames.VIEW_COMPONENT,
+ AndroidType.VIEW,
+ AndroidClassNames.VIEW_COMPONENT_MANAGER,
+ CodeBlock.of("this, false /* hasFragmentBindings */"));
+
+ final ClassName component;
+ final AndroidType androidType;
+ final ClassName manager;
+ final CodeBlock componentManagerInitArgs;
+
+ Type(
+ ClassName component,
+ AndroidType androidType,
+ ClassName manager,
+ CodeBlock componentManagerInitArgs) {
+ this.component = component;
+ this.androidType = androidType;
+ this.manager = manager;
+ this.componentManagerInitArgs = componentManagerInitArgs;
+ }
+
+ AndroidType androidType() {
+ return androidType;
+ }
+
+ private static Type of(TypeElement element, TypeElement baseElement) {
+ if (Processors.hasAnnotation(element, AndroidClassNames.HILT_ANDROID_APP)) {
+ return forHiltAndroidApp(element, baseElement);
+ }
+ return forAndroidEntryPoint(element, baseElement);
+ }
+
+ private static Type forHiltAndroidApp(TypeElement element, TypeElement baseElement) {
+ ProcessorErrors.checkState(
+ Processors.isAssignableFrom(baseElement, AndroidClassNames.APPLICATION),
+ element,
+ "@HiltAndroidApp base class must extend Application. Found: %s",
+ baseElement);
+ return Type.APPLICATION;
+ }
+
+ private static Type forAndroidEntryPoint(TypeElement element, TypeElement baseElement) {
+ if (Processors.isAssignableFrom(baseElement, AndroidClassNames.ACTIVITY)) {
+ ProcessorErrors.checkState(
+ Processors.isAssignableFrom(baseElement, AndroidClassNames.COMPONENT_ACTIVITY),
+ element,
+ "Activities annotated with @AndroidEntryPoint must be a subclass of "
+ + "androidx.activity.ComponentActivity. (e.g. FragmentActivity, "
+ + "AppCompatActivity, etc.)"
+ );
+ return Type.ACTIVITY;
+ } else if (Processors.isAssignableFrom(baseElement, AndroidClassNames.SERVICE)) {
+ return Type.SERVICE;
+ } else if (Processors.isAssignableFrom(baseElement, AndroidClassNames.BROADCAST_RECEIVER)) {
+ return Type.BROADCAST_RECEIVER;
+ } else if (Processors.isAssignableFrom(baseElement, AndroidClassNames.FRAGMENT)) {
+ return Type.FRAGMENT;
+ } else if (Processors.isAssignableFrom(baseElement, AndroidClassNames.VIEW)) {
+ boolean withFragmentBindings =
+ Processors.hasAnnotation(element, AndroidClassNames.WITH_FRAGMENT_BINDINGS);
+ return withFragmentBindings ? Type.VIEW : Type.VIEW_NO_FRAGMENT;
+ } else if (Processors.isAssignableFrom(baseElement, AndroidClassNames.APPLICATION)) {
+ throw new BadInputException(
+ "@AndroidEntryPoint cannot be used on an Application. Use @HiltAndroidApp instead.",
+ element);
+ }
+ throw new BadInputException(
+ "@AndroidEntryPoint base class must extend ComponentActivity, (support) Fragment, "
+ + "View, Service, or BroadcastReceiver.",
+ element);
+ }
+ }
+
+ private static void checkConsistentAnnotations(
+ TypeElement element, AndroidEntryPointMetadata baseMetadata) {
+ TypeElement baseElement = baseMetadata.element();
+ checkAnnotationsMatch(element, baseElement, AndroidClassNames.WITH_FRAGMENT_BINDINGS);
+
+ ProcessorErrors.checkState(
+ baseMetadata.allowsOptionalInjection()
+ || !Processors.hasAnnotation(element, AndroidClassNames.OPTIONAL_INJECT),
+ element,
+ "@OptionalInject Hilt class cannot extend from a non-optional @AndroidEntryPoint "
+ + "base: %s",
+ element);
+ }
+
+ private static void checkAnnotationsMatch(
+ TypeElement element, TypeElement baseElement, ClassName annotationName) {
+ boolean isAnnotated = Processors.hasAnnotation(element, annotationName);
+ boolean isBaseAnnotated = Processors.hasAnnotation(baseElement, annotationName);
+ ProcessorErrors.checkState(
+ isAnnotated == isBaseAnnotated,
+ element,
+ isBaseAnnotated
+ ? "Classes that extend an @%1$s base class must also be annotated @%1$s"
+ : "Classes that extend a @AndroidEntryPoint base class must not use @%1$s when the "
+ + "base class "
+ + "does not use @%1$s",
+ annotationName.simpleName());
+ }
+}
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointProcessor.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointProcessor.java
new file mode 100644
index 000000000..7bb9b9afe
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointProcessor.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.androidentrypoint;
+
+import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.ImmutableSet;
+import dagger.hilt.android.processor.internal.AndroidClassNames;
+import dagger.hilt.processor.internal.BaseProcessor;
+import java.util.Set;
+import javax.annotation.processing.Processor;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
+
+/**
+ * Processor that creates a module for classes marked with {@link
+ * dagger.hilt.android.AndroidEntryPoint}.
+ */
+@IncrementalAnnotationProcessor(ISOLATING)
+@AutoService(Processor.class)
+public final class AndroidEntryPointProcessor extends BaseProcessor {
+
+ @Override
+ public Set<String> getSupportedAnnotationTypes() {
+ return ImmutableSet.of(
+ AndroidClassNames.ANDROID_ENTRY_POINT.toString(),
+ AndroidClassNames.HILT_ANDROID_APP.toString());
+ }
+
+ @Override
+ public Set<String> getSupportedOptions() {
+ return HiltCompilerOptions.getProcessorOptions();
+ }
+
+ @Override
+ public boolean delayErrors() {
+ return true;
+ }
+
+ @Override
+ public void processEach(TypeElement annotation, Element element) throws Exception {
+ AndroidEntryPointMetadata metadata = AndroidEntryPointMetadata.of(getProcessingEnv(), element);
+ new InjectorEntryPointGenerator(getProcessingEnv(), metadata).generate();
+ switch (metadata.androidType()) {
+ case APPLICATION:
+ new ApplicationGenerator(getProcessingEnv(), metadata).generate();
+ break;
+ case ACTIVITY:
+ new ActivityGenerator(getProcessingEnv(), metadata).generate();
+ break;
+ case BROADCAST_RECEIVER:
+ new BroadcastReceiverGenerator(getProcessingEnv(), metadata).generate();
+ break;
+ case FRAGMENT:
+ new FragmentGenerator(
+ getProcessingEnv(), metadata )
+ .generate();
+ break;
+ case SERVICE:
+ new ServiceGenerator(getProcessingEnv(), metadata).generate();
+ break;
+ case VIEW:
+ new ViewGenerator(getProcessingEnv(), metadata).generate();
+ break;
+ default:
+ throw new IllegalStateException("Unknown Hilt type: " + metadata.androidType());
+ }
+ }
+}
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/ApplicationGenerator.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/ApplicationGenerator.java
new file mode 100644
index 000000000..f16e06dee
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/ApplicationGenerator.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.androidentrypoint;
+
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.TypeVariableName;
+import dagger.hilt.android.processor.internal.AndroidClassNames;
+import dagger.hilt.processor.internal.ComponentNames;
+import dagger.hilt.processor.internal.Processors;
+import java.io.IOException;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Modifier;
+
+/** Generates an Hilt Application for an @AndroidEntryPoint app class. */
+public final class ApplicationGenerator {
+ private final ProcessingEnvironment env;
+ private final AndroidEntryPointMetadata metadata;
+ private final ClassName wrapperClassName;
+
+ public ApplicationGenerator(ProcessingEnvironment env, AndroidEntryPointMetadata metadata) {
+ this.env = env;
+ this.metadata = metadata;
+ wrapperClassName = metadata.generatedClassName();
+ }
+
+ // @Generated("ApplicationGenerator")
+ // abstract class Hilt_$APP extends $BASE implements ComponentManager<ApplicationComponent> {
+ // ...
+ // }
+ public void generate() throws IOException {
+ TypeSpec.Builder typeSpecBuilder =
+ TypeSpec.classBuilder(wrapperClassName.simpleName())
+ .addOriginatingElement(metadata.element())
+ .superclass(metadata.baseClassName())
+ .addModifiers(metadata.generatedClassModifiers())
+ .addField(componentManagerField())
+ .addMethod(componentManagerMethod());
+
+ Generators.addGeneratedBaseClassJavadoc(typeSpecBuilder, AndroidClassNames.HILT_ANDROID_APP);
+ Processors.addGeneratedAnnotation(typeSpecBuilder, env, getClass());
+
+ metadata.baseElement().getTypeParameters().stream()
+ .map(TypeVariableName::get)
+ .forEachOrdered(typeSpecBuilder::addTypeVariable);
+
+ Generators.copyLintAnnotations(metadata.element(), typeSpecBuilder);
+ Generators.addComponentOverride(metadata, typeSpecBuilder);
+
+ typeSpecBuilder.addMethod(onCreateMethod());
+
+ JavaFile.builder(metadata.elementClassName().packageName(), typeSpecBuilder.build())
+ .build()
+ .writeTo(env.getFiler());
+ }
+
+ // private final ApplicationComponentManager<ApplicationComponent> componentManager =
+ // new ApplicationComponentManager(/* creatorType */);
+ private FieldSpec componentManagerField() {
+ ParameterSpec managerParam = metadata.componentManagerParam();
+ return FieldSpec.builder(managerParam.type, managerParam.name)
+ .addModifiers(Modifier.PRIVATE, Modifier.FINAL)
+ .initializer("new $T($L)", AndroidClassNames.APPLICATION_COMPONENT_MANAGER, creatorType())
+ .build();
+ }
+
+ // protected ApplicationComponentManager<ApplicationComponent> componentManager() {
+ // return componentManager();
+ // }
+ private MethodSpec componentManagerMethod() {
+ return MethodSpec.methodBuilder("componentManager")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .returns(metadata.componentManagerParam().type)
+ .addStatement("return $N", metadata.componentManagerParam())
+ .build();
+ }
+
+ // new Supplier<ApplicationComponent>() {
+ // @Override
+ // public ApplicationComponent get() {
+ // return DaggerApplicationComponent.builder()
+ // .applicationContextModule(new ApplicationContextModule(Hilt_$APP.this))
+ // .build();
+ // }
+ // }
+ private TypeSpec creatorType() {
+ ClassName component =
+ ComponentNames.generatedComponent(
+ metadata.elementClassName(), AndroidClassNames.SINGLETON_COMPONENT);
+ return TypeSpec.anonymousClassBuilder("")
+ .addSuperinterface(AndroidClassNames.COMPONENT_SUPPLIER)
+ .addMethod(
+ MethodSpec.methodBuilder("get")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC)
+ .returns(TypeName.OBJECT)
+ .addStatement(
+ "return $T.builder()\n"
+ + ".applicationContextModule(new $T($T.this))\n"
+ + ".build()",
+ Processors.prepend(Processors.getEnclosedClassName(component), "Dagger"),
+ AndroidClassNames.APPLICATION_CONTEXT_MODULE,
+ wrapperClassName)
+ .build())
+ .build();
+ }
+
+ // @CallSuper
+ // @Override
+ // public void onCreate() {
+ // // This is a known unsafe cast but should be fine if the only use is
+ // // $APP extends Hilt_$APP
+ // generatedComponent().inject(($APP) this);
+ // super.onCreate();
+ // }
+ private MethodSpec onCreateMethod() {
+ return MethodSpec.methodBuilder("onCreate")
+ .addAnnotation(AndroidClassNames.CALL_SUPER)
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC)
+ .addCode(injectCodeBlock())
+ .addStatement("super.onCreate()")
+ .build();
+ }
+
+ // // This is a known unsafe cast but should be fine if the only use is
+ // // $APP extends Hilt_$APP
+ // generatedComponent().inject$APP(($APP) this);
+ private CodeBlock injectCodeBlock() {
+ return CodeBlock.builder()
+ .add("// This is a known unsafe cast, but is safe in the only correct use case:\n")
+ .add("// $T extends $T\n", metadata.elementClassName(), metadata.generatedClassName())
+ .addStatement(
+ "(($T) generatedComponent()).$L($L)",
+ metadata.injectorClassName(),
+ metadata.injectMethodName(),
+ Generators.unsafeCastThisTo(metadata.elementClassName()))
+ .build();
+ }
+}
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/BUILD b/java/dagger/hilt/android/processor/internal/androidentrypoint/BUILD
new file mode 100644
index 000000000..55e9ddc02
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/BUILD
@@ -0,0 +1,109 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Description:
+# Hilt android processors.
+
+package(default_visibility = ["//:src"])
+
+java_plugin(
+ name = "plugin",
+ generates_api = 1,
+ processor_class = "dagger.hilt.android.processor.internal.androidentrypoint.AndroidEntryPointProcessor",
+ tags = [
+ "annotation=dagger.hilt.android.AndroidEntryPoint;" +
+ "genclass=${package}.Hilt_${outerclasses}${classname};" +
+ "genclass=${package}.${outerclasses}${classname}_EntryPoint",
+ ],
+ deps = [
+ ":processor_lib",
+ ],
+)
+
+java_library(
+ name = "processor_lib",
+ srcs = ["AndroidEntryPointProcessor.java"],
+ deps = [
+ ":android_generators",
+ ":compiler_options",
+ ":metadata",
+ "//java/dagger/hilt/android/processor/internal:android_classnames",
+ "//java/dagger/hilt/processor/internal:base_processor",
+ "//java/dagger/hilt/processor/internal:processors",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/auto:service",
+ "@google_bazel_common//third_party/java/incap",
+ ],
+)
+
+java_library(
+ name = "android_generators",
+ srcs = [
+ "ActivityGenerator.java",
+ "ApplicationGenerator.java",
+ "BroadcastReceiverGenerator.java",
+ "FragmentGenerator.java",
+ "Generators.java",
+ "InjectorEntryPointGenerator.java",
+ "ServiceGenerator.java",
+ "ViewGenerator.java",
+ ],
+ deps = [
+ ":metadata",
+ "//java/dagger/hilt/android/processor/internal:android_classnames",
+ "//java/dagger/hilt/android/processor/internal:utils",
+ "//java/dagger/hilt/processor/internal:classnames",
+ "//java/dagger/hilt/processor/internal:component_names",
+ "//java/dagger/hilt/processor/internal:processor_errors",
+ "//java/dagger/hilt/processor/internal:processors",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/codegen/langmodel",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
+
+java_library(
+ name = "metadata",
+ srcs = [
+ "AndroidEntryPointMetadata.java",
+ ],
+ deps = [
+ ":compiler_options",
+ "//java/dagger/hilt/android/processor/internal:android_classnames",
+ "//java/dagger/hilt/processor/internal:classnames",
+ "//java/dagger/hilt/processor/internal:components",
+ "//java/dagger/hilt/processor/internal:kotlin",
+ "//java/dagger/hilt/processor/internal:processor_errors",
+ "//java/dagger/hilt/processor/internal:processors",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/codegen/kotlin",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
+
+java_library(
+ name = "compiler_options",
+ srcs = ["HiltCompilerOptions.java"],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/BroadcastReceiverGenerator.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/BroadcastReceiverGenerator.java
new file mode 100644
index 000000000..f8e9f6078
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/BroadcastReceiverGenerator.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.androidentrypoint;
+
+import static dagger.internal.codegen.langmodel.DaggerElements.getMethodDescriptor;
+
+import com.google.common.collect.Iterables;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.TypeVariableName;
+import dagger.hilt.android.processor.internal.AndroidClassNames;
+import dagger.hilt.android.processor.internal.MoreTypes;
+import dagger.hilt.processor.internal.Processors;
+import java.io.IOException;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.util.ElementFilter;
+
+/** Generates an Hilt BroadcastReceiver class for the @AndroidEntryPoint annotated class. */
+public final class BroadcastReceiverGenerator {
+
+ private static final String ON_RECEIVE_DESCRIPTOR =
+ "onReceive(Landroid/content/Context;Landroid/content/Intent;)V";
+
+ private final ProcessingEnvironment env;
+ private final AndroidEntryPointMetadata metadata;
+ private final ClassName generatedClassName;
+
+ public BroadcastReceiverGenerator(
+ ProcessingEnvironment env, AndroidEntryPointMetadata metadata) {
+ this.env = env;
+ this.metadata = metadata;
+
+ generatedClassName = metadata.generatedClassName();
+ }
+
+ // @Generated("BroadcastReceiverGenerator")
+ // abstract class Hilt_$CLASS extends $BASE {
+ // ...
+ // }
+ public void generate() throws IOException {
+ TypeSpec.Builder builder =
+ TypeSpec.classBuilder(generatedClassName.simpleName())
+ .addOriginatingElement(metadata.element())
+ .superclass(metadata.baseClassName())
+ .addModifiers(metadata.generatedClassModifiers())
+ .addMethod(onReceiveMethod());
+
+ Generators.addGeneratedBaseClassJavadoc(builder, AndroidClassNames.ANDROID_ENTRY_POINT);
+ Processors.addGeneratedAnnotation(builder, env, getClass());
+ Generators.copyConstructors(metadata.baseElement(), builder);
+
+ metadata.baseElement().getTypeParameters().stream()
+ .map(TypeVariableName::get)
+ .forEachOrdered(builder::addTypeVariable);
+
+ Generators.addInjectionMethods(metadata, builder);
+ Generators.copyLintAnnotations(metadata.element(), builder);
+
+ // Add an unused field used as a marker to let the bytecode injector know this receiver will
+ // need to be injected with a super.onReceive call. This is only necessary if no concrete
+ // onReceive call is implemented in any of the super classes.
+ if (metadata.requiresBytecodeInjection() && !isOnReceiveImplemented(metadata.baseElement())) {
+ builder.addField(
+ FieldSpec.builder(
+ TypeName.BOOLEAN,
+ "onReceiveBytecodeInjectionMarker",
+ Modifier.PRIVATE,
+ Modifier.FINAL)
+ .initializer("false")
+ .build());
+ }
+
+ JavaFile.builder(generatedClassName.packageName(),
+ builder.build()).build().writeTo(env.getFiler());
+ }
+
+ private static boolean isOnReceiveImplemented(TypeElement typeElement) {
+ boolean isImplemented =
+ ElementFilter.methodsIn(typeElement.getEnclosedElements()).stream()
+ .anyMatch(
+ methodElement ->
+ getMethodDescriptor(methodElement).equals(ON_RECEIVE_DESCRIPTOR)
+ && !methodElement.getModifiers().contains(Modifier.ABSTRACT));
+ if (isImplemented) {
+ return true;
+ } else if (typeElement.getSuperclass().getKind() != TypeKind.NONE) {
+ return isOnReceiveImplemented(MoreTypes.asTypeElement(typeElement.getSuperclass()));
+ } else {
+ return false;
+ }
+ }
+
+ // @Override
+ // public void onReceive(Context context, Intent intent) {
+ // inject(context);
+ // super.onReceive();
+ // }
+ private MethodSpec onReceiveMethod() throws IOException {
+ MethodSpec.Builder method =
+ MethodSpec.methodBuilder("onReceive")
+ .addAnnotation(Override.class)
+ .addAnnotation(AndroidClassNames.CALL_SUPER)
+ .addModifiers(Modifier.PUBLIC)
+ .addParameter(ParameterSpec.builder(AndroidClassNames.CONTEXT, "context").build())
+ .addParameter(ParameterSpec.builder(AndroidClassNames.INTENT, "intent").build())
+ .addStatement("inject(context)");
+
+ if (metadata.overridesAndroidEntryPointClass()) {
+ // We directly call super.onReceive here because we know Hilt base classes have a
+ // non-abstract onReceive method. However, because the Hilt base class may not be generated
+ // already we cannot fall down to the below logic to find it.
+ method.addStatement("super.onReceive(context, intent)");
+ } else {
+ // Get the onReceive method element from BroadcastReceiver.
+ ExecutableElement onReceiveElement =
+ Iterables.getOnlyElement(
+ MoreTypes.findMethods(
+ env.getElementUtils()
+ .getTypeElement(AndroidClassNames.BROADCAST_RECEIVER.toString()),
+ "onReceive"));
+
+ // If the base class or one of its super classes implements onReceive, call super.onReceive()
+ MoreTypes.findInheritedMethod(env.getTypeUtils(), metadata.baseElement(), onReceiveElement)
+ .filter(onReceive -> !onReceive.getModifiers().contains(Modifier.ABSTRACT))
+ .ifPresent(onReceive -> method.addStatement("super.onReceive(context, intent)"));
+ }
+
+ return method.build();
+ }
+}
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/FragmentGenerator.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/FragmentGenerator.java
new file mode 100644
index 000000000..4ef479f46
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/FragmentGenerator.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.androidentrypoint;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.TypeVariableName;
+import dagger.hilt.android.processor.internal.AndroidClassNames;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.Processors;
+import java.io.IOException;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Modifier;
+
+/** Generates an Hilt Fragment class for the @AndroidEntryPoint annotated class. */
+public final class FragmentGenerator {
+ private static final FieldSpec COMPONENT_CONTEXT_FIELD =
+ FieldSpec.builder(AndroidClassNames.CONTEXT_WRAPPER, "componentContext")
+ .addModifiers(Modifier.PRIVATE)
+ .build();
+
+ private final ProcessingEnvironment env;
+ private final AndroidEntryPointMetadata metadata;
+ private final ClassName generatedClassName;
+
+ public FragmentGenerator(
+ ProcessingEnvironment env,
+ AndroidEntryPointMetadata metadata ) {
+ this.env = env;
+ this.metadata = metadata;
+ generatedClassName = metadata.generatedClassName();
+ }
+
+ public void generate() throws IOException {
+ JavaFile.builder(generatedClassName.packageName(), createTypeSpec())
+ .build()
+ .writeTo(env.getFiler());
+ }
+
+ // @Generated("FragmentGenerator")
+ // abstract class Hilt_$CLASS extends $BASE implements ComponentManager<?> {
+ // ...
+ // }
+ TypeSpec createTypeSpec() {
+ TypeSpec.Builder builder =
+ TypeSpec.classBuilder(generatedClassName.simpleName())
+ .addOriginatingElement(metadata.element())
+ .superclass(metadata.baseClassName())
+ .addModifiers(metadata.generatedClassModifiers())
+ .addField(COMPONENT_CONTEXT_FIELD)
+ .addMethod(onAttachContextMethod())
+ .addMethod(onAttachActivityMethod())
+ .addMethod(initializeComponentContextMethod())
+ .addMethod(getContextMethod())
+ .addMethod(inflatorMethod());
+
+ Generators.addGeneratedBaseClassJavadoc(builder, AndroidClassNames.ANDROID_ENTRY_POINT);
+ Processors.addGeneratedAnnotation(builder, env, getClass());
+ Generators.copyLintAnnotations(metadata.element(), builder);
+ Generators.addSuppressAnnotation(builder, "deprecation");
+ Generators.copyConstructors(metadata.baseElement(), builder);
+
+ metadata.baseElement().getTypeParameters().stream()
+ .map(TypeVariableName::get)
+ .forEachOrdered(builder::addTypeVariable);
+
+ Generators.addComponentOverride(metadata, builder);
+
+ Generators.addInjectionMethods(metadata, builder);
+
+ if (!metadata.overridesAndroidEntryPointClass() ) {
+ builder.addMethod(getDefaultViewModelProviderFactory());
+ }
+
+ return builder.build();
+ }
+
+ // @CallSuper
+ // @Override
+ // public void onAttach(Activity activity) {
+ // super.onAttach(activity);
+ // initializeComponentContext();
+ // }
+ private static MethodSpec onAttachContextMethod() {
+ return MethodSpec.methodBuilder("onAttach")
+ .addAnnotation(Override.class)
+ .addAnnotation(AndroidClassNames.CALL_SUPER)
+ .addModifiers(Modifier.PUBLIC)
+ .addParameter(AndroidClassNames.CONTEXT, "context")
+ .addStatement("super.onAttach(context)")
+ .addStatement("initializeComponentContext()")
+ .build();
+ }
+
+ // @CallSuper
+ // @Override
+ // public void onAttach(Activity activity) {
+ // super.onAttach(activity);
+ // Preconditions.checkState(
+ // componentContext == null || FragmentComponentManager.findActivity(
+ // componentContext) == activity, "...");
+ // initializeComponentContext();
+ // }
+ private static MethodSpec onAttachActivityMethod() {
+ return MethodSpec.methodBuilder("onAttach")
+ .addAnnotation(Override.class)
+ .addAnnotation(AndroidClassNames.CALL_SUPER)
+ .addAnnotation(AndroidClassNames.MAIN_THREAD)
+ .addModifiers(Modifier.PUBLIC)
+ .addParameter(AndroidClassNames.ACTIVITY, "activity")
+ .addStatement("super.onAttach(activity)")
+ .addStatement(
+ "$T.checkState($N == null || $T.findActivity($N) == activity, $S)",
+ ClassNames.PRECONDITIONS,
+ COMPONENT_CONTEXT_FIELD,
+ AndroidClassNames.FRAGMENT_COMPONENT_MANAGER,
+ COMPONENT_CONTEXT_FIELD,
+ "onAttach called multiple times with different Context! "
+ + "Hilt Fragments should not be retained.")
+ .addStatement("initializeComponentContext()")
+ .build();
+ }
+
+ // private void initializeComponentContext() {
+ // // Only inject on the first call to onAttach.
+ // if (componentContext == null) {
+ // // Note: The LayoutInflater provided by this componentContext may be different from super
+ // // Fragment's because we are getting it from base context instead of cloning from super
+ // // Fragment's LayoutInflater.
+ // componentContext = FragmentComponentManager.createContextWrapper(super.getContext(), this);
+ // inject();
+ // }
+ // }
+ private MethodSpec initializeComponentContextMethod() {
+ return MethodSpec.methodBuilder("initializeComponentContext")
+ .addModifiers(Modifier.PRIVATE)
+ .addComment("Only inject on the first call to onAttach.")
+ .beginControlFlow("if ($N == null)", COMPONENT_CONTEXT_FIELD)
+ .addComment(
+ "Note: The LayoutInflater provided by this componentContext may be different from"
+ + " super Fragment's because we getting it from base context instead of cloning"
+ + " from the super Fragment's LayoutInflater.")
+ .addStatement(
+ "$N = $T.createContextWrapper(super.getContext(), this)",
+ COMPONENT_CONTEXT_FIELD,
+ metadata.componentManager())
+ .addStatement("inject()")
+ .endControlFlow()
+ .build();
+ }
+
+ // @Override
+ // public Context getContext() {
+ // return componentContext;
+ // }
+ private static MethodSpec getContextMethod() {
+ return MethodSpec.methodBuilder("getContext")
+ .returns(AndroidClassNames.CONTEXT)
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC)
+ .addStatement("return $N", COMPONENT_CONTEXT_FIELD)
+ .build();
+ }
+
+ // @Override
+ // public LayoutInflater onGetLayoutInflater(Bundle savedInstanceState) {
+ // LayoutInflater inflater = super.onGetLayoutInflater(savedInstanceState);
+ // return LayoutInflater.from(FragmentComponentManager.createContextWrapper(inflater, this));
+ // }
+ private MethodSpec inflatorMethod() {
+ return MethodSpec.methodBuilder("onGetLayoutInflater")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC)
+ .addParameter(AndroidClassNames.BUNDLE, "savedInstanceState")
+ .returns(AndroidClassNames.LAYOUT_INFLATER)
+ .addStatement(
+ "$T inflater = super.onGetLayoutInflater(savedInstanceState)",
+ AndroidClassNames.LAYOUT_INFLATER)
+ .addStatement(
+ "return $T.from($T.createContextWrapper(inflater, this))",
+ AndroidClassNames.LAYOUT_INFLATER,
+ metadata.componentManager())
+ .build();
+ }
+
+ // @Override
+ // public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
+ // return DefaultViewModelFactories.getFragmentFactory(this);
+ // }
+ private MethodSpec getDefaultViewModelProviderFactory() {
+ return MethodSpec.methodBuilder("getDefaultViewModelProviderFactory")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC)
+ .returns(AndroidClassNames.VIEW_MODEL_PROVIDER_FACTORY)
+ .addStatement(
+ "return $T.getFragmentFactory(this)",
+ AndroidClassNames.DEFAULT_VIEW_MODEL_FACTORIES)
+ .build();
+ }
+}
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/Generators.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/Generators.java
new file mode 100644
index 000000000..daadd032d
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/Generators.java
@@ -0,0 +1,493 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.androidentrypoint;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.extension.DaggerCollectors.toOptional;
+import static javax.lang.model.element.Modifier.PRIVATE;
+
+import com.google.common.base.Preconditions;
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.hilt.android.processor.internal.AndroidClassNames;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.Processors;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.util.ElementFilter;
+
+/** Helper class for writing Hilt generators. */
+final class Generators {
+
+ static void addGeneratedBaseClassJavadoc(TypeSpec.Builder builder, ClassName annotation) {
+ builder.addJavadoc("A generated base class to be extended by the @$T annotated class. If using"
+ + " the Gradle plugin, this is swapped as the base class via bytecode transformation.",
+ annotation);
+ }
+
+ /**
+ * Copies all constructors with arguments to the builder.
+ */
+ static void copyConstructors(TypeElement baseClass, TypeSpec.Builder builder) {
+ copyConstructors(baseClass, CodeBlock.builder().build(), builder);
+ }
+
+ /**
+ * Copies all constructors with arguments along with an appended body to the builder.
+ */
+ static void copyConstructors(TypeElement baseClass, CodeBlock body, TypeSpec.Builder builder) {
+ List<ExecutableElement> constructors =
+ ElementFilter.constructorsIn(baseClass.getEnclosedElements())
+ .stream()
+ .filter(constructor -> !constructor.getModifiers().contains(PRIVATE))
+ .collect(Collectors.toList());
+
+ if (constructors.size() == 1
+ && getOnlyElement(constructors).getParameters().isEmpty()
+ && body.isEmpty()) {
+ // No need to copy the constructor if the default constructor will handle it.
+ return;
+ }
+
+ constructors.forEach(constructor -> builder.addMethod(copyConstructor(constructor, body)));
+ }
+
+ /** Returns Optional with AnnotationSpec for Nullable if found on element, empty otherwise. */
+ private static Optional<AnnotationSpec> getNullableAnnotationSpec(Element element) {
+ for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
+ if (annotationMirror
+ .getAnnotationType()
+ .asElement()
+ .getSimpleName()
+ .contentEquals("Nullable")) {
+ AnnotationSpec annotationSpec = AnnotationSpec.get(annotationMirror);
+ // If using the android internal Nullable, convert it to the externally-visible version.
+ return AndroidClassNames.NULLABLE_INTERNAL.equals(annotationSpec.type)
+ ? Optional.of(AnnotationSpec.builder(AndroidClassNames.NULLABLE).build())
+ : Optional.of(annotationSpec);
+ }
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Returns a ParameterSpec of the input parameter, @Nullable annotated if existing in original
+ * (this does not handle Nullable type annotations).
+ */
+ private static ParameterSpec getParameterSpecWithNullable(VariableElement parameter) {
+ ParameterSpec.Builder builder = ParameterSpec.get(parameter).toBuilder();
+ getNullableAnnotationSpec(parameter).ifPresent(builder::addAnnotation);
+ return builder.build();
+ }
+
+ /**
+ * Returns a {@link MethodSpec} for a constructor matching the given {@link ExecutableElement}
+ * constructor signature, and just calls super. If the constructor is
+ * {@link android.annotation.TargetApi} guarded, adds the TargetApi as well.
+ */
+ // Example:
+ // Foo(Param1 param1, Param2 param2) {
+ // super(param1, param2);
+ // }
+ static MethodSpec copyConstructor(ExecutableElement constructor) {
+ return copyConstructor(constructor, CodeBlock.builder().build());
+ }
+
+ private static MethodSpec copyConstructor(ExecutableElement constructor, CodeBlock body) {
+ List<ParameterSpec> params =
+ constructor.getParameters().stream()
+ .map(parameter -> getParameterSpecWithNullable(parameter))
+ .collect(Collectors.toList());
+
+ final MethodSpec.Builder builder =
+ MethodSpec.constructorBuilder()
+ .addParameters(params)
+ .addStatement(
+ "super($L)",
+ params.stream().map(param -> param.name).collect(Collectors.joining(", ")))
+ .addCode(body);
+
+ constructor.getAnnotationMirrors().stream()
+ .filter(a -> Processors.hasAnnotation(a, AndroidClassNames.TARGET_API))
+ .collect(toOptional())
+ .map(AnnotationSpec::get)
+ .ifPresent(builder::addAnnotation);
+
+ return builder.build();
+ }
+
+ /**
+ * Copies the Android lint annotations from the annotated element to the generated element.
+ *
+ * <p>Note: For now we only copy over {@link android.annotation.TargetApi}.
+ */
+ static void copyLintAnnotations(Element element, TypeSpec.Builder builder) {
+ if (Processors.hasAnnotation(element, AndroidClassNames.TARGET_API)) {
+ builder.addAnnotation(
+ AnnotationSpec.get(
+ Processors.getAnnotationMirror(element, AndroidClassNames.TARGET_API)));
+ }
+ }
+
+ // @Override
+ // public CompT generatedComponent() {
+ // return componentManager().generatedComponent();
+ // }
+ static void addComponentOverride(AndroidEntryPointMetadata metadata, TypeSpec.Builder builder) {
+ if (metadata.overridesAndroidEntryPointClass()) {
+ // We don't need to override this method if we are extending a Hilt type.
+ return;
+ }
+ builder
+ .addSuperinterface(ClassNames.GENERATED_COMPONENT_MANAGER_HOLDER)
+ .addMethod(
+ MethodSpec.methodBuilder("generatedComponent")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .returns(TypeName.OBJECT)
+ .addStatement("return $L.generatedComponent()", componentManagerCallBlock(metadata))
+ .build());
+ }
+
+ /** Adds the inject() and optionally the componentManager() methods to allow for injection. */
+ static void addInjectionMethods(AndroidEntryPointMetadata metadata, TypeSpec.Builder builder) {
+ switch (metadata.androidType()) {
+ case ACTIVITY:
+ case FRAGMENT:
+ case VIEW:
+ case SERVICE:
+ addComponentManagerMethods(metadata, builder);
+ // fall through
+ case BROADCAST_RECEIVER:
+ addInjectMethod(metadata, builder);
+ break;
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ // @Override
+ // public FragmentComponentManager componentManager() {
+ // if (componentManager == null) {
+ // synchronize (componentManagerLock) {
+ // if (componentManager == null) {
+ // componentManager = createComponentManager();
+ // }
+ // }
+ // }
+ // return componentManager;
+ // }
+ private static void addComponentManagerMethods(
+ AndroidEntryPointMetadata metadata, TypeSpec.Builder typeSpecBuilder) {
+ if (metadata.overridesAndroidEntryPointClass()) {
+ // We don't need to override this method if we are extending a Hilt type.
+ return;
+ }
+
+ ParameterSpec managerParam = metadata.componentManagerParam();
+ typeSpecBuilder.addField(componentManagerField(metadata));
+
+ typeSpecBuilder.addMethod(createComponentManagerMethod(metadata));
+
+ MethodSpec.Builder methodSpecBuilder =
+ MethodSpec.methodBuilder("componentManager")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .returns(managerParam.type)
+ .beginControlFlow("if ($N == null)", managerParam);
+
+ // Views do not do double-checked locking because this is called from the constructor
+ if (metadata.androidType() != AndroidEntryPointMetadata.AndroidType.VIEW) {
+ typeSpecBuilder.addField(componentManagerLockField());
+
+ methodSpecBuilder
+ .beginControlFlow("synchronized (componentManagerLock)")
+ .beginControlFlow("if ($N == null)", managerParam);
+ }
+
+ methodSpecBuilder
+ .addStatement("$N = createComponentManager()", managerParam)
+ .endControlFlow();
+
+ if (metadata.androidType() != AndroidEntryPointMetadata.AndroidType.VIEW) {
+ methodSpecBuilder
+ .endControlFlow()
+ .endControlFlow();
+ }
+
+ methodSpecBuilder.addStatement("return $N", managerParam);
+
+ typeSpecBuilder.addMethod(methodSpecBuilder.build());
+ }
+
+ // protected FragmentComponentManager createComponentManager() {
+ // return new FragmentComponentManager(initArgs);
+ // }
+ private static MethodSpec createComponentManagerMethod(AndroidEntryPointMetadata metadata) {
+ Preconditions.checkState(
+ metadata.componentManagerInitArgs().isPresent(),
+ "This method should not have been called for metadata where the init args are not"
+ + " present.");
+ return MethodSpec.methodBuilder("createComponentManager")
+ .addModifiers(Modifier.PROTECTED)
+ .returns(metadata.componentManager())
+ .addStatement(
+ "return new $T($L)",
+ metadata.componentManager(),
+ metadata.componentManagerInitArgs().get())
+ .build();
+ }
+
+ // private volatile ComponentManager componentManager;
+ private static FieldSpec componentManagerField(AndroidEntryPointMetadata metadata) {
+ ParameterSpec managerParam = metadata.componentManagerParam();
+ FieldSpec.Builder builder = FieldSpec.builder(managerParam.type, managerParam.name)
+ .addModifiers(Modifier.PRIVATE);
+
+ // Views do not need volatile since these are set in the constructor if ever set.
+ if (metadata.androidType() != AndroidEntryPointMetadata.AndroidType.VIEW) {
+ builder.addModifiers(Modifier.VOLATILE);
+ }
+
+ return builder.build();
+ }
+
+ // private final Object componentManagerLock = new Object();
+ private static FieldSpec componentManagerLockField() {
+ return FieldSpec.builder(TypeName.get(Object.class), "componentManagerLock")
+ .addModifiers(Modifier.PRIVATE, Modifier.FINAL)
+ .initializer("new Object()")
+ .build();
+ }
+
+ // protected void inject() {
+ // if (!injected) {
+ // generatedComponent().inject$CLASS(($CLASS) this);
+ // injected = true;
+ // }
+ // }
+ private static void addInjectMethod(
+ AndroidEntryPointMetadata metadata, TypeSpec.Builder typeSpecBuilder) {
+ MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder("inject")
+ .addModifiers(Modifier.PROTECTED);
+
+ // Check if the parent is a Hilt type. If it isn't or if it is but it
+ // wasn't injected by hilt, then return.
+ // Object parent = ...depends on type...
+ // if (!(parent instanceof GeneratedComponentManager)
+ // || ((parent instanceof InjectedByHilt) &&
+ // !((InjectedByHilt) parent).wasInjectedByHilt())) {
+ // return;
+ //
+ if (metadata.allowsOptionalInjection()) {
+ methodSpecBuilder
+ .addStatement("$T parent = $L", ClassNames.OBJECT, getParentCodeBlock(metadata))
+ .beginControlFlow(
+ "if (!(parent instanceof $T) "
+ + "|| ((parent instanceof $T) && !(($T) parent).wasInjectedByHilt()))",
+ ClassNames.GENERATED_COMPONENT_MANAGER,
+ AndroidClassNames.INJECTED_BY_HILT,
+ AndroidClassNames.INJECTED_BY_HILT)
+ .addStatement("return")
+ .endControlFlow();
+ }
+
+ typeSpecBuilder.addField(injectedField(metadata));
+
+ switch (metadata.androidType()) {
+ case ACTIVITY:
+ case FRAGMENT:
+ case VIEW:
+ case SERVICE:
+ // Only add @Override if an ancestor extends a generated Hilt class.
+ // When using bytecode injection, this isn't always guaranteed.
+ if (metadata.overridesAndroidEntryPointClass()
+ && ancestorExtendsGeneratedHiltClass(metadata)) {
+ methodSpecBuilder.addAnnotation(Override.class);
+ }
+ methodSpecBuilder
+ .beginControlFlow("if (!injected)")
+ .addStatement("injected = true")
+ .addStatement(
+ "(($T) $L).$L($L)",
+ metadata.injectorClassName(),
+ generatedComponentCallBlock(metadata),
+ metadata.injectMethodName(),
+ unsafeCastThisTo(metadata.elementClassName()))
+ .endControlFlow();
+ break;
+ case BROADCAST_RECEIVER:
+ typeSpecBuilder.addField(injectedLockField());
+
+ methodSpecBuilder
+ .addParameter(ParameterSpec.builder(AndroidClassNames.CONTEXT, "context").build())
+ .beginControlFlow("if (!injected)")
+ .beginControlFlow("synchronized (injectedLock)")
+ .beginControlFlow("if (!injected)")
+ .addStatement(
+ "(($T) $T.generatedComponent(context)).$L($L)",
+ metadata.injectorClassName(),
+ metadata.componentManager(),
+ metadata.injectMethodName(),
+ unsafeCastThisTo(metadata.elementClassName()))
+ .addStatement("injected = true")
+ .endControlFlow()
+ .endControlFlow()
+ .endControlFlow();
+ break;
+ default:
+ throw new AssertionError();
+ }
+
+ // Also add a wasInjectedByHilt method if needed.
+ // Even if we aren't optionally injected, if we override an optionally injected Hilt class
+ // we also need to override the wasInjectedByHilt method.
+ if (metadata.allowsOptionalInjection() || metadata.baseAllowsOptionalInjection()) {
+ typeSpecBuilder.addMethod(
+ MethodSpec.methodBuilder("wasInjectedByHilt")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC)
+ .returns(boolean.class)
+ .addStatement("return injected")
+ .build());
+ // Only add the interface though if this class allows optional injection (not that it
+ // really matters since if the base allows optional injection the class implements the
+ // interface anyway). But it is probably better to be consistent about only optionally
+ // injected classes extend the interface.
+ if (metadata.allowsOptionalInjection()) {
+ typeSpecBuilder.addSuperinterface(AndroidClassNames.INJECTED_BY_HILT);
+ }
+ }
+
+ typeSpecBuilder.addMethod(methodSpecBuilder.build());
+ }
+
+ private static CodeBlock getParentCodeBlock(AndroidEntryPointMetadata metadata) {
+ switch (metadata.androidType()) {
+ case ACTIVITY:
+ case SERVICE:
+ return CodeBlock.of("getApplicationContext()");
+ case FRAGMENT:
+ return CodeBlock.of("getHost()");
+ case VIEW:
+ return CodeBlock.of(
+ "$L.maybeGetParentComponentManager()", componentManagerCallBlock(metadata));
+ case BROADCAST_RECEIVER:
+ // Broadcast receivers receive a "context" parameter
+ return CodeBlock.of("context.getApplicationContext()");
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * Returns the call to {@code generatedComponent()} with casts if needed.
+ *
+ * <p>A cast is required when the root generated Hilt class uses bytecode injection because
+ * subclasses won't have access to the {@code generatedComponent()} method in that case.
+ */
+ private static CodeBlock generatedComponentCallBlock(AndroidEntryPointMetadata metadata) {
+ return CodeBlock.of(
+ "$L.generatedComponent()",
+ !metadata.isRootMetadata() && metadata.rootMetadata().requiresBytecodeInjection()
+ ? unsafeCastThisTo(ClassNames.GENERATED_COMPONENT_MANAGER_HOLDER)
+ : "this");
+ }
+
+ /**
+ * Returns the call to {@code componentManager()} with casts if needed.
+ *
+ * <p>A cast is required when the root generated Hilt class uses bytecode injection because
+ * subclasses won't have access to the {@code componentManager()} method in that case.
+ */
+ private static CodeBlock componentManagerCallBlock(AndroidEntryPointMetadata metadata) {
+ return CodeBlock.of(
+ "$L.componentManager()",
+ !metadata.isRootMetadata() && metadata.rootMetadata().requiresBytecodeInjection()
+ ? unsafeCastThisTo(ClassNames.GENERATED_COMPONENT_MANAGER_HOLDER)
+ : "this");
+ }
+
+ static CodeBlock unsafeCastThisTo(ClassName castType) {
+ return CodeBlock.of("$T.<$T>unsafeCast(this)", ClassNames.UNSAFE_CASTS, castType);
+ }
+
+ /** Returns {@code true} if the an ancestor annotated class extends the generated class */
+ private static boolean ancestorExtendsGeneratedHiltClass(AndroidEntryPointMetadata metadata) {
+ while (metadata.baseMetadata().isPresent()) {
+ metadata = metadata.baseMetadata().get();
+ if (!metadata.requiresBytecodeInjection()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // private boolean injected = false;
+ private static FieldSpec injectedField(AndroidEntryPointMetadata metadata) {
+ FieldSpec.Builder builder = FieldSpec.builder(TypeName.BOOLEAN, "injected")
+ .addModifiers(Modifier.PRIVATE);
+
+ // Broadcast receivers do double-checked locking so this needs to be volatile
+ if (metadata.androidType() == AndroidEntryPointMetadata.AndroidType.BROADCAST_RECEIVER) {
+ builder.addModifiers(Modifier.VOLATILE);
+ }
+
+ // Views should not add an initializer here as this runs after the super constructor
+ // and may reset state set during the super constructor call.
+ if (metadata.androidType() != AndroidEntryPointMetadata.AndroidType.VIEW) {
+ builder.initializer("false");
+ }
+ return builder.build();
+ }
+
+ // private final Object injectedLock = new Object();
+ private static FieldSpec injectedLockField() {
+ return FieldSpec.builder(TypeName.OBJECT, "injectedLock")
+ .addModifiers(Modifier.PRIVATE, Modifier.FINAL)
+ .initializer("new $T()", TypeName.OBJECT)
+ .build();
+ }
+
+ /**
+ * Adds the SupressWarnings to supress a warning in the generated code.
+ *
+ * @param keys the string keys of the warnings to suppress, e.g. 'deprecation', 'unchecked', etc.
+ */
+ public static void addSuppressAnnotation(TypeSpec.Builder builder, String... keys) {
+ AnnotationSpec.Builder annotationBuilder = AnnotationSpec.builder(SuppressWarnings.class);
+ for (String key : keys) {
+ annotationBuilder.addMember("value", "$S", key);
+ }
+ builder.addAnnotation(annotationBuilder.build());
+ }
+
+ private Generators() {}
+}
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/HiltCompilerOptions.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/HiltCompilerOptions.java
new file mode 100644
index 000000000..577410d81
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/HiltCompilerOptions.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.androidentrypoint;
+
+import java.util.Arrays;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.processing.ProcessingEnvironment;
+
+/** Hilt annotation processor options. */
+// TODO(danysantiago): Consider consolidating with Dagger compiler options logic.
+// TODO(user): Move this class to dagger/hilt/processor/internal
+public final class HiltCompilerOptions {
+
+ /** Processor options which can have true or false values. */
+ public enum BooleanOption {
+ /**
+ * Flag that disables validating the superclass of @AndroidEntryPoint are Hilt_ generated,
+ * classes. This flag is to be used internally by the Gradle plugin, enabling the bytecode
+ * transformation to change the superclass.
+ */
+ DISABLE_ANDROID_SUPERCLASS_VALIDATION(
+ "android.internal.disableAndroidSuperclassValidation", false),
+
+ /** Flag that disables check on modules to be annotated with @InstallIn. */
+ DISABLE_MODULES_HAVE_INSTALL_IN_CHECK("disableModulesHaveInstallInCheck", false);
+
+ private final String name;
+ private final boolean defaultValue;
+
+ BooleanOption(String name, boolean defaultValue) {
+ this.name = name;
+ this.defaultValue = defaultValue;
+ }
+
+ public boolean get(ProcessingEnvironment env) {
+ String value = env.getOptions().get(getQualifiedName());
+ if (value == null) {
+ return defaultValue;
+ }
+ // TODO(danysantiago): Strictly verify input, either 'true' or 'false' and nothing else.
+ return Boolean.parseBoolean(value);
+ }
+
+ public String getQualifiedName() {
+ return "dagger.hilt." + name;
+ }
+ }
+
+ public static Set<String> getProcessorOptions() {
+ return Arrays.stream(BooleanOption.values())
+ .map(BooleanOption::getQualifiedName)
+ .collect(Collectors.toSet());
+ }
+
+ private HiltCompilerOptions() {}
+}
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/InjectorEntryPointGenerator.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/InjectorEntryPointGenerator.java
new file mode 100644
index 000000000..e9e217ddc
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/InjectorEntryPointGenerator.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.androidentrypoint;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeSpec;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.Processors;
+import java.io.IOException;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Modifier;
+
+/** Generates an entry point that allows for injection of the given activity */
+public final class InjectorEntryPointGenerator {
+ private final ProcessingEnvironment env;
+ private final AndroidEntryPointMetadata metadata;
+
+ public InjectorEntryPointGenerator(
+ ProcessingEnvironment env, AndroidEntryPointMetadata metadata) {
+ this.env = env;
+ this.metadata = metadata;
+ }
+
+ // @Generated("InjectorEntryPointGenerator")
+ // @InstallIn({$SCOPES})
+ // public interface FooActivity_GeneratedInjector {
+ // void injectFoo(FooActivity foo);
+ // }
+ public void generate() throws IOException {
+ ClassName name = metadata.injectorClassName();
+ TypeSpec.Builder builder =
+ TypeSpec.interfaceBuilder(name.simpleName())
+ .addOriginatingElement(metadata.element())
+ .addAnnotation(Processors.getOriginatingElementAnnotation(metadata.element()))
+ .addAnnotation(ClassNames.GENERATED_ENTRY_POINT)
+ .addAnnotation(metadata.injectorInstallInAnnotation())
+ .addModifiers(Modifier.PUBLIC)
+ .addMethod(
+ MethodSpec.methodBuilder(metadata.injectMethodName())
+ .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
+ .addParameter(
+ metadata.elementClassName(),
+ Processors.upperToLowerCamel(metadata.elementClassName().simpleName()))
+ .build());
+
+ Processors.addGeneratedAnnotation(builder, env, getClass());
+ Generators.copyLintAnnotations(metadata.element(), builder);
+
+ JavaFile.builder(name.packageName(), builder.build()).build().writeTo(env.getFiler());
+ }
+}
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/ServiceGenerator.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/ServiceGenerator.java
new file mode 100644
index 000000000..a80a21545
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/ServiceGenerator.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.androidentrypoint;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.TypeVariableName;
+import dagger.hilt.android.processor.internal.AndroidClassNames;
+import dagger.hilt.processor.internal.Processors;
+import java.io.IOException;
+import java.util.List;
+import java.util.stream.Collectors;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.util.ElementFilter;
+
+/** Generates an Hilt Service class for the @AndroidEntryPoint annotated class. */
+public final class ServiceGenerator {
+ private final ProcessingEnvironment env;
+ private final AndroidEntryPointMetadata metadata;
+ private final ClassName generatedClassName;
+
+ public ServiceGenerator(ProcessingEnvironment env, AndroidEntryPointMetadata metadata) {
+ this.env = env;
+ this.metadata = metadata;
+
+ generatedClassName = metadata.generatedClassName();
+ }
+
+ // @Generated("ServiceGenerator")
+ // abstract class Hilt_$CLASS extends $BASE {
+ // ...
+ // }
+ public void generate() throws IOException {
+ TypeSpec.Builder builder =
+ TypeSpec.classBuilder(generatedClassName.simpleName())
+ .addOriginatingElement(metadata.element())
+ .superclass(metadata.baseClassName())
+ .addModifiers(metadata.generatedClassModifiers())
+ .addMethods(baseClassConstructors())
+ .addMethod(onCreateMethod());
+
+ Generators.addGeneratedBaseClassJavadoc(builder, AndroidClassNames.ANDROID_ENTRY_POINT);
+ Processors.addGeneratedAnnotation(builder, env, getClass());
+ Generators.copyLintAnnotations(metadata.element(), builder);
+
+ metadata.baseElement().getTypeParameters().stream()
+ .map(TypeVariableName::get)
+ .forEachOrdered(builder::addTypeVariable);
+
+ Generators.addInjectionMethods(metadata, builder);
+
+ Generators.addComponentOverride(metadata, builder);
+
+ JavaFile.builder(generatedClassName.packageName(), builder.build())
+ .build().writeTo(env.getFiler());
+ }
+
+ private List<MethodSpec> baseClassConstructors() {
+ return ElementFilter.constructorsIn(metadata.baseElement().getEnclosedElements())
+ .stream()
+ .map((constructor) -> {
+ List<ParameterSpec> params =
+ constructor.getParameters()
+ .stream()
+ .map(p -> ParameterSpec.builder(TypeName.get(p.asType()), p.toString()).build())
+ .collect(Collectors.toList());
+
+ return MethodSpec.constructorBuilder()
+ .addParameters(params)
+ .addStatement(
+ "super($L)",
+ params.stream().map(p -> p.name).collect(Collectors.joining(",")))
+ .build();
+ })
+ .collect(Collectors.toList());
+ }
+
+ // @CallSuper
+ // @Override
+ // protected void onCreate() {
+ // inject();
+ // super.onCreate();
+ // }
+ private MethodSpec onCreateMethod() throws IOException {
+ return MethodSpec.methodBuilder("onCreate")
+ .addAnnotation(AndroidClassNames.CALL_SUPER)
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC)
+ .addStatement("inject()")
+ .addStatement("super.onCreate()")
+ .build();
+ }
+}
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/ViewGenerator.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/ViewGenerator.java
new file mode 100644
index 000000000..412b09a26
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/ViewGenerator.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.androidentrypoint;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.Visibility;
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.TypeVariableName;
+import dagger.hilt.android.processor.internal.AndroidClassNames;
+import dagger.hilt.processor.internal.Processors;
+import java.io.IOException;
+import java.util.List;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+
+/** Generates an Hilt View class for the @AndroidEntryPoint annotated class. */
+public final class ViewGenerator {
+ private final ProcessingEnvironment env;
+ private final AndroidEntryPointMetadata metadata;
+ private final ClassName generatedClassName;
+
+ public ViewGenerator(ProcessingEnvironment env, AndroidEntryPointMetadata metadata) {
+ this.env = env;
+ this.metadata = metadata;
+
+ generatedClassName = metadata.generatedClassName();
+ }
+
+ // @Generated("ViewGenerator")
+ // abstract class Hilt_$CLASS extends $BASE implements
+ // ComponentManagerHolder<ViewComponentManager<$CLASS_EntryPoint>> {
+ // ...
+ // }
+ public void generate() throws IOException {
+ // Note: we do not use the Generators helper methods here because injection is called
+ // from the constructor where the double-check pattern doesn't work (due to the super
+ // constructor being called before fields are initialized) and because it isn't necessary
+ // since the object isn't done constructing yet.
+
+ TypeSpec.Builder builder =
+ TypeSpec.classBuilder(generatedClassName.simpleName())
+ .addOriginatingElement(metadata.element())
+ .superclass(metadata.baseClassName())
+ .addModifiers(metadata.generatedClassModifiers());
+
+ Generators.addGeneratedBaseClassJavadoc(builder, AndroidClassNames.ANDROID_ENTRY_POINT);
+ Processors.addGeneratedAnnotation(builder, env, getClass());
+ Generators.copyLintAnnotations(metadata.element(), builder);
+
+ metadata.baseElement().getTypeParameters().stream()
+ .map(TypeVariableName::get)
+ .forEachOrdered(builder::addTypeVariable);
+
+ Generators.addComponentOverride(metadata, builder);
+
+ Generators.addInjectionMethods(metadata, builder);
+
+ ElementFilter.constructorsIn(metadata.baseElement().getEnclosedElements()).stream()
+ .filter(this::isConstructorVisibleToGeneratedClass)
+ .forEach(constructor -> builder.addMethod(constructorMethod(constructor)));
+
+ JavaFile.builder(generatedClassName.packageName(), builder.build())
+ .build()
+ .writeTo(env.getFiler());
+ }
+
+ private boolean isConstructorVisibleToGeneratedClass(ExecutableElement constructorElement) {
+ if (Visibility.ofElement(constructorElement) == Visibility.DEFAULT
+ && !isInOurPackage(constructorElement)) {
+ return false;
+ } else if (Visibility.ofElement(constructorElement) == Visibility.PRIVATE) {
+ return false;
+ }
+
+ // We extend the base class, so both protected and public methods are always accessible.
+ return true;
+ }
+
+ /**
+ * Returns a pass-through constructor matching the base class's provided constructorElement. The
+ * generated constructor simply calls super(), then inject().
+ *
+ * <p>Eg
+ *
+ * <pre>
+ * Hilt_$CLASS(Context context, ...) {
+ * super(context, ...);
+ * inject();
+ * }
+ * </pre>
+ */
+ private MethodSpec constructorMethod(ExecutableElement constructorElement) {
+ MethodSpec.Builder constructor =
+ Generators.copyConstructor(constructorElement).toBuilder();
+
+ if (isRestrictedApiConstructor(constructorElement)) {
+ // 4 parameter constructors are only available on @TargetApi(21).
+ constructor.addAnnotation(
+ AnnotationSpec.builder(AndroidClassNames.TARGET_API).addMember("value", "21").build());
+ }
+
+ constructor.addStatement("inject()");
+
+ return constructor.build();
+ }
+
+ private boolean isRestrictedApiConstructor(ExecutableElement constructor) {
+ if (constructor.getParameters().size() != 4) {
+ return false;
+ }
+
+ List<? extends VariableElement> constructorParams = constructor.getParameters();
+ for (int i = 0; i < constructorParams.size(); i++) {
+ TypeMirror type = constructorParams.get(i).asType();
+ Element element = env.getTypeUtils().asElement(type);
+ switch (i) {
+ case 0:
+ if (!isFirstRestrictedParameter(element)) {
+ return false;
+ }
+ break;
+ case 1:
+ if (!isSecondRestrictedParameter(element)) {
+ return false;
+ }
+ break;
+ case 2:
+ if (!isThirdRestrictedParameter(type)) {
+ return false;
+ }
+ break;
+ case 3:
+ if (!isFourthRestrictedParameter(type)) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static boolean isFourthRestrictedParameter(TypeMirror type) {
+ return type.getKind().isPrimitive()
+ && Processors.getPrimitiveType(type).getKind() == TypeKind.INT;
+ }
+
+ private static boolean isThirdRestrictedParameter(TypeMirror type) {
+ return type.getKind().isPrimitive()
+ && Processors.getPrimitiveType(type).getKind() == TypeKind.INT;
+ }
+
+ private static boolean isSecondRestrictedParameter(Element element) {
+ return element instanceof TypeElement
+ && Processors.isAssignableFrom(
+ MoreElements.asType(element), AndroidClassNames.ATTRIBUTE_SET);
+ }
+
+ private static boolean isFirstRestrictedParameter(Element element) {
+ return element instanceof TypeElement
+ && Processors.isAssignableFrom(MoreElements.asType(element), AndroidClassNames.CONTEXT);
+ }
+
+ private boolean isInOurPackage(ExecutableElement constructorElement) {
+ return MoreElements.getPackage(constructorElement)
+ .equals(MoreElements.getPackage(metadata.element()));
+ }
+}
diff --git a/java/dagger/hilt/android/processor/internal/bindvalue/BUILD b/java/dagger/hilt/android/processor/internal/bindvalue/BUILD
new file mode 100644
index 000000000..cbc51b3c5
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/bindvalue/BUILD
@@ -0,0 +1,60 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Description:
+# Hilt android library for binding values in test processors.
+
+package(default_visibility = ["//:src"])
+
+java_plugin(
+ name = "bind_value_processor",
+ generates_api = 1,
+ processor_class = "dagger.hilt.android.processor.internal.bindvalue.BindValueProcessor",
+ deps = [
+ ":bind_value_processor_lib",
+ ],
+)
+
+java_library(
+ name = "bind_value_processor_lib",
+ srcs = [
+ "BindValueGenerator.java",
+ "BindValueMetadata.java",
+ "BindValueProcessor.java",
+ ],
+ deps = [
+ "//:dagger_with_compiler",
+ "//java/dagger/hilt/processor/internal:base_processor",
+ "//java/dagger/hilt/processor/internal:classnames",
+ "//java/dagger/hilt/processor/internal:components",
+ "//java/dagger/hilt/processor/internal:kotlin",
+ "//java/dagger/hilt/processor/internal:processor_errors",
+ "//java/dagger/hilt/processor/internal:processors",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/codegen/kotlin",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/auto:service",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/incap",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@google_bazel_common//third_party/java/jsr250_annotations",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/android/processor/internal/bindvalue/BindValueGenerator.java b/java/dagger/hilt/android/processor/internal/bindvalue/BindValueGenerator.java
new file mode 100644
index 000000000..108a15dd9
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/bindvalue/BindValueGenerator.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.bindvalue;
+
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static java.util.Comparator.comparing;
+
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeSpec;
+import dagger.Module;
+import dagger.Provides;
+import dagger.hilt.android.processor.internal.bindvalue.BindValueMetadata.BindValueElement;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.Components;
+import dagger.hilt.processor.internal.Processors;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
+import java.io.IOException;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Modifier;
+
+/**
+ * Generates a SINGLETON module for all {@code BindValue} annotated fields in a test class.
+ */
+final class BindValueGenerator {
+ private static final String SUFFIX = "_BindValueModule";
+
+ private final ProcessingEnvironment env;
+ private final BindValueMetadata metadata;
+ private final ClassName testClassName;
+ private final ClassName className;
+
+ BindValueGenerator(ProcessingEnvironment env, BindValueMetadata metadata) {
+ this.env = env;
+ this.metadata = metadata;
+ testClassName = ClassName.get(metadata.testElement());
+ className = Processors.append(testClassName, SUFFIX);
+ }
+
+ // @Module
+ // @InstallIn(SingletonComponent.class)
+ // public final class FooTest_BindValueModule {
+ // // providesMethods ...
+ // }
+ void generate() throws IOException {
+ TypeSpec.Builder builder =
+ TypeSpec.classBuilder(className)
+ .addOriginatingElement(metadata.testElement())
+ .addAnnotation(Processors.getOriginatingElementAnnotation(metadata.testElement()))
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .addAnnotation(Module.class)
+ .addAnnotation(
+ Components.getInstallInAnnotationSpec(
+ ImmutableSet.of(ClassNames.SINGLETON_COMPONENT)))
+ .addMethod(providesTestMethod());
+
+ Processors.addGeneratedAnnotation(builder, env, getClass());
+
+ metadata.bindValueElements().stream()
+ .map(this::providesMethod)
+ .sorted(comparing(MethodSpec::toString))
+ .forEachOrdered(builder::addMethod);
+
+ JavaFile.builder(className.packageName(), builder.build())
+ .build()
+ .writeTo(env.getFiler());
+ }
+
+ // @Provides
+ // static FooTest providesFooTest(@ApplicationContext Context context) {
+ // return (FooTest)
+ // ((TestApplicationComponentManager)
+ // ((TestApplicationComponentManagerHolder) context).componentManager())
+ // .getTestInstance();
+ // }
+ private MethodSpec providesTestMethod() {
+ String methodName = "provides" + testClassName.simpleName();
+ MethodSpec.Builder builder =
+ MethodSpec.methodBuilder(methodName)
+ .addAnnotation(Provides.class)
+ .addModifiers(Modifier.STATIC)
+ .addParameter(
+ ParameterSpec.builder(ClassNames.CONTEXT, "context")
+ .addAnnotation(ClassNames.APPLICATION_CONTEXT)
+ .build())
+ .returns(testClassName)
+ .addStatement(
+ "return ($T) (($T) (($T) context).componentManager()).getTestInstance()",
+ testClassName,
+ ClassNames.TEST_APPLICATION_COMPONENT_MANAGER,
+ ClassNames.TEST_APPLICATION_COMPONENT_MANAGER_HOLDER);
+ return builder.build();
+ }
+
+ // @Provides
+ // @BarQualifier
+ // static Bar providesBarQualifierBar(FooTest test) {
+ // return test.bar;
+ // }
+ private MethodSpec providesMethod(BindValueElement bindValue) {
+ // We only allow fields in the Test class, which should have unique variable names.
+ String methodName = "provides"
+ + LOWER_CAMEL.to(UPPER_CAMEL, bindValue.variableElement().getSimpleName().toString());
+ MethodSpec.Builder builder =
+ MethodSpec.methodBuilder(methodName)
+ .addAnnotation(Provides.class)
+ .addModifiers(Modifier.STATIC)
+ .returns(ClassName.get(bindValue.variableElement().asType()));
+
+ if (bindValue.variableElement().getModifiers().contains(Modifier.STATIC)) {
+ builder.addStatement(
+ "return $T.$L", testClassName, bindValue.variableElement().getSimpleName());
+ } else {
+ builder
+ .addParameter(testClassName, "test")
+ .addStatement(
+ "return $L",
+ bindValue.getterElement().isPresent()
+ ? CodeBlock.of("test.$L()", bindValue.getterElement().get().getSimpleName())
+ : CodeBlock.of("test.$L", bindValue.variableElement().getSimpleName()));
+ }
+
+ ClassName annotationClassName = bindValue.annotationName();
+ if (BindValueMetadata.BIND_VALUE_INTO_MAP_ANNOTATIONS.contains(annotationClassName)) {
+ builder.addAnnotation(IntoMap.class);
+ // It is safe to call get() on the Optional<AnnotationMirror> returned by mapKey()
+ // because a @BindValueIntoMap is required to have one and is checked in
+ // BindValueMetadata.BindValueElement.create().
+ builder.addAnnotation(AnnotationSpec.get(bindValue.mapKey().get()));
+ } else if (BindValueMetadata.BIND_VALUE_INTO_SET_ANNOTATIONS.contains(annotationClassName)) {
+ builder.addAnnotation(IntoSet.class);
+ } else if (BindValueMetadata.BIND_ELEMENTS_INTO_SET_ANNOTATIONS.contains(annotationClassName)) {
+ builder.addAnnotation(ElementsIntoSet.class);
+ }
+ bindValue.qualifier().ifPresent(q -> builder.addAnnotation(AnnotationSpec.get(q)));
+ return builder.build();
+ }
+}
diff --git a/java/dagger/hilt/android/processor/internal/bindvalue/BindValueMetadata.java b/java/dagger/hilt/android/processor/internal/bindvalue/BindValueMetadata.java
new file mode 100644
index 000000000..bf5028829
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/bindvalue/BindValueMetadata.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.bindvalue;
+
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.KotlinMetadataUtils;
+import dagger.hilt.processor.internal.ProcessorErrors;
+import dagger.hilt.processor.internal.Processors;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import java.util.Collection;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+
+/**
+ * Represents metadata for a test class that has {@code BindValue} fields.
+ */
+@AutoValue
+abstract class BindValueMetadata {
+ static final ImmutableSet<ClassName> BIND_VALUE_ANNOTATIONS =
+ ImmutableSet.of(
+ ClassNames.ANDROID_BIND_VALUE);
+ static final ImmutableSet<ClassName> BIND_VALUE_INTO_SET_ANNOTATIONS =
+ ImmutableSet.of(
+ ClassNames.ANDROID_BIND_VALUE_INTO_SET);
+ static final ImmutableSet<ClassName> BIND_ELEMENTS_INTO_SET_ANNOTATIONS =
+ ImmutableSet.of(
+ ClassNames.ANDROID_BIND_ELEMENTS_INTO_SET);
+ static final ImmutableSet<ClassName> BIND_VALUE_INTO_MAP_ANNOTATIONS =
+ ImmutableSet.of(
+ ClassNames.ANDROID_BIND_VALUE_INTO_MAP);
+
+ /** @return the {@code TestRoot} annotated class's name. */
+ abstract TypeElement testElement();
+
+ /** @return a {@link ImmutableSet} of elements annotated with @BindValue. */
+ abstract ImmutableSet<BindValueElement> bindValueElements();
+
+ /** @return a new BindValueMetadata instance. */
+ static BindValueMetadata create(TypeElement testElement, Collection<Element> bindValueElements) {
+
+ ImmutableSet.Builder<BindValueElement> elements = ImmutableSet.builder();
+ for (Element element : bindValueElements) {
+ elements.add(BindValueElement.create(element));
+ }
+
+ return new AutoValue_BindValueMetadata(testElement, elements.build());
+ }
+
+ @AutoValue
+ abstract static class BindValueElement {
+ abstract VariableElement variableElement();
+
+ abstract ClassName annotationName();
+
+ abstract Optional<AnnotationMirror> qualifier();
+
+ abstract Optional<AnnotationMirror> mapKey();
+
+ abstract Optional<ExecutableElement> getterElement();
+
+ static BindValueElement create(Element element) {
+ ImmutableList<ClassName> bindValues = BindValueProcessor.getBindValueAnnotations(element);
+ ProcessorErrors.checkState(
+ bindValues.size() == 1,
+ element,
+ "Fields can be annotated with only one of @BindValue, @BindValueIntoMap,"
+ + " @BindElementsIntoSet, @BindValueIntoSet. Found: %s",
+ bindValues.stream().map(m -> "@" + m.simpleName()).collect(toImmutableList()));
+ ClassName annotationClassName = bindValues.get(0);
+
+ ProcessorErrors.checkState(
+ element.getKind() == ElementKind.FIELD,
+ element,
+ "@%s can only be used with fields. Found: %s",
+ annotationClassName.simpleName(),
+ element);
+
+ KotlinMetadataUtil metadataUtil = KotlinMetadataUtils.getMetadataUtil();
+ Optional<ExecutableElement> propertyGetter =
+ metadataUtil.hasMetadata(element)
+ ? metadataUtil.getPropertyGetter(MoreElements.asVariable(element))
+ : Optional.empty();
+ if (propertyGetter.isPresent()) {
+ ProcessorErrors.checkState(
+ !propertyGetter.get().getModifiers().contains(Modifier.PRIVATE),
+ element,
+ "@%s field getter cannot be private. Found: %s",
+ annotationClassName.simpleName(),
+ element);
+ } else {
+ ProcessorErrors.checkState(
+ !element.getModifiers().contains(Modifier.PRIVATE),
+ element,
+ "@%s fields cannot be private. Found: %s",
+ annotationClassName.simpleName(),
+ element);
+ }
+
+ ProcessorErrors.checkState(
+ !Processors.hasAnnotation(element, Inject.class),
+ element,
+ "@%s fields cannot be used with @Inject annotation. Found %s",
+ annotationClassName.simpleName(),
+ element);
+
+ ImmutableList<AnnotationMirror> qualifiers = Processors.getQualifierAnnotations(element);
+ ProcessorErrors.checkState(
+ qualifiers.size() <= 1,
+ element,
+ "@%s fields cannot have more than one qualifier. Found %s",
+ annotationClassName.simpleName(),
+ qualifiers);
+
+ ImmutableList<AnnotationMirror> mapKeys = Processors.getMapKeyAnnotations(element);
+ Optional<AnnotationMirror> optionalMapKeys;
+ if (BIND_VALUE_INTO_MAP_ANNOTATIONS.contains(annotationClassName)) {
+ ProcessorErrors.checkState(
+ mapKeys.size() == 1,
+ element,
+ "@BindValueIntoMap fields must have exactly one @MapKey. Found %s",
+ mapKeys);
+ optionalMapKeys = Optional.of(mapKeys.get(0));
+ } else {
+ ProcessorErrors.checkState(
+ mapKeys.isEmpty(),
+ element,
+ "@MapKey can only be used on @BindValueIntoMap fields, not @%s fields",
+ annotationClassName.simpleName());
+ optionalMapKeys = Optional.empty();
+ }
+
+ ImmutableList<AnnotationMirror> scopes = Processors.getScopeAnnotations(element);
+ ProcessorErrors.checkState(
+ scopes.isEmpty(),
+ element,
+ "@%s fields cannot be scoped. Found %s",
+ annotationClassName.simpleName(),
+ scopes);
+
+ return new AutoValue_BindValueMetadata_BindValueElement(
+ (VariableElement) element,
+ annotationClassName,
+ qualifiers.isEmpty()
+ ? Optional.<AnnotationMirror>empty()
+ : Optional.<AnnotationMirror>of(qualifiers.get(0)),
+ optionalMapKeys,
+ propertyGetter);
+ }
+ }
+}
diff --git a/java/dagger/hilt/android/processor/internal/bindvalue/BindValueProcessor.java b/java/dagger/hilt/android/processor/internal/bindvalue/BindValueProcessor.java
new file mode 100644
index 000000000..060b07793
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/bindvalue/BindValueProcessor.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.bindvalue;
+
+import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.service.AutoService;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.TypeName;
+import dagger.hilt.processor.internal.BaseProcessor;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.ProcessorErrors;
+import dagger.hilt.processor.internal.Processors;
+import dagger.internal.codegen.extension.DaggerStreams;
+import java.util.Collection;
+import java.util.Map;
+import javax.annotation.processing.Processor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.TypeElement;
+import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
+
+/** Provides a test's @BindValue fields to the SINGLETON component. */
+@IncrementalAnnotationProcessor(ISOLATING)
+@AutoService(Processor.class)
+public final class BindValueProcessor extends BaseProcessor {
+
+ private static final ImmutableSet<ClassName> SUPPORTED_ANNOTATIONS =
+ ImmutableSet.<ClassName>builder()
+ .addAll(BindValueMetadata.BIND_VALUE_ANNOTATIONS)
+ .addAll(BindValueMetadata.BIND_VALUE_INTO_SET_ANNOTATIONS)
+ .addAll(BindValueMetadata.BIND_ELEMENTS_INTO_SET_ANNOTATIONS)
+ .addAll(BindValueMetadata.BIND_VALUE_INTO_MAP_ANNOTATIONS)
+ .build();
+
+ private final Multimap<TypeElement, Element> testRootMap = ArrayListMultimap.create();
+
+ @Override
+ public ImmutableSet<String> getSupportedAnnotationTypes() {
+ return SUPPORTED_ANNOTATIONS.stream()
+ .map(TypeName::toString)
+ .collect(DaggerStreams.toImmutableSet());
+ }
+
+ @Override
+ protected void preRoundProcess(RoundEnvironment roundEnv) {
+ testRootMap.clear();
+ }
+
+ @Override
+ public void processEach(TypeElement annotation, Element element) throws Exception {
+ ClassName annotationClassName = ClassName.get(annotation);
+ Element enclosingElement = element.getEnclosingElement();
+ // Restrict BindValue to the direct test class (e.g. not allowed in a base test class) because
+ // otherwise generated BindValue modules from the base class will not associate with the
+ // correct test class. This would make the modules apply globally which would be a weird
+ // difference since just moving a declaration to the parent would change whether the module is
+ // limited to the test that declares it to global.
+ ProcessorErrors.checkState(
+ enclosingElement.getKind() == ElementKind.CLASS
+ && (Processors.hasAnnotation(enclosingElement, ClassNames.HILT_ANDROID_TEST)
+ ),
+ enclosingElement,
+ "@%s can only be used within a class annotated with "
+ + "@HiltAndroidTest. Found: %s",
+ annotationClassName.simpleName(),
+ enclosingElement);
+
+ testRootMap.put(MoreElements.asType(enclosingElement), element);
+ }
+
+ @Override
+ public void postRoundProcess(RoundEnvironment roundEnvironment) throws Exception {
+ // Generate a module for each testing class with a @BindValue field.
+ for (Map.Entry<TypeElement, Collection<Element>> e : testRootMap.asMap().entrySet()) {
+ BindValueMetadata metadata = BindValueMetadata.create(e.getKey(), e.getValue());
+ new BindValueGenerator(getProcessingEnv(), metadata).generate();
+ }
+ }
+
+ static ImmutableList<ClassName> getBindValueAnnotations(Element element) {
+ ImmutableList.Builder<ClassName> builder = ImmutableList.builder();
+ for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
+ TypeName tn = AnnotationSpec.get(annotation).type;
+ if (SUPPORTED_ANNOTATIONS.contains(tn)) {
+ builder.add((ClassName) tn); // the cast is checked by .contains()
+ }
+ }
+ return builder.build();
+ }
+}
diff --git a/java/dagger/hilt/android/processor/internal/customtestapplication/BUILD b/java/dagger/hilt/android/processor/internal/customtestapplication/BUILD
new file mode 100644
index 000000000..e9721d5d3
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/customtestapplication/BUILD
@@ -0,0 +1,53 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# A processor for @dagger.hilt.android.testing.CustomTestApplication.
+
+package(default_visibility = ["//:src"])
+
+java_plugin(
+ name = "processor",
+ generates_api = 1,
+ processor_class = "dagger.hilt.android.processor.internal.customtestapplication.CustomTestApplicationProcessor",
+ deps = [":processor_lib"],
+)
+
+java_library(
+ name = "processor_lib",
+ srcs = [
+ "CustomTestApplicationGenerator.java",
+ "CustomTestApplicationMetadata.java",
+ "CustomTestApplicationProcessor.java",
+ ],
+ deps = [
+ "//java/dagger/hilt/processor/internal:base_processor",
+ "//java/dagger/hilt/processor/internal:classnames",
+ "//java/dagger/hilt/processor/internal:processor_errors",
+ "//java/dagger/hilt/processor/internal:processors",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/auto:service",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/incap",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/android/processor/internal/customtestapplication/CustomTestApplicationGenerator.java b/java/dagger/hilt/android/processor/internal/customtestapplication/CustomTestApplicationGenerator.java
new file mode 100644
index 000000000..51b7ef466
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/customtestapplication/CustomTestApplicationGenerator.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.customtestapplication;
+
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.Processors;
+import java.io.IOException;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Modifier;
+
+/**
+ * Generates an Android Application that holds the Singleton component.
+ */
+final class CustomTestApplicationGenerator {
+ private static final ParameterSpec COMPONENT_MANAGER =
+ ParameterSpec.builder(ClassNames.TEST_APPLICATION_COMPONENT_MANAGER, "componentManager")
+ .build();
+
+ private final ProcessingEnvironment processingEnv;
+ private final CustomTestApplicationMetadata metadata;
+
+ public CustomTestApplicationGenerator(
+ ProcessingEnvironment processingEnv, CustomTestApplicationMetadata metadata) {
+ this.processingEnv = processingEnv;
+ this.metadata = metadata;
+ }
+
+ public void generate() throws IOException {
+ TypeSpec.Builder generator =
+ TypeSpec.classBuilder(metadata.appName())
+ .addOriginatingElement(metadata.element())
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .superclass(metadata.baseAppName())
+ .addSuperinterface(
+ ParameterizedTypeName.get(ClassNames.GENERATED_COMPONENT_MANAGER, TypeName.OBJECT))
+ .addSuperinterface(ClassNames.TEST_APPLICATION_COMPONENT_MANAGER_HOLDER)
+ .addField(getComponentManagerField())
+ .addMethod(getAttachBaseContextMethod())
+ .addMethod(getComponentManagerMethod())
+ .addMethod(getComponentMethod());
+
+ Processors.addGeneratedAnnotation(
+ generator, processingEnv, CustomTestApplicationProcessor.class);
+
+ JavaFile.builder(metadata.appName().packageName(), generator.build())
+ .build()
+ .writeTo(processingEnv.getFiler());
+ }
+
+ // Initialize this in attachBaseContext to not pull it into the main dex.
+ /** private TestApplicationComponentManager componentManager; */
+ private static FieldSpec getComponentManagerField() {
+ return FieldSpec.builder(COMPONENT_MANAGER.type, COMPONENT_MANAGER.name, Modifier.PRIVATE)
+ .build();
+ }
+
+ /**
+ * Initializes application fields. These fields are initialized in attachBaseContext to avoid
+ * potential multidexing issues.
+ *
+ * <pre><code>
+ * {@literal @Override} protected void attachBaseContext(Context base) {
+ * super.attachBaseContext(base);
+ * componentManager = new TestApplicationComponentManager(this);
+ * }
+ * </code></pre>
+ */
+ private static MethodSpec getAttachBaseContextMethod() {
+ return MethodSpec.methodBuilder("attachBaseContext")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PROTECTED, Modifier.FINAL)
+ .addParameter(ClassNames.CONTEXT, "base")
+ .addStatement("super.attachBaseContext(base)")
+ .addStatement("$N = new $T(this)", COMPONENT_MANAGER, COMPONENT_MANAGER.type)
+ .build();
+ }
+
+ private static MethodSpec getComponentMethod() {
+ return MethodSpec.methodBuilder("generatedComponent")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .returns(TypeName.OBJECT)
+ .addStatement("return $N.generatedComponent()", COMPONENT_MANAGER)
+ .build();
+ }
+
+ private static MethodSpec getComponentManagerMethod() {
+ return MethodSpec.methodBuilder("componentManager")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .returns(TypeName.OBJECT)
+ .addStatement("return $N", COMPONENT_MANAGER)
+ .build();
+ }
+}
diff --git a/java/dagger/hilt/android/processor/internal/customtestapplication/CustomTestApplicationMetadata.java b/java/dagger/hilt/android/processor/internal/customtestapplication/CustomTestApplicationMetadata.java
new file mode 100644
index 000000000..de8e3f7b3
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/customtestapplication/CustomTestApplicationMetadata.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.customtestapplication;
+
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.squareup.javapoet.ClassName;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.ProcessorErrors;
+import dagger.hilt.processor.internal.Processors;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+
+/** Stores the metadata for a custom base test application. */
+@AutoValue
+abstract class CustomTestApplicationMetadata {
+ /** Returns the annotated element. */
+ abstract TypeElement element();
+
+ /** Returns the name of the base application. */
+ abstract ClassName baseAppName();
+
+ /** Returns the name of the generated application */
+ ClassName appName() {
+ return Processors.append(
+ Processors.getEnclosedClassName(ClassName.get(element())), "_Application");
+ }
+
+ static CustomTestApplicationMetadata of(Element element, Elements elements) {
+ Preconditions.checkState(
+ Processors.hasAnnotation(element, ClassNames.CUSTOM_TEST_APPLICATION),
+ "The given element, %s, is not annotated with @%s.",
+ element,
+ ClassNames.CUSTOM_TEST_APPLICATION.simpleName());
+
+ ProcessorErrors.checkState(
+ MoreElements.isType(element),
+ element,
+ "@%s should only be used on classes or interfaces but found: %s",
+ ClassNames.CUSTOM_TEST_APPLICATION.simpleName(),
+ element);
+
+ TypeElement baseAppElement = getBaseElement(element, elements);
+
+ return new AutoValue_CustomTestApplicationMetadata(
+ MoreElements.asType(element), ClassName.get(baseAppElement));
+ }
+
+ private static TypeElement getBaseElement(Element element, Elements elements) {
+ TypeElement baseElement =
+ Processors.getAnnotationClassValue(
+ elements,
+ Processors.getAnnotationMirror(element, ClassNames.CUSTOM_TEST_APPLICATION),
+ "value");
+
+ TypeElement baseSuperclassElement = baseElement;
+ while (!baseSuperclassElement.getSuperclass().getKind().equals(TypeKind.NONE)) {
+ ProcessorErrors.checkState(
+ !Processors.hasAnnotation(baseSuperclassElement, ClassNames.HILT_ANDROID_APP),
+ element,
+ "@%s value cannot be annotated with @%s. Found: %s",
+ ClassNames.CUSTOM_TEST_APPLICATION.simpleName(),
+ ClassNames.HILT_ANDROID_APP.simpleName(),
+ baseSuperclassElement);
+
+ ImmutableList<VariableElement> injectFields =
+ ElementFilter.fieldsIn(baseSuperclassElement.getEnclosedElements()).stream()
+ .filter(field -> Processors.hasAnnotation(field, ClassNames.INJECT))
+ .collect(toImmutableList());
+ ProcessorErrors.checkState(
+ injectFields.isEmpty(),
+ element,
+ "@%s does not support application classes (or super classes) with @Inject fields. Found "
+ + "%s with @Inject fields %s.",
+ ClassNames.CUSTOM_TEST_APPLICATION.simpleName(),
+ baseSuperclassElement,
+ injectFields);
+
+ ImmutableList<ExecutableElement> injectMethods =
+ ElementFilter.methodsIn(baseSuperclassElement.getEnclosedElements()).stream()
+ .filter(method -> Processors.hasAnnotation(method, ClassNames.INJECT))
+ .collect(toImmutableList());
+ ProcessorErrors.checkState(
+ injectMethods.isEmpty(),
+ element,
+ "@%s does not support application classes (or super classes) with @Inject methods. Found "
+ + "%s with @Inject methods %s.",
+ ClassNames.CUSTOM_TEST_APPLICATION.simpleName(),
+ baseSuperclassElement,
+ injectMethods);
+
+ ImmutableList<ExecutableElement> injectConstructors =
+ ElementFilter.constructorsIn(baseSuperclassElement.getEnclosedElements()).stream()
+ .filter(method -> Processors.hasAnnotation(method, ClassNames.INJECT))
+ .collect(toImmutableList());
+ ProcessorErrors.checkState(
+ injectConstructors.isEmpty(),
+ element,
+ "@%s does not support application classes (or super classes) with @Inject constructors. "
+ + "Found %s with @Inject constructors %s.",
+ ClassNames.CUSTOM_TEST_APPLICATION.simpleName(),
+ baseSuperclassElement,
+ injectConstructors);
+
+ baseSuperclassElement = MoreTypes.asTypeElement(baseSuperclassElement.getSuperclass());
+ }
+
+ // We check this last because if the base type is a @HiltAndroidApp we'd accidentally fail
+ // with this message instead of the one above when the superclass hasn't yet been generated.
+ ProcessorErrors.checkState(
+ Processors.isAssignableFrom(baseElement, ClassNames.APPLICATION),
+ element,
+ "@%s value should be an instance of %s. Found: %s",
+ ClassNames.CUSTOM_TEST_APPLICATION.simpleName(),
+ ClassNames.APPLICATION,
+ baseElement);
+
+ return baseElement;
+ }
+}
diff --git a/java/dagger/hilt/android/processor/internal/customtestapplication/CustomTestApplicationProcessor.java b/java/dagger/hilt/android/processor/internal/customtestapplication/CustomTestApplicationProcessor.java
new file mode 100644
index 000000000..fd1a6c87d
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/customtestapplication/CustomTestApplicationProcessor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.customtestapplication;
+
+import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.ImmutableSet;
+import dagger.hilt.processor.internal.BaseProcessor;
+import dagger.hilt.processor.internal.ClassNames;
+import javax.annotation.processing.Processor;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
+
+/** Processes usages of {@link dagger.hilt.android.testing.CustomTestApplication}. */
+@IncrementalAnnotationProcessor(ISOLATING)
+@AutoService(Processor.class)
+public final class CustomTestApplicationProcessor extends BaseProcessor {
+
+ @Override
+ public ImmutableSet<String> getSupportedAnnotationTypes() {
+ return ImmutableSet.of(ClassNames.CUSTOM_TEST_APPLICATION.toString());
+ }
+
+ @Override
+ public void processEach(TypeElement annotation, Element element) throws Exception {
+ CustomTestApplicationMetadata metadata =
+ CustomTestApplicationMetadata.of(element, getElementUtils());
+ new CustomTestApplicationGenerator(getProcessingEnv(), metadata).generate();
+ }
+}
diff --git a/java/dagger/hilt/android/processor/internal/uninstallmodules/BUILD b/java/dagger/hilt/android/processor/internal/uninstallmodules/BUILD
new file mode 100644
index 000000000..73c46061e
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/uninstallmodules/BUILD
@@ -0,0 +1,48 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# A processor for @dagger.hilt.android.testing.UninstallModules.
+
+package(default_visibility = ["//:src"])
+
+java_plugin(
+ name = "processor",
+ generates_api = 1,
+ processor_class = "dagger.hilt.android.processor.internal.uninstallmodules.UninstallModulesProcessor",
+ deps = [":processor_lib"],
+)
+
+java_library(
+ name = "processor_lib",
+ srcs = [
+ "UninstallModulesProcessor.java",
+ ],
+ deps = [
+ "//java/dagger/hilt/processor/internal:base_processor",
+ "//java/dagger/hilt/processor/internal:classnames",
+ "//java/dagger/hilt/processor/internal:processor_errors",
+ "//java/dagger/hilt/processor/internal:processors",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/auto:service",
+ "@google_bazel_common//third_party/java/incap",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/android/processor/internal/uninstallmodules/UninstallModulesProcessor.java b/java/dagger/hilt/android/processor/internal/uninstallmodules/UninstallModulesProcessor.java
new file mode 100644
index 000000000..e92f0f0a4
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/uninstallmodules/UninstallModulesProcessor.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.uninstallmodules;
+
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.service.AutoService;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import dagger.hilt.processor.internal.BaseProcessor;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.ProcessorErrors;
+import dagger.hilt.processor.internal.Processors;
+import java.util.Set;
+import javax.annotation.processing.Processor;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
+
+/** Validates {@link dagger.hilt.android.testing.UninstallModules} usages. */
+@IncrementalAnnotationProcessor(ISOLATING)
+@AutoService(Processor.class)
+public final class UninstallModulesProcessor extends BaseProcessor {
+
+ @Override
+ public Set<String> getSupportedAnnotationTypes() {
+ return ImmutableSet.of(ClassNames.IGNORE_MODULES.toString());
+ }
+
+ @Override
+ public void processEach(TypeElement annotation, Element element) throws Exception {
+ // TODO(bcorso): Consider using RootType to check this?
+ // TODO(bcorso): Loosen this restriction to allow defining sets of ignored modules in libraries.
+ ProcessorErrors.checkState(
+ MoreElements.isType(element)
+ && Processors.hasAnnotation(element, ClassNames.HILT_ANDROID_TEST),
+ element,
+ "@%s should only be used on test classes annotated with @%s, but found: %s",
+ annotation.getSimpleName(),
+ ClassNames.HILT_ANDROID_TEST.simpleName(),
+ element);
+
+ ImmutableList<TypeElement> invalidModules =
+ Processors.getAnnotationClassValues(
+ getElementUtils(),
+ Processors.getAnnotationMirror(element, ClassNames.IGNORE_MODULES),
+ "value")
+ .stream()
+ .filter(
+ module ->
+ !(Processors.hasAnnotation(module, ClassNames.MODULE)
+ && Processors.hasAnnotation(module, ClassNames.INSTALL_IN)))
+ .collect(toImmutableList());
+
+ ProcessorErrors.checkState(
+ invalidModules.isEmpty(),
+ // TODO(b/152801981): Point to the annotation value rather than the annotated element.
+ element,
+ "@%s should only include modules annotated with both @Module and @InstallIn, but found: "
+ + "%s.",
+ annotation.getSimpleName(),
+ invalidModules);
+ }
+}
diff --git a/java/dagger/hilt/android/processor/internal/viewmodel/BUILD b/java/dagger/hilt/android/processor/internal/viewmodel/BUILD
new file mode 100644
index 000000000..b45925fe8
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/viewmodel/BUILD
@@ -0,0 +1,84 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# ViewModelInject processor.
+load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_library")
+
+package(default_visibility = ["//:src"])
+
+java_plugin(
+ name = "processor",
+ generates_api = 1,
+ processor_class = "dagger.hilt.android.processor.internal.viewmodel.ViewModelProcessor",
+ deps = [":processor_lib"],
+)
+
+kt_jvm_library(
+ name = "processor_lib",
+ srcs = [
+ "ViewModelMetadata.kt",
+ "ViewModelModuleGenerator.kt",
+ "ViewModelProcessor.kt",
+ ],
+ deps = [
+ "//java/dagger/hilt/android/processor/internal:android_classnames",
+ "//java/dagger/hilt/processor/internal:base_processor",
+ "//java/dagger/hilt/processor/internal:classnames",
+ "//java/dagger/hilt/processor/internal:processor_errors",
+ "//java/dagger/hilt/processor/internal:processors",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/auto:service",
+ "@google_bazel_common//third_party/java/incap",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
+
+java_plugin(
+ name = "validation_plugin",
+ deps = [":validation_plugin_lib"],
+)
+
+kt_jvm_library(
+ name = "validation_plugin_lib",
+ srcs = [
+ "ViewModelValidationPlugin.kt",
+ ],
+ deps = [
+ "//:spi",
+ "//java/dagger/hilt/android/processor/internal:android_classnames",
+ "//java/dagger/hilt/processor/internal:processors",
+ "//java/dagger/internal/guava:graph",
+ "@google_bazel_common//third_party/java/auto:service",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
+
+# See: https://github.com/bazelbuild/rules_kotlin/issues/324
+alias(
+ name = "libprocessor_lib-src.jar",
+ actual = ":processor_lib-sources.jar",
+)
+
+alias(
+ name = "libvalidation_plugin_lib-src.jar",
+ actual = ":validation_plugin_lib-sources.jar",
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelMetadata.kt b/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelMetadata.kt
new file mode 100644
index 000000000..789fbfea2
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelMetadata.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.viewmodel
+
+import com.google.auto.common.MoreElements
+import com.squareup.javapoet.ClassName
+import dagger.hilt.android.processor.internal.AndroidClassNames
+import dagger.hilt.processor.internal.ClassNames
+import dagger.hilt.processor.internal.ProcessorErrors
+import dagger.hilt.processor.internal.Processors
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.element.Modifier
+import javax.lang.model.element.NestingKind
+import javax.lang.model.element.TypeElement
+import javax.lang.model.util.ElementFilter
+
+/**
+ * Data class that represents a Hilt injected ViewModel
+ */
+internal class ViewModelMetadata private constructor(
+ val typeElement: TypeElement
+) {
+ val className = ClassName.get(typeElement)
+
+ val modulesClassName = ClassName.get(
+ MoreElements.getPackage(typeElement).qualifiedName.toString(),
+ "${className.simpleNames().joinToString("_")}_HiltModules"
+ )
+
+ companion object {
+ internal fun create(
+ processingEnv: ProcessingEnvironment,
+ typeElement: TypeElement,
+ ): ViewModelMetadata? {
+ val types = processingEnv.typeUtils
+ val elements = processingEnv.elementUtils
+
+ ProcessorErrors.checkState(
+ types.isSubtype(
+ typeElement.asType(),
+ elements.getTypeElement(AndroidClassNames.VIEW_MODEL.toString()).asType()
+ ),
+ typeElement,
+ "@HiltViewModel is only supported on types that subclass %s.",
+ AndroidClassNames.VIEW_MODEL
+ )
+
+ ElementFilter.constructorsIn(typeElement.enclosedElements).filter { constructor ->
+ ProcessorErrors.checkState(
+ !Processors.hasAnnotation(constructor, ClassNames.ASSISTED_INJECT),
+ constructor,
+ "ViewModel constructor should be annotated with @Inject instead of @AssistedInject."
+ )
+ Processors.hasAnnotation(constructor, ClassNames.INJECT)
+ }.let { injectConstructors ->
+ ProcessorErrors.checkState(
+ injectConstructors.size == 1,
+ typeElement,
+ "@HiltViewModel annotated class should contain exactly one @Inject " +
+ "annotated constructor."
+ )
+
+ injectConstructors.forEach { constructor ->
+ ProcessorErrors.checkState(
+ !constructor.modifiers.contains(Modifier.PRIVATE),
+ constructor,
+ "@Inject annotated constructors must not be private."
+ )
+ }
+ }
+
+ ProcessorErrors.checkState(
+ typeElement.nestingKind != NestingKind.MEMBER ||
+ typeElement.modifiers.contains(Modifier.STATIC),
+ typeElement,
+ "@HiltViewModel may only be used on inner classes if they are static."
+ )
+
+ Processors.getScopeAnnotations(typeElement).let { scopeAnnotations ->
+ ProcessorErrors.checkState(
+ scopeAnnotations.isEmpty(),
+ typeElement,
+ "@HiltViewModel classes should not be scoped. Found: %s",
+ scopeAnnotations.joinToString()
+ )
+ }
+
+ return ViewModelMetadata(
+ typeElement
+ )
+ }
+ }
+}
diff --git a/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelModuleGenerator.kt b/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelModuleGenerator.kt
new file mode 100644
index 000000000..846f7d261
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelModuleGenerator.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.viewmodel
+
+import com.google.auto.common.GeneratedAnnotationSpecs
+import com.squareup.javapoet.AnnotationSpec
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.TypeSpec
+import dagger.hilt.android.processor.internal.AndroidClassNames
+import dagger.hilt.processor.internal.ClassNames
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.SourceVersion
+import javax.lang.model.element.Modifier
+import javax.lang.model.util.Elements
+
+/**
+ * Source generator to support Hilt injection of ViewModels.
+ *
+ * Should generate:
+ * ```
+ * public final class $_HiltModules {
+ * @Module
+ * @InstallIn(ViewModelComponent.class)
+ * public static abstract class BindsModule {
+ * @Binds
+ * @IntoMap
+ * @StringKey("pkg.$")
+ * @HiltViewModelMap
+ * public abstract ViewModel bind($ vm)
+ * }
+ * @Module
+ * @InstallIn(ActivityRetainedComponent.class)
+ * public static final class KeyModule {
+ * @Provides
+ * @IntoSet
+ * @HiltViewModelMap.KeySet
+ * public static String provide() {
+ * return "pkg.$";
+ * }
+ * }
+ * }
+ * ```
+ */
+internal class ViewModelModuleGenerator(
+ private val processingEnv: ProcessingEnvironment,
+ private val injectedViewModel: ViewModelMetadata
+) {
+ fun generate() {
+ val modulesTypeSpec = TypeSpec.classBuilder(injectedViewModel.modulesClassName)
+ .addOriginatingElement(injectedViewModel.typeElement)
+ .addGeneratedAnnotation(processingEnv.elementUtils, processingEnv.sourceVersion)
+ .addAnnotation(
+ AnnotationSpec.builder(ClassNames.ORIGINATING_ELEMENT)
+ .addMember(
+ "topLevelClass",
+ "$T.class",
+ injectedViewModel.className.topLevelClassName()
+ )
+ .build()
+ )
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .addType(getBindsModuleTypeSpec())
+ .addType(getKeyModuleTypeSpec())
+ .addMethod(
+ MethodSpec.constructorBuilder()
+ .addModifiers(Modifier.PRIVATE)
+ .build()
+ )
+ .build()
+ JavaFile.builder(injectedViewModel.modulesClassName.packageName(), modulesTypeSpec)
+ .build()
+ .writeTo(processingEnv.filer)
+ }
+
+ private fun getBindsModuleTypeSpec() = createModuleTypeSpec(
+ className = "BindsModule",
+ component = AndroidClassNames.VIEW_MODEL_COMPONENT
+ )
+ .addModifiers(Modifier.ABSTRACT)
+ .addMethod(getViewModelBindsMethod())
+ .build()
+
+ private fun getViewModelBindsMethod() =
+ MethodSpec.methodBuilder("binds")
+ .addAnnotation(ClassNames.BINDS)
+ .addAnnotation(ClassNames.INTO_MAP)
+ .addAnnotation(
+ AnnotationSpec.builder(ClassNames.STRING_KEY)
+ .addMember("value", S, injectedViewModel.className.reflectionName())
+ .build()
+ )
+ .addAnnotation(AndroidClassNames.HILT_VIEW_MODEL_MAP_QUALIFIER)
+ .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
+ .returns(AndroidClassNames.VIEW_MODEL)
+ .addParameter(injectedViewModel.className, "vm")
+ .build()
+
+ private fun getKeyModuleTypeSpec() = createModuleTypeSpec(
+ className = "KeyModule",
+ component = AndroidClassNames.ACTIVITY_RETAINED_COMPONENT
+ )
+ .addModifiers(Modifier.FINAL)
+ .addMethod(
+ MethodSpec.constructorBuilder()
+ .addModifiers(Modifier.PRIVATE)
+ .build()
+ )
+ .addMethod(getViewModelKeyProvidesMethod())
+ .build()
+
+ private fun getViewModelKeyProvidesMethod() =
+ MethodSpec.methodBuilder("provide")
+ .addAnnotation(ClassNames.PROVIDES)
+ .addAnnotation(ClassNames.INTO_SET)
+ .addAnnotation(AndroidClassNames.HILT_VIEW_MODEL_KEYS_QUALIFIER)
+ .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+ .returns(String::class.java)
+ .addStatement("return $S", injectedViewModel.className.reflectionName())
+ .build()
+
+ private fun createModuleTypeSpec(className: String, component: ClassName) =
+ TypeSpec.classBuilder(className)
+ .addOriginatingElement(injectedViewModel.typeElement)
+ .addAnnotation(ClassNames.MODULE)
+ .addAnnotation(
+ AnnotationSpec.builder(ClassNames.INSTALL_IN)
+ .addMember("value", "$T.class", component)
+ .build()
+ )
+ .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+
+ companion object {
+
+ const val L = "\$L"
+ const val T = "\$T"
+ const val N = "\$N"
+ const val S = "\$S"
+ const val W = "\$W"
+
+ private fun TypeSpec.Builder.addGeneratedAnnotation(
+ elements: Elements,
+ sourceVersion: SourceVersion
+ ) = apply {
+ GeneratedAnnotationSpecs.generatedAnnotationSpec(
+ elements,
+ sourceVersion,
+ ViewModelProcessor::class.java
+ ).ifPresent { generatedAnnotation ->
+ addAnnotation(generatedAnnotation)
+ }
+ }
+ }
+}
diff --git a/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelProcessor.kt b/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelProcessor.kt
new file mode 100644
index 000000000..97ebe521d
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelProcessor.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.viewmodel
+
+import com.google.auto.common.MoreElements
+import com.google.auto.service.AutoService
+import dagger.hilt.android.processor.internal.AndroidClassNames
+import dagger.hilt.processor.internal.BaseProcessor
+import javax.annotation.processing.Processor
+import javax.annotation.processing.RoundEnvironment
+import javax.lang.model.SourceVersion
+import javax.lang.model.element.Element
+import javax.lang.model.element.TypeElement
+import net.ltgt.gradle.incap.IncrementalAnnotationProcessor
+import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType
+
+/**
+ * Annotation processor for @ViewModelInject.
+ */
+@AutoService(Processor::class)
+@IncrementalAnnotationProcessor(IncrementalAnnotationProcessorType.ISOLATING)
+class ViewModelProcessor : BaseProcessor() {
+
+ private val parsedElements = mutableSetOf<TypeElement>()
+
+ override fun getSupportedAnnotationTypes() = setOf(
+ AndroidClassNames.HILT_VIEW_MODEL.toString()
+ )
+
+ override fun getSupportedSourceVersion() = SourceVersion.latest()
+
+ override fun processEach(annotation: TypeElement, element: Element) {
+ val typeElement = MoreElements.asType(element)
+ if (parsedElements.add(typeElement)) {
+ ViewModelMetadata.create(
+ processingEnv,
+ typeElement,
+ )?.let { viewModelMetadata ->
+ ViewModelModuleGenerator(
+ processingEnv,
+ viewModelMetadata
+ ).generate()
+ }
+ }
+ }
+
+ override fun postRoundProcess(roundEnv: RoundEnvironment?) {
+ parsedElements.clear()
+ }
+}
diff --git a/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelValidationPlugin.kt b/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelValidationPlugin.kt
new file mode 100644
index 000000000..a8e57dc9d
--- /dev/null
+++ b/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelValidationPlugin.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.viewmodel
+
+import com.google.auto.common.MoreTypes.asElement
+import com.google.auto.service.AutoService
+import com.google.common.graph.EndpointPair
+import com.google.common.graph.ImmutableNetwork
+import com.squareup.javapoet.ClassName
+import dagger.hilt.android.processor.internal.AndroidClassNames
+import dagger.hilt.processor.internal.Processors.hasAnnotation
+import dagger.model.Binding
+import dagger.model.BindingGraph
+import dagger.model.BindingGraph.Edge
+import dagger.model.BindingGraph.Node
+import dagger.model.BindingKind
+import dagger.spi.BindingGraphPlugin
+import dagger.spi.DiagnosticReporter
+import javax.tools.Diagnostic.Kind
+
+/**
+ * Plugin to validate users do not inject @HiltViewModel classes.
+ */
+@AutoService(BindingGraphPlugin::class)
+class ViewModelValidationPlugin : BindingGraphPlugin {
+
+ override fun visitGraph(bindingGraph: BindingGraph, diagnosticReporter: DiagnosticReporter) {
+ if (bindingGraph.rootComponentNode().isSubcomponent()) {
+ // This check does not work with partial graphs since it needs to take into account the source
+ // component.
+ return
+ }
+
+ val network: ImmutableNetwork<Node, Edge> = bindingGraph.network()
+ bindingGraph.dependencyEdges().forEach { edge ->
+ val pair: EndpointPair<Node> = network.incidentNodes(edge)
+ val target: Node = pair.target()
+ val source: Node = pair.source()
+ if (target is Binding &&
+ isHiltViewModelBinding(target) &&
+ !isInternalHiltViewModelUsage(source)
+ ) {
+ diagnosticReporter.reportDependency(
+ Kind.ERROR,
+ edge,
+ "\nInjection of an @HiltViewModel class is prohibited since it does not create a " +
+ "ViewModel instance correctly.\nAccess the ViewModel via the Android APIs " +
+ "(e.g. ViewModelProvider) instead." +
+ "\nInjected ViewModel: ${target.key().type()}\n"
+ )
+ }
+ }
+ }
+
+ private fun isHiltViewModelBinding(target: Binding): Boolean {
+ // Make sure this is from an @Inject constructor rather than an overridden binding like an
+ // @Provides and that the class is annotated with @HiltViewModel.
+ return target.kind() == BindingKind.INJECTION &&
+ hasAnnotation(asElement(target.key().type()), AndroidClassNames.HILT_VIEW_MODEL)
+ }
+
+ private fun isInternalHiltViewModelUsage(source: Node): Boolean {
+ // We expect @HiltViewModel classes to be bound into a map with an @Binds like
+ // @Binds
+ // @IntoMap
+ // @StringKey(...)
+ // @HiltViewModelMap
+ // abstract ViewModel bindViewModel(FooViewModel vm)
+ //
+ // So we check that it is a multibinding contribution with the internal qualifier.
+ // TODO(erichang): Should we check for even more things?
+ return source is Binding &&
+ source.key().qualifier().isPresent() &&
+ ClassName.get(source.key().qualifier().get().getAnnotationType()) ==
+ AndroidClassNames.HILT_VIEW_MODEL_MAP_QUALIFIER &&
+ source.key().multibindingContributionIdentifier().isPresent()
+ }
+}
diff --git a/java/dagger/hilt/android/qualifiers/ActivityContext.java b/java/dagger/hilt/android/qualifiers/ActivityContext.java
new file mode 100644
index 000000000..cfcc40efa
--- /dev/null
+++ b/java/dagger/hilt/android/qualifiers/ActivityContext.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.qualifiers;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.inject.Qualifier;
+
+/** Annotation for a {@code Context} that corresponds to the activity. */
+@Qualifier
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
+public @interface ActivityContext {}
diff --git a/java/dagger/hilt/android/qualifiers/ApplicationContext.java b/java/dagger/hilt/android/qualifiers/ApplicationContext.java
new file mode 100644
index 000000000..226ef7585
--- /dev/null
+++ b/java/dagger/hilt/android/qualifiers/ApplicationContext.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.qualifiers;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+import javax.inject.Qualifier;
+
+/** Annotation for an Application Context dependency. */
+@Qualifier
+@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
+public @interface ApplicationContext {}
diff --git a/java/dagger/hilt/android/qualifiers/BUILD b/java/dagger/hilt/android/qualifiers/BUILD
new file mode 100644
index 000000000..26b45ec74
--- /dev/null
+++ b/java/dagger/hilt/android/qualifiers/BUILD
@@ -0,0 +1,40 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Hilt Android qualifiers
+
+package(default_visibility = ["//:src"])
+
+android_library(
+ name = "qualifiers",
+ srcs = [
+ "ActivityContext.java",
+ "ApplicationContext.java",
+ ],
+ deps = [
+ ":package_info",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ ],
+)
+
+java_library(
+ name = "package_info",
+ srcs = ["package-info.java"],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/android/qualifiers/package-info.java b/java/dagger/hilt/android/qualifiers/package-info.java
new file mode 100644
index 000000000..2cbd7447d
--- /dev/null
+++ b/java/dagger/hilt/android/qualifiers/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** This package contains Hilt's built-in Android {@link javax.inject.Qualifier} annotations. */
+package dagger.hilt.android.qualifiers;
diff --git a/java/dagger/hilt/android/scopes/ActivityRetainedScoped.java b/java/dagger/hilt/android/scopes/ActivityRetainedScoped.java
new file mode 100644
index 000000000..c61325dcc
--- /dev/null
+++ b/java/dagger/hilt/android/scopes/ActivityRetainedScoped.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.scopes;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+import javax.inject.Scope;
+
+/**
+ * Scope annotation for bindings that should exist for the life of an activity, surviving
+ * configuration.
+ */
+@Scope
+@Retention(CLASS)
+public @interface ActivityRetainedScoped {}
diff --git a/java/dagger/hilt/android/scopes/ActivityScoped.java b/java/dagger/hilt/android/scopes/ActivityScoped.java
new file mode 100644
index 000000000..9f128f716
--- /dev/null
+++ b/java/dagger/hilt/android/scopes/ActivityScoped.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.scopes;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+import javax.inject.Scope;
+
+/**
+ * Scope annotation for bindings that should exist for the life of an activity.
+ */
+@Scope
+@Retention(CLASS)
+public @interface ActivityScoped {}
diff --git a/java/dagger/hilt/android/scopes/BUILD b/java/dagger/hilt/android/scopes/BUILD
new file mode 100644
index 000000000..e74ac9e38
--- /dev/null
+++ b/java/dagger/hilt/android/scopes/BUILD
@@ -0,0 +1,60 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+package(default_visibility = ["//:src"])
+
+# Description:
+# Hilt scopes.
+
+android_library(
+ name = "scopes",
+ srcs = [
+ "ActivityScoped.java",
+ "FragmentScoped.java",
+ "ServiceScoped.java",
+ "ViewScoped.java",
+ ],
+ deps = [
+ ":package_info",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ ],
+)
+
+android_library(
+ name = "activity_retained_scoped",
+ srcs = ["ActivityRetainedScoped.java"],
+ deps = [
+ ":package_info",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ ],
+)
+
+android_library(
+ name = "view_model_scoped",
+ srcs = ["ViewModelScoped.java"],
+ deps = [
+ ":package_info",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ ],
+)
+
+java_library(
+ name = "package_info",
+ srcs = ["package-info.java"],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/android/scopes/FragmentScoped.java b/java/dagger/hilt/android/scopes/FragmentScoped.java
new file mode 100644
index 000000000..bc6c75e2f
--- /dev/null
+++ b/java/dagger/hilt/android/scopes/FragmentScoped.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.scopes;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+import javax.inject.Scope;
+
+/**
+ * Scope annotation for bindings that should exist for the life of a fragment.
+ */
+@Scope
+@Retention(CLASS)
+public @interface FragmentScoped {}
diff --git a/java/dagger/hilt/android/scopes/ServiceScoped.java b/java/dagger/hilt/android/scopes/ServiceScoped.java
new file mode 100644
index 000000000..84205f26d
--- /dev/null
+++ b/java/dagger/hilt/android/scopes/ServiceScoped.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.scopes;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+import javax.inject.Scope;
+
+/**
+ * Scope annotation for bindings that should exist for the life of a service.
+ */
+@Scope
+@Retention(CLASS)
+public @interface ServiceScoped {}
diff --git a/java/dagger/hilt/android/scopes/ViewModelScoped.java b/java/dagger/hilt/android/scopes/ViewModelScoped.java
new file mode 100644
index 000000000..8861125e9
--- /dev/null
+++ b/java/dagger/hilt/android/scopes/ViewModelScoped.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.scopes;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import javax.inject.Scope;
+
+/**
+ * Scope annotation for bindings that should exist for the life of a a single {@link
+ * androidx.lifecycle.ViewModel}.
+ *
+ * <p>Use this scope annotation when you want to define a dependency in the {@link
+ * dagger.hilt.android.components.ViewModelComponent} for which a single instance will be provided
+ * across all other dependencies for a single {@link
+ * dagger.hilt.android.lifecycle.HiltViewModel}-annotated {@code ViewModel}. Other {@code
+ * ViewModel}s that request the scoped dependency will receive a different instance. For sharing the
+ * same instance of a dependency across all {@code ViewModel}s use a scope from one of the parent
+ * components of {@code dagger.hilt.android.components.ViewModelComponent}, such as {@link
+ * javax.inject.Singleton} or {@link dagger.hilt.android.scopes.ActivityRetainedScoped}.
+ *
+ * <p>For example:
+ *
+ * <pre>
+ * &#64;Module
+ * &#64;InstallIn(ViewModelComponent.class)
+ * public final class ViewModelMovieModule {
+ * &#64;Provides
+ * &#64;ViewModelScoped
+ * public static MovieRepository provideRepo(SavedStateHandle handle) {
+ * return new MovieRepository(handle.getString("movie-id"));
+ * }
+ * }
+ *
+ * public final class MovieDetailFetcher {
+ * &#64;Inject MovieDetailFetcher(MovieRepository movieRepo) {
+ * // ...
+ * }
+ * }
+ *
+ * public final class MoviePosterFetcher {
+ * &#64;Inject MoviePosterFetcher(MovieRepository movieRepo) {
+ * // ...
+ * }
+ * }
+ *
+ * &#64;HiltViewModel
+ * public class MovieViewModel extends ViewModel {
+ * &#64;Inject
+ * public MovieViewModel(MovieDetailFetcher detailFetcher, MoviePosterFetcher posterFetcher) {
+ * // Both detailFetcher and posterFetcher will contain the same instance of
+ * // the MovieRepository.
+ * }
+ * }
+ * </pre>
+ *
+ * @see dagger.hilt.android.lifecycle.HiltViewModel
+ * @see dagger.hilt.android.components.ViewModelComponent
+ */
+@Scope
+@Retention(RetentionPolicy.CLASS)
+public @interface ViewModelScoped {}
diff --git a/java/dagger/hilt/android/scopes/ViewScoped.java b/java/dagger/hilt/android/scopes/ViewScoped.java
new file mode 100644
index 000000000..abf7c0a93
--- /dev/null
+++ b/java/dagger/hilt/android/scopes/ViewScoped.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.scopes;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import javax.inject.Scope;
+
+/**
+ * Scope annotation for bindings that should exist for the life of a View.
+ */
+@Scope
+@Retention(CLASS)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface ViewScoped {}
diff --git a/java/dagger/hilt/android/scopes/package-info.java b/java/dagger/hilt/android/scopes/package-info.java
new file mode 100644
index 000000000..6f8f9895c
--- /dev/null
+++ b/java/dagger/hilt/android/scopes/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package contains Hilt's built-in Android {@link javax.inject.Scope} annotations.
+ *
+ * @see <a href="https://dagger.dev/hilt/components.md#component-lifetimes">Component Lifetimes</a>
+ */
+package dagger.hilt.android.scopes;
diff --git a/java/dagger/hilt/android/testing/AndroidManifest.xml b/java/dagger/hilt/android/testing/AndroidManifest.xml
new file mode 100644
index 000000000..26ec66946
--- /dev/null
+++ b/java/dagger/hilt/android/testing/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2020 The Dagger Authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.hilt.android.testing">
+ <uses-sdk android:minSdkVersion="14" />
+</manifest>
diff --git a/java/dagger/hilt/android/testing/BUILD b/java/dagger/hilt/android/testing/BUILD
new file mode 100644
index 000000000..47db287e5
--- /dev/null
+++ b/java/dagger/hilt/android/testing/BUILD
@@ -0,0 +1,238 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Description:
+# Testing libraries for Hilt Android.
+
+load("//:build_defs.bzl", "POM_VERSION_ALPHA")
+load("//tools:maven.bzl", "gen_maven_artifact")
+
+package(default_visibility = ["//:src"])
+
+android_library(
+ name = "custom_test_application",
+ testonly = 1,
+ srcs = ["CustomTestApplication.java"],
+ exported_plugins = [
+ "//java/dagger/hilt/android/processor/internal/customtestapplication:processor",
+ ],
+ exports = [
+ "//java/dagger/hilt/android/internal/testing:test_application_component_manager",
+ "//java/dagger/hilt/android/internal/testing:test_application_component_manager_holder",
+ "//java/dagger/hilt/internal:component_manager",
+ ],
+ deps = [
+ ":package_info",
+ "//java/dagger/hilt:generates_root_input",
+ ],
+)
+
+android_library(
+ name = "hilt_android_test",
+ testonly = 1,
+ srcs = ["HiltAndroidTest.java"],
+ exported_plugins = [
+ "//java/dagger/hilt/processor/internal/root:plugin",
+ "//java/dagger/hilt/android/processor/internal/androidentrypoint:plugin",
+ "//java/dagger/hilt/android/processor/internal/viewmodel:validation_plugin",
+ ],
+ exports = [
+ ":hilt_android_rule",
+ ":hilt_test_application",
+ ":on_component_ready_runner",
+ ":package_info",
+ "//:dagger_with_compiler",
+ "//java/dagger/hilt:install_in",
+ "//java/dagger/hilt/android/components",
+ "//java/dagger/hilt/android/internal/builders",
+ "//java/dagger/hilt/android/internal/managers",
+ "//java/dagger/hilt/android/internal/modules",
+ "//java/dagger/hilt/android/internal/testing:test_application_component_manager",
+ "//java/dagger/hilt/android/internal/testing:test_application_component_manager_holder",
+ "//java/dagger/hilt/android/internal/testing:test_injector",
+ "//java/dagger/hilt/android/scopes",
+ "//java/dagger/hilt/internal:component_entry_point",
+ "//java/dagger/hilt/internal:component_manager",
+ "//java/dagger/hilt/internal:generated_entry_point",
+ "//java/dagger/hilt/internal:preconditions",
+ "//java/dagger/hilt/migration:disable_install_in_check",
+ "@maven//:androidx_annotation_annotation",
+ "@maven//:androidx_multidex_multidex",
+ "@maven//:androidx_test_core",
+ ],
+ deps = [
+ "//java/dagger/hilt:generates_root_input",
+ ],
+)
+
+android_library(
+ name = "hilt_android_rule",
+ testonly = 1,
+ srcs = ["HiltAndroidRule.java"],
+ deps = [
+ ":package_info",
+ "//java/dagger/hilt/android/internal/testing:mark_that_rules_ran_rule",
+ "//java/dagger/hilt/internal:preconditions",
+ "@maven//:junit_junit",
+ ],
+)
+
+android_library(
+ name = "hilt_test_application",
+ testonly = 1,
+ srcs = [
+ "HiltTestApplication.java",
+ ],
+ deps = [
+ ":on_component_ready_runner",
+ ":package_info",
+ "//java/dagger/hilt/android/internal/testing:test_application_component_manager",
+ "//java/dagger/hilt/android/internal/testing:test_application_component_manager_holder",
+ "//java/dagger/hilt/internal:component_manager",
+ "@maven//:androidx_multidex_multidex",
+ ],
+)
+
+android_library(
+ name = "on_component_ready_runner",
+ testonly = 1,
+ srcs = ["OnComponentReadyRunner.java"],
+ deps = [
+ ":package_info",
+ "//:dagger_with_compiler",
+ "//java/dagger/hilt:entry_point",
+ "//java/dagger/hilt/android/internal/testing:test_application_component_manager_holder",
+ "//java/dagger/hilt/internal:component_manager",
+ "//java/dagger/hilt/internal:preconditions",
+ "@google_bazel_common//third_party/java/auto:value",
+ ],
+)
+
+android_library(
+ name = "uninstall_modules",
+ testonly = 1,
+ srcs = ["UninstallModules.java"],
+ exported_plugins = [
+ "//java/dagger/hilt/android/processor/internal/uninstallmodules:processor",
+ ],
+ deps = [
+ ":package_info",
+ ],
+)
+
+java_library(
+ name = "bind_value",
+ testonly = 1,
+ srcs = [
+ "BindElementsIntoSet.java",
+ "BindValue.java",
+ "BindValueIntoMap.java",
+ "BindValueIntoSet.java",
+ ],
+ exported_plugins = [
+ "//java/dagger/hilt/android/processor/internal/bindvalue:bind_value_processor",
+ ],
+ exports = [
+ "//java/dagger/hilt:install_in",
+ "//java/dagger/hilt/android/qualifiers",
+ ],
+ deps = [
+ ":package_info",
+ "//java/dagger/hilt:generates_root_input",
+ ],
+)
+
+java_library(
+ name = "package_info",
+ srcs = ["package-info.java"],
+ deps = [
+ "@google_bazel_common//third_party/java/jsr305_annotations",
+ ],
+)
+
+android_library(
+ name = "artifact-lib",
+ testonly = 1,
+ tags = ["maven_coordinates=com.google.dagger:hilt-android-testing:" + POM_VERSION_ALPHA],
+ exports = [
+ ":bind_value",
+ ":custom_test_application",
+ ":hilt_android_test",
+ ":package_info",
+ ":uninstall_modules",
+ "//java/dagger/hilt/android:artifact-lib",
+ "//java/dagger/hilt/testing:test_install_in",
+ ],
+)
+
+gen_maven_artifact(
+ name = "artifact",
+ testonly = 1,
+ artifact_coordinates = "com.google.dagger:hilt-android-testing:" + POM_VERSION_ALPHA,
+ artifact_name = "Hilt Android Testing",
+ artifact_target = ":artifact-lib",
+ artifact_target_libs = [
+ "//java/dagger/hilt/android/internal/testing:mark_that_rules_ran_rule",
+ "//java/dagger/hilt/android/internal/testing:test_application_component_manager",
+ "//java/dagger/hilt/android/internal/testing:test_application_component_manager_holder",
+ "//java/dagger/hilt/android/internal/testing:test_component_data",
+ "//java/dagger/hilt/android/internal/testing:test_injector",
+ "//java/dagger/hilt/android/testing:bind_value",
+ "//java/dagger/hilt/android/testing:custom_test_application",
+ "//java/dagger/hilt/android/testing:hilt_android_rule",
+ "//java/dagger/hilt/android/testing:hilt_android_test",
+ "//java/dagger/hilt/android/testing:hilt_test_application",
+ "//java/dagger/hilt/android/testing:on_component_ready_runner",
+ "//java/dagger/hilt/android/testing:package_info",
+ "//java/dagger/hilt/testing:test_install_in",
+ "//java/dagger/hilt/testing:package_info",
+ "//java/dagger/hilt/android/testing:uninstall_modules",
+ ],
+ artifact_target_maven_deps = [
+ "androidx.activity:activity",
+ "androidx.annotation:annotation",
+ "androidx.fragment:fragment",
+ "androidx.lifecycle:lifecycle-viewmodel",
+ "androidx.lifecycle:lifecycle-viewmodel-savedstate",
+ "androidx.multidex:multidex",
+ "androidx.test:core",
+ "com.google.code.findbugs:jsr305",
+ "com.google.dagger:dagger",
+ "com.google.dagger:hilt-android",
+ "javax.inject:javax.inject",
+ "junit:junit",
+ ],
+ artifact_target_maven_deps_banned = [
+ "com.google.guava:guava",
+ "javax.annotation:jsr250-api",
+ ],
+ javadoc_android_api_level = 30,
+ javadoc_exclude_packages = [
+ "dagger.hilt.internal",
+ "dagger.hilt.android.internal",
+ ],
+ javadoc_root_packages = [
+ "dagger.hilt.android.testing",
+ ],
+ javadoc_srcs = [
+ "//java/dagger/hilt:hilt_android_testing_filegroup",
+ "//java/dagger/hilt:hilt_testing_filegroup",
+ ],
+ manifest = "AndroidManifest.xml",
+ packaging = "aar",
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/android/testing/BindElementsIntoSet.java b/java/dagger/hilt/android/testing/BindElementsIntoSet.java
new file mode 100644
index 000000000..620e0c588
--- /dev/null
+++ b/java/dagger/hilt/android/testing/BindElementsIntoSet.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.testing;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import dagger.hilt.GeneratesRootInput;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that can be used on a test field to contribute the value into the {@link
+ * dagger.hilt.components.SingletonComponent} as an {@link
+ * dagger.multibindings.ElementsIntoSet} for the given type. Example usage:
+ *
+ * <pre><code>
+ * public class FooTest{
+ * ...
+ * {@literal @}BindElementsIntoSet Set<String> bindedSet = ImmutableSet.of("bar", "baz");
+ * ...
+ * }
+ * </code></pre>
+ *
+ * Here, bindedSet will be accessible to the entire application for your test. This is functionally
+ * equivalent to installing the following module in your test:
+ *
+ * <pre><code>
+ * {@literal @}Module
+ * {@literal @}InstallIn
+ * interface MyModule {
+ * {@literal @}Provides
+ * {@literal @}ElementsIntoSet
+ * Set<String> bindSet() {
+ * return ImmutableSet.of("bar", "baz");
+ * }
+ * }
+ * </code></pre>
+ *
+ * Also see {@link BindValueIntoSet}, where you can gather individual elements into one set and bind
+ * it to the application.
+ */
+@Retention(CLASS)
+@Target({ElementType.FIELD})
+@GeneratesRootInput
+public @interface BindElementsIntoSet {}
diff --git a/java/dagger/hilt/android/testing/BindValue.java b/java/dagger/hilt/android/testing/BindValue.java
new file mode 100644
index 000000000..c0164f8db
--- /dev/null
+++ b/java/dagger/hilt/android/testing/BindValue.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.testing;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import dagger.hilt.GeneratesRootInput;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that can be used on a test field to contribute the value into the {@link
+ * dagger.hilt.components.SingletonComponent}. Example usage:
+ *
+ * <pre><code>
+ * public class FooTest{
+ * ...
+ * {@literal @}BindValue Bar boundBar = new Bar();
+ * ...
+ * }
+ * </code></pre>
+ *
+ * Here {@code boundBar} will be accessible to the entire application for your test.
+ */
+@Retention(CLASS)
+@Target({ElementType.FIELD})
+@GeneratesRootInput
+public @interface BindValue {}
diff --git a/java/dagger/hilt/android/testing/BindValueIntoMap.java b/java/dagger/hilt/android/testing/BindValueIntoMap.java
new file mode 100644
index 000000000..b1dfcb3ef
--- /dev/null
+++ b/java/dagger/hilt/android/testing/BindValueIntoMap.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.testing;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import dagger.hilt.GeneratesRootInput;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that can be used on a test field to contribute the value into the {@link
+ * dagger.hilt.components.SingletonComponent} as an {@link dagger.multibindings.IntoMap}
+ * for the given type. Example usage:
+ *
+ * <pre><code>
+ * public class FooTest{
+ * ...
+ * {@literal @}BindValueIntoMap
+ * {@literal @}MyMapKey(KEY1)
+ * String boundBar = "bar";
+ *
+ * {@literal @}BindValueIntoMap
+ * {@literal @}MyMapKey(KEY2)
+ * String boundBaz = "baz";
+ * ...
+ * }
+ * </code></pre>
+ *
+ * Here the map that contains all the bound elements (in this case "bar" and "baz") will be
+ * accessible to the entire application for your test.
+ */
+@Retention(CLASS)
+@Target({ElementType.FIELD})
+@GeneratesRootInput
+public @interface BindValueIntoMap {}
diff --git a/java/dagger/hilt/android/testing/BindValueIntoSet.java b/java/dagger/hilt/android/testing/BindValueIntoSet.java
new file mode 100644
index 000000000..690a55cb4
--- /dev/null
+++ b/java/dagger/hilt/android/testing/BindValueIntoSet.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.testing;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import dagger.hilt.GeneratesRootInput;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that can be used on a test field to contribute the value into the {@link
+ * dagger.hilt.components.SingletonComponent} as an {@link dagger.multibindings.IntoSet}
+ * for the given type. Example usage:
+ *
+ * <pre><code>
+ * public class FooTest{
+ * ...
+ * {@literal @}BindValueIntoSet String boundBar = "bar";
+ * {@literal @}BindValueIntoSet String boundBaz = "baz";
+ * ...
+ * }
+ * </code></pre>
+ *
+ * Here the set that contains all the bound elements (in this case "bar" and "baz") will be
+ * accessible to the entire application for your test. Also see {@link BindElementsIntoSet}, where
+ * you can gather individual elements into one set and bind it to the application.
+ */
+@Retention(CLASS)
+@Target({ElementType.FIELD})
+@GeneratesRootInput
+public @interface BindValueIntoSet {}
diff --git a/java/dagger/hilt/android/testing/CustomTestApplication.java b/java/dagger/hilt/android/testing/CustomTestApplication.java
new file mode 100644
index 000000000..36a4410d1
--- /dev/null
+++ b/java/dagger/hilt/android/testing/CustomTestApplication.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.testing;
+
+import dagger.hilt.GeneratesRootInput;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that creates an application with the given base type that can be used for any
+ * test in the given build.
+ *
+ * <p>This annotation is useful for creating an application that can be used with instrumentation
+ * tests in gradle, since every instrumentation test must share the same application type.
+ */
+@Target({ElementType.TYPE})
+@GeneratesRootInput
+public @interface CustomTestApplication {
+
+ /** Returns the base {@link android.app.Application} class. */
+ Class<?> value();
+}
diff --git a/java/dagger/hilt/android/testing/HiltAndroidRule.java b/java/dagger/hilt/android/testing/HiltAndroidRule.java
new file mode 100644
index 000000000..32a34f729
--- /dev/null
+++ b/java/dagger/hilt/android/testing/HiltAndroidRule.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.testing;
+
+import static dagger.hilt.internal.Preconditions.checkNotNull;
+
+import dagger.hilt.android.internal.testing.MarkThatRulesRanRule;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * A {@link TestRule} for Hilt that can be used with JVM or Instrumentation tests.
+ *
+ * <p>This rule is required. The Dagger component will not be created without this test rule.
+ */
+public final class HiltAndroidRule implements TestRule {
+ private final MarkThatRulesRanRule rule;
+
+ /** Creates a new instance of the rules. Tests should pass {@code this}. */
+ public HiltAndroidRule(Object testInstance) {
+ this.rule = new MarkThatRulesRanRule(checkNotNull(testInstance));
+ }
+
+ @Override public Statement apply(Statement baseStatement, Description description) {
+ return rule.apply(baseStatement, description);
+ }
+
+ /**
+ * Completes Dagger injection. Must be called before accessing inject types. Must be called after
+ * any non-static test module have been added. If {@link #delayComponentReady} was used, this must
+ * be called after {@link #componentReady}.
+ */
+ public void inject() {
+ rule.inject();
+ }
+
+ /**
+ * Delays creating the component until {@link #componentReady} is called. This is only necessary
+ * in the case that a dynamically bound value (e.g. configuring an @BindValue field in @Before
+ * or @Test method) is requested before test case execution begins.
+ *
+ * <p>Examples of early binding requests include an Activity launched by a test rule, or an entry
+ * points in a {@link OnComponentReadyRunner}.
+ *
+ * <p>If this method is called, {@link #componentReady} must be called before the test case
+ * finishes.
+ */
+ public HiltAndroidRule delayComponentReady() {
+ rule.delayComponentReady();
+ return this;
+ }
+
+ /**
+ * Completes Dagger component creation if {@link delayComponentReady} was used. Binds the current
+ * value of {@link BindValue} fields. Normally this happens automatically. This method may only be
+ * called if {@link delayComponentReady} was used to delay value binding.
+ *
+ * @return an instance of the test rule for chaining
+ */
+ public HiltAndroidRule componentReady() {
+ rule.componentReady();
+ return this;
+ }
+}
diff --git a/java/dagger/hilt/android/testing/HiltAndroidTest.java b/java/dagger/hilt/android/testing/HiltAndroidTest.java
new file mode 100644
index 000000000..fa15f92a5
--- /dev/null
+++ b/java/dagger/hilt/android/testing/HiltAndroidTest.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.testing;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import dagger.hilt.GeneratesRootInput;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/** Annotation used for marking an Android emulator tests that require injection. */
+// Set the retention to RUNTIME because we check it via reflection in the HiltAndroidRule.
+@Retention(RUNTIME)
+@Target({ElementType.TYPE})
+@GeneratesRootInput
+public @interface HiltAndroidTest {}
diff --git a/java/dagger/hilt/android/testing/HiltTestApplication.java b/java/dagger/hilt/android/testing/HiltTestApplication.java
new file mode 100644
index 000000000..97eb4cbb2
--- /dev/null
+++ b/java/dagger/hilt/android/testing/HiltTestApplication.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.testing;
+
+import android.content.Context;
+import androidx.multidex.MultiDexApplication;
+import dagger.hilt.android.internal.testing.TestApplicationComponentManager;
+import dagger.hilt.android.internal.testing.TestApplicationComponentManagerHolder;
+import dagger.hilt.internal.GeneratedComponentManager;
+
+/**
+ * An application that can be used for Android instrumentation or Robolectric tests using Hilt.
+ */
+public final class HiltTestApplication extends MultiDexApplication
+ implements GeneratedComponentManager<Object>, TestApplicationComponentManagerHolder {
+
+ // This field is initialized in attachBaseContext to avoid pulling the generated component into
+ // the main dex. We could possibly avoid this by class loading TestComponentDataSupplier lazily
+ // rather than in the TestApplicationComponentManager constructor.
+ private TestApplicationComponentManager componentManager;
+
+ @Override
+ protected final void attachBaseContext(Context base) {
+ super.attachBaseContext(base);
+ componentManager = new TestApplicationComponentManager(this);
+ }
+
+ @Override
+ public final Object componentManager() {
+ return componentManager;
+ }
+
+ @Override
+ public final Object generatedComponent() {
+ return componentManager.generatedComponent();
+ }
+}
diff --git a/java/dagger/hilt/android/testing/OnComponentReadyRunner.java b/java/dagger/hilt/android/testing/OnComponentReadyRunner.java
new file mode 100644
index 000000000..925a19f49
--- /dev/null
+++ b/java/dagger/hilt/android/testing/OnComponentReadyRunner.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.testing;
+
+import android.app.Application;
+import android.content.Context;
+import com.google.auto.value.AutoValue;
+import dagger.hilt.EntryPoints;
+import dagger.hilt.android.internal.testing.TestApplicationComponentManagerHolder;
+import dagger.hilt.internal.GeneratedComponentManager;
+import dagger.hilt.internal.Preconditions;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Provides access to the Singleton component in tests, so that Rules can access it after custom
+ * test modules have been added.
+ */
+public final class OnComponentReadyRunner {
+ private final List<EntryPointListener<?>> listeners = new ArrayList<>();
+ private GeneratedComponentManager<?> componentManager;
+ private boolean componentHostSet = false;
+
+ /** Used by generated code, to notify listeners that the component has been created. */
+ public void setComponentManager(GeneratedComponentManager<?> componentManager) {
+ Preconditions.checkState(!componentHostSet, "Component host was already set.");
+ componentHostSet = true;
+ this.componentManager = componentManager;
+ for (EntryPointListener<?> listener : listeners) {
+ listener.deliverComponent(componentManager);
+ }
+ }
+
+ /** Must be called on the test thread, before the Statement is evaluated. */
+ public static <T> void addListener(
+ Context context, Class<T> entryPoint, OnComponentReadyListener<T> listener) {
+ Application application = (Application) context.getApplicationContext();
+ if (application instanceof TestApplicationComponentManagerHolder) {
+ TestApplicationComponentManagerHolder managerHolder =
+ (TestApplicationComponentManagerHolder) application;
+ OnComponentReadyRunnerHolder runnerHolder =
+ (OnComponentReadyRunnerHolder) managerHolder.componentManager();
+ runnerHolder.getOnComponentReadyRunner().addListenerInternal(entryPoint, listener);
+ }
+ }
+
+ private <T> void addListenerInternal(Class<T> entryPoint, OnComponentReadyListener<T> listener) {
+ if (componentHostSet) {
+ // If the componentHost was already set, just call through immediately
+ runListener(componentManager, entryPoint, listener);
+ } else {
+ listeners.add(EntryPointListener.create(entryPoint, listener));
+ }
+ }
+
+ public boolean isEmpty() {
+ return listeners.isEmpty();
+ }
+
+ @AutoValue
+ abstract static class EntryPointListener<T> {
+ static <T> EntryPointListener<T> create(
+ Class<T> entryPoint, OnComponentReadyListener<T> listener) {
+ return new AutoValue_OnComponentReadyRunner_EntryPointListener<T>(entryPoint, listener);
+ }
+
+ abstract Class<T> entryPoint();
+
+ abstract OnComponentReadyListener<T> listener();
+
+ private void deliverComponent(GeneratedComponentManager<?> object) {
+ runListener(object, entryPoint(), listener());
+ }
+ }
+
+ private static <T> void runListener(
+ GeneratedComponentManager<?> componentManager,
+ Class<T> entryPoint,
+ OnComponentReadyListener<T> listener) {
+ try {
+ listener.onComponentReady(EntryPoints.get(componentManager, entryPoint));
+ } catch (RuntimeException | Error t) {
+ throw t;
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+
+ /** Public for use by generated code and {@link TestApplicationComponentManager} */
+ public interface OnComponentReadyRunnerHolder {
+ OnComponentReadyRunner getOnComponentReadyRunner();
+ }
+
+ /** Rules should register an implementation of this to get access to the singleton component */
+ public interface OnComponentReadyListener<T> {
+ void onComponentReady(T entryPoint) throws Throwable;
+ }
+}
diff --git a/java/dagger/hilt/android/testing/UninstallModules.java b/java/dagger/hilt/android/testing/UninstallModules.java
new file mode 100644
index 000000000..6480c10f5
--- /dev/null
+++ b/java/dagger/hilt/android/testing/UninstallModules.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.testing;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation used to uninstall modules that have previously been installed with {@link
+ * dagger.hilt.InstallIn}.
+ *
+ * <p>This feature should only be used in tests. It is useful for replacing production bindings with
+ * fake bindings. The basic idea is to allow users to uninstall the module that provided the
+ * production binding so that a fake binding can be provided by the test.
+ *
+ * <p>Example:
+ *
+ * <pre><code>
+ * {@literal @}HiltAndroidTest
+ * {@literal @}UninstallModules({
+ * ProdFooModule.class,
+ * })
+ * public class MyTest {
+ * {@literal @}Module
+ * {@literal @}InstallIn(SingletonComponent.class)
+ * interface FakeFooModule {
+ * {@literal @}Binds Foo bindFoo(FakeFoo fakeFoo);
+ * }
+ * }
+ * </code></pre>
+ */
+@Target({ElementType.TYPE})
+public @interface UninstallModules {
+
+ /**
+ * Returns the list of classes to uninstall.
+ *
+ * <p>These classes must be annotated with both {@link dagger.Module} and {@link
+ * dagger.hilt.InstallIn}.
+ *
+ * <p>Note:A module that is included as part of another module's {@link dagger.Module#includes()}
+ * cannot be truly uninstalled until the including module is also uninstalled.
+ */
+ Class<?>[] value() default {};
+}
diff --git a/java/dagger/hilt/android/testing/package-info.java b/java/dagger/hilt/android/testing/package-info.java
new file mode 100644
index 000000000..fae8e7de0
--- /dev/null
+++ b/java/dagger/hilt/android/testing/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package contains APIs for writing Android local or instrumentation tests with Hilt.
+ *
+ * @see <a href="https://dagger.dev/hilt/testing">Hilt Testing</a>
+ */
+@ParametersAreNonnullByDefault
+package dagger.hilt.android.testing;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/java/dagger/hilt/codegen/BUILD b/java/dagger/hilt/codegen/BUILD
new file mode 100644
index 000000000..c88182b0e
--- /dev/null
+++ b/java/dagger/hilt/codegen/BUILD
@@ -0,0 +1,39 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# This package contains sources used within code generated sources.
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "originating_element",
+ srcs = ["OriginatingElement.java"],
+ exported_plugins = [
+ "//java/dagger/hilt/processor/internal/originatingelement:processor",
+ ],
+ deps = [
+ ":package_info",
+ ],
+)
+
+java_library(
+ name = "package_info",
+ srcs = ["package-info.java"],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/codegen/OriginatingElement.java b/java/dagger/hilt/codegen/OriginatingElement.java
new file mode 100644
index 000000000..c53a4301a
--- /dev/null
+++ b/java/dagger/hilt/codegen/OriginatingElement.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.codegen;
+
+/**
+ * An annotation used to specify the originating element that triggered the code generation of a
+ * type. This annotation should only be used on generated code and is meant to be used by code
+ * generators that generate Hilt modules, entry points, etc. Failure to use this annotation may mean
+ * improper test isolation for generated classes.
+ *
+ * <p>This annotation should be used on any generated top-level class that either contains generated
+ * modules (or entry points) or contains annotations that will generate modules (or entry points).
+ *
+ * <p>Example: Suppose we have the following use of an annotation, {@code MyAnnotation}.
+ *
+ * <pre><code>
+ * class Outer {
+ * static class Inner {
+ * {@literal @}MyAnnotation Foo foo;
+ * }
+ * }
+ * </code></pre>
+ *
+ * <p>If {@code MyAnnotation} generates an entry point, it should be annotated as follows:
+ *
+ * <pre><code>
+ * {@literal @}OriginatingElement(topLevelClass = Outer.class)
+ * {@literal @}EntryPoint
+ * {@literal @}InstallIn(SingletonComponent.class) {
+ * ...
+ * }
+ * </code></pre>
+ */
+// TODO(bcorso): Consider just advising/enforcing that all top-level classes use this annotation.
+public @interface OriginatingElement {
+ /** Returns the top-level class enclosing the originating element. */
+ Class<?> topLevelClass();
+}
diff --git a/java/dagger/hilt/codegen/package-info.java b/java/dagger/hilt/codegen/package-info.java
new file mode 100644
index 000000000..b6bf709af
--- /dev/null
+++ b/java/dagger/hilt/codegen/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package contains APIs for code generators that produce code that will be processed by Hilt.
+ */
+// TODO(danysantiago): Add documentation about other code generators that produce input for Hilt
+package dagger.hilt.codegen;
diff --git a/java/dagger/hilt/components/BUILD b/java/dagger/hilt/components/BUILD
new file mode 100644
index 000000000..48dc8cd26
--- /dev/null
+++ b/java/dagger/hilt/components/BUILD
@@ -0,0 +1,40 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Hilt components
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "components",
+ srcs = [
+ "SingletonComponent.java",
+ ],
+ deps = [
+ ":package_info",
+ "//java/dagger/hilt:define_component",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ ],
+)
+
+java_library(
+ name = "package_info",
+ srcs = ["package-info.java"],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/components/SingletonComponent.java b/java/dagger/hilt/components/SingletonComponent.java
new file mode 100644
index 000000000..07e8fe731
--- /dev/null
+++ b/java/dagger/hilt/components/SingletonComponent.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.components;
+
+import dagger.hilt.DefineComponent;
+import javax.inject.Singleton;
+
+/** A Hilt component for singleton bindings. */
+@Singleton
+@DefineComponent
+public interface SingletonComponent {}
diff --git a/java/dagger/hilt/components/package-info.java b/java/dagger/hilt/components/package-info.java
new file mode 100644
index 000000000..3c8cb18dd
--- /dev/null
+++ b/java/dagger/hilt/components/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package contains Hilt's built-in {@link dagger.Component}s.
+ *
+ * @see <a href="https://dagger.dev/hilt/components.md">Hilt Components</a>
+ */
+package dagger.hilt.components;
diff --git a/java/dagger/hilt/internal/BUILD b/java/dagger/hilt/internal/BUILD
new file mode 100644
index 000000000..dc245d1d7
--- /dev/null
+++ b/java/dagger/hilt/internal/BUILD
@@ -0,0 +1,68 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Internal Hilt libraries
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "generated_component",
+ srcs = [
+ "GeneratedComponent.java",
+ ],
+)
+
+java_library(
+ name = "component_manager",
+ srcs = [
+ "GeneratedComponentManager.java",
+ "GeneratedComponentManagerHolder.java",
+ ],
+ exports = [
+ ":preconditions",
+ ":unsafe_casts",
+ ],
+)
+
+java_library(
+ name = "preconditions",
+ srcs = [
+ "Preconditions.java",
+ ],
+)
+
+java_library(
+ name = "unsafe_casts",
+ srcs = [
+ "UnsafeCasts.java",
+ ],
+)
+
+java_library(
+ name = "component_entry_point",
+ srcs = ["ComponentEntryPoint.java"],
+ deps = ["//java/dagger/hilt:generates_root_input"],
+)
+
+java_library(
+ name = "generated_entry_point",
+ srcs = ["GeneratedEntryPoint.java"],
+ deps = ["//java/dagger/hilt:generates_root_input"],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/internal/ComponentEntryPoint.java b/java/dagger/hilt/internal/ComponentEntryPoint.java
new file mode 100644
index 000000000..3967e1bd4
--- /dev/null
+++ b/java/dagger/hilt/internal/ComponentEntryPoint.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.internal;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import dagger.hilt.GeneratesRootInput;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation marking generated interfaces for entry points for which there is also a corresponding
+ * generated Component. Component entry points differ from normal entry points in that they may be
+ * filtered out in tests.
+ */
+@Target(ElementType.TYPE)
+@Retention(CLASS)
+@GeneratesRootInput
+// TODO(bcorso): Rename and publicly strip these references out of hilt.
+public @interface ComponentEntryPoint {}
diff --git a/java/dagger/hilt/internal/GeneratedComponent.java b/java/dagger/hilt/internal/GeneratedComponent.java
new file mode 100644
index 000000000..4d85d37c7
--- /dev/null
+++ b/java/dagger/hilt/internal/GeneratedComponent.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.internal;
+
+/** A marker interface indicating that this is a Hilt generated component. */
+public interface GeneratedComponent {}
diff --git a/java/dagger/hilt/internal/GeneratedComponentManager.java b/java/dagger/hilt/internal/GeneratedComponentManager.java
new file mode 100644
index 000000000..bd837a37e
--- /dev/null
+++ b/java/dagger/hilt/internal/GeneratedComponentManager.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.internal;
+
+/** An interface that provides a managed generated component. */
+// TODO(bcorso): Consider either removing type parameter or using actual component type in usages.
+public interface GeneratedComponentManager<T> {
+ T generatedComponent();
+}
diff --git a/java/dagger/hilt/internal/GeneratedComponentManagerHolder.java b/java/dagger/hilt/internal/GeneratedComponentManagerHolder.java
new file mode 100644
index 000000000..f65e2dff3
--- /dev/null
+++ b/java/dagger/hilt/internal/GeneratedComponentManagerHolder.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.internal;
+
+/** An interface that provides a managed generated component holder. */
+public interface GeneratedComponentManagerHolder extends GeneratedComponentManager<Object> {
+
+ public GeneratedComponentManager<?> componentManager();
+}
diff --git a/java/dagger/hilt/internal/GeneratedEntryPoint.java b/java/dagger/hilt/internal/GeneratedEntryPoint.java
new file mode 100644
index 000000000..76319b9cf
--- /dev/null
+++ b/java/dagger/hilt/internal/GeneratedEntryPoint.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.internal;
+
+import dagger.hilt.GeneratesRootInput;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/** Do not use. Only for use from Hilt generators. */
+@Target(ElementType.TYPE)
+@GeneratesRootInput
+public @interface GeneratedEntryPoint {}
diff --git a/java/dagger/hilt/internal/Preconditions.java b/java/dagger/hilt/internal/Preconditions.java
new file mode 100644
index 000000000..b2a84dbda
--- /dev/null
+++ b/java/dagger/hilt/internal/Preconditions.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.internal;
+
+/**
+ * A partial copy of Guava's {@code com.google.common.base.Preconditions} meant to be used by
+ * generated code. TODO(danysantiago): Consolidate with dagger.internal.Preconditions
+ */
+public final class Preconditions {
+
+ /**
+ * Ensures that an object reference passed as a parameter to the calling method is not null.
+ *
+ * @param reference an object reference
+ * @return the non-null reference that was validated
+ * @throws NullPointerException if {@code reference} is null
+ */
+ public static <T> T checkNotNull(T reference) {
+ if (reference == null) {
+ throw new NullPointerException();
+ }
+ return reference;
+ }
+
+ /**
+ * Ensures that an object reference passed as a parameter to the calling method is not null.
+ *
+ * @param reference an object reference
+ * @param errorMessage the exception message to use if the check fails
+ * @return the non-null reference that was validated
+ * @throws NullPointerException if {@code reference} is null
+ */
+ public static <T> T checkNotNull(T reference, String errorMessage) {
+ if (reference == null) {
+ throw new NullPointerException(errorMessage);
+ }
+ return reference;
+ }
+
+ /**
+ * Ensures the truth of an expression involving one or more parameters to the calling method.
+ *
+ * @param expression a boolean expression
+ * @param errorMessageTemplate a template for the exception message should the check fail. The
+ * message is formed by replacing each occurrence of {@code "%s"} with the corresponding
+ * argument value from {@code args}.
+ * @param args the arguments to be substituted into the message template.
+ * @throws IllegalArgumentException if {@code expression} is false
+ */
+ public static void checkArgument(
+ boolean expression, String errorMessageTemplate, Object... args) {
+ if (!expression) {
+ throw new IllegalArgumentException(String.format(errorMessageTemplate, args));
+ }
+ }
+
+ /**
+ * Ensures the truth of an expression involving one or more parameters to the calling method.
+ *
+ * @param expression a boolean expression
+ * @param errorMessageTemplate a template for the exception message should the check fail. The
+ * message is formed by replacing each occurrence of {@code "%s"} with the corresponding
+ * argument value from {@code args}.
+ * @param args the arguments to be substituted into the message template.
+ * @throws IllegalStateException if {@code expression} is false
+ */
+ public static void checkState(boolean expression, String errorMessageTemplate, Object... args) {
+ if (!expression) {
+ throw new IllegalStateException(String.format(errorMessageTemplate, args));
+ }
+ }
+
+ private Preconditions() {}
+}
diff --git a/java/dagger/hilt/internal/UnsafeCasts.java b/java/dagger/hilt/internal/UnsafeCasts.java
new file mode 100644
index 000000000..796dfce3b
--- /dev/null
+++ b/java/dagger/hilt/internal/UnsafeCasts.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.internal;
+
+/** Runtime utility method for performing a casting in generated code. */
+public final class UnsafeCasts {
+
+ // Only used where code generations makes it safe.
+ @SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
+ public static <T> T unsafeCast(Object obj) {
+ return (T) obj;
+ }
+
+ private UnsafeCasts() {}
+}
diff --git a/java/dagger/hilt/internal/aliasof/AliasOfPropagatedData.java b/java/dagger/hilt/internal/aliasof/AliasOfPropagatedData.java
new file mode 100644
index 000000000..25ea7043a
--- /dev/null
+++ b/java/dagger/hilt/internal/aliasof/AliasOfPropagatedData.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.internal.aliasof;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** An annotation used to aggregate AliasOf values in a common location. */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.CLASS)
+public @interface AliasOfPropagatedData {
+ Class<? extends Annotation> defineComponentScope();
+
+ Class<? extends Annotation> alias();
+}
diff --git a/java/dagger/hilt/internal/aliasof/BUILD b/java/dagger/hilt/internal/aliasof/BUILD
new file mode 100644
index 000000000..3e96ed4d9
--- /dev/null
+++ b/java/dagger/hilt/internal/aliasof/BUILD
@@ -0,0 +1,28 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# The annotation for classes generated by @AliasOf.
+
+package(default_visibility = ["//:src"])
+
+android_library(
+ name = "aliasof",
+ srcs = ["AliasOfPropagatedData.java"],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/internal/definecomponent/BUILD b/java/dagger/hilt/internal/definecomponent/BUILD
new file mode 100644
index 000000000..b1d56c409
--- /dev/null
+++ b/java/dagger/hilt/internal/definecomponent/BUILD
@@ -0,0 +1,32 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# The annotations for classes generated by @DefineComponent and @DefineComponent.Factory.
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "definecomponent",
+ srcs = glob(["*.java"]),
+ visibility = [
+ "//java/dagger/hilt:__pkg__",
+ "//java/dagger/hilt/android:__pkg__",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/internal/definecomponent/DefineComponentClasses.java b/java/dagger/hilt/internal/definecomponent/DefineComponentClasses.java
new file mode 100644
index 000000000..25555fbc9
--- /dev/null
+++ b/java/dagger/hilt/internal/definecomponent/DefineComponentClasses.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.internal.definecomponent;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation used to aggregate {@link dagger.hilt.DefineComponent} types in a common location.
+ *
+ * <p>Note: The types are given using {@link String} rather than {@link Class} since the {@link
+ * dagger.hilt.DefineComponent} type is not necessarily in the same package and not necessarily
+ * public.
+ */
+@Retention(CLASS)
+@Target(TYPE)
+public @interface DefineComponentClasses {
+ /**
+ * Returns the fully qualified name of the {@link dagger.hilt.DefineComponent} type, or an empty
+ * string if it wasn't given.
+ */
+ String component() default "";
+
+ /**
+ * Returns the fully qualified name of the {@link dagger.hilt.DefineComponent.Builder} type, or an
+ * empty string if it wasn't given.
+ */
+ String builder() default "";
+}
diff --git a/java/dagger/hilt/internal/definecomponent/DefineComponentNoParent.java b/java/dagger/hilt/internal/definecomponent/DefineComponentNoParent.java
new file mode 100644
index 000000000..d5da7601f
--- /dev/null
+++ b/java/dagger/hilt/internal/definecomponent/DefineComponentNoParent.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.internal.definecomponent;
+
+/** A class used by {@link DefineComponent#parent()} as the default type when no parent is given. */
+public final class DefineComponentNoParent {
+ private DefineComponentNoParent() {}
+}
diff --git a/java/dagger/hilt/internal/generatesrootinput/BUILD b/java/dagger/hilt/internal/generatesrootinput/BUILD
new file mode 100644
index 000000000..8e54ac431
--- /dev/null
+++ b/java/dagger/hilt/internal/generatesrootinput/BUILD
@@ -0,0 +1,28 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# The annotations for classes generated by @GeneratesRootInput.
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "generatesrootinput",
+ srcs = ["GeneratesRootInputPropagatedData.java"],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/internal/generatesrootinput/GeneratesRootInputPropagatedData.java b/java/dagger/hilt/internal/generatesrootinput/GeneratesRootInputPropagatedData.java
new file mode 100644
index 000000000..d4917f8a1
--- /dev/null
+++ b/java/dagger/hilt/internal/generatesrootinput/GeneratesRootInputPropagatedData.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.internal.generatesrootinput;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation used to aggregate {@link dagger.hilt.GeneratesRootInput} types in a common
+ * location.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.CLASS)
+public @interface GeneratesRootInputPropagatedData {
+ Class<? extends Annotation> value();
+}
diff --git a/java/dagger/hilt/migration/AliasOf.java b/java/dagger/hilt/migration/AliasOf.java
new file mode 100644
index 000000000..b68d4f12d
--- /dev/null
+++ b/java/dagger/hilt/migration/AliasOf.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.migration;
+
+import dagger.hilt.GeneratesRootInput;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Defines an alias between an existing Hilt scope and the annotated scope. For example, the
+ * following code makes {@literal @}MyScope a functional replacement for {@literal @}ActivityScope.
+ *
+ * <p>
+ *
+ * <pre>
+ * {@literal @}Scope
+ * {@literal @}AliasOf(ActivityScope.class)
+ * public {@literal @}interface MyScope{}
+ * </pre>
+ *
+ * <p>
+ */
+@Target(ElementType.ANNOTATION_TYPE)
+@Retention(RetentionPolicy.CLASS)
+@GeneratesRootInput
+public @interface AliasOf {
+ /** Returns the existing Hilt scope that the annotated scope is aliasing. */
+ Class<? extends Annotation> value();
+}
diff --git a/java/dagger/hilt/migration/BUILD b/java/dagger/hilt/migration/BUILD
new file mode 100644
index 000000000..a14410d0b
--- /dev/null
+++ b/java/dagger/hilt/migration/BUILD
@@ -0,0 +1,57 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Description:
+# Libraries for migration.
+
+package(default_visibility = ["//:src"])
+
+android_library(
+ name = "alias_of",
+ srcs = [
+ "AliasOf.java",
+ ],
+ exported_plugins = [
+ "//java/dagger/hilt/processor/internal/aliasof:processor",
+ ],
+ exports = [
+ "//java/dagger/hilt/internal/aliasof",
+ ],
+ deps = [
+ ":package_info",
+ "//java/dagger/hilt:generates_root_input",
+ ],
+)
+
+java_library(
+ name = "disable_install_in_check",
+ srcs = ["DisableInstallInCheck.java"],
+ exported_plugins = [
+ "//java/dagger/hilt/processor/internal/disableinstallincheck:processor",
+ ],
+ exports = [
+ ],
+ deps = [
+ ":package_info",
+ ],
+)
+
+java_library(
+ name = "package_info",
+ srcs = ["package-info.java"],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/migration/DisableInstallInCheck.java b/java/dagger/hilt/migration/DisableInstallInCheck.java
new file mode 100644
index 000000000..acb625267
--- /dev/null
+++ b/java/dagger/hilt/migration/DisableInstallInCheck.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.migration;
+
+/**
+ * Marks a {@link dagger.Module}-annotated class to allow it to have no {@link
+ * dagger.hilt.InstallIn} annotation.
+ *
+ * <p>Use this annotation on modules to suppress the error of a missing {@link
+ * dagger.hilt.InstallIn} annotation. This is useful in cases where non-Hilt Dagger code must be
+ * used long-term. If the issue is widespread, consider changing the error behavior with the
+ * compiler flag {@code dagger.hilt.disableModulesHaveInstallInCheck} instead.
+ */
+public @interface DisableInstallInCheck {}
diff --git a/java/dagger/hilt/migration/package-info.java b/java/dagger/hilt/migration/package-info.java
new file mode 100644
index 000000000..bc269e31e
--- /dev/null
+++ b/java/dagger/hilt/migration/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package contains APIs to help migrating a codebase to Hilt.
+ *
+ * @see <a href="https://dagger.dev/hilt/migration">Migration to Hilt</a>
+ */
+package dagger.hilt.migration;
diff --git a/java/dagger/hilt/package-info.java b/java/dagger/hilt/package-info.java
new file mode 100644
index 000000000..568b0605b
--- /dev/null
+++ b/java/dagger/hilt/package-info.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package contains the core APIs for Hilt.
+ *
+ * <p>Hilt provides a standard way to incorporate Dagger dependency injection into an Android
+ * application.
+ *
+ * @see <a href="https://dagger.dev/hilt">Hilt Developer Docs</a>
+ */
+@ParametersAreNonnullByDefault
+package dagger.hilt;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/java/dagger/hilt/processor/BUILD b/java/dagger/hilt/processor/BUILD
new file mode 100644
index 000000000..87adcf34f
--- /dev/null
+++ b/java/dagger/hilt/processor/BUILD
@@ -0,0 +1,120 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Hilt android processors.
+
+load("//:build_defs.bzl", "POM_VERSION_ALPHA")
+load("//tools:maven.bzl", "gen_maven_artifact")
+
+package(default_visibility = ["//:src"])
+
+# TODO(bcorso): merge this into :artifact-lib once we remove hilt-android-compiler artifact.
+java_library(
+ name = "artifact-lib-shared",
+ exports = [
+ "//java/dagger/hilt/android/processor/internal/androidentrypoint:processor_lib",
+ "//java/dagger/hilt/android/processor/internal/bindvalue:bind_value_processor_lib",
+ "//java/dagger/hilt/android/processor/internal/customtestapplication:processor_lib",
+ "//java/dagger/hilt/android/processor/internal/uninstallmodules:processor_lib",
+ "//java/dagger/hilt/android/processor/internal/viewmodel:processor_lib",
+ "//java/dagger/hilt/android/processor/internal/viewmodel:validation_plugin_lib",
+ "//java/dagger/hilt/processor/internal/aggregateddeps:processor_lib",
+ "//java/dagger/hilt/processor/internal/aliasof:processor_lib",
+ "//java/dagger/hilt/processor/internal/definecomponent:processor_lib",
+ "//java/dagger/hilt/processor/internal/generatesrootinput:processor_lib",
+ "//java/dagger/hilt/processor/internal/originatingelement:processor_lib",
+ "//java/dagger/hilt/processor/internal/root:processor_lib",
+ "//java/dagger/internal/codegen:processor",
+ ],
+)
+
+java_library(
+ name = "artifact-lib",
+ tags = ["maven_coordinates=com.google.dagger:hilt-compiler:" + POM_VERSION_ALPHA],
+ visibility = ["//visibility:private"],
+ exports = [
+ ":artifact-lib-shared",
+ ],
+)
+
+gen_maven_artifact(
+ name = "artifact",
+ artifact_coordinates = "com.google.dagger:hilt-compiler:" + POM_VERSION_ALPHA,
+ artifact_name = "Hilt Processor",
+ artifact_target = ":artifact-lib",
+ artifact_target_libs = [
+ "//java/dagger/hilt/android/processor/internal:android_classnames",
+ "//java/dagger/hilt/android/processor/internal:utils",
+ "//java/dagger/hilt/android/processor/internal/androidentrypoint:android_generators",
+ "//java/dagger/hilt/android/processor/internal/androidentrypoint:compiler_options",
+ "//java/dagger/hilt/android/processor/internal/androidentrypoint:metadata",
+ "//java/dagger/hilt/android/processor/internal/androidentrypoint:processor_lib",
+ "//java/dagger/hilt/android/processor/internal/bindvalue:bind_value_processor_lib",
+ "//java/dagger/hilt/android/processor/internal/customtestapplication:processor_lib",
+ "//java/dagger/hilt/android/processor/internal/viewmodel:validation_plugin_lib",
+ "//java/dagger/hilt/android/processor/internal/uninstallmodules:processor_lib",
+ "//java/dagger/hilt/android/processor/internal/viewmodel:processor_lib",
+ "//java/dagger/hilt/processor/internal:base_processor",
+ "//java/dagger/hilt/processor/internal:classnames",
+ "//java/dagger/hilt/processor/internal:component_descriptor",
+ "//java/dagger/hilt/processor/internal:component_names",
+ "//java/dagger/hilt/processor/internal:components",
+ "//java/dagger/hilt/processor/internal:kotlin",
+ "//java/dagger/hilt/processor/internal:processor_errors",
+ "//java/dagger/hilt/processor/internal:processors",
+ "//java/dagger/hilt/processor/internal/aggregateddeps:component_dependencies",
+ "//java/dagger/hilt/processor/internal/aggregateddeps:processor_lib",
+ "//java/dagger/hilt/processor/internal/aliasof:alias_ofs",
+ "//java/dagger/hilt/processor/internal/aliasof:processor_lib",
+ "//java/dagger/hilt/processor/internal/definecomponent:define_components",
+ "//java/dagger/hilt/processor/internal/definecomponent:processor_lib",
+ "//java/dagger/hilt/processor/internal/generatesrootinput:generates_root_inputs",
+ "//java/dagger/hilt/processor/internal/generatesrootinput:processor_lib",
+ "//java/dagger/hilt/processor/internal/originatingelement:processor_lib",
+ "//java/dagger/hilt/processor/internal/root:processor_lib",
+ "//java/dagger/hilt/processor/internal/root:root_metadata",
+ "//java/dagger/hilt/processor/internal/root:root_type",
+ ],
+ artifact_target_maven_deps = [
+ "com.google.auto:auto-common",
+ "com.google.code.findbugs:jsr305",
+ "com.google.dagger:dagger-compiler",
+ "com.google.dagger:dagger",
+ "com.google.dagger:dagger-spi",
+ "com.google.guava:failureaccess",
+ "com.google.guava:guava",
+ "com.squareup:javapoet",
+ "javax.annotation:jsr250-api",
+ "javax.inject:javax.inject",
+ "net.ltgt.gradle.incap:incap",
+ "org.jetbrains.kotlin:kotlin-stdlib",
+ "org.jetbrains.kotlinx:kotlinx-metadata-jvm",
+ ],
+ javadoc_android_api_level = 30,
+ javadoc_root_packages = [
+ "dagger.hilt.processor",
+ "dagger.hilt.android.processor",
+ ],
+ javadoc_srcs = [
+ "//java/dagger/hilt:hilt_processing_filegroup",
+ ],
+ shaded_deps = ["@maven//:com_google_auto_auto_common"],
+ shaded_rules = ["rule com.google.auto.common.** dagger.hilt.android.shaded.auto.common.@1"],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/processor/internal/AnnotationValues.java b/java/dagger/hilt/processor/internal/AnnotationValues.java
new file mode 100644
index 000000000..584d8f950
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/AnnotationValues.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValuesWithDefaults;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.AnnotationValueVisitor;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleAnnotationValueVisitor8;
+
+/** A utility class for working with {@link AnnotationValue} instances. */
+// TODO(bcorso): Update auto-common maven import so we can use it rather than this copy.
+public final class AnnotationValues {
+
+ private AnnotationValues() {}
+
+ private static class DefaultVisitor<T> extends SimpleAnnotationValueVisitor8<T, Void> {
+ final Class<T> clazz;
+
+ DefaultVisitor(Class<T> clazz) {
+ this.clazz = checkNotNull(clazz);
+ }
+
+ @Override
+ public T defaultAction(Object o, Void unused) {
+ throw new IllegalArgumentException(
+ "Expected a " + clazz.getSimpleName() + ", got instead: " + o);
+ }
+ }
+
+ private static final class TypeMirrorVisitor extends DefaultVisitor<DeclaredType> {
+ static final TypeMirrorVisitor INSTANCE = new TypeMirrorVisitor();
+
+ TypeMirrorVisitor() {
+ super(DeclaredType.class);
+ }
+
+ @Override
+ public DeclaredType visitType(TypeMirror value, Void unused) {
+ return MoreTypes.asDeclared(value);
+ }
+ }
+
+ /**
+ * Returns the value as a class.
+ *
+ * @throws IllegalArgumentException if the value is not a class.
+ */
+ public static DeclaredType getTypeMirror(AnnotationValue value) {
+ return TypeMirrorVisitor.INSTANCE.visit(value);
+ }
+
+ private static final class EnumVisitor extends DefaultVisitor<VariableElement> {
+ static final EnumVisitor INSTANCE = new EnumVisitor();
+
+ EnumVisitor() {
+ super(VariableElement.class);
+ }
+
+ @Override
+ public VariableElement visitEnumConstant(VariableElement value, Void unused) {
+ return value;
+ }
+ }
+
+ /**
+ * Returns the value as a VariableElement.
+ *
+ * @throws IllegalArgumentException if the value is not an enum.
+ */
+ public static VariableElement getEnum(AnnotationValue value) {
+ return EnumVisitor.INSTANCE.visit(value);
+ }
+
+ /**
+ * Returns the value as a string.
+ *
+ * @throws IllegalArgumentException if the value is not a string.
+ */
+ public static String getString(AnnotationValue value) {
+ return valueOfType(value, String.class);
+ }
+
+ private static <T> T valueOfType(AnnotationValue annotationValue, Class<T> type) {
+ Object value = annotationValue.getValue();
+ if (!type.isInstance(value)) {
+ throw new IllegalArgumentException(
+ "Expected " + type.getSimpleName() + ", got instead: " + value);
+ }
+ return type.cast(value);
+ }
+
+ /** Returns the int value of an annotation */
+ public static int getIntValue(AnnotationMirror annotation, String valueName) {
+ return (int) getAnnotationValue(annotation, valueName).getValue();
+ }
+
+ /** Returns an optional int value of an annotation if the value name is present */
+ public static Optional<Integer> getOptionalIntValue(
+ AnnotationMirror annotation, String valueName) {
+ return isValuePresent(annotation, valueName)
+ ? Optional.of(getIntValue(annotation, valueName))
+ : Optional.empty();
+ }
+
+ /** Returns the String value of an annotation */
+ public static String getStringValue(AnnotationMirror annotation, String valueName) {
+ return (String) getAnnotationValue(annotation, valueName).getValue();
+ }
+
+ /** Returns an optional String value of an annotation if the value name is present */
+ public static Optional<String> getOptionalStringValue(
+ AnnotationMirror annotation, String valueName) {
+ return isValuePresent(annotation, valueName)
+ ? Optional.of(getStringValue(annotation, valueName))
+ : Optional.empty();
+ }
+
+ /** Returns the int array value of an annotation */
+ public static int[] getIntArrayValue(AnnotationMirror annotation, String valueName) {
+ return getAnnotationValues(getAnnotationValue(annotation, valueName)).stream()
+ .mapToInt(it -> (int) it.getValue())
+ .toArray();
+ }
+
+ /** Returns the String array value of an annotation */
+ public static String[] getStringArrayValue(AnnotationMirror annotation, String valueName) {
+ return getAnnotationValues(getAnnotationValue(annotation, valueName)).stream()
+ .map(it -> (String) it.getValue())
+ .toArray(String[]::new);
+ }
+
+ private static boolean isValuePresent(AnnotationMirror annotation, String valueName) {
+ return getAnnotationValuesWithDefaults(annotation).keySet().stream()
+ .anyMatch(member -> member.getSimpleName().contentEquals(valueName));
+ }
+
+ /**
+ * Returns the list of values represented by an array annotation value.
+ *
+ * @throws IllegalArgumentException unless {@code annotationValue} represents an array
+ */
+ public static ImmutableList<AnnotationValue> getAnnotationValues(
+ AnnotationValue annotationValue) {
+ return annotationValue.accept(AS_ANNOTATION_VALUES, null);
+ }
+
+ private static final AnnotationValueVisitor<ImmutableList<AnnotationValue>, String>
+ AS_ANNOTATION_VALUES =
+ new SimpleAnnotationValueVisitor8<ImmutableList<AnnotationValue>, String>() {
+ @Override
+ public ImmutableList<AnnotationValue> visitArray(
+ List<? extends AnnotationValue> vals, String elementName) {
+ return ImmutableList.copyOf(vals);
+ }
+
+ @Override
+ protected ImmutableList<AnnotationValue> defaultAction(Object o, String elementName) {
+ throw new IllegalArgumentException(elementName + " is not an array: " + o);
+ }
+ };
+}
diff --git a/java/dagger/hilt/processor/internal/BUILD b/java/dagger/hilt/processor/internal/BUILD
new file mode 100644
index 000000000..baff1d823
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/BUILD
@@ -0,0 +1,149 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Internal code for implementing Hilt processors.
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "base_processor",
+ srcs = [
+ "BaseProcessor.java",
+ "ProcessorErrorHandler.java",
+ ],
+ deps = [
+ ":processor_errors",
+ ":processors",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
+
+java_library(
+ name = "processor_errors",
+ srcs = [
+ "BadInputException.java",
+ "ErrorTypeException.java",
+ "ProcessorErrors.java",
+ ],
+ deps = [
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/error_prone:annotations",
+ "@google_bazel_common//third_party/java/jsr305_annotations",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
+
+java_library(
+ name = "processors",
+ srcs = [
+ "AnnotationValues.java",
+ "Processors.java",
+ ],
+ deps = [
+ ":classnames",
+ ":kotlin",
+ ":processor_errors",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/codegen/kotlin",
+ "//java/dagger/internal/codegen/langmodel",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@google_bazel_common//third_party/java/jsr305_annotations",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ "@maven//:com_google_auto_auto_common",
+ "@maven//:org_jetbrains_kotlin_kotlin_stdlib",
+ "@maven//:org_jetbrains_kotlinx_kotlinx_metadata_jvm",
+ ],
+)
+
+java_library(
+ name = "classnames",
+ srcs = [
+ "ClassNames.java",
+ ],
+ deps = [
+ "@google_bazel_common//third_party/java/javapoet",
+ ],
+)
+
+java_library(
+ name = "component_names",
+ srcs = [
+ "ComponentNames.java",
+ ],
+ deps = [
+ ":processors",
+ "@google_bazel_common//third_party/java/javapoet",
+ ],
+)
+
+java_library(
+ name = "component_descriptor",
+ srcs = [
+ "ComponentDescriptor.java",
+ "ComponentGenerator.java",
+ "ComponentTree.java",
+ ],
+ deps = [
+ ":classnames",
+ ":processors",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:graph",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/javapoet",
+ ],
+)
+
+java_library(
+ name = "components",
+ srcs = [
+ "Components.java",
+ ],
+ deps = [
+ ":classnames",
+ ":component_descriptor",
+ ":kotlin",
+ ":processor_errors",
+ ":processors",
+ "//java/dagger/hilt/processor/internal/definecomponent:define_components",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
+
+java_library(
+ name = "kotlin",
+ srcs = ["KotlinMetadataUtils.java"],
+ deps = [
+ "//:dagger_with_compiler",
+ "//java/dagger/internal/codegen/kotlin",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/processor/internal/BadInputException.java b/java/dagger/hilt/processor/internal/BadInputException.java
new file mode 100644
index 000000000..d9617688e
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/BadInputException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal;
+
+import com.google.common.collect.ImmutableList;
+import javax.lang.model.element.Element;
+
+/**
+ * Exception to throw when input code has caused an error.
+ * Includes elements to point to for the cause of the error
+ */
+public final class BadInputException extends RuntimeException {
+ private final ImmutableList<Element> badElements;
+
+ public BadInputException(String message, Element badElement) {
+ super(message);
+ this.badElements = ImmutableList.of(badElement);
+ }
+
+ public BadInputException(String message, Iterable<? extends Element> badElements) {
+ super(message);
+ this.badElements = ImmutableList.copyOf(badElements);
+ }
+
+ public ImmutableList<Element> getBadElements() {
+ return badElements;
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/BaseProcessor.java b/java/dagger/hilt/processor/internal/BaseProcessor.java
new file mode 100644
index 000000000..4961cd570
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/BaseProcessor.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.SetMultimap;
+import com.squareup.javapoet.ClassName;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.Messager;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+/**
+ * Implements default configurations for Processors, and provides structure for exception handling.
+ *
+ * <p>By default #process() will do the following:
+ *
+ * <ol>
+ * <li> #preRoundProcess()
+ * <li> foreach element:
+ * <ul><li> #processEach()</ul>
+ * </li>
+ * <li> #postRoundProcess()
+ * <li> #claimAnnotation()
+ * </ol>
+ *
+ * <p>#processEach() allows each element to be processed, even if exceptions are thrown. Due to the
+ * non-deterministic ordering of the processed elements, this is needed to ensure a consistent set
+ * of exceptions are thrown with each build.
+ */
+public abstract class BaseProcessor extends AbstractProcessor {
+ /** Stores the state of processing for a given annotation and element. */
+ @AutoValue
+ abstract static class ProcessingState {
+ private static ProcessingState of(TypeElement annotation, Element element) {
+ // We currently only support TypeElements directly annotated with the annotation.
+ // TODO(bcorso): Switch to using BasicAnnotationProcessor if we need more than this.
+ // Note: Switching to BasicAnnotationProcessor is currently not possible because of cyclic
+ // references to generated types in our API. For example, an @AndroidEntryPoint annotated
+ // element will indefinitely defer its own processing because it extends a generated type
+ // that it's responsible for generating.
+ checkState(MoreElements.isType(element));
+ checkState(Processors.hasAnnotation(element, ClassName.get(annotation)));
+ return new AutoValue_BaseProcessor_ProcessingState(
+ ClassName.get(annotation),
+ ClassName.get(MoreElements.asType(element)));
+ }
+
+ /** Returns the class name of the annotation. */
+ abstract ClassName annotationClassName();
+
+ /** Returns the type name of the annotated element. */
+ abstract ClassName elementClassName();
+
+ /** Returns the annotation that triggered the processing. */
+ TypeElement annotation(Elements elements) {
+ return elements.getTypeElement(elementClassName().toString());
+ }
+
+ /** Returns the annotated element to process. */
+ TypeElement element(Elements elements) {
+ return elements.getTypeElement(annotationClassName().toString());
+ }
+ }
+
+ private final Set<ProcessingState> stateToReprocess = new LinkedHashSet<>();
+ private Elements elements;
+ private Types types;
+ private Messager messager;
+ private ProcessorErrorHandler errorHandler;
+
+ /** Used to perform initialization before each round of processing. */
+ protected void preRoundProcess(RoundEnvironment roundEnv) {};
+
+ /**
+ * Called for each element in a round that uses a supported annotation.
+ *
+ * Note that an exception can be thrown for each element in the round. This is usually preferred
+ * over throwing only the first exception in a round. Only throwing the first exception in the
+ * round can lead to flaky errors that are dependent on the non-deterministic ordering that the
+ * elements are processed in.
+ */
+ protected void processEach(TypeElement annotation, Element element) throws Exception {};
+
+ /**
+ * Used to perform post processing at the end of a round. This is especially useful for handling
+ * additional processing that depends on aggregate data, that cannot be handled in #processEach().
+ *
+ * <p>Note: this will not be called if an exception is thrown during #processEach() -- if we have
+ * already detected errors on an annotated element, performing post processing on an aggregate
+ * will just produce more (perhaps non-deterministic) errors.
+ */
+ protected void postRoundProcess(RoundEnvironment roundEnv) throws Exception {};
+
+ /** @return true if you want to claim annotations after processing each round. Default false. */
+ protected boolean claimAnnotations() {
+ return false;
+ }
+
+ /**
+ * @return true if you want to delay errors to the last round. Useful if the processor
+ * generates code for symbols used a lot in the user code. Delaying allows as much code to
+ * compile as possible for correctly configured types and reduces error spam.
+ */
+ protected boolean delayErrors() {
+ return false;
+ }
+
+
+ @Override
+ public synchronized void init(ProcessingEnvironment processingEnvironment) {
+ super.init(processingEnvironment);
+ this.messager = processingEnv.getMessager();
+ this.elements = processingEnv.getElementUtils();
+ this.types = processingEnv.getTypeUtils();
+ this.errorHandler = new ProcessorErrorHandler(processingEnvironment);
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ /**
+ * This should not be overridden, as it defines the order of the processing.
+ */
+ @Override
+ public final boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ preRoundProcess(roundEnv);
+
+ boolean roundError = false;
+
+ // Gather the set of new and deferred elements to process, grouped by annotation.
+ SetMultimap<TypeElement, Element> elementMultiMap = LinkedHashMultimap.create();
+ for (ProcessingState processingState : stateToReprocess) {
+ elementMultiMap.put(processingState.annotation(elements), processingState.element(elements));
+ }
+ for (TypeElement annotation : annotations) {
+ elementMultiMap.putAll(annotation, roundEnv.getElementsAnnotatedWith(annotation));
+ }
+
+ // Clear the processing state before reprocessing.
+ stateToReprocess.clear();
+
+ for (Map.Entry<TypeElement, Collection<Element>> entry : elementMultiMap.asMap().entrySet()) {
+ TypeElement annotation = entry.getKey();
+ for (Element element : entry.getValue()) {
+ try {
+ processEach(annotation, element);
+ } catch (Exception e) {
+ if (e instanceof ErrorTypeException && !roundEnv.processingOver()) {
+ // Allow an extra round to reprocess to try to resolve this type.
+ stateToReprocess.add(ProcessingState.of(annotation, element));
+ } else {
+ errorHandler.recordError(e);
+ roundError = true;
+ }
+ }
+ }
+ }
+
+ if (!roundError) {
+ try {
+ postRoundProcess(roundEnv);
+ } catch (Exception e) {
+ errorHandler.recordError(e);
+ }
+ }
+
+ if (!delayErrors() || roundEnv.processingOver()) {
+ errorHandler.checkErrors();
+ }
+
+ return claimAnnotations();
+ }
+
+ /** @return the error handle for the processor. */
+ protected final ProcessorErrorHandler getErrorHandler() {
+ return errorHandler;
+ }
+
+ public final ProcessingEnvironment getProcessingEnv() {
+ return processingEnv;
+ }
+
+ public final Elements getElementUtils() {
+ return elements;
+ }
+
+ public final Types getTypeUtils() {
+ return types;
+ }
+
+ public final Messager getMessager() {
+ return messager;
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/ClassNames.java b/java/dagger/hilt/processor/internal/ClassNames.java
new file mode 100644
index 000000000..234ea7b45
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/ClassNames.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal;
+
+import static com.squareup.javapoet.ClassName.get;
+
+import com.squareup.javapoet.ClassName;
+
+/** Holder for commonly used class names. */
+public final class ClassNames {
+ public static final ClassName ORIGINATING_ELEMENT =
+ get("dagger.hilt.codegen", "OriginatingElement");
+ public static final ClassName AGGREGATED_DEPS =
+ get("dagger.hilt.processor.internal.aggregateddeps", "AggregatedDeps");
+ public static final ClassName GENERATED_COMPONENT =
+ get("dagger.hilt.internal", "GeneratedComponent");
+ public static final ClassName GENERATED_COMPONENT_MANAGER =
+ get("dagger.hilt.internal", "GeneratedComponentManager");
+ public static final ClassName GENERATED_COMPONENT_MANAGER_HOLDER =
+ get("dagger.hilt.internal", "GeneratedComponentManagerHolder");
+ public static final ClassName IGNORE_MODULES =
+ get("dagger.hilt.android.testing", "UninstallModules");
+
+ public static final ClassName DEFINE_COMPONENT = get("dagger.hilt", "DefineComponent");
+ public static final ClassName DEFINE_COMPONENT_BUILDER =
+ get("dagger.hilt", "DefineComponent", "Builder");
+ public static final ClassName DEFINE_COMPONENT_NO_PARENT =
+ get("dagger.hilt.internal.definecomponent", "DefineComponentNoParent");
+ public static final ClassName DEFINE_COMPONENT_CLASSES =
+ get("dagger.hilt.internal.definecomponent", "DefineComponentClasses");
+
+ public static final ClassName ASSISTED_INJECT = get("dagger.assisted", "AssistedInject");
+ public static final ClassName BINDS =
+ get("dagger", "Binds");
+ public static final ClassName BINDS_OPTIONAL_OF =
+ get("dagger", "BindsOptionalOf");
+ public static final ClassName MODULE = get("dagger", "Module");
+ public static final ClassName MULTIBINDS =
+ get("dagger.multibindings", "Multibinds");
+ public static final ClassName INTO_MAP = get("dagger.multibindings", "IntoMap");
+ public static final ClassName INTO_SET = get("dagger.multibindings", "IntoSet");
+ public static final ClassName STRING_KEY = get("dagger.multibindings", "StringKey");
+ public static final ClassName PROVIDES =
+ get("dagger", "Provides");
+ public static final ClassName COMPONENT = get("dagger", "Component");
+ public static final ClassName COMPONENT_BUILDER = get("dagger", "Component", "Builder");
+ public static final ClassName SUBCOMPONENT = get("dagger", "Subcomponent");
+ public static final ClassName SUBCOMPONENT_BUILDER =
+ get("dagger", "Subcomponent", "Builder");
+ public static final ClassName PRODUCTION_COMPONENT =
+ get("dagger.producers", "ProductionComponent");
+
+ public static final ClassName CONTRIBUTES_ANDROID_INJECTOR =
+ get("dagger.android", "ContributesAndroidInjector");
+
+ public static final ClassName INJECT =
+ get("javax.inject", "Inject");
+ public static final ClassName QUALIFIER =
+ get("javax.inject", "Qualifier");
+ public static final ClassName SCOPE =
+ get("javax.inject", "Scope");
+ public static final ClassName PROVIDER = get("javax.inject", "Provider");
+ public static final ClassName DISABLE_INSTALL_IN_CHECK =
+ get("dagger.hilt.migration", "DisableInstallInCheck");
+ public static final ClassName ALIAS_OF = get("dagger.hilt.migration", "AliasOf");
+ public static final ClassName ALIAS_OF_PROPAGATED_DATA =
+ get("dagger.hilt.internal.aliasof", "AliasOfPropagatedData");
+
+ public static final ClassName GENERATES_ROOT_INPUT = get("dagger.hilt", "GeneratesRootInput");
+ public static final ClassName GENERATES_ROOT_INPUT_PROPAGATED_DATA =
+ get("dagger.hilt.internal.generatesrootinput", "GeneratesRootInputPropagatedData");
+
+ public static final ClassName ACTIVITY_SCOPED =
+ get("dagger.hilt.android.scopes", "ActivityScoped");
+ public static final ClassName FRAGMENT_SCOPED =
+ get("dagger.hilt.android.scopes", "FragmentScoped");
+ public static final ClassName SERVICE_SCOPED = get("dagger.hilt.android.scopes", "ServiceScoped");
+ public static final ClassName VIEW_SCOPED = get("dagger.hilt.android.scopes", "ViewScoped");
+
+ public static final ClassName INSTALL_IN =
+ get("dagger.hilt", "InstallIn");
+ public static final ClassName TEST_INSTALL_IN = get("dagger.hilt.testing", "TestInstallIn");
+ public static final ClassName ENTRY_POINT =
+ get("dagger.hilt", "EntryPoint");
+ public static final ClassName ENTRY_POINTS = get("dagger.hilt", "EntryPoints");
+ public static final ClassName COMPONENT_ENTRY_POINT =
+ get("dagger.hilt.internal", "ComponentEntryPoint");
+ public static final ClassName GENERATED_ENTRY_POINT =
+ get("dagger.hilt.internal", "GeneratedEntryPoint");
+ public static final ClassName UNSAFE_CASTS = get("dagger.hilt.internal", "UnsafeCasts");
+ public static final ClassName ROOT_PROCESSOR =
+ get("dagger.hilt.processor.internal.root", "RootProcessor");
+
+ public static final ClassName SINGLETON = get("javax.inject", "Singleton");
+
+ // TODO(erichang): Move these class names out when we factor out the android portion
+ public static final ClassName APPLICATION = get("android.app", "Application");
+ public static final ClassName MULTI_DEX_APPLICATION =
+ get("androidx.multidex", "MultiDexApplication");
+ public static final ClassName ANDROID_ENTRY_POINT =
+ get("dagger.hilt.android", "AndroidEntryPoint");
+ public static final ClassName HILT_ANDROID_APP =
+ get("dagger.hilt.android", "HiltAndroidApp");
+ public static final ClassName CONTEXT = get("android.content", "Context");
+ public static final ClassName APPLICATION_PROVIDER =
+ get("androidx.test.core.app", "ApplicationProvider");
+ public static final ClassName COMPONENT_SUPPLIER =
+ get("dagger.hilt.android.internal.managers", "ComponentSupplier");
+ public static final ClassName APPLICATION_CONTEXT_MODULE =
+ get("dagger.hilt.android.internal.modules", "ApplicationContextModule");
+ public static final ClassName INTERNAL_TEST_ROOT =
+ get("dagger.hilt.android.internal.testing", "InternalTestRoot");
+ public static final ClassName TEST_INJECTOR =
+ get("dagger.hilt.android.internal.testing", "TestInjector");
+ public static final ClassName TEST_APPLICATION_INJECTOR =
+ get("dagger.hilt.android.internal.testing", "TestApplicationInjector");
+ public static final ClassName TEST_APPLICATION_COMPONENT_MANAGER =
+ get("dagger.hilt.android.internal.testing", "TestApplicationComponentManager");
+ public static final ClassName TEST_APPLICATION_COMPONENT_MANAGER_HOLDER =
+ get("dagger.hilt.android.internal.testing", "TestApplicationComponentManagerHolder");
+ public static final ClassName TEST_INSTANCE_HOLDER =
+ get("dagger.hilt.android.internal.testing", "TestInstanceHolder");
+ public static final ClassName HILT_ANDROID_TEST =
+ get("dagger.hilt.android.testing", "HiltAndroidTest");
+ public static final ClassName CUSTOM_TEST_APPLICATION =
+ get("dagger.hilt.android.testing", "CustomTestApplication");
+ public static final ClassName ON_COMPONENT_READY_RUNNER =
+ get("dagger.hilt.android.testing", "OnComponentReadyRunner");
+ public static final ClassName ON_COMPONENT_READY_RUNNER_HOLDER =
+ get("dagger.hilt.android.testing", "OnComponentReadyRunner", "OnComponentReadyRunnerHolder");
+ public static final ClassName ANDROID_BIND_VALUE =
+ get("dagger.hilt.android.testing", "BindValue");
+ public static final ClassName ANDROID_BIND_ELEMENTS_INTO_SET =
+ get("dagger.hilt.android.testing", "BindElementsIntoSet");
+ public static final ClassName ANDROID_BIND_VALUE_INTO_MAP =
+ get("dagger.hilt.android.testing", "BindValueIntoMap");
+ public static final ClassName ANDROID_BIND_VALUE_INTO_SET =
+ get("dagger.hilt.android.testing", "BindValueIntoSet");
+ public static final ClassName APPLICATION_CONTEXT =
+ get("dagger.hilt.android.qualifiers", "ApplicationContext");
+ public static final ClassName TEST_COMPONENT_DATA =
+ get("dagger.hilt.android.internal.testing", "TestComponentData");
+ public static final ClassName TEST_COMPONENT_DATA_SUPPLIER =
+ get("dagger.hilt.android.internal.testing", "TestComponentDataSupplier");
+
+ public static final ClassName CLASS = get("java.lang", "Class");
+ public static final ClassName LIST = get("java.util", "List");
+ public static final ClassName SET = get("java.util", "Set");
+ public static final ClassName MAP = get("java.util", "Map");
+ public static final ClassName HASH_MAP = get("java.util", "HashMap");
+ public static final ClassName HASH_SET = get("java.util", "HashSet");
+ public static final ClassName COLLECTIONS = get("java.util", "Collections");
+ public static final ClassName ARRAYS = get("java.util", "Arrays");
+
+ // Standard components
+ public static final ClassName SINGLETON_COMPONENT =
+ get("dagger.hilt.components", "SingletonComponent");
+ public static final ClassName ACTIVITY_COMPONENT =
+ get("dagger.hilt.android.components", "ActivityComponent");
+
+ public static final ClassName PRECONDITIONS = get("dagger.hilt.internal", "Preconditions");
+
+ public static final ClassName OBJECT = get("java.lang", "Object");
+
+ // Kotlin-specific class names
+ public static final ClassName KOTLIN_METADATA = get("kotlin", "Metadata");
+
+ private ClassNames() {}
+}
diff --git a/java/dagger/hilt/processor/internal/ComponentDescriptor.java b/java/dagger/hilt/processor/internal/ComponentDescriptor.java
new file mode 100644
index 000000000..5d3cef97a
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/ComponentDescriptor.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import java.util.Optional;
+
+// TODO(bcorso): Reduce the visibility of this class and return ClassNames instead.
+// TODO(erichang): Rename this class so it doesn't conflict with
+// dagger.internal.codegen.ComponentDescriptor
+/** Represents a single component in the hierarchy. */
+@AutoValue
+public abstract class ComponentDescriptor {
+ public static Builder builder() {
+ return new AutoValue_ComponentDescriptor.Builder()
+ .scopes(ImmutableSet.of());
+ }
+
+ /** Returns the {@link ClassName} for this component descriptor. */
+ public abstract ClassName component();
+
+ /** Returns the {@link ClassName}s for the scopes of this component descriptor. */
+ public abstract ImmutableSet<ClassName> scopes();
+
+ /** Returns the {@link ClassName} for the creator interface. if it exists. */
+ public abstract Optional<ClassName> creator();
+
+ /** Returns the {@link ClassName} for the parent, if it exists. */
+ public abstract Optional<ComponentDescriptor> parent();
+
+ /** Returns {@code true} if the descriptor represents a root component. */
+ public boolean isRoot() {
+ return !parent().isPresent();
+ }
+
+ /**
+ * Returns {@code true} if the given {@link ComponentDescriptor} represents the same {@link
+ * #component()}.
+ */
+ // TODO(b/144939893): Remove equals and hashcode once we have unique ComponentDescriptor instances
+ @Override
+ public final boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof ComponentDescriptor)) {
+ return false;
+ }
+ ComponentDescriptor that = (ComponentDescriptor) obj;
+
+ // Only check the component name, which should map 1:1 to each component descriptor created
+ // by DefineComponents#componentDescriptor(Element). However, if users are building their own
+ // ComponentDescriptors manually, then this might not be true. We should lock down the builder
+ // method to avoid that.
+ return component().equals(that.component());
+ }
+
+ @Override
+ public final int hashCode() {
+ return component().hashCode();
+ }
+
+ /** Builder for ComponentDescriptor. */
+ @AutoValue.Builder
+ public interface Builder {
+ Builder component(ClassName component);
+
+ Builder scopes(ImmutableSet<ClassName> scopes);
+
+ Builder scopes(ClassName... scopes);
+
+ Builder creator(ClassName creator);
+
+ Builder parent(ComponentDescriptor parent);
+
+
+ ComponentDescriptor build();
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/ComponentGenerator.java b/java/dagger/hilt/processor/internal/ComponentGenerator.java
new file mode 100644
index 000000000..3a4bf1e78
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/ComponentGenerator.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal;
+
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static java.util.Comparator.comparing;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Utf8;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableList;
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+
+/** Generates a Dagger component or subcomponent interface. */
+// TODO(bcorso): Make this non-public
+public final class ComponentGenerator {
+ private static final Joiner JOINER = Joiner.on(".");
+ private static final Comparator<ClassName> SIMPLE_NAME_SORTER =
+ Comparator.comparing((ClassName c) -> JOINER.join(c.simpleNames()))
+ .thenComparing(ClassName::compareTo);
+ private static final Comparator<TypeName> TYPE_NAME_SORTER = comparing(TypeName::toString);
+
+ private final ProcessingEnvironment processingEnv;
+ private final ClassName name;
+ private final TypeElement rootElement;
+ private final Optional<ClassName> superclass;
+ private final ImmutableList<ClassName> modules;
+ private final ImmutableList<TypeName> entryPoints;
+ private final ImmutableCollection<ClassName> scopes;
+ private final ImmutableList<AnnotationSpec> extraAnnotations;
+ private final ClassName componentAnnotation;
+ private final Optional<TypeSpec> componentBuilder;
+
+ public ComponentGenerator(
+ ProcessingEnvironment processingEnv,
+ ClassName name,
+ TypeElement rootElement,
+ Optional<ClassName> superclass,
+ Set<? extends ClassName> modules,
+ Set<? extends TypeName> entryPoints,
+ ImmutableCollection<ClassName> scopes,
+ ImmutableList<AnnotationSpec> extraAnnotations,
+ ClassName componentAnnotation,
+ Optional<TypeSpec> componentBuilder) {
+ this.processingEnv = processingEnv;
+ this.name = name;
+ this.rootElement = rootElement;
+ this.superclass = superclass;
+ this.modules = modules.stream().sorted(SIMPLE_NAME_SORTER).collect(toImmutableList());
+ this.entryPoints = entryPoints.stream().sorted(TYPE_NAME_SORTER).collect(toImmutableList());
+ this.scopes = scopes;
+ this.extraAnnotations = extraAnnotations;
+ this.componentAnnotation = componentAnnotation;
+ this.componentBuilder = componentBuilder;
+ }
+
+ public TypeSpec generate() throws IOException {
+ TypeSpec.Builder generator =
+ TypeSpec.classBuilder(name)
+ // Public because components from a scope below must reference to create
+ .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
+ .addOriginatingElement(rootElement)
+ .addAnnotation(getComponentAnnotation());
+
+ componentBuilder.ifPresent(generator::addType);
+
+ scopes.forEach(generator::addAnnotation);
+
+ addEntryPoints(generator);
+
+ superclass.ifPresent(generator::superclass);
+
+ generator.addAnnotations(extraAnnotations);
+
+ return generator.build();
+ }
+
+ /** Returns the component annotation with the list of modules to install for the component. */
+ private AnnotationSpec getComponentAnnotation() {
+ AnnotationSpec.Builder builder = AnnotationSpec.builder(componentAnnotation);
+ modules.forEach(module -> builder.addMember("modules", "$T.class", module));
+ return builder.build();
+ }
+
+ /**
+ * Adds entry points to the component.
+ *
+ * See b/140979968. If the entry points exceed 65763 bytes, we have to partition them to avoid the
+ * limit. To be safe, we split at 60000 bytes.
+ */
+ private void addEntryPoints(TypeSpec.Builder builder) throws IOException {
+ int currBytes = 0;
+ List<Integer> partitionIndexes = new ArrayList<>();
+
+ partitionIndexes.add(0);
+ for (int i = 0; i < entryPoints.size(); i++) {
+ // This over estimates the actual length because it includes the fully qualified name (FQN).
+ // TODO(bcorso): Have a better way to estimate the upper bound. For example, most types will
+ // not include the FQN, but we'll have to consider all of the different subtypes of TypeName,
+ // simple name collisions, etc...
+ int nextBytes = Utf8.encodedLength(entryPoints.get(i).toString());
+
+ // To be safe, we split at 60000 to account for the component name, spaces, commas, etc...
+ if (currBytes + nextBytes > 60000) {
+ partitionIndexes.add(i);
+ currBytes = 0;
+ }
+
+ currBytes += nextBytes;
+ }
+ partitionIndexes.add(entryPoints.size());
+
+ if (partitionIndexes.size() <= 2) {
+ // No extra partitions are needed, so just add all of the entrypoints as is.
+ builder.addSuperinterfaces(entryPoints);
+ } else {
+ // Create interfaces for each partition.
+ // The partitioned interfaces will be added to the component instead of the real entry points.
+ for (int i = 1; i < partitionIndexes.size(); i++) {
+ int startIndex = partitionIndexes.get(i - 1);
+ int endIndex = partitionIndexes.get(i);
+ builder.addSuperinterface(
+ createPartitionInterface(entryPoints.subList(startIndex, endIndex), i));
+ }
+ }
+ }
+
+ private ClassName createPartitionInterface(List<TypeName> partition, int partitionIndex)
+ throws IOException {
+ // TODO(bcorso): Nest the partion inside the HiltComponents wrapper rather than appending name
+ ClassName partitionName =
+ Processors.append(
+ Processors.getEnclosedClassName(name), "_EntryPointPartition" + partitionIndex);
+ TypeSpec.Builder builder =
+ TypeSpec.interfaceBuilder(partitionName)
+ .addOriginatingElement(rootElement)
+ .addModifiers(Modifier.ABSTRACT)
+ .addSuperinterfaces(partition);
+
+ Processors.addGeneratedAnnotation(builder, processingEnv, ClassNames.ROOT_PROCESSOR.toString());
+
+ JavaFile.builder(name.packageName(), builder.build()).build().writeTo(processingEnv.getFiler());
+ return partitionName;
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/ComponentNames.java b/java/dagger/hilt/processor/internal/ComponentNames.java
new file mode 100644
index 000000000..fab4d1995
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/ComponentNames.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal;
+
+import com.squareup.javapoet.ClassName;
+
+/**
+ * Utility class for getting the generated component name.
+ *
+ * <p>This should not be used externally.
+ */
+public final class ComponentNames {
+ private ComponentNames() {}
+
+ /** Returns the name of the generated component wrapper. */
+ public static ClassName generatedComponentsWrapper(ClassName root) {
+ return Processors.append(Processors.getEnclosedClassName(root), "_HiltComponents");
+ }
+
+ /** Returns the name of the generated component. */
+ public static ClassName generatedComponent(ClassName root, ClassName component) {
+ return generatedComponentsWrapper(root).nestedClass(componentName(component));
+ }
+
+ /**
+ * Returns the shortened component name by replacing the ending "Component" with "C" if it exists.
+ *
+ * <p>This is a hack because nested subcomponents in Dagger generate extremely long class names
+ * that hit the 256 character limit.
+ */
+ // TODO(bcorso): See if this issue can be fixed in Dagger, e.g. by using static subcomponents.
+ private static String componentName(ClassName component) {
+ // TODO(bcorso): How do we want to handle collisions across packages? Currently, we only handle
+ // collisions across enclosing elements since namespacing by package would likely lead to too
+ // long of class names.
+ // Note: This uses regex matching so we only match if the name ends in "Component"
+ return Processors.getEnclosedName(component).replaceAll("Component$", "C");
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/ComponentTree.java b/java/dagger/hilt/processor/internal/ComponentTree.java
new file mode 100644
index 000000000..6d2137a02
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/ComponentTree.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal;
+
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.graph.GraphBuilder;
+import com.google.common.graph.Graphs;
+import com.google.common.graph.ImmutableGraph;
+import com.google.common.graph.MutableGraph;
+import com.squareup.javapoet.ClassName;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/** A representation of the full tree of scopes. */
+public final class ComponentTree {
+ private final ImmutableGraph<ComponentDescriptor> graph;
+ private final ComponentDescriptor root;
+
+ /** Creates a new tree from a set of descriptors. */
+ public static ComponentTree from(Set<ComponentDescriptor> descriptors) {
+ MutableGraph<ComponentDescriptor> graph =
+ GraphBuilder.directed().allowsSelfLoops(false).build();
+
+ descriptors.forEach(
+ descriptor -> {
+ graph.addNode(descriptor);
+ descriptor.parent().ifPresent(parent -> graph.putEdge(parent, descriptor));
+ });
+
+ return new ComponentTree(ImmutableGraph.copyOf(graph));
+ }
+
+ private ComponentTree(ImmutableGraph<ComponentDescriptor> graph) {
+ this.graph = Preconditions.checkNotNull(graph);
+ Preconditions.checkState(
+ !Graphs.hasCycle(graph),
+ "Component graph has cycles: %s",
+ graph.nodes());
+
+ // Check that each component has a unique descriptor
+ Map<ClassName, ComponentDescriptor> descriptors = new HashMap<>();
+ for (ComponentDescriptor descriptor : graph.nodes()) {
+ if (descriptors.containsKey(descriptor.component())) {
+ ComponentDescriptor prevDescriptor = descriptors.get(descriptor.component());
+ Preconditions.checkState(
+ // TODO(b/144939893): Use "==" instead of ".equals()"?
+ descriptor.equals(prevDescriptor),
+ "%s has mismatching descriptors:\n"
+ + " %s\n\n"
+ + " %s\n\n",
+ prevDescriptor.component(),
+ prevDescriptor,
+ descriptor);
+ }
+ descriptors.put(descriptor.component(), descriptor);
+ }
+
+ ImmutableList<ComponentDescriptor> roots =
+ graph.nodes().stream()
+ .filter(node -> graph.inDegree(node) == 0)
+ .collect(toImmutableList());
+
+ Preconditions.checkState(
+ roots.size() == 1,
+ "Component graph must have exactly 1 root. Found: %s",
+ roots.stream().map(ComponentDescriptor::component).collect(toImmutableList()));
+
+ root = Iterables.getOnlyElement(roots);
+ }
+
+ public ImmutableSet<ComponentDescriptor> getComponentDescriptors() {
+ return ImmutableSet.copyOf(graph.nodes());
+ }
+
+ public ImmutableSet<ComponentDescriptor> childrenOf(ComponentDescriptor componentDescriptor) {
+ return ImmutableSet.copyOf(graph.successors(componentDescriptor));
+ }
+
+ public ImmutableGraph<ComponentDescriptor> graph() {
+ return graph;
+ }
+
+ public ComponentDescriptor root() {
+ return root;
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/Components.java b/java/dagger/hilt/processor/internal/Components.java
new file mode 100644
index 000000000..661dd8a9b
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/Components.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal;
+
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import dagger.hilt.processor.internal.definecomponent.DefineComponents;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
+
+/** Helper methods for defining components and the component hierarchy. */
+public final class Components {
+ // TODO(bcorso): Remove this once all usages are replaced with #getComponents().
+ /**
+ * Returns the {@link ComponentDescriptor}s for a given element annotated with {@link
+ * dagger.hilt.InstallIn}.
+ */
+ public static ImmutableSet<ComponentDescriptor> getComponentDescriptors(
+ Elements elements, Element element) {
+ DefineComponents defineComponents = DefineComponents.create();
+ return getComponents(elements, element).stream()
+ .map(component -> elements.getTypeElement(component.canonicalName()))
+ // TODO(b/144939893): Memoize ComponentDescriptors so we're not recalculating.
+ .map(defineComponents::componentDescriptor)
+ .collect(toImmutableSet());
+ }
+
+ /** Returns the {@link dagger.hilt.InstallIn} components for a given element. */
+ public static ImmutableSet<ClassName> getComponents(Elements elements, Element element) {
+ ImmutableSet<ClassName> components;
+ if (Processors.hasAnnotation(element, ClassNames.INSTALL_IN)
+ || Processors.hasAnnotation(element, ClassNames.TEST_INSTALL_IN)) {
+ components = getHiltInstallInComponents(elements, element);
+ } else {
+ // Check the enclosing element in case it passed in module is a companion object. This helps
+ // in cases where the element was arrived at by checking a binding method and moving outward.
+ Element enclosing = element.getEnclosingElement();
+ if (enclosing != null
+ && MoreElements.isType(enclosing)
+ && MoreElements.isType(element)
+ && Processors.hasAnnotation(enclosing, ClassNames.MODULE)
+ && KotlinMetadataUtils.getMetadataUtil().isCompanionObjectClass(
+ MoreElements.asType(element))) {
+ return getComponents(elements, enclosing);
+ }
+ if (Processors.hasErrorTypeAnnotation(element)) {
+ throw new BadInputException(
+ "Error annotation found on element " + element + ". Look above for compilation errors",
+ element);
+ } else {
+ throw new BadInputException(
+ String.format(
+ "An @InstallIn annotation is required for: %s." ,
+ element),
+ element);
+ }
+ }
+
+ return components;
+ }
+
+ public static AnnotationSpec getInstallInAnnotationSpec(ImmutableSet<ClassName> components) {
+ Preconditions.checkArgument(!components.isEmpty());
+ AnnotationSpec.Builder builder = AnnotationSpec.builder(ClassNames.INSTALL_IN);
+ components.forEach(component -> builder.addMember("value", "$T.class", component));
+ return builder.build();
+ }
+
+ private static ImmutableSet<ClassName> getHiltInstallInComponents(
+ Elements elements, Element element) {
+ Preconditions.checkArgument(
+ Processors.hasAnnotation(element, ClassNames.INSTALL_IN)
+ || Processors.hasAnnotation(element, ClassNames.TEST_INSTALL_IN));
+
+ ImmutableSet<TypeElement> components =
+ ImmutableSet.copyOf(
+ Processors.hasAnnotation(element, ClassNames.INSTALL_IN)
+ ? Processors.getAnnotationClassValues(
+ elements,
+ Processors.getAnnotationMirror(element, ClassNames.INSTALL_IN),
+ "value")
+ : Processors.getAnnotationClassValues(
+ elements,
+ Processors.getAnnotationMirror(element, ClassNames.TEST_INSTALL_IN),
+ "components"));
+
+ ImmutableSet<TypeElement> undefinedComponents =
+ components.stream()
+ .filter(component -> !Processors.hasAnnotation(component, ClassNames.DEFINE_COMPONENT))
+ .collect(toImmutableSet());
+
+ ProcessorErrors.checkState(
+ undefinedComponents.isEmpty(),
+ element,
+ "@InstallIn, can only be used with @DefineComponent-annotated classes, but found: %s",
+ undefinedComponents);
+
+ return components.stream().map(ClassName::get).collect(toImmutableSet());
+ }
+
+ private Components() {}
+}
diff --git a/java/dagger/hilt/processor/internal/ErrorTypeException.java b/java/dagger/hilt/processor/internal/ErrorTypeException.java
new file mode 100644
index 000000000..6f8f67e06
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/ErrorTypeException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal;
+
+import javax.lang.model.element.Element;
+
+/**
+ * Exception to throw when a required {@link Element} is or inherits from an error kind.
+ *
+ * <p>Includes element to point to for the cause of the error
+ */
+public final class ErrorTypeException extends RuntimeException {
+ private final Element badElement;
+
+ public ErrorTypeException(String message, Element badElement) {
+ super(message);
+ this.badElement = badElement;
+ }
+
+ public Element getBadElement() {
+ return badElement;
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/KotlinMetadataUtils.java b/java/dagger/hilt/processor/internal/KotlinMetadataUtils.java
new file mode 100644
index 000000000..ec5ddf871
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/KotlinMetadataUtils.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal;
+
+import dagger.Component;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import javax.inject.Singleton;
+
+/** A single-use provider of {@link KotlinMetadataUtil}. */
+// TODO(erichang): Revert this, should be wrapped with a Dagger module.
+public final class KotlinMetadataUtils {
+
+ @Singleton
+ @Component
+ interface MetadataComponent {
+ KotlinMetadataUtil get();
+ }
+
+ /** Gets the metadata util. */
+ public static KotlinMetadataUtil getMetadataUtil() {
+ return DaggerKotlinMetadataUtils_MetadataComponent.create().get();
+ }
+
+ private KotlinMetadataUtils() {}
+}
diff --git a/java/dagger/hilt/processor/internal/ProcessorErrorHandler.java b/java/dagger/hilt/processor/internal/ProcessorErrorHandler.java
new file mode 100644
index 000000000..460e8a946
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/ProcessorErrorHandler.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Throwables;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import javax.annotation.processing.Messager;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.util.Elements;
+import javax.tools.Diagnostic.Kind;
+
+/** Utility class to handle keeping track of errors during processing. */
+final class ProcessorErrorHandler {
+
+ private static final String FAILURE_PREFIX = "[Hilt]\n";
+
+ // Special characters to make the tag red and bold to draw attention since
+ // this error can get drowned out by other errors resulting from missing
+ // symbols when we can't generate code.
+ private static final String FAILURE_SUFFIX =
+ "\n\033[1;31m[Hilt] Processing did not complete. See error above for details.\033[0m";
+
+ private final Messager messager;
+ private final Elements elements;
+ private final List<HiltError> hiltErrors;
+
+ ProcessorErrorHandler(ProcessingEnvironment env) {
+ this.messager = env.getMessager();
+ this.elements = env.getElementUtils();
+ this.hiltErrors = new ArrayList<>();
+ }
+
+ /**
+ * Records an error message for some exception to the messager. This can be used to handle
+ * exceptions gracefully that would otherwise be propagated out of the {@code process} method. The
+ * message is stored in order to allow the build to continue as far as it can. The build will be
+ * failed with a {@link Kind#ERROR} in {@link #checkErrors} if an error was recorded with this
+ * method.
+ */
+ void recordError(Throwable t) {
+ // Store messages to allow the build to continue as far as it can. The build will
+ // be failed in checkErrors when processing is over.
+
+ if (t instanceof BadInputException) {
+ BadInputException badInput = (BadInputException) t;
+ for (Element element : badInput.getBadElements()) {
+ hiltErrors.add(HiltError.of(badInput.getMessage(), element));
+ }
+ } else if (t instanceof ErrorTypeException) {
+ ErrorTypeException badInput = (ErrorTypeException) t;
+ hiltErrors.add(HiltError.of(badInput.getMessage(), badInput.getBadElement()));
+ } else if (t.getMessage() != null) {
+ hiltErrors.add(HiltError.of(t.getMessage() + ": " + Throwables.getStackTraceAsString(t)));
+ } else {
+ hiltErrors.add(HiltError.of(t.getClass() + ": " + Throwables.getStackTraceAsString(t)));
+ }
+ }
+
+ /** Checks for any recorded errors. This should be called at the end of process every round. */
+ void checkErrors() {
+ if (!hiltErrors.isEmpty()) {
+ hiltErrors.forEach(
+ hiltError -> {
+ if (hiltError.element().isPresent()) {
+ Element element = hiltError.element().get();
+ if (MoreElements.isType(element)) {
+ // If the error type is a TypeElement, get a new one just in case it was thrown in a
+ // previous round we can report the correct instance. Otherwise, this leads to
+ // issues in AndroidStudio when linking an error to the proper element.
+ // TODO(bcorso): Consider only allowing TypeElement errors when delaying errors,
+ // or maybe even removing delayed errors altogether.
+ element =
+ elements.getTypeElement(
+ MoreElements.asType(element).getQualifiedName().toString());
+ }
+ messager.printMessage(Kind.ERROR, hiltError.message(), element);
+ } else {
+ messager.printMessage(Kind.ERROR, hiltError.message());
+ }
+ });
+ hiltErrors.clear();
+ }
+ }
+
+ @AutoValue
+ abstract static class HiltError {
+ static HiltError of(String message) {
+ return of(message, Optional.empty());
+ }
+
+ static HiltError of(String message, Element element) {
+ return of(message, Optional.of(element));
+ }
+
+ private static HiltError of(String message, Optional<Element> element) {
+ return new AutoValue_ProcessorErrorHandler_HiltError(
+ FAILURE_PREFIX + message + FAILURE_SUFFIX, element);
+ }
+
+ abstract String message();
+
+ abstract Optional<Element> element();
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/ProcessorErrors.java b/java/dagger/hilt/processor/internal/ProcessorErrors.java
new file mode 100644
index 000000000..b1578daa3
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/ProcessorErrors.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.errorprone.annotations.FormatMethod;
+import com.google.errorprone.annotations.FormatString;
+import java.util.Collection;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+/** Static helper methods for throwing errors during code generation. */
+public final class ProcessorErrors {
+ /**
+ * Ensures the truth of an expression involving the state of the calling instance, but not
+ * involving any parameters to the calling method.
+ *
+ * @param expression a boolean expression
+ * @param badElement the element that was at fault
+ * @param errorMessage the exception message to use if the check fails; will be converted to a
+ * string using {@link String#valueOf(Object)}
+ * @throws BadInputException if {@code expression} is false
+ */
+ public static void checkState(
+ boolean expression,
+ Element badElement,
+ @Nullable Object errorMessage) {
+ Preconditions.checkNotNull(badElement);
+ if (!expression) {
+ throw new BadInputException(String.valueOf(errorMessage), badElement);
+ }
+ }
+
+ /**
+ * Ensures the truth of an expression involving the state of the calling instance, but not
+ * involving any parameters to the calling method.
+ *
+ * <p>e.g. checkState(foo.isABar(), "Failed because of %s is not a bar", foo);
+ *
+ * @param expression a boolean expression
+ * @param badElement the element that was at fault
+ * @param errorMessageTemplate a template for the exception message should the check fail. The
+ * message is formed by replacing each {@code %s} placeholder in the template with an
+ * argument. These are matched by position - the first {@code %s} gets {@code
+ * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message in
+ * square braces. Unmatched placeholders will be left as-is.
+ * @param errorMessageArgs the arguments to be substituted into the message template. Arguments
+ * are converted to strings using {@link String#valueOf(Object)}.
+ * @throws BadInputException if {@code expression} is false
+ * @throws NullPointerException if the check fails and either {@code errorMessageTemplate} or
+ * {@code errorMessageArgs} is null (don't let this happen)
+ */
+ @FormatMethod
+ public static void checkState(
+ boolean expression,
+ Element badElement,
+ @Nullable @FormatString String errorMessageTemplate,
+ @Nullable Object... errorMessageArgs) {
+ Preconditions.checkNotNull(badElement);
+ if (!expression) {
+ throw new BadInputException(
+ String.format(errorMessageTemplate, errorMessageArgs), badElement);
+ }
+ }
+
+ /**
+ * Ensures the truth of an expression involving the state of the calling instance, but not
+ * involving any parameters to the calling method.
+ *
+ * @param expression a boolean expression
+ * @param badElements the element that were at fault
+ * @param errorMessage the exception message to use if the check fails; will be converted to a
+ * string using {@link String#valueOf(Object)}
+ * @throws BadInputException if {@code expression} is false
+ */
+ public static void checkState(
+ boolean expression,
+ Collection<? extends Element> badElements,
+ @Nullable Object errorMessage) {
+ Preconditions.checkNotNull(badElements);
+ if (!expression) {
+ Preconditions.checkState(!badElements.isEmpty());
+ throw new BadInputException(String.valueOf(errorMessage), badElements);
+ }
+ }
+
+ /**
+ * Ensures the truth of an expression involving the state of the calling instance, but not
+ * involving any parameters to the calling method.
+ *
+ * @param expression a boolean expression
+ * @param badElements the elements that were at fault
+ * @param errorMessageTemplate a template for the exception message should the check fail. The
+ * message is formed by replacing each {@code %s} placeholder in the template with an
+ * argument. These are matched by position - the first {@code %s} gets {@code
+ * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message in
+ * square braces. Unmatched placeholders will be left as-is.
+ * @param errorMessageArgs the arguments to be substituted into the message template. Arguments
+ * are converted to strings using {@link String#valueOf(Object)}.
+ * @throws BadInputException if {@code expression} is false
+ * @throws NullPointerException if the check fails and either {@code errorMessageTemplate} or
+ * {@code errorMessageArgs} is null (don't let this happen)
+ */
+ @FormatMethod
+ public static void checkState(
+ boolean expression,
+ Collection<? extends Element> badElements,
+ @Nullable @FormatString String errorMessageTemplate,
+ @Nullable Object... errorMessageArgs) {
+ Preconditions.checkNotNull(badElements);
+ if (!expression) {
+ Preconditions.checkState(!badElements.isEmpty());
+ throw new BadInputException(
+ String.format(errorMessageTemplate, errorMessageArgs), badElements);
+ }
+ }
+
+ /**
+ * Ensures that the given element is not an error kind and does not inherit from an error kind.
+ *
+ * @param element the element to check
+ * @throws ErrorTypeException if {@code element} inherits from an error kind.
+ */
+ public static void checkNotErrorKind(TypeElement element) {
+ TypeMirror currType = element.asType();
+ ImmutableList.Builder<String> typeHierarchy = ImmutableList.builder();
+ while (currType.getKind() != TypeKind.NONE) {
+ typeHierarchy.add(currType.toString());
+ if (currType.getKind() == TypeKind.ERROR) {
+ throw new ErrorTypeException(
+ String.format(
+ "%s, type hierarchy contains error kind, %s."
+ + "\n\tThe partially resolved hierarchy is:\n\t\t%s",
+ element,
+ currType,
+ typeHierarchy.build().stream().collect(Collectors.joining(" -> "))),
+ element);
+ }
+ currType = MoreTypes.asTypeElement(currType).getSuperclass();
+ }
+ }
+
+ private ProcessorErrors() {}
+}
diff --git a/java/dagger/hilt/processor/internal/Processors.java b/java/dagger/hilt/processor/internal/Processors.java
new file mode 100644
index 000000000..b33c19d67
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/Processors.java
@@ -0,0 +1,940 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal;
+
+import static com.google.auto.common.MoreElements.asPackage;
+import static com.google.auto.common.MoreElements.asVariable;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.extension.DaggerCollectors.toOptional;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.auto.common.AnnotationMirrors;
+import com.google.auto.common.GeneratedAnnotations;
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.base.CaseFormat;
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.SetMultimap;
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.extension.DaggerStreams;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import java.lang.annotation.Annotation;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.inject.Qualifier;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ErrorType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.SimpleAnnotationValueVisitor7;
+import javax.lang.model.util.SimpleTypeVisitor7;
+
+/** Static helper methods for writing a processor. */
+public final class Processors {
+
+ public static final String CONSTRUCTOR_NAME = "<init>";
+
+ public static final String STATIC_INITIALIZER_NAME = "<clinit>";
+
+ private static final String JAVA_CLASS = "java.lang.Class";
+
+ /** Returns a map from {@link AnnotationMirror} attribute name to {@link AnnotationValue}s */
+ public static ImmutableMap<String, AnnotationValue> getAnnotationValues(Elements elements,
+ AnnotationMirror annotation) {
+ ImmutableMap.Builder<String, AnnotationValue> annotationMembers = ImmutableMap.builder();
+ for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> e
+ : elements.getElementValuesWithDefaults(annotation).entrySet()) {
+ annotationMembers.put(e.getKey().getSimpleName().toString(), e.getValue());
+ }
+ return annotationMembers.build();
+ }
+
+ /**
+ * Returns a multimap from attribute name to the values that are an array of annotation mirrors.
+ * The returned map will not contain mappings for any attributes that are not Annotation Arrays.
+ *
+ * <p>e.g. if the input was the annotation mirror for
+ * <pre>
+ * {@literal @}Foo({{@literal @}Bar("hello"), {@literal @}Bar("world")})
+ * </pre>
+ * the map returned would have "value" map to a set containing the two @Bar annotation mirrors.
+ */
+ public static Multimap<String, AnnotationMirror> getAnnotationAnnotationArrayValues(
+ Elements elements, AnnotationMirror annotation) {
+ SetMultimap<String, AnnotationMirror> annotationMembers = LinkedHashMultimap.create();
+ for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> e
+ : elements.getElementValuesWithDefaults(annotation).entrySet()) {
+ String attribute = e.getKey().getSimpleName().toString();
+ Set<AnnotationMirror> annotationMirrors = new LinkedHashSet<>();
+ e.getValue().accept(new AnnotationMirrorAnnotationValueVisitor(), annotationMirrors);
+ annotationMembers.putAll(attribute, annotationMirrors);
+ }
+ return annotationMembers;
+ }
+
+ private static final class AnnotationMirrorAnnotationValueVisitor
+ extends SimpleAnnotationValueVisitor7<Void, Set<AnnotationMirror>> {
+
+ @Override
+ public Void visitArray(List<? extends AnnotationValue> vals, Set<AnnotationMirror> types) {
+ for (AnnotationValue val : vals) {
+ val.accept(this, types);
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitAnnotation(AnnotationMirror a, Set<AnnotationMirror> annotationMirrors) {
+ annotationMirrors.add(a);
+ return null;
+ }
+ }
+
+ /** Returns the {@link TypeElement} for a class attribute on an annotation. */
+ public static TypeElement getAnnotationClassValue(
+ Elements elements, AnnotationMirror annotation, String key) {
+ return Iterables.getOnlyElement(getAnnotationClassValues(elements, annotation, key));
+ }
+
+ /** Returns a list of {@link TypeElement}s for a class attribute on an annotation. */
+ public static ImmutableList<TypeElement> getAnnotationClassValues(
+ Elements elements, AnnotationMirror annotation, String key) {
+ ImmutableList<TypeElement> values = getOptionalAnnotationClassValues(elements, annotation, key);
+
+ ProcessorErrors.checkState(
+ values.size() >= 1,
+ // TODO(b/152801981): Point to the annotation value rather than the annotated element.
+ annotation.getAnnotationType().asElement(),
+ "@%s, '%s' class is invalid or missing: %s",
+ annotation.getAnnotationType().asElement().getSimpleName(),
+ key,
+ annotation);
+
+ return values;
+ }
+
+ /** Returns a multimap from attribute name to elements for class valued attributes. */
+ private static Multimap<String, DeclaredType> getAnnotationClassValues(
+ Elements elements, AnnotationMirror annotation) {
+ Element javaClass = elements.getTypeElement(JAVA_CLASS);
+ SetMultimap<String, DeclaredType> annotationMembers = LinkedHashMultimap.create();
+ for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> e :
+ elements.getElementValuesWithDefaults(annotation).entrySet()) {
+ Optional<DeclaredType> returnType = getOptionalDeclaredType(e.getKey().getReturnType());
+ if (returnType.isPresent() && returnType.get().asElement().equals(javaClass)) {
+ String attribute = e.getKey().getSimpleName().toString();
+ Set<DeclaredType> declaredTypes = new LinkedHashSet<DeclaredType>();
+ e.getValue().accept(new DeclaredTypeAnnotationValueVisitor(), declaredTypes);
+ annotationMembers.putAll(attribute, declaredTypes);
+ }
+ }
+ return annotationMembers;
+ }
+
+ /** Returns an optional {@link TypeElement} for a class attribute on an annotation. */
+ public static Optional<TypeElement> getOptionalAnnotationClassValue(
+ Elements elements, AnnotationMirror annotation, String key) {
+ return getAnnotationClassValues(elements, annotation).get(key).stream()
+ .map(MoreTypes::asTypeElement)
+ .collect(toOptional());
+ }
+
+ /** Returns a list of {@link TypeElement}s for a class attribute on an annotation. */
+ public static ImmutableList<TypeElement> getOptionalAnnotationClassValues(
+ Elements elements, AnnotationMirror annotation, String key) {
+ return ImmutableList.copyOf(
+ getAnnotationClassValues(elements, annotation).get(key).stream()
+ .map(MoreTypes::asTypeElement)
+ .collect(Collectors.toList()));
+ }
+
+ private static final class DeclaredTypeAnnotationValueVisitor
+ extends SimpleAnnotationValueVisitor7<Void, Set<DeclaredType>> {
+
+ @Override public Void visitArray(
+ List<? extends AnnotationValue> vals, Set<DeclaredType> types) {
+ for (AnnotationValue val : vals) {
+ val.accept(this, types);
+ }
+ return null;
+ }
+
+ @Override public Void visitType(TypeMirror t, Set<DeclaredType> types) {
+ DeclaredType declared = MoreTypes.asDeclared(t);
+ checkNotNull(declared);
+ types.add(declared);
+ return null;
+ }
+ }
+
+ /**
+ * If the received mirror represents a primitive type or an array of primitive types, this returns
+ * the represented primitive type. Otherwise throws an IllegalStateException.
+ */
+ public static PrimitiveType getPrimitiveType(TypeMirror type) {
+ return type.accept(
+ new SimpleTypeVisitor7<PrimitiveType, Void> () {
+ @Override public PrimitiveType visitArray(ArrayType type, Void unused) {
+ return getPrimitiveType(type.getComponentType());
+ }
+
+ @Override public PrimitiveType visitPrimitive(PrimitiveType type, Void unused) {
+ return type;
+ }
+
+ @Override public PrimitiveType defaultAction(TypeMirror type, Void unused) {
+ throw new IllegalStateException("Unhandled type: " + type);
+ }
+ }, null /* the Void accumulator */);
+ }
+
+ /**
+ * Returns an {@link Optional#of} the declared type if the received mirror represents a declared
+ * type or an array of declared types, otherwise returns {@link Optional#empty}.
+ */
+ public static Optional<DeclaredType> getOptionalDeclaredType(TypeMirror type) {
+ return Optional.ofNullable(
+ type.accept(
+ new SimpleTypeVisitor7<DeclaredType, Void>(null /* defaultValue */) {
+ @Override
+ public DeclaredType visitArray(ArrayType type, Void unused) {
+ return MoreTypes.asDeclared(type.getComponentType());
+ }
+
+ @Override
+ public DeclaredType visitDeclared(DeclaredType type, Void unused) {
+ return type;
+ }
+
+ @Override
+ public DeclaredType visitError(ErrorType type, Void unused) {
+ return type;
+ }
+ },
+ null /* the Void accumulator */));
+ }
+
+ /**
+ * Returns the declared type if the received mirror represents a declared type or an array of
+ * declared types, otherwise throws an {@link IllegalStateException}.
+ */
+ public static DeclaredType getDeclaredType(TypeMirror type) {
+ return getOptionalDeclaredType(type)
+ .orElseThrow(() -> new IllegalStateException("Not a declared type: " + type));
+ }
+
+ /** Gets the values from an annotation value representing a string array. */
+ public static ImmutableList<String> getStringArrayAnnotationValue(AnnotationValue value) {
+ return value.accept(new SimpleAnnotationValueVisitor7<ImmutableList<String>, Void>() {
+ @Override
+ public ImmutableList<String> defaultAction(Object o, Void unused) {
+ throw new IllegalStateException("Expected an array, got instead: " + o);
+ }
+
+ @Override
+ public ImmutableList<String> visitArray(List<? extends AnnotationValue> values,
+ Void unused) {
+ ImmutableList.Builder<String> builder = ImmutableList.builder();
+ for (AnnotationValue value : values) {
+ builder.add(getStringAnnotationValue(value));
+ }
+ return builder.build();
+ }
+ }, /* unused accumulator */ null);
+ }
+
+ /** Gets the values from an annotation value representing an int. */
+ public static Boolean getBooleanAnnotationValue(AnnotationValue value) {
+ return value.accept(
+ new SimpleAnnotationValueVisitor7<Boolean, Void>() {
+ @Override
+ public Boolean defaultAction(Object o, Void unused) {
+ throw new IllegalStateException("Expected a boolean, got instead: " + o);
+ }
+
+ @Override
+ public Boolean visitBoolean(boolean value, Void unused) {
+ return value;
+ }
+ }, /* unused accumulator */
+ null);
+ }
+
+ /** Gets the values from an annotation value representing an int. */
+ public static Integer getIntAnnotationValue(AnnotationValue value) {
+ return value.accept(new SimpleAnnotationValueVisitor7<Integer, Void>() {
+ @Override
+ public Integer defaultAction(Object o, Void unused) {
+ throw new IllegalStateException("Expected an int, got instead: " + o);
+ }
+
+ @Override
+ public Integer visitInt(int value, Void unused) {
+ return value;
+ }
+ }, /* unused accumulator */ null);
+ }
+
+ /** Gets the values from an annotation value representing a long. */
+ public static Long getLongAnnotationValue(AnnotationValue value) {
+ return value.accept(
+ new SimpleAnnotationValueVisitor7<Long, Void>() {
+ @Override
+ public Long defaultAction(Object o, Void unused) {
+ throw new IllegalStateException("Expected an int, got instead: " + o);
+ }
+
+ @Override
+ public Long visitLong(long value, Void unused) {
+ return value;
+ }
+ },
+ null /* unused accumulator */);
+ }
+
+ /** Gets the values from an annotation value representing a string. */
+ public static String getStringAnnotationValue(AnnotationValue value) {
+ return value.accept(new SimpleAnnotationValueVisitor7<String, Void>() {
+ @Override
+ public String defaultAction(Object o, Void unused) {
+ throw new IllegalStateException("Expected a string, got instead: " + o);
+ }
+
+ @Override
+ public String visitString(String value, Void unused) {
+ return value;
+ }
+ }, /* unused accumulator */ null);
+ }
+
+ /** Gets the values from an annotation value representing a DeclaredType. */
+ public static DeclaredType getDeclaredTypeAnnotationValue(AnnotationValue value) {
+ return value.accept(
+ new SimpleAnnotationValueVisitor7<DeclaredType, Void>() {
+ @Override
+ public DeclaredType defaultAction(Object o, Void unused) {
+ throw new IllegalStateException("Expected a TypeMirror, got instead: " + o);
+ }
+
+ @Override
+ public DeclaredType visitType(TypeMirror typeMirror, Void unused) {
+ return MoreTypes.asDeclared(typeMirror);
+ }
+ }, /* unused accumulator */
+ null);
+ }
+
+ private static final SimpleAnnotationValueVisitor7<ImmutableSet<VariableElement>, Void>
+ ENUM_ANNOTATION_VALUE_VISITOR =
+ new SimpleAnnotationValueVisitor7<ImmutableSet<VariableElement>, Void>() {
+ @Override
+ public ImmutableSet<VariableElement> defaultAction(Object o, Void unused) {
+ throw new IllegalStateException(
+ "Expected an Enum or an Enum array, got instead: " + o);
+ }
+
+ @Override
+ public ImmutableSet<VariableElement> visitArray(
+ List<? extends AnnotationValue> values, Void unused) {
+ ImmutableSet.Builder<VariableElement> builder = ImmutableSet.builder();
+ for (AnnotationValue value : values) {
+ builder.addAll(value.accept(this, null));
+ }
+ return builder.build();
+ }
+
+ @Override
+ public ImmutableSet<VariableElement> visitEnumConstant(
+ VariableElement value, Void unused) {
+ return ImmutableSet.of(value);
+ }
+ };
+
+ /** Gets the values from an annotation value representing a Enum array. */
+ public static ImmutableSet<VariableElement> getEnumArrayAnnotationValue(AnnotationValue value) {
+ return value.accept(ENUM_ANNOTATION_VALUE_VISITOR, /* unused accumulator */ null);
+ }
+
+ /** Converts an annotation value map to be keyed by the attribute name. */
+ public static ImmutableMap<String, AnnotationValue> convertToAttributeNameMap(
+ Map<? extends ExecutableElement, ? extends AnnotationValue> annotationValues) {
+ ImmutableMap.Builder<String, AnnotationValue> builder = ImmutableMap.builder();
+ for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> e
+ : annotationValues.entrySet()) {
+ String attribute = e.getKey().getSimpleName().toString();
+ builder.put(attribute, e.getValue());
+ }
+ return builder.build();
+ }
+
+ /** Returns the given elements containing package element. */
+ public static PackageElement getPackageElement(Element originalElement) {
+ checkNotNull(originalElement);
+ for (Element e = originalElement; e != null; e = e.getEnclosingElement()) {
+ if (e instanceof PackageElement) {
+ return (PackageElement) e;
+ }
+ }
+ throw new IllegalStateException("Cannot find a package for " + originalElement);
+ }
+
+ public static TypeElement getTopLevelType(Element originalElement) {
+ checkNotNull(originalElement);
+ for (Element e = originalElement; e != null; e = e.getEnclosingElement()) {
+ if (isTopLevel(e)) {
+ return MoreElements.asType(e);
+ }
+ }
+ throw new IllegalStateException("Cannot find a top-level type for " + originalElement);
+ }
+
+ /** Returns true if the given element is a top-level element. */
+ public static boolean isTopLevel(Element element) {
+ return element.getEnclosingElement().getKind() == ElementKind.PACKAGE;
+ }
+
+ /** Returns true if the given element is annotated with the given annotation. */
+ public static boolean hasAnnotation(Element element, Class<? extends Annotation> annotation) {
+ return element.getAnnotation(annotation) != null;
+ }
+
+ /** Returns true if the given element has an annotation with the given class name. */
+ public static boolean hasAnnotation(Element element, ClassName className) {
+ return getAnnotationMirrorOptional(element, className).isPresent();
+ }
+
+ /** Returns true if the given element has an annotation with the given class name. */
+ public static boolean hasAnnotation(AnnotationMirror mirror, ClassName className) {
+ return hasAnnotation(mirror.getAnnotationType().asElement(), className);
+ }
+
+ /** Returns true if the given element is annotated with the given annotation. */
+ public static boolean hasAnnotation(
+ AnnotationMirror mirror, Class<? extends Annotation> annotation) {
+ return hasAnnotation(mirror.getAnnotationType().asElement(), annotation);
+ }
+
+ /** Returns true if the given element has an annotation that is an error kind. */
+ public static boolean hasErrorTypeAnnotation(Element element) {
+ for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
+ if (annotationMirror.getAnnotationType().getKind() == TypeKind.ERROR) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Returns all elements in the round that are annotated with at least 1 of the given
+ * annotations.
+ */
+ @SafeVarargs
+ public static ImmutableSet<Element> getElementsAnnotatedWith(RoundEnvironment roundEnv,
+ Class<? extends Annotation>... annotations) {
+ ImmutableSet.Builder<Element> builder = ImmutableSet.builder();
+ for (Class<? extends Annotation> annotation : annotations){
+ builder.addAll(roundEnv.getElementsAnnotatedWith(annotation));
+ }
+
+ return builder.build();
+ }
+
+ /**
+ * Returns the name of a class, including prefixing with enclosing class names. i.e. for inner
+ * class Foo enclosed by Bar, returns Bar_Foo instead of just Foo
+ */
+ public static String getEnclosedName(ClassName name) {
+ return Joiner.on('_').join(name.simpleNames());
+ }
+
+ /** Returns the name of a class. See {@link #getEnclosedName(ClassName)}. */
+ public static String getEnclosedName(TypeElement element) {
+ return getEnclosedName(ClassName.get(element));
+ }
+
+ /**
+ * Returns an equivalent class name with the {@code .} (dots) used for inner classes replaced with
+ * {@code _}.
+ */
+ public static ClassName getEnclosedClassName(ClassName className) {
+ return ClassName.get(className.packageName(), getEnclosedName(className));
+ }
+
+ /** Returns the fully qualified class name, with _ instead of . */
+ public static String getFullyQualifiedEnclosedClassName(ClassName className) {
+ return className.packageName().replace('.', '_') + getEnclosedName(className);
+ }
+
+ /**
+ * Returns the fully qualified class name, with _ instead of . For elements that are not type
+ * elements, this continues to append the simple name of elements. For example,
+ * foo_bar_Outer_Inner_fooMethod.
+ */
+ public static String getFullEnclosedName(Element element) {
+ Preconditions.checkNotNull(element);
+ String qualifiedName = "";
+ while (element != null) {
+ if (element.getKind().equals(ElementKind.PACKAGE)) {
+ qualifiedName = asPackage(element).getQualifiedName() + qualifiedName;
+ } else {
+ // This check is needed to keep the name stable when compiled with jdk8 vs jdk11. jdk11
+ // contains newly added "module" enclosing elements of packages, which adds an addtional "_"
+ // prefix to the name due to an empty module element compared with jdk8.
+ if (!element.getSimpleName().toString().isEmpty()) {
+ qualifiedName = "." + element.getSimpleName() + qualifiedName;
+ }
+ }
+ element = element.getEnclosingElement();
+ }
+ return qualifiedName.replace('.', '_');
+ }
+
+ /** Appends the given string to the end of the class name. */
+ public static ClassName append(ClassName name, String suffix) {
+ return name.peerClass(name.simpleName() + suffix);
+ }
+
+ /** Prepends the given string to the beginning of the class name. */
+ public static ClassName prepend(ClassName name, String prefix) {
+ return name.peerClass(prefix + name.simpleName());
+ }
+
+ /**
+ * Removes the string {@code suffix} from the simple name of {@code type} and returns it.
+ *
+ * @throws BadInputException if the simple name of {@code type} does not end with {@code suffix}
+ */
+ public static ClassName removeNameSuffix(TypeElement type, String suffix) {
+ ClassName originalName = ClassName.get(type);
+ String originalSimpleName = originalName.simpleName();
+ ProcessorErrors.checkState(originalSimpleName.endsWith(suffix),
+ type, "Name of type %s must end with '%s'", originalName, suffix);
+ String withoutSuffix =
+ originalSimpleName.substring(0, originalSimpleName.length() - suffix.length());
+ return originalName.peerClass(withoutSuffix);
+ }
+
+ /** @see #getAnnotationMirror(Element, ClassName) */
+ public static AnnotationMirror getAnnotationMirror(
+ Element element, Class<? extends Annotation> annotationClass) {
+ return getAnnotationMirror(element, ClassName.get(annotationClass));
+ }
+
+ /** @see #getAnnotationMirror(Element, ClassName) */
+ public static AnnotationMirror getAnnotationMirror(Element element, String annotationClassName) {
+ return getAnnotationMirror(element, ClassName.bestGuess(annotationClassName));
+ }
+
+ /**
+ * Returns the annotation mirror from the given element that corresponds to the given class.
+ *
+ * @throws IllegalStateException if the given element isn't annotated with that annotation.
+ */
+ public static AnnotationMirror getAnnotationMirror(Element element, ClassName className) {
+ Optional<AnnotationMirror> annotationMirror = getAnnotationMirrorOptional(element, className);
+ if (annotationMirror.isPresent()) {
+ return annotationMirror.get();
+ } else {
+ throw new IllegalStateException(
+ String.format(
+ "Couldn't find annotation %s on element %s. Found annotations: %s",
+ className, element.getSimpleName(), element.getAnnotationMirrors()));
+ }
+ }
+
+ /**
+ * Returns the annotation mirror from the given element that corresponds to the given class.
+ *
+ * @throws {@link IllegalArgumentException} if 2 or more annotations are found.
+ * @return {@link Optional#empty()} if no annotation is found on the element.
+ */
+ static Optional<AnnotationMirror> getAnnotationMirrorOptional(
+ Element element, ClassName className) {
+ return element.getAnnotationMirrors().stream()
+ .filter(mirror -> ClassName.get(mirror.getAnnotationType()).equals(className))
+ .collect(toOptional());
+ }
+
+ /** @return true if element inherits directly or indirectly from the className */
+ public static boolean isAssignableFrom(TypeElement element, ClassName className) {
+ return isAssignableFromAnyOf(element, ImmutableSet.of(className));
+ }
+
+ /** @return true if element inherits directly or indirectly from any of the classNames */
+ public static boolean isAssignableFromAnyOf(TypeElement element,
+ ImmutableSet<ClassName> classNames) {
+ for (ClassName className : classNames) {
+ if (ClassName.get(element).equals(className)) {
+ return true;
+ }
+ }
+
+ TypeMirror superClass = element.getSuperclass();
+ // None type is returned if this is an interface or Object
+ // Error type is returned for classes that are generated by this processor
+ if ((superClass.getKind() != TypeKind.NONE) && (superClass.getKind() != TypeKind.ERROR)) {
+ Preconditions.checkState(superClass.getKind() == TypeKind.DECLARED);
+ if (isAssignableFromAnyOf(MoreTypes.asTypeElement(superClass), classNames)) {
+ return true;
+ }
+ }
+
+ for (TypeMirror iface : element.getInterfaces()) {
+ // Skip errors and keep looking. This is especially needed for classes generated by this
+ // processor.
+ if (iface.getKind() == TypeKind.ERROR) {
+ continue;
+ }
+ Preconditions.checkState(iface.getKind() == TypeKind.DECLARED,
+ "Interface type is %s", iface.getKind());
+ if (isAssignableFromAnyOf(MoreTypes.asTypeElement(iface), classNames)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /** Returns methods from a given TypeElement, not including constructors. */
+ public static ImmutableList<ExecutableElement> getMethods(TypeElement element) {
+ ImmutableList.Builder<ExecutableElement> builder = ImmutableList.builder();
+ for (Element e : element.getEnclosedElements()) {
+ // Only look for executable elements, not fields, etc
+ if (e instanceof ExecutableElement) {
+ ExecutableElement method = (ExecutableElement) e;
+ if (!method.getSimpleName().contentEquals(CONSTRUCTOR_NAME)
+ && !method.getSimpleName().contentEquals(STATIC_INITIALIZER_NAME)) {
+ builder.add(method);
+ }
+ }
+ }
+ return builder.build();
+ }
+
+ public static ImmutableList<ExecutableElement> getConstructors(TypeElement element) {
+ ImmutableList.Builder<ExecutableElement> builder = ImmutableList.builder();
+ for (Element enclosed : element.getEnclosedElements()) {
+ // Only look for executable elements, not fields, etc
+ if (enclosed instanceof ExecutableElement) {
+ ExecutableElement method = (ExecutableElement) enclosed;
+ if (method.getSimpleName().contentEquals(CONSTRUCTOR_NAME)) {
+ builder.add(method);
+ }
+ }
+ }
+ return builder.build();
+ }
+
+ /**
+ * Returns all transitive methods from a given TypeElement, not including constructors. Also does
+ * not include methods from Object or that override methods on Object.
+ */
+ public static ImmutableList<ExecutableElement> getAllMethods(TypeElement element) {
+ ImmutableList.Builder<ExecutableElement> builder = ImmutableList.builder();
+ builder.addAll(
+ Iterables.filter(
+ getMethods(element),
+ method -> {
+ return !isObjectMethod(method);
+ }));
+ TypeMirror superclass = element.getSuperclass();
+ if (superclass.getKind() != TypeKind.NONE) {
+ TypeElement superclassElement = MoreTypes.asTypeElement(superclass);
+ builder.addAll(getAllMethods(superclassElement));
+ }
+ for (TypeMirror iface : element.getInterfaces()) {
+ builder.addAll(getAllMethods(MoreTypes.asTypeElement(iface)));
+ }
+ return builder.build();
+ }
+
+ /** Checks that the given element is not the error type. */
+ public static void checkForCompilationError(TypeElement e) {
+ ProcessorErrors.checkState(e.asType().getKind() != TypeKind.ERROR, e,
+ "Unable to resolve the type %s. Look for compilation errors above related to this type.",
+ e);
+ }
+
+ private static void addInterfaceMethods(
+ TypeElement type, ImmutableList.Builder<ExecutableElement> interfaceMethods) {
+ for (TypeMirror interfaceMirror : type.getInterfaces()) {
+ TypeElement interfaceElement = MoreTypes.asTypeElement(interfaceMirror);
+ interfaceMethods.addAll(getMethods(interfaceElement));
+ addInterfaceMethods(interfaceElement, interfaceMethods);
+ }
+ }
+
+ /**
+ * Finds methods of interfaces implemented by {@code type}. This method also checks the
+ * superinterfaces of those interfaces. This method does not check the interfaces of any
+ * superclass of {@code type}.
+ */
+ public static ImmutableList<ExecutableElement> methodsOnInterfaces(TypeElement type) {
+ ImmutableList.Builder<ExecutableElement> interfaceMethods = new ImmutableList.Builder<>();
+ addInterfaceMethods(type, interfaceMethods);
+ return interfaceMethods.build();
+ }
+
+ /** Returns MapKey annotated annotations found on an element. */
+ public static ImmutableList<AnnotationMirror> getMapKeyAnnotations(Element element) {
+ return getAnnotationsAnnotatedWith(element, ClassName.get("dagger", "MapKey"));
+ }
+
+ /** Returns Qualifier annotated annotations found on an element. */
+ public static ImmutableList<AnnotationMirror> getQualifierAnnotations(Element element) {
+ // TODO(bcorso): Consolidate this logic with InjectionAnnotations in Dagger
+ ImmutableSet<? extends AnnotationMirror> qualifiers =
+ AnnotationMirrors.getAnnotatedAnnotations(element, Qualifier.class);
+ KotlinMetadataUtil metadataUtil = KotlinMetadataUtils.getMetadataUtil();
+ if (element.getKind() == ElementKind.FIELD
+ // static fields are generally not supported, no need to get qualifier from kotlin metadata
+ && !element.getModifiers().contains(STATIC)
+ && metadataUtil.hasMetadata(element)) {
+ VariableElement fieldElement = asVariable(element);
+ return Stream.concat(
+ qualifiers.stream(),
+ metadataUtil.isMissingSyntheticPropertyForAnnotations(fieldElement)
+ ? Stream.empty()
+ : metadataUtil
+ .getSyntheticPropertyAnnotations(fieldElement, Qualifier.class)
+ .stream())
+ .map(AnnotationMirrors.equivalence()::wrap)
+ .distinct()
+ .map(Wrapper::get)
+ .collect(DaggerStreams.toImmutableList());
+ } else {
+ return ImmutableList.copyOf(qualifiers);
+ }
+ }
+
+ /** Returns Scope annotated annotations found on an element. */
+ public static ImmutableList<AnnotationMirror> getScopeAnnotations(Element element) {
+ return getAnnotationsAnnotatedWith(element, ClassNames.SCOPE);
+ }
+
+ /** Returns annotations of element that are annotated with subAnnotation */
+ public static ImmutableList<AnnotationMirror> getAnnotationsAnnotatedWith(
+ Element element, ClassName subAnnotation) {
+ ImmutableList.Builder<AnnotationMirror> builder = ImmutableList.builder();
+ element.getAnnotationMirrors().stream()
+ .filter(annotation -> hasAnnotation(annotation, subAnnotation))
+ .forEach(builder::add);
+ return builder.build();
+ }
+
+ /** Returns true if there are any annotations of element that are annotated with subAnnotation */
+ public static boolean hasAnnotationsAnnotatedWith(Element element, ClassName subAnnotation) {
+ return !getAnnotationsAnnotatedWith(element, subAnnotation).isEmpty();
+ }
+
+ /**
+ * Returns true iff the given {@code method} is one of the public or protected methods on {@link
+ * Object}, or an overridden version thereof.
+ *
+ * <p>This method ignores the return type of the given method, but this is generally fine since
+ * two methods which only differ by their return type will cause a compiler error. (e.g. a
+ * non-static method with the signature {@code int equals(Object)})
+ */
+ public static boolean isObjectMethod(ExecutableElement method) {
+ // First check if this method is directly defined on Object
+ Element enclosingElement = method.getEnclosingElement();
+ if (enclosingElement.getKind() == ElementKind.CLASS
+ && TypeName.get(enclosingElement.asType()).equals(TypeName.OBJECT)) {
+ return true;
+ }
+
+ if (method.getModifiers().contains(Modifier.STATIC)) {
+ return false;
+ }
+ switch (method.getSimpleName().toString()) {
+ case "equals":
+ return (method.getParameters().size() == 1)
+ && (method.getParameters().get(0).asType().toString().equals("java.lang.Object"));
+ case "hashCode":
+ case "toString":
+ case "clone":
+ case "getClass":
+ case "notify":
+ case "notifyAll":
+ case "finalize":
+ return method.getParameters().isEmpty();
+ case "wait":
+ if (method.getParameters().isEmpty()) {
+ return true;
+ } else if ((method.getParameters().size() == 1)
+ && (method.getParameters().get(0).asType().toString().equals("long"))) {
+ return true;
+ } else if ((method.getParameters().size() == 2)
+ && (method.getParameters().get(0).asType().toString().equals("long"))
+ && (method.getParameters().get(1).asType().toString().equals("int"))) {
+ return true;
+ }
+ return false;
+ default:
+ return false;
+ }
+ }
+
+ public static ParameterSpec parameterSpecFromVariableElement(VariableElement element) {
+ return ParameterSpec.builder(
+ ClassName.get(element.asType()),
+ upperToLowerCamel(element.getSimpleName().toString()))
+ .build();
+ }
+
+ /**
+ * Shortcut for converting from upper camel to lower camel case
+ *
+ * <p>Example: "SomeString" => "someString"
+ */
+ public static String upperToLowerCamel(String upperCamel) {
+ return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, upperCamel);
+ }
+
+ /** @return copy of the given MethodSpec as {@link MethodSpec.Builder} with method body removed */
+ public static MethodSpec.Builder copyMethodSpecWithoutBody(MethodSpec methodSpec) {
+ MethodSpec.Builder builder;
+
+ if (methodSpec.isConstructor()) {
+ // Constructors cannot have return types
+ builder = MethodSpec.constructorBuilder();
+ } else {
+ builder = MethodSpec.methodBuilder(methodSpec.name)
+ .returns(methodSpec.returnType);
+ }
+
+ return builder
+ .addAnnotations(methodSpec.annotations)
+ .addModifiers(methodSpec.modifiers)
+ .addParameters(methodSpec.parameters)
+ .addExceptions(methodSpec.exceptions)
+ .addJavadoc(methodSpec.javadoc.toString())
+ .addTypeVariables(methodSpec.typeVariables);
+ }
+
+ /** @return A method spec for an empty constructor (useful for abstract Dagger modules). */
+ public static MethodSpec privateEmptyConstructor() {
+ return MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE).build();
+ }
+
+ /**
+ * Returns true if the given method is annotated with one of the annotations Dagger recognizes
+ * for abstract methods (e.g. @Binds).
+ */
+ public static boolean hasDaggerAbstractMethodAnnotation(ExecutableElement method) {
+ return hasAnnotation(method, ClassNames.BINDS)
+ || hasAnnotation(method, ClassNames.BINDS_OPTIONAL_OF)
+ || hasAnnotation(method, ClassNames.MULTIBINDS)
+ || hasAnnotation(method, ClassNames.CONTRIBUTES_ANDROID_INJECTOR);
+ }
+
+ public static ImmutableSet<TypeElement> toTypeElements(Elements elements, String[] classes) {
+ return FluentIterable.from(classes).transform(elements::getTypeElement).toSet();
+ }
+
+ public static ImmutableSet<ClassName> toClassNames(Iterable<TypeElement> elements) {
+ return FluentIterable.from(elements).transform(ClassName::get).toSet();
+ }
+
+ public static boolean requiresModuleInstance(Elements elements, TypeElement module) {
+ // Binding methods that lack ABSTRACT or STATIC require module instantiation.
+ // Required by Dagger. See b/31489617.
+ return ElementFilter.methodsIn(elements.getAllMembers(module)).stream()
+ .filter(Processors::isBindingMethod)
+ .map(ExecutableElement::getModifiers)
+ .anyMatch(modifiers -> !modifiers.contains(ABSTRACT) && !modifiers.contains(STATIC));
+ }
+
+ private static boolean isBindingMethod(ExecutableElement method) {
+ return hasAnnotation(method, ClassNames.PROVIDES)
+ || hasAnnotation(method, ClassNames.BINDS)
+ || hasAnnotation(method, ClassNames.BINDS_OPTIONAL_OF)
+ || hasAnnotation(method, ClassNames.MULTIBINDS);
+ }
+
+ public static void addGeneratedAnnotation(
+ TypeSpec.Builder typeSpecBuilder, ProcessingEnvironment env, Class<?> generatorClass) {
+ addGeneratedAnnotation(typeSpecBuilder, env, generatorClass.getName());
+ }
+
+ public static void addGeneratedAnnotation(
+ TypeSpec.Builder typeSpecBuilder, ProcessingEnvironment env, String generatorClass) {
+ GeneratedAnnotations.generatedAnnotation(env.getElementUtils(), env.getSourceVersion())
+ .ifPresent(
+ annotation ->
+ typeSpecBuilder.addAnnotation(
+ AnnotationSpec.builder(ClassName.get(annotation))
+ .addMember("value", "$S", generatorClass)
+ .build()));
+ }
+
+ public static AnnotationSpec getOriginatingElementAnnotation(TypeElement element) {
+ TypeName rawType = rawTypeName(ClassName.get(getTopLevelType(element)));
+ return AnnotationSpec.builder(ClassNames.ORIGINATING_ELEMENT)
+ .addMember("topLevelClass", "$T.class", rawType)
+ .build();
+ }
+
+ /**
+ * Returns the {@link TypeName} for the raw type of the given type name. If the argument isn't a
+ * parameterized type, it returns the argument unchanged.
+ */
+ public static TypeName rawTypeName(TypeName typeName) {
+ return (typeName instanceof ParameterizedTypeName)
+ ? ((ParameterizedTypeName) typeName).rawType
+ : typeName;
+ }
+
+ private Processors() {}
+}
diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDeps.java b/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDeps.java
new file mode 100644
index 000000000..cc955ef39
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDeps.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.aggregateddeps;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+
+// TODO(bcorso): Change this API to clearly represent that each AggeregatedDeps should only contain
+// a single module, entry point, or component entry point.
+/** Annotation for propagating dependency information through javac runs. */
+@Retention(CLASS)
+public @interface AggregatedDeps {
+ /** Returns the components that this dependency will be installed in. */
+ String[] components();
+
+ /** Returns the test this dependency is associated with, otherwise an empty string. */
+ String test() default "";
+
+ /** Returns the deps that this dep replaces. */
+ String[] replaces() default {};
+
+ String[] modules() default {};
+
+ String[] entryPoints() default {};
+
+ String[] componentEntryPoints() default {};
+}
diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsGenerator.java b/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsGenerator.java
new file mode 100644
index 000000000..bcc2dfe04
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsGenerator.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.aggregateddeps;
+
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.TypeSpec;
+import dagger.hilt.processor.internal.Processors;
+import java.io.IOException;
+import java.util.Optional;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * Generates the @AggregatedDeps annotated class used to pass information
+ * about modules and entry points through multiple javac runs.
+ */
+final class AggregatedDepsGenerator {
+ static final String AGGREGATING_PACKAGE = "hilt_aggregated_deps";
+ private static final ClassName AGGREGATED_DEPS =
+ ClassName.get("dagger.hilt.processor.internal.aggregateddeps", "AggregatedDeps");
+
+ private final String dependencyType;
+ private final TypeElement dependency;
+ private final Optional<ClassName> testName;
+ private final ImmutableSet<ClassName> components;
+ private final ImmutableSet<ClassName> replacedDependencies;
+ private final ProcessingEnvironment processingEnv;
+
+ AggregatedDepsGenerator(
+ String dependencyType,
+ TypeElement dependency,
+ Optional<ClassName> testName,
+ ImmutableSet<ClassName> components,
+ ImmutableSet<ClassName> replacedDependencies,
+ ProcessingEnvironment processingEnv) {
+ this.dependencyType = dependencyType;
+ this.dependency = dependency;
+ this.testName = testName;
+ this.components = components;
+ this.replacedDependencies = replacedDependencies;
+ this.processingEnv = processingEnv;
+ }
+
+ void generate() throws IOException {
+ ClassName name =
+ ClassName.get(
+ AGGREGATING_PACKAGE, Processors.getFullEnclosedName(dependency) + "ModuleDeps");
+ TypeSpec.Builder generator =
+ TypeSpec.classBuilder(name.simpleName())
+ .addOriginatingElement(dependency)
+ .addAnnotation(aggregatedDepsAnnotation())
+ .addJavadoc("Generated class to pass information through multiple javac runs.\n");
+
+ Processors.addGeneratedAnnotation(generator, processingEnv, getClass());
+
+ JavaFile.builder(name.packageName(), generator.build())
+ .build()
+ .writeTo(processingEnv.getFiler());
+ }
+
+ private AnnotationSpec aggregatedDepsAnnotation() {
+ AnnotationSpec.Builder annotationBuilder = AnnotationSpec.builder(AGGREGATED_DEPS);
+ components.forEach(component -> annotationBuilder.addMember("components", "$S", component));
+ replacedDependencies.forEach(dep -> annotationBuilder.addMember("replaces", "$S", dep));
+ testName.ifPresent(test -> annotationBuilder.addMember("test", "$S", test));
+ annotationBuilder.addMember(dependencyType, "$S", dependency.getQualifiedName());
+ return annotationBuilder.build();
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsProcessor.java b/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsProcessor.java
new file mode 100644
index 000000000..58c938f88
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsProcessor.java
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.aggregateddeps;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
+import static com.google.auto.common.MoreElements.asType;
+import static com.google.auto.common.MoreElements.getPackage;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.hilt.android.processor.internal.androidentrypoint.HiltCompilerOptions.BooleanOption.DISABLE_MODULES_HAVE_INSTALL_IN_CHECK;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static javax.lang.model.element.ElementKind.CLASS;
+import static javax.lang.model.element.ElementKind.INTERFACE;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.STATIC;
+import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import dagger.hilt.processor.internal.BaseProcessor;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.Components;
+import dagger.hilt.processor.internal.KotlinMetadataUtils;
+import dagger.hilt.processor.internal.ProcessorErrors;
+import dagger.hilt.processor.internal.Processors;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import javax.annotation.processing.Processor;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.SimpleAnnotationValueVisitor8;
+import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
+
+/** Processor that outputs dummy files to propagate information through multiple javac runs. */
+@IncrementalAnnotationProcessor(ISOLATING)
+@AutoService(Processor.class)
+public final class AggregatedDepsProcessor extends BaseProcessor {
+
+ private static final ImmutableSet<ClassName> ENTRY_POINT_ANNOTATIONS =
+ ImmutableSet.of(
+ ClassNames.ENTRY_POINT,
+ ClassNames.GENERATED_ENTRY_POINT,
+ ClassNames.COMPONENT_ENTRY_POINT);
+
+ private static final ImmutableSet<ClassName> MODULE_ANNOTATIONS =
+ ImmutableSet.of(
+ ClassNames.MODULE);
+
+ private static final ImmutableSet<ClassName> INSTALL_IN_ANNOTATIONS =
+ ImmutableSet.of(ClassNames.INSTALL_IN, ClassNames.TEST_INSTALL_IN);
+
+ private final Set<Element> seen = new HashSet<>();
+
+ @Override
+ public Set<String> getSupportedAnnotationTypes() {
+ return ImmutableSet.builder()
+ .addAll(INSTALL_IN_ANNOTATIONS)
+ .addAll(MODULE_ANNOTATIONS)
+ .addAll(ENTRY_POINT_ANNOTATIONS)
+ .build()
+ .stream()
+ .map(Object::toString)
+ .collect(toImmutableSet());
+ }
+
+ @Override
+ public void processEach(TypeElement annotation, Element element) throws Exception {
+ if (!seen.add(element)) {
+ return;
+ }
+
+ Optional<ClassName> installInAnnotation = getAnnotation(element, INSTALL_IN_ANNOTATIONS);
+ Optional<ClassName> entryPointAnnotation = getAnnotation(element, ENTRY_POINT_ANNOTATIONS);
+ Optional<ClassName> moduleAnnotation = getAnnotation(element, MODULE_ANNOTATIONS);
+
+ boolean hasInstallIn = installInAnnotation.isPresent();
+ boolean isEntryPoint = entryPointAnnotation.isPresent();
+ boolean isModule = moduleAnnotation.isPresent();
+
+ ProcessorErrors.checkState(
+ !hasInstallIn || isEntryPoint || isModule,
+ element,
+ "@%s-annotated classes must also be annotated with @Module or @EntryPoint: %s",
+ installInAnnotation.map(ClassName::simpleName).orElse("@InstallIn"),
+ element);
+
+ ProcessorErrors.checkState(
+ !(isEntryPoint && isModule),
+ element,
+ "@%s and @%s cannot be used on the same interface: %s",
+ moduleAnnotation.map(ClassName::simpleName).orElse("@Module"),
+ entryPointAnnotation.map(ClassName::simpleName).orElse("@EntryPoint"),
+ element);
+
+ if (isModule) {
+ processModule(element, installInAnnotation, moduleAnnotation.get());
+ } else if (isEntryPoint) {
+ processEntryPoint(element, installInAnnotation, entryPointAnnotation.get());
+ } else {
+ throw new AssertionError();
+ }
+ }
+
+ private void processModule(
+ Element element, Optional<ClassName> installInAnnotation, ClassName moduleAnnotation)
+ throws Exception {
+ ProcessorErrors.checkState(
+ installInAnnotation.isPresent()
+ || isDaggerGeneratedModule(element)
+ || installInCheckDisabled(element),
+ element,
+ "%s is missing an @InstallIn annotation. If this was intentional, see"
+ + " https://dagger.dev/hilt/compiler-options#disable-install-in-check for how to disable this"
+ + " check.",
+ element);
+
+ if (!installInAnnotation.isPresent()) {
+ // Modules without @InstallIn or @TestInstallIn annotations don't need to be processed further
+ return;
+ }
+
+ ProcessorErrors.checkState(
+ element.getKind() == CLASS || element.getKind() == INTERFACE,
+ element,
+ "Only classes and interfaces can be annotated with @Module: %s",
+ element);
+ TypeElement module = asType(element);
+
+ ProcessorErrors.checkState(
+ Processors.isTopLevel(module)
+ || module.getModifiers().contains(STATIC)
+ || module.getModifiers().contains(ABSTRACT)
+ || Processors.hasAnnotation(module.getEnclosingElement(), ClassNames.HILT_ANDROID_TEST),
+ module,
+ "Nested @%s modules must be static unless they are directly nested within a test. "
+ + "Found: %s",
+ installInAnnotation.get().simpleName(),
+ module);
+
+ // Check that if Dagger needs an instance of the module, Hilt can provide it automatically by
+ // calling a visible empty constructor.
+ ProcessorErrors.checkState(
+ !daggerRequiresModuleInstance(module) || hasVisibleEmptyConstructor(module),
+ module,
+ "Modules that need to be instantiated by Hilt must have a visible, empty constructor.");
+
+ // TODO(b/28989613): This should really be fixed in Dagger. Remove once Dagger bug is fixed.
+ ImmutableList<ExecutableElement> abstractMethodsWithMissingBinds =
+ ElementFilter.methodsIn(module.getEnclosedElements()).stream()
+ .filter(method -> method.getModifiers().contains(ABSTRACT))
+ .filter(method -> !Processors.hasDaggerAbstractMethodAnnotation(method))
+ .collect(toImmutableList());
+ ProcessorErrors.checkState(
+ abstractMethodsWithMissingBinds.isEmpty(),
+ module,
+ "Found unimplemented abstract methods, %s, in an abstract module, %s. "
+ + "Did you forget to add a Dagger binding annotation (e.g. @Binds)?",
+ abstractMethodsWithMissingBinds,
+ module);
+
+ ImmutableList<TypeElement> replacedModules = ImmutableList.of();
+ if (Processors.hasAnnotation(module, ClassNames.TEST_INSTALL_IN)) {
+ Optional<TypeElement> originatingTestElement = getOriginatingTestElement(module);
+ ProcessorErrors.checkState(
+ !originatingTestElement.isPresent(),
+ // TODO(b/152801981): this should really error on the annotation value
+ module,
+ "@TestInstallIn modules cannot be nested in (or originate from) a "
+ + "@HiltAndroidTest-annotated class: %s",
+ originatingTestElement
+ .map(testElement -> testElement.getQualifiedName().toString())
+ .orElse(""));
+
+ AnnotationMirror testInstallIn =
+ Processors.getAnnotationMirror(module, ClassNames.TEST_INSTALL_IN);
+ replacedModules =
+ Processors.getAnnotationClassValues(getElementUtils(), testInstallIn, "replaces");
+
+ ProcessorErrors.checkState(
+ !replacedModules.isEmpty(),
+ // TODO(b/152801981): this should really error on the annotation value
+ module,
+ "@TestInstallIn#replaces() cannot be empty. Use @InstallIn instead.");
+
+ ImmutableList<TypeElement> nonInstallInModules =
+ replacedModules.stream()
+ .filter(
+ replacedModule ->
+ !Processors.hasAnnotation(replacedModule, ClassNames.INSTALL_IN))
+ .collect(toImmutableList());
+
+ ProcessorErrors.checkState(
+ nonInstallInModules.isEmpty(),
+ // TODO(b/152801981): this should really error on the annotation value
+ module,
+ "@TestInstallIn#replaces() can only contain @InstallIn modules, but found: %s",
+ nonInstallInModules);
+
+ ImmutableList<TypeElement> hiltWrapperModules =
+ replacedModules.stream()
+ .filter(
+ replacedModule ->
+ replacedModule.getSimpleName().toString().startsWith("HiltWrapper_"))
+ .collect(toImmutableList());
+
+ ProcessorErrors.checkState(
+ hiltWrapperModules.isEmpty(),
+ // TODO(b/152801981): this should really error on the annotation value
+ module,
+ "@TestInstallIn#replaces() cannot contain Hilt generated public wrapper modules, "
+ + "but found: %s. ",
+ hiltWrapperModules);
+
+ if (!getPackage(module).getQualifiedName().toString().startsWith("dagger.hilt")) {
+ // Prevent external users from overriding Hilt's internal modules. Techincally, except for
+ // ApplicationContextModule, making all modules pkg-private should be enough but this is an
+ // extra measure of precaution.
+ ImmutableList<TypeElement> hiltInternalModules =
+ replacedModules.stream()
+ .filter(
+ replacedModule ->
+ getPackage(replacedModule)
+ .getQualifiedName()
+ .toString()
+ .startsWith("dagger.hilt"))
+ .collect(toImmutableList());
+
+ ProcessorErrors.checkState(
+ hiltInternalModules.isEmpty(),
+ // TODO(b/152801981): this should really error on the annotation value
+ module,
+ "@TestInstallIn#replaces() cannot contain internal Hilt modules, but found: %s. ",
+ hiltInternalModules);
+ }
+
+ // Prevent users from uninstalling test-specific @InstallIn modules.
+ ImmutableList<TypeElement> replacedTestSpecificInstallIn =
+ replacedModules.stream()
+ .filter(replacedModule -> getOriginatingTestElement(replacedModule).isPresent())
+ .collect(toImmutableList());
+
+ ProcessorErrors.checkState(
+ replacedTestSpecificInstallIn.isEmpty(),
+ // TODO(b/152801981): this should really error on the annotation value
+ module,
+ "@TestInstallIn#replaces() cannot replace test specific @InstallIn modules, but found: "
+ + "%s. Please remove the @InstallIn module manually rather than replacing it.",
+ replacedTestSpecificInstallIn);
+ }
+
+ generateAggregatedDeps(
+ "modules",
+ module,
+ moduleAnnotation,
+ replacedModules.stream().map(ClassName::get).collect(toImmutableSet()));
+ }
+
+ private void processEntryPoint(
+ Element element, Optional<ClassName> installInAnnotation, ClassName entryPointAnnotation)
+ throws Exception {
+ ProcessorErrors.checkState(
+ installInAnnotation.isPresent() ,
+ element,
+ "@%s %s must also be annotated with @InstallIn",
+ entryPointAnnotation.simpleName(),
+ element);
+
+ ProcessorErrors.checkState(
+ !Processors.hasAnnotation(element, ClassNames.TEST_INSTALL_IN),
+ element,
+ "@TestInstallIn can only be used with modules");
+
+ ProcessorErrors.checkState(
+ element.getKind() == INTERFACE,
+ element,
+ "Only interfaces can be annotated with @%s: %s",
+ entryPointAnnotation.simpleName(),
+ element);
+ TypeElement entryPoint = asType(element);
+
+ generateAggregatedDeps(
+ entryPointAnnotation.equals(ClassNames.COMPONENT_ENTRY_POINT)
+ ? "componentEntryPoints"
+ : "entryPoints",
+ entryPoint,
+ entryPointAnnotation,
+ ImmutableSet.of());
+ }
+
+ private void generateAggregatedDeps(
+ String key,
+ TypeElement element,
+ ClassName annotation,
+ ImmutableSet<ClassName> replacedModules)
+ throws Exception {
+ // Get @InstallIn components here to catch errors before skipping user's pkg-private element.
+ ImmutableSet<ClassName> components = Components.getComponents(getElementUtils(), element);
+
+ if (isValidKind(element)) {
+ Optional<PkgPrivateMetadata> pkgPrivateMetadata =
+ PkgPrivateMetadata.of(getElementUtils(), element, annotation);
+ if (pkgPrivateMetadata.isPresent()) {
+ if (key.contentEquals("modules")) {
+ new PkgPrivateModuleGenerator(getProcessingEnv(), pkgPrivateMetadata.get()).generate();
+ } else {
+ new PkgPrivateEntryPointGenerator(getProcessingEnv(), pkgPrivateMetadata.get())
+ .generate();
+ }
+ } else {
+ Optional<ClassName> testName = getOriginatingTestElement(element).map(ClassName::get);
+ new AggregatedDepsGenerator(
+ key, element, testName, components, replacedModules, getProcessingEnv())
+ .generate();
+ }
+ }
+ }
+
+ private static Optional<ClassName> getAnnotation(
+ Element element, ImmutableSet<ClassName> annotations) {
+ ImmutableSet<ClassName> usedAnnotations =
+ annotations.stream()
+ .filter(annotation -> Processors.hasAnnotation(element, annotation))
+ .collect(toImmutableSet());
+
+ if (usedAnnotations.isEmpty()) {
+ return Optional.empty();
+ }
+
+ ProcessorErrors.checkState(
+ usedAnnotations.size() == 1,
+ element,
+ "Only one of the following annotations can be used on %s: %s",
+ element,
+ usedAnnotations);
+
+ return Optional.of(getOnlyElement(usedAnnotations));
+ }
+
+ private Optional<TypeElement> getOriginatingTestElement(Element element) {
+ TypeElement topLevelType = getOriginatingTopLevelType(element);
+ return Processors.hasAnnotation(topLevelType, ClassNames.HILT_ANDROID_TEST)
+ ? Optional.of(asType(topLevelType))
+ : Optional.empty();
+ }
+
+ private TypeElement getOriginatingTopLevelType(Element element) {
+ TypeElement topLevelType = Processors.getTopLevelType(element);
+ if (Processors.hasAnnotation(topLevelType, ClassNames.ORIGINATING_ELEMENT)) {
+ return getOriginatingTopLevelType(
+ Processors.getAnnotationClassValue(
+ getElementUtils(),
+ Processors.getAnnotationMirror(topLevelType, ClassNames.ORIGINATING_ELEMENT),
+ "topLevelClass"));
+ }
+ return topLevelType;
+ }
+
+ private static boolean isValidKind(Element element) {
+ // don't go down the rabbit hole of analyzing undefined types. N.B. we don't issue
+ // an error here because javac already has and we don't want to spam the user.
+ return element.asType().getKind() != TypeKind.ERROR;
+ }
+
+ private boolean installInCheckDisabled(Element element) {
+ return DISABLE_MODULES_HAVE_INSTALL_IN_CHECK.get(getProcessingEnv())
+ || Processors.hasAnnotation(element, ClassNames.DISABLE_INSTALL_IN_CHECK);
+ }
+
+ /**
+ * When using Dagger Producers, don't process generated modules. They will not have the expected
+ * annotations.
+ */
+ private static boolean isDaggerGeneratedModule(Element element) {
+ if (!Processors.hasAnnotation(element, ClassNames.MODULE)) {
+ return false;
+ }
+ return element.getAnnotationMirrors().stream()
+ .filter(mirror -> isGenerated(mirror))
+ .map(mirror -> asString(getOnlyElement(asList(getAnnotationValue(mirror, "value")))))
+ .anyMatch(value -> value.startsWith("dagger"));
+ }
+
+ private static List<? extends AnnotationValue> asList(AnnotationValue value) {
+ return value.accept(
+ new SimpleAnnotationValueVisitor8<List<? extends AnnotationValue>, Void>() {
+ @Override
+ public List<? extends AnnotationValue> visitArray(
+ List<? extends AnnotationValue> value, Void unused) {
+ return value;
+ }
+ },
+ null);
+ }
+
+ private static String asString(AnnotationValue value) {
+ return value.accept(
+ new SimpleAnnotationValueVisitor8<String, Void>() {
+ @Override
+ public String visitString(String value, Void unused) {
+ return value;
+ }
+ },
+ null);
+ }
+
+ private static boolean isGenerated(AnnotationMirror annotationMirror) {
+ Name name = asType(annotationMirror.getAnnotationType().asElement()).getQualifiedName();
+ return name.contentEquals("javax.annotation.Generated")
+ || name.contentEquals("javax.annotation.processing.Generated");
+ }
+
+ private static boolean daggerRequiresModuleInstance(TypeElement module) {
+ return !module.getModifiers().contains(ABSTRACT)
+ && !hasOnlyStaticProvides(module)
+ // Skip ApplicationContextModule, since Hilt manages this module internally.
+ && !ClassNames.APPLICATION_CONTEXT_MODULE.equals(ClassName.get(module))
+ // Skip Kotlin object modules since all their provision methods are static
+ && !isKotlinObject(module);
+ }
+
+ private static boolean isKotlinObject(TypeElement type) {
+ KotlinMetadataUtil metadataUtil = KotlinMetadataUtils.getMetadataUtil();
+ return metadataUtil.isObjectClass(type) || metadataUtil.isCompanionObjectClass(type);
+ }
+
+ private static boolean hasOnlyStaticProvides(TypeElement module) {
+ // TODO(erichang): Check for @Produces too when we have a producers story
+ return ElementFilter.methodsIn(module.getEnclosedElements()).stream()
+ .filter(method -> Processors.hasAnnotation(method, ClassNames.PROVIDES))
+ .allMatch(method -> method.getModifiers().contains(STATIC));
+ }
+
+ private static boolean hasVisibleEmptyConstructor(TypeElement type) {
+ List<ExecutableElement> constructors = ElementFilter.constructorsIn(type.getEnclosedElements());
+ return constructors.isEmpty()
+ || constructors.stream()
+ .filter(constructor -> constructor.getParameters().isEmpty())
+ .anyMatch(
+ constructor ->
+ !constructor.getModifiers().contains(PRIVATE)
+ );
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/BUILD b/java/dagger/hilt/processor/internal/aggregateddeps/BUILD
new file mode 100644
index 000000000..ebbc941bc
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/aggregateddeps/BUILD
@@ -0,0 +1,88 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# A processor that aggregates metadata about Hilt @InstallIn annotations
+
+package(default_visibility = ["//:src"])
+
+# TODO(bcorso): Remove all AggregatedDeps usage from the processor class path.
+java_library(
+ name = "annotation",
+ srcs = ["AggregatedDeps.java"],
+ exports = [
+ "//java/dagger/hilt/codegen:originating_element",
+ ],
+)
+
+java_plugin(
+ name = "plugin",
+ generates_api = 1,
+ processor_class = "dagger.hilt.processor.internal.aggregateddeps.AggregatedDepsProcessor",
+ deps = [":processor_lib"],
+)
+
+java_library(
+ name = "processor_lib",
+ srcs = [
+ "AggregatedDepsGenerator.java",
+ "AggregatedDepsProcessor.java",
+ "PkgPrivateEntryPointGenerator.java",
+ "PkgPrivateMetadata.java",
+ "PkgPrivateModuleGenerator.java",
+ ],
+ deps = [
+ "//:dagger_with_compiler",
+ "//java/dagger/hilt/android/processor/internal/androidentrypoint:compiler_options",
+ "//java/dagger/hilt/processor/internal:base_processor",
+ "//java/dagger/hilt/processor/internal:classnames",
+ "//java/dagger/hilt/processor/internal:components",
+ "//java/dagger/hilt/processor/internal:kotlin",
+ "//java/dagger/hilt/processor/internal:processor_errors",
+ "//java/dagger/hilt/processor/internal:processors",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/codegen/kotlin",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/auto:service",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/incap",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@google_bazel_common//third_party/java/jsr250_annotations",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
+
+java_library(
+ name = "component_dependencies",
+ srcs = [
+ "ComponentDependencies.java",
+ ],
+ deps = [
+ ":processor_lib",
+ "//java/dagger/hilt/processor/internal:classnames",
+ "//java/dagger/hilt/processor/internal:component_descriptor",
+ "//java/dagger/hilt/processor/internal:processor_errors",
+ "//java/dagger/hilt/processor/internal:processors",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/javapoet",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/ComponentDependencies.java b/java/dagger/hilt/processor/internal/aggregateddeps/ComponentDependencies.java
new file mode 100644
index 000000000..23ed148c0
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/aggregateddeps/ComponentDependencies.java
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.aggregateddeps;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.hilt.processor.internal.aggregateddeps.AggregatedDepsGenerator.AGGREGATING_PACKAGE;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.SetMultimap;
+import com.squareup.javapoet.ClassName;
+import dagger.hilt.processor.internal.AnnotationValues;
+import dagger.hilt.processor.internal.BadInputException;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.ComponentDescriptor;
+import dagger.hilt.processor.internal.ProcessorErrors;
+import dagger.hilt.processor.internal.Processors;
+import dagger.hilt.processor.internal.aggregateddeps.ComponentDependencies.AggregatedDepMetadata.DependencyType;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
+
+/** Represents information needed to create a component (i.e. modules, entry points, etc) */
+@AutoValue
+public abstract class ComponentDependencies {
+ private static Builder builder() {
+ return new AutoValue_ComponentDependencies.Builder();
+ }
+
+ /** Returns the modules for a component, without any filtering. */
+ public abstract Dependencies modules();
+
+ /** Returns the entry points associated with the given a component. */
+ public abstract Dependencies entryPoints();
+
+ /** Returns the component entry point associated with the given a component. */
+ public abstract Dependencies componentEntryPoints();
+
+ @AutoValue.Builder
+ abstract static class Builder {
+ abstract Dependencies.Builder modulesBuilder();
+
+ abstract Dependencies.Builder entryPointsBuilder();
+
+ abstract Dependencies.Builder componentEntryPointsBuilder();
+
+ abstract ComponentDependencies autoBuild();
+
+ ComponentDependencies build(Elements elements) {
+ validateModules(modulesBuilder().build(), elements);
+ return autoBuild();
+ }
+ }
+
+ /** A key used for grouping a test dependency by both its component and test name. */
+ @AutoValue
+ abstract static class TestDepKey {
+ static TestDepKey of(ClassName component, ClassName test) {
+ return new AutoValue_ComponentDependencies_TestDepKey(component, test);
+ }
+
+ /** Returns the name of the component this dependency should be installed in. */
+ abstract ClassName component();
+
+ /** Returns the name of the test that this dependency should be installed in. */
+ abstract ClassName test();
+ }
+
+ /**
+ * Holds a set of component dependencies, e.g. modules or entry points.
+ *
+ * <p>This class handles separating dependencies into global and test dependencies. Global
+ * dependencies are installed with every test, where test dependencies are only installed with the
+ * specified test. The total set of dependencies includes all global + test dependencies.
+ */
+ @AutoValue
+ public abstract static class Dependencies {
+ static Builder builder() {
+ return new AutoValue_ComponentDependencies_Dependencies.Builder();
+ }
+
+ /** Returns the global deps keyed by component. */
+ abstract ImmutableSetMultimap<ClassName, TypeElement> globalDeps();
+
+ /** Returns the global test deps keyed by component. */
+ abstract ImmutableSetMultimap<ClassName, TypeElement> globalTestDeps();
+
+ /** Returns the test deps keyed by component and test. */
+ abstract ImmutableSetMultimap<TestDepKey, TypeElement> testDeps();
+
+ /** Returns the uninstalled test deps keyed by test. */
+ abstract ImmutableSetMultimap<ClassName, TypeElement> uninstalledTestDeps();
+
+ /** Returns the global uninstalled test deps. */
+ abstract ImmutableSet<TypeElement> globalUninstalledTestDeps();
+
+ /** Returns the dependencies to be installed in the given component for the given root. */
+ public ImmutableSet<TypeElement> get(ClassName component, ClassName root, boolean isTestRoot) {
+ if (!isTestRoot) {
+ return globalDeps().get(component);
+ }
+
+ ImmutableSet<TypeElement> uninstalledTestDepsForRoot = uninstalledTestDeps().get(root);
+ return ImmutableSet.<TypeElement>builder()
+ .addAll(
+ globalDeps().get(component).stream()
+ .filter(dep -> !uninstalledTestDepsForRoot.contains(dep))
+ .filter(dep -> !globalUninstalledTestDeps().contains(dep))
+ .collect(toImmutableSet()))
+ .addAll(globalTestDeps().get(component))
+ .addAll(testDeps().get(TestDepKey.of(component, root)))
+ .build();
+ }
+
+ @AutoValue.Builder
+ abstract static class Builder {
+ abstract ImmutableSetMultimap.Builder<ClassName, TypeElement> globalDepsBuilder();
+
+ abstract ImmutableSetMultimap.Builder<ClassName, TypeElement> globalTestDepsBuilder();
+
+ abstract ImmutableSetMultimap.Builder<TestDepKey, TypeElement> testDepsBuilder();
+
+ abstract ImmutableSetMultimap.Builder<ClassName, TypeElement> uninstalledTestDepsBuilder();
+
+ abstract ImmutableSet.Builder<TypeElement> globalUninstalledTestDepsBuilder();
+
+ abstract Dependencies build();
+ }
+ }
+
+ /**
+ * Pulls the component dependencies from the {@code packageName}.
+ *
+ * <p>Dependency files are generated by the {@link AggregatedDepsProcessor}, and have the form:
+ *
+ * <pre>{@code
+ * {@literal @}AggregatedDeps(
+ * components = {
+ * "foo.FooComponent",
+ * "bar.BarComponent"
+ * },
+ * modules = "baz.BazModule"
+ * )
+ *
+ * }</pre>
+ */
+ public static ComponentDependencies from(
+ ImmutableSet<ComponentDescriptor> descriptors, Elements elements) {
+ Map<String, ComponentDescriptor> descriptorLookup = descriptorLookupMap(descriptors);
+ ImmutableList<AggregatedDepMetadata> metadatas =
+ getAggregatedDeps(elements).stream()
+ .map(deps -> AggregatedDepMetadata.create(deps, descriptorLookup, elements))
+ .collect(toImmutableList());
+
+ ComponentDependencies.Builder componentDependencies = ComponentDependencies.builder();
+ for (AggregatedDepMetadata metadata : metadatas) {
+ Dependencies.Builder builder = null;
+ switch (metadata.dependencyType()) {
+ case MODULE:
+ builder = componentDependencies.modulesBuilder();
+ break;
+ case ENTRY_POINT:
+ builder = componentDependencies.entryPointsBuilder();
+ break;
+ case COMPONENT_ENTRY_POINT:
+ builder = componentDependencies.componentEntryPointsBuilder();
+ break;
+ }
+
+ for (ComponentDescriptor componentDescriptor : metadata.componentDescriptors()) {
+ ClassName component = componentDescriptor.component();
+ if (metadata.testElement().isPresent()) {
+ // In this case the @InstallIn or @TestInstallIn applies to only the given test root.
+ ClassName test = ClassName.get(metadata.testElement().get());
+ builder.testDepsBuilder().put(TestDepKey.of(component, test), metadata.dependency());
+ builder.uninstalledTestDepsBuilder().putAll(test, metadata.replacedDependencies());
+ } else {
+ // In this case the @InstallIn or @TestInstallIn applies to all roots
+ if (!metadata.replacedDependencies().isEmpty()) {
+ // If there are replacedDependencies() it means this is a @TestInstallIn
+ builder.globalTestDepsBuilder().put(component, metadata.dependency());
+ builder.globalUninstalledTestDepsBuilder().addAll(metadata.replacedDependencies());
+ } else {
+ builder.globalDepsBuilder().put(component, metadata.dependency());
+ }
+ }
+ }
+ }
+
+ // Collect all @UninstallModules.
+ // TODO(b/176438516): Filter @UninstallModules at the root.
+ metadatas.stream()
+ .filter(metadata -> metadata.testElement().isPresent())
+ .map(metadata -> metadata.testElement().get())
+ .distinct()
+ .filter(testElement -> Processors.hasAnnotation(testElement, ClassNames.IGNORE_MODULES))
+ .forEach(
+ testElement ->
+ componentDependencies
+ .modulesBuilder()
+ .uninstalledTestDepsBuilder()
+ .putAll(
+ ClassName.get(testElement), getUninstalledModules(testElement, elements)));
+
+ return componentDependencies.build(elements);
+ }
+
+ private static ImmutableMap<String, ComponentDescriptor> descriptorLookupMap(
+ ImmutableSet<ComponentDescriptor> descriptors) {
+ ImmutableMap.Builder<String, ComponentDescriptor> builder = ImmutableMap.builder();
+ for (ComponentDescriptor descriptor : descriptors) {
+ // This is a temporary hack to map the old ApplicationComponent to the new SingletonComponent.
+ // Technically, this is only needed for backwards compatibility with libraries using the old
+ // processor since new processors should convert to the new SingletonComponent when generating
+ // the metadata class.
+ if (descriptor.component().equals(ClassNames.SINGLETON_COMPONENT)) {
+ builder.put("dagger.hilt.android.components.ApplicationComponent", descriptor);
+ }
+ builder.put(descriptor.component().toString(), descriptor);
+ }
+ return builder.build();
+ }
+
+ // Validate that the @UninstallModules doesn't contain any test modules.
+ private static Dependencies validateModules(Dependencies moduleDeps, Elements elements) {
+ SetMultimap<ClassName, TypeElement> invalidTestModules = HashMultimap.create();
+ moduleDeps.testDeps().entries().stream()
+ .filter(
+ e -> moduleDeps.uninstalledTestDeps().containsEntry(e.getKey().test(), e.getValue()))
+ .forEach(e -> invalidTestModules.put(e.getKey().test(), e.getValue()));
+
+ // Currently we don't have a good way to throw an error for all tests, so we sort (to keep the
+ // error reporting order stable) and then choose the first test.
+ // TODO(bcorso): Consider using ProcessorErrorHandler directly to report all errors at once?
+ Optional<ClassName> invalidTest =
+ invalidTestModules.keySet().stream()
+ .min((test1, test2) -> test1.toString().compareTo(test2.toString()));
+ if (invalidTest.isPresent()) {
+ throw new BadInputException(
+ String.format(
+ "@UninstallModules on test, %s, should not containing test modules, "
+ + "but found: %s",
+ invalidTest.get(),
+ invalidTestModules.get(invalidTest.get()).stream()
+ // Sort modules to keep stable error messages.
+ .sorted((test1, test2) -> test1.toString().compareTo(test2.toString()))
+ .collect(toImmutableList())),
+ elements.getTypeElement(invalidTest.get().toString()));
+ }
+ return moduleDeps;
+ }
+
+ private static ImmutableSet<TypeElement> getUninstalledModules(
+ TypeElement testElement, Elements elements) {
+ ImmutableList<TypeElement> userUninstallModules =
+ Processors.getAnnotationClassValues(
+ elements,
+ Processors.getAnnotationMirror(testElement, ClassNames.IGNORE_MODULES),
+ "value");
+
+ // For pkg-private modules, find the generated wrapper class and uninstall that instead.
+ return userUninstallModules.stream()
+ .map(uninstallModule -> getPublicDependency(uninstallModule, elements))
+ .collect(toImmutableSet());
+ }
+
+ /** Returns the public Hilt wrapper module, or the module itself if its already public. */
+ private static TypeElement getPublicDependency(TypeElement dependency, Elements elements) {
+ return PkgPrivateMetadata.of(elements, dependency, ClassNames.MODULE)
+ .map(metadata -> elements.getTypeElement(metadata.generatedClassName().toString()))
+ .orElse(dependency);
+ }
+
+ /** Returns the top-level elements of the aggregated deps package. */
+ private static ImmutableList<AnnotationMirror> getAggregatedDeps(Elements elements) {
+ PackageElement packageElement = elements.getPackageElement(AGGREGATING_PACKAGE);
+ checkState(
+ packageElement != null,
+ "Couldn't find package %s. Did you mark your @Module classes with @InstallIn annotations?",
+ AGGREGATING_PACKAGE);
+
+ List<? extends Element> aggregatedDepsElements = packageElement.getEnclosedElements();
+ checkState(
+ !aggregatedDepsElements.isEmpty(),
+ "No dependencies found. Did you mark your @Module classes with @InstallIn annotations?");
+
+ ImmutableList.Builder<AnnotationMirror> builder = ImmutableList.builder();
+ for (Element element : aggregatedDepsElements) {
+ ProcessorErrors.checkState(
+ element.getKind() == ElementKind.CLASS,
+ element,
+ "Only classes may be in package %s. Did you add custom code in the package?",
+ AGGREGATING_PACKAGE);
+
+ AnnotationMirror aggregatedDeps =
+ Processors.getAnnotationMirror(element, ClassNames.AGGREGATED_DEPS);
+ ProcessorErrors.checkState(
+ aggregatedDeps != null,
+ element,
+ "Classes in package %s must be annotated with @AggregatedDeps: %s. Found: %s.",
+ AGGREGATING_PACKAGE,
+ element.getSimpleName(),
+ element.getAnnotationMirrors());
+
+ builder.add(aggregatedDeps);
+ }
+ return builder.build();
+ }
+
+ @AutoValue
+ abstract static class AggregatedDepMetadata {
+ static AggregatedDepMetadata create(
+ AnnotationMirror aggregatedDeps,
+ Map<String, ComponentDescriptor> descriptorLookup,
+ Elements elements) {
+ ImmutableMap<String, AnnotationValue> aggregatedDepsValues =
+ Processors.getAnnotationValues(elements, aggregatedDeps);
+
+ return new AutoValue_ComponentDependencies_AggregatedDepMetadata(
+ getTestElement(aggregatedDepsValues.get("test"), elements),
+ getComponents(aggregatedDepsValues.get("components"), descriptorLookup),
+ getDependencyType(
+ aggregatedDepsValues.get("modules"),
+ aggregatedDepsValues.get("entryPoints"),
+ aggregatedDepsValues.get("componentEntryPoints")),
+ getDependency(
+ aggregatedDepsValues.get("modules"),
+ aggregatedDepsValues.get("entryPoints"),
+ aggregatedDepsValues.get("componentEntryPoints"),
+ elements),
+ getReplacedDependencies(aggregatedDepsValues.get("replaces"), elements));
+ }
+
+ enum DependencyType {
+ MODULE,
+ ENTRY_POINT,
+ COMPONENT_ENTRY_POINT
+ }
+
+ abstract Optional<TypeElement> testElement();
+
+ abstract ImmutableList<ComponentDescriptor> componentDescriptors();
+
+ abstract DependencyType dependencyType();
+
+ abstract TypeElement dependency();
+
+ abstract ImmutableSet<TypeElement> replacedDependencies();
+
+ private static Optional<TypeElement> getTestElement(
+ AnnotationValue testValue, Elements elements) {
+ checkNotNull(testValue);
+ String test = AnnotationValues.getString(testValue);
+ return test.isEmpty() ? Optional.empty() : Optional.of(elements.getTypeElement(test));
+ }
+
+ private static ImmutableList<ComponentDescriptor> getComponents(
+ AnnotationValue componentsValue, Map<String, ComponentDescriptor> descriptorLookup) {
+ checkNotNull(componentsValue);
+ ImmutableList<String> componentNames =
+ AnnotationValues.getAnnotationValues(componentsValue).stream()
+ .map(AnnotationValues::getString)
+ .collect(toImmutableList());
+
+ checkState(!componentNames.isEmpty());
+ ImmutableList.Builder<ComponentDescriptor> components = ImmutableList.builder();
+ for (String componentName : componentNames) {
+ checkState(
+ descriptorLookup.containsKey(componentName),
+ "%s is not a valid Component. Did you add or remove code in package %s?",
+ componentName,
+ AGGREGATING_PACKAGE);
+ components.add(descriptorLookup.get(componentName));
+ }
+ return components.build();
+ }
+
+ private static DependencyType getDependencyType(
+ AnnotationValue modulesValue,
+ AnnotationValue entryPointsValue,
+ AnnotationValue componentEntryPointsValue) {
+ checkNotNull(modulesValue);
+ checkNotNull(entryPointsValue);
+ checkNotNull(componentEntryPointsValue);
+
+ ImmutableSet.Builder<DependencyType> dependencyTypes = ImmutableSet.builder();
+ if (!AnnotationValues.getAnnotationValues(modulesValue).isEmpty()) {
+ dependencyTypes.add(DependencyType.MODULE);
+ }
+ if (!AnnotationValues.getAnnotationValues(entryPointsValue).isEmpty()) {
+ dependencyTypes.add(DependencyType.ENTRY_POINT);
+ }
+ if (!AnnotationValues.getAnnotationValues(componentEntryPointsValue).isEmpty()) {
+ dependencyTypes.add(DependencyType.COMPONENT_ENTRY_POINT);
+ }
+ return getOnlyElement(dependencyTypes.build());
+ }
+
+ private static TypeElement getDependency(
+ AnnotationValue modulesValue,
+ AnnotationValue entryPointsValue,
+ AnnotationValue componentEntryPointsValue,
+ Elements elements) {
+ checkNotNull(modulesValue);
+ checkNotNull(entryPointsValue);
+ checkNotNull(componentEntryPointsValue);
+
+ return elements.getTypeElement(
+ AnnotationValues.getString(
+ getOnlyElement(
+ ImmutableList.<AnnotationValue>builder()
+ .addAll(AnnotationValues.getAnnotationValues(modulesValue))
+ .addAll(AnnotationValues.getAnnotationValues(entryPointsValue))
+ .addAll(AnnotationValues.getAnnotationValues(componentEntryPointsValue))
+ .build())));
+ }
+
+ private static ImmutableSet<TypeElement> getReplacedDependencies(
+ AnnotationValue replacedDependenciesValue, Elements elements) {
+ // Allow null values to support libraries using a Hilt version before @TestInstallIn was added
+ return replacedDependenciesValue == null
+ ? ImmutableSet.of()
+ : AnnotationValues.getAnnotationValues(replacedDependenciesValue).stream()
+ .map(AnnotationValues::getString)
+ .map(elements::getTypeElement)
+ .map(replacedDep -> getPublicDependency(replacedDep, elements))
+ .collect(toImmutableSet());
+ }
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateEntryPointGenerator.java b/java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateEntryPointGenerator.java
new file mode 100644
index 000000000..6dac59fb7
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateEntryPointGenerator.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.aggregateddeps;
+
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.TypeSpec;
+import dagger.hilt.processor.internal.Processors;
+import java.io.IOException;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Modifier;
+
+/**
+ * Generates a public Dagger entrypoint that includes a user's pkg-private entrypoint. This allows a
+ * user's entrypoint to use pkg-private visibility to hide from external packages.
+ */
+final class PkgPrivateEntryPointGenerator {
+ private final ProcessingEnvironment env;
+ private final PkgPrivateMetadata metadata;
+
+ PkgPrivateEntryPointGenerator(ProcessingEnvironment env, PkgPrivateMetadata metadata) {
+ this.env = env;
+ this.metadata = metadata;
+ }
+
+ // This method creates the following generated code for an EntryPoint in pkg.MyEntryPoint that is
+ // package
+ // private
+ //
+ // package pkg; //same package
+ //
+ // import dagger.hilt.InstallIn;
+ // import dagger.hilt.EntryPoint;;
+ // import javax.annotation.Generated;
+ //
+ // @Generated("dagger.hilt.processor.internal.aggregateddeps.PkgPrivateEntryPointGenerator")
+ // @InstallIn(InstallIn.Component.ACTIVITY)
+ // @EntryPoint
+ // public final class HiltWrapper_MyEntryPoint extends MyEntryPoint {
+ // }
+ void generate() throws IOException {
+
+ TypeSpec.Builder entryPointInterfaceBuilder =
+ TypeSpec.interfaceBuilder(metadata.generatedClassName().simpleName())
+ .addOriginatingElement(metadata.getTypeElement())
+ .addAnnotation(Processors.getOriginatingElementAnnotation(metadata.getTypeElement()))
+ .addModifiers(Modifier.PUBLIC)
+ .addSuperinterface(metadata.baseClassName())
+ .addAnnotation(metadata.getAnnotation());
+
+ Processors.addGeneratedAnnotation(entryPointInterfaceBuilder, env, getClass());
+
+ if (metadata.getOptionalInstallInAnnotationMirror().isPresent()) {
+ entryPointInterfaceBuilder.addAnnotation(
+ AnnotationSpec.get(metadata.getOptionalInstallInAnnotationMirror().get()));
+ }
+
+ JavaFile.builder(
+ metadata.generatedClassName().packageName(), entryPointInterfaceBuilder.build())
+ .build()
+ .writeTo(env.getFiler());
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateMetadata.java b/java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateMetadata.java
new file mode 100644
index 000000000..66f175151
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateMetadata.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.aggregateddeps;
+
+import static com.google.auto.common.Visibility.effectiveVisibilityOfElement;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.Visibility;
+import com.google.auto.value.AutoValue;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.TypeName;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.Processors;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
+
+/** PkgPrivateModuleMetadata contains a set of utilities for processing package private modules. */
+@AutoValue
+abstract class PkgPrivateMetadata {
+ private static final String PREFIX = "HiltWrapper_";
+
+ /** Returns the base class name of the elemenet. */
+ TypeName baseClassName() {
+ return TypeName.get(getTypeElement().asType());
+ }
+
+ /** Returns TypeElement for the module element the metadata object represents */
+ abstract TypeElement getTypeElement();
+
+ /**
+ * Returns an optional @InstallIn AnnotationMirror for the module element the metadata object
+ * represents
+ */
+ abstract Optional<AnnotationMirror> getOptionalInstallInAnnotationMirror();
+
+ /** Return the Type of this package private element. */
+ abstract ClassName getAnnotation();
+
+ /** Returns the expected genenerated classname for the element the metadata object represents */
+ final ClassName generatedClassName() {
+ return Processors.prepend(
+ Processors.getEnclosedClassName(ClassName.get(getTypeElement())), PREFIX);
+ }
+
+ /**
+ * Returns an Optional PkgPrivateMetadata requiring Hilt processing, otherwise returns an empty
+ * Optional.
+ */
+ static Optional<PkgPrivateMetadata> of(Elements elements, Element element, ClassName annotation) {
+ // If this is a public element no wrapping is needed
+ if (effectiveVisibilityOfElement(element) == Visibility.PUBLIC) {
+ return Optional.empty();
+ }
+
+ Optional<AnnotationMirror> installIn;
+ if (Processors.hasAnnotation(element, ClassNames.INSTALL_IN)) {
+ installIn = Optional.of(Processors.getAnnotationMirror(element, ClassNames.INSTALL_IN));
+ } else if (Processors.hasAnnotation(element, ClassNames.TEST_INSTALL_IN)) {
+ installIn = Optional.of(Processors.getAnnotationMirror(element, ClassNames.TEST_INSTALL_IN));
+ } else {
+ throw new IllegalStateException(
+ "Expected element to be annotated with @InstallIn: " + element);
+ }
+
+ if (annotation.equals(ClassNames.MODULE)
+ ) {
+ // Skip modules that require a module instance. Required by
+ // dagger (b/31489617)
+ if (Processors.requiresModuleInstance(elements, MoreElements.asType(element))) {
+ return Optional.empty();
+ }
+ }
+ return Optional.of(
+ new AutoValue_PkgPrivateMetadata(MoreElements.asType(element), installIn, annotation));
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateModuleGenerator.java b/java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateModuleGenerator.java
new file mode 100644
index 000000000..18ff0bd2d
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateModuleGenerator.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.aggregateddeps;
+
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.TypeSpec;
+import dagger.hilt.processor.internal.Processors;
+import java.io.IOException;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Modifier;
+
+/**
+ * Generates a public Dagger module that includes a user's pkg-private module. This allows a user's
+ * module to use pkg-private visibility to hide from external packages, but still allows Hilt to
+ * install the module when the component is created in another package.
+ */
+final class PkgPrivateModuleGenerator {
+ private final ProcessingEnvironment env;
+ private final PkgPrivateMetadata metadata;
+
+ PkgPrivateModuleGenerator(ProcessingEnvironment env, PkgPrivateMetadata metadata) {
+ this.env = env;
+ this.metadata = metadata;
+ }
+
+ // This method creates the following generated code for a pkg-private module, pkg.MyModule:
+ //
+ // package pkg; //same as module
+ //
+ // import dagger.Module;
+ // import dagger.hilt.InstallIn;
+ // import javax.annotation.Generated;
+ //
+ // @Generated("dagger.hilt.processor.internal.aggregateddeps.PkgPrivateModuleGenerator")
+ // @InstallIn(ActivityComponent.class)
+ // @Module(includes = MyModule.class)
+ // public final class HiltModuleWrapper_MyModule {}
+ void generate() throws IOException {
+ TypeSpec.Builder builder =
+ TypeSpec.classBuilder(metadata.generatedClassName().simpleName())
+ .addOriginatingElement(metadata.getTypeElement())
+ .addAnnotation(Processors.getOriginatingElementAnnotation(metadata.getTypeElement()))
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ // generated @InstallIn is exactly the same as the module being processed
+ .addAnnotation(
+ AnnotationSpec.get(metadata.getOptionalInstallInAnnotationMirror().get()))
+ .addAnnotation(
+ AnnotationSpec.builder(metadata.getAnnotation())
+ .addMember("includes", "$T.class", metadata.getTypeElement())
+ .build());
+
+ Processors.addGeneratedAnnotation(builder, env, getClass());
+
+ JavaFile.builder(metadata.generatedClassName().packageName(), builder.build())
+ .build()
+ .writeTo(env.getFiler());
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/aliasof/AliasOfProcessor.java b/java/dagger/hilt/processor/internal/aliasof/AliasOfProcessor.java
new file mode 100644
index 000000000..6efd6431e
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/aliasof/AliasOfProcessor.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.aliasof;
+
+import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.ImmutableSet;
+import dagger.hilt.processor.internal.BaseProcessor;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.ProcessorErrors;
+import dagger.hilt.processor.internal.Processors;
+import java.util.Set;
+import javax.annotation.processing.Processor;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
+
+/** Processes the annotations annotated with {@link dagger.hilt.migration.AliasOf} */
+@IncrementalAnnotationProcessor(ISOLATING)
+@AutoService(Processor.class)
+public final class AliasOfProcessor extends BaseProcessor {
+ @Override
+ public Set<String> getSupportedAnnotationTypes() {
+ return ImmutableSet.of(ClassNames.ALIAS_OF.toString());
+ }
+
+ @Override
+ public void processEach(TypeElement annotation, Element element) throws Exception {
+ ProcessorErrors.checkState(
+ Processors.hasAnnotation(element, ClassNames.SCOPE),
+ element,
+ "%s should only be used on scopes." + " However, it was found annotating %s",
+ annotation,
+ element);
+ AnnotationMirror annotationMirror =
+ Processors.getAnnotationMirror(element, ClassNames.ALIAS_OF);
+
+ Element defineComponentScope =
+ Processors.getAnnotationClassValue(getElementUtils(), annotationMirror, "value");
+
+ new AliasOfPropagatedDataGenerator(getProcessingEnv(), element, defineComponentScope)
+ .generate();
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/aliasof/AliasOfPropagatedDataGenerator.java b/java/dagger/hilt/processor/internal/aliasof/AliasOfPropagatedDataGenerator.java
new file mode 100644
index 000000000..75c4c15ac
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/aliasof/AliasOfPropagatedDataGenerator.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.aliasof;
+
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.TypeSpec;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.Processors;
+import java.io.IOException;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Element;
+
+/** Generates resource files for {@link dagger.hilt.migration.AliasOf}. */
+final class AliasOfPropagatedDataGenerator {
+
+ private final ProcessingEnvironment processingEnv;
+ private final Element aliasScope;
+ private final Element defineComponentScope;
+
+ AliasOfPropagatedDataGenerator(
+ ProcessingEnvironment processingEnv, Element aliasScope, Element defineComponentScope) {
+ this.processingEnv = processingEnv;
+ this.aliasScope = aliasScope;
+ this.defineComponentScope = defineComponentScope;
+ }
+
+ void generate() throws IOException {
+ TypeSpec.Builder generator =
+ TypeSpec.classBuilder(Processors.getFullEnclosedName(aliasScope))
+ .addOriginatingElement(aliasScope)
+ .addAnnotation(
+ AnnotationSpec.builder(ClassNames.ALIAS_OF_PROPAGATED_DATA)
+ .addMember("defineComponentScope", "$T.class", defineComponentScope)
+ .addMember("alias", "$T.class", aliasScope)
+ .build())
+ .addJavadoc("Generated class for aggregating scope aliases. \n");
+
+ Processors.addGeneratedAnnotation(generator, processingEnv, getClass());
+
+ JavaFile.builder(AliasOfs.AGGREGATING_PACKAGE, generator.build())
+ .build()
+ .writeTo(processingEnv.getFiler());
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/aliasof/AliasOfs.java b/java/dagger/hilt/processor/internal/aliasof/AliasOfs.java
new file mode 100644
index 000000000..930a72e27
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/aliasof/AliasOfs.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.aliasof;
+
+import static com.google.common.base.Suppliers.memoize;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.ProcessorErrors;
+import dagger.hilt.processor.internal.Processors;
+import java.util.List;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
+
+/**
+ * Extracts a multimap of aliases annotated with {@link dagger.hilt.migration.AliasOf} mapping them
+ * to scopes they are alias of.
+ */
+public final class AliasOfs {
+ static final String AGGREGATING_PACKAGE = AliasOfs.class.getPackage().getName() + ".codegen";
+
+ private final ProcessingEnvironment processingEnvironment;
+ private final ImmutableSet<ClassName> defineComponentScopes;
+ private final Supplier<ImmutableMultimap<ClassName, ClassName>> aliases =
+ memoize(() -> getAliases());
+
+ public AliasOfs(
+ ProcessingEnvironment processingEnvironment, ImmutableSet<ClassName> defineComponentScopes) {
+ this.defineComponentScopes = defineComponentScopes;
+ this.processingEnvironment = processingEnvironment;
+ }
+
+ public ImmutableSet<ClassName> getAliasesFor(ClassName defineComponentScope) {
+ return ImmutableSet.copyOf(aliases.get().get(defineComponentScope));
+ }
+
+ private ImmutableMultimap<ClassName, ClassName> getAliases() {
+ Elements elements = processingEnvironment.getElementUtils();
+ PackageElement packageElement = elements.getPackageElement(AGGREGATING_PACKAGE);
+ if (packageElement == null) {
+ return ImmutableMultimap.of();
+ }
+ List<? extends Element> scopeAliasElements = packageElement.getEnclosedElements();
+ Preconditions.checkState(
+ !scopeAliasElements.isEmpty(), "No scope aliases Found in package %s.", packageElement);
+
+ ImmutableMultimap.Builder<ClassName, ClassName> builder = ImmutableMultimap.builder();
+ for (Element element : scopeAliasElements) {
+ ProcessorErrors.checkState(
+ element.getKind() == ElementKind.CLASS,
+ element,
+ "Only classes may be in package %s. Did you add custom code in the package?",
+ packageElement);
+
+ AnnotationMirror annotationMirror =
+ Processors.getAnnotationMirror(element, ClassNames.ALIAS_OF_PROPAGATED_DATA);
+
+ ProcessorErrors.checkState(
+ annotationMirror != null,
+ element,
+ "Classes in package %s must be annotated with @%s: %s."
+ + " Found: %s. Files in this package are generated, did you add custom code in the"
+ + " package? ",
+ packageElement,
+ ClassNames.ALIAS_OF_PROPAGATED_DATA,
+ element.getSimpleName(),
+ element.getAnnotationMirrors());
+
+ TypeElement defineComponentScope =
+ Processors.getAnnotationClassValue(elements, annotationMirror, "defineComponentScope");
+ TypeElement alias = Processors.getAnnotationClassValue(elements, annotationMirror, "alias");
+
+ Preconditions.checkState(
+ defineComponentScopes.contains(ClassName.get(defineComponentScope)),
+ "The scope %s cannot be an alias for %s. You can only have aliases of a scope defined"
+ + " directly on a @DefineComponent type.",
+ ClassName.get(alias),
+ ClassName.get(defineComponentScope));
+
+ builder.put(ClassName.get(defineComponentScope), ClassName.get(alias));
+ }
+
+ return builder.build();
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/aliasof/BUILD b/java/dagger/hilt/processor/internal/aliasof/BUILD
new file mode 100644
index 000000000..d3f90f2fa
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/aliasof/BUILD
@@ -0,0 +1,64 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# A processor for @dagger.hilt.AliasOfProcessor.
+
+package(default_visibility = ["//:src"])
+
+java_plugin(
+ name = "processor",
+ generates_api = 1,
+ processor_class = "dagger.hilt.processor.internal.aliasof.AliasOfProcessor",
+ deps = [":processor_lib"],
+)
+
+java_library(
+ name = "processor_lib",
+ srcs = [
+ "AliasOfProcessor.java",
+ "AliasOfPropagatedDataGenerator.java",
+ ],
+ deps = [
+ ":alias_ofs",
+ "//java/dagger/hilt/processor/internal:base_processor",
+ "//java/dagger/hilt/processor/internal:classnames",
+ "//java/dagger/hilt/processor/internal:processor_errors",
+ "//java/dagger/hilt/processor/internal:processors",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/auto:service",
+ "@google_bazel_common//third_party/java/incap",
+ "@google_bazel_common//third_party/java/javapoet",
+ ],
+)
+
+java_library(
+ name = "alias_ofs",
+ srcs = [
+ "AliasOfs.java",
+ ],
+ deps = [
+ "//java/dagger/hilt/processor/internal:classnames",
+ "//java/dagger/hilt/processor/internal:processor_errors",
+ "//java/dagger/hilt/processor/internal:processors",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/javapoet",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/processor/internal/definecomponent/BUILD b/java/dagger/hilt/processor/internal/definecomponent/BUILD
new file mode 100644
index 000000000..7edec1cba
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/definecomponent/BUILD
@@ -0,0 +1,69 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# A processor for @dagger.hilt.DefineComponent.
+
+package(default_visibility = ["//:src"])
+
+java_plugin(
+ name = "processor",
+ generates_api = 1,
+ processor_class = "dagger.hilt.processor.internal.definecomponent.DefineComponentProcessor",
+ deps = [":processor_lib"],
+)
+
+java_library(
+ name = "processor_lib",
+ srcs = [
+ "DefineComponentProcessor.java",
+ ],
+ deps = [
+ ":define_components",
+ "//java/dagger/hilt/processor/internal:base_processor",
+ "//java/dagger/hilt/processor/internal:classnames",
+ "//java/dagger/hilt/processor/internal:processors",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/auto:service",
+ "@google_bazel_common//third_party/java/incap",
+ "@google_bazel_common//third_party/java/javapoet",
+ ],
+)
+
+java_library(
+ name = "define_components",
+ srcs = [
+ "DefineComponentBuilderMetadatas.java",
+ "DefineComponentMetadatas.java",
+ "DefineComponents.java",
+ ],
+ deps = [
+ "//java/dagger/hilt/processor/internal:classnames",
+ "//java/dagger/hilt/processor/internal:component_descriptor",
+ "//java/dagger/hilt/processor/internal:processor_errors",
+ "//java/dagger/hilt/processor/internal:processors",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:graph",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/processor/internal/definecomponent/DefineComponentBuilderMetadatas.java b/java/dagger/hilt/processor/internal/definecomponent/DefineComponentBuilderMetadatas.java
new file mode 100644
index 000000000..0f10f399c
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/definecomponent/DefineComponentBuilderMetadatas.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.definecomponent;
+
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.TypeName;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.ProcessorErrors;
+import dagger.hilt.processor.internal.Processors;
+import dagger.hilt.processor.internal.definecomponent.DefineComponentMetadatas.DefineComponentMetadata;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+
+/** Metadata for types annotated with {@link dagger.hilt.DefineComponent.Builder}. */
+final class DefineComponentBuilderMetadatas {
+ static DefineComponentBuilderMetadatas create(DefineComponentMetadatas componentMetadatas) {
+ return new DefineComponentBuilderMetadatas(componentMetadatas);
+ }
+
+ private final Map<Element, DefineComponentBuilderMetadata> builderMetadatas = new HashMap<>();
+ private final DefineComponentMetadatas componentMetadatas;
+
+ private DefineComponentBuilderMetadatas(DefineComponentMetadatas componentMetadatas) {
+ this.componentMetadatas = componentMetadatas;
+ }
+
+ DefineComponentBuilderMetadata get(Element element) {
+ if (!builderMetadatas.containsKey(element)) {
+ builderMetadatas.put(element, getUncached(element));
+ }
+ return builderMetadatas.get(element);
+ }
+
+ private DefineComponentBuilderMetadata getUncached(Element element) {
+ ProcessorErrors.checkState(
+ Processors.hasAnnotation(element, ClassNames.DEFINE_COMPONENT_BUILDER),
+ element,
+ "%s, expected to be annotated with @DefineComponent.Builder. Found: %s",
+ element,
+ element.getAnnotationMirrors());
+
+ // TODO(bcorso): Allow abstract classes?
+ ProcessorErrors.checkState(
+ element.getKind().equals(ElementKind.INTERFACE),
+ element,
+ "@DefineComponent.Builder is only allowed on interfaces. Found: %s",
+ element);
+ TypeElement builder = MoreElements.asType(element);
+
+ // TODO(bcorso): Allow extending interfaces?
+ ProcessorErrors.checkState(
+ builder.getInterfaces().isEmpty(),
+ builder,
+ "@DefineComponent.Builder %s, cannot extend a super class or interface. Found: %s",
+ builder,
+ builder.getInterfaces());
+
+ // TODO(bcorso): Allow type parameters?
+ ProcessorErrors.checkState(
+ builder.getTypeParameters().isEmpty(),
+ builder,
+ "@DefineComponent.Builder %s, cannot have type parameters.",
+ builder.asType());
+
+ List<VariableElement> nonStaticFields =
+ ElementFilter.fieldsIn(builder.getEnclosedElements()).stream()
+ .filter(method -> !method.getModifiers().contains(STATIC))
+ .collect(Collectors.toList());
+ ProcessorErrors.checkState(
+ nonStaticFields.isEmpty(),
+ builder,
+ "@DefineComponent.Builder %s, cannot have non-static fields. Found: %s",
+ builder,
+ nonStaticFields);
+
+ List<ExecutableElement> buildMethods =
+ ElementFilter.methodsIn(builder.getEnclosedElements()).stream()
+ .filter(method -> !method.getModifiers().contains(STATIC))
+ .filter(method -> method.getParameters().isEmpty())
+ .collect(Collectors.toList());
+
+ ProcessorErrors.checkState(
+ buildMethods.size() == 1,
+ builder,
+ "@DefineComponent.Builder %s, must have exactly 1 build method that takes no parameters. "
+ + "Found: %s",
+ builder,
+ buildMethods);
+
+ ExecutableElement buildMethod = buildMethods.get(0);
+ TypeMirror component = buildMethod.getReturnType();
+ ProcessorErrors.checkState(
+ buildMethod.getReturnType().getKind().equals(TypeKind.DECLARED)
+ && Processors.hasAnnotation(
+ MoreTypes.asTypeElement(component), ClassNames.DEFINE_COMPONENT),
+ builder,
+ "@DefineComponent.Builder method, %s#%s, must return a @DefineComponent type. Found: %s",
+ builder,
+ buildMethod,
+ component);
+
+ List<ExecutableElement> nonStaticNonBuilderMethods =
+ ElementFilter.methodsIn(builder.getEnclosedElements()).stream()
+ .filter(method -> !method.getModifiers().contains(STATIC))
+ .filter(method -> !method.equals(buildMethod))
+ .filter(method -> !TypeName.get(method.getReturnType()).equals(ClassName.get(builder)))
+ .collect(Collectors.toList());
+
+ ProcessorErrors.checkState(
+ nonStaticNonBuilderMethods.isEmpty(),
+ nonStaticNonBuilderMethods,
+ "@DefineComponent.Builder %s, all non-static methods must return %s or %s. Found: %s",
+ builder,
+ builder,
+ component,
+ nonStaticNonBuilderMethods);
+
+ return new AutoValue_DefineComponentBuilderMetadatas_DefineComponentBuilderMetadata(
+ builder,
+ buildMethod,
+ componentMetadatas.get(MoreTypes.asTypeElement(component)));
+ }
+
+ @AutoValue
+ abstract static class DefineComponentBuilderMetadata {
+ abstract TypeElement builder();
+
+ abstract ExecutableElement buildMethod();
+
+ abstract DefineComponentMetadata componentMetadata();
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/definecomponent/DefineComponentMetadatas.java b/java/dagger/hilt/processor/internal/definecomponent/DefineComponentMetadatas.java
new file mode 100644
index 000000000..aa69e40e1
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/definecomponent/DefineComponentMetadatas.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.definecomponent;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationElementAndValue;
+import static com.google.auto.common.MoreElements.asType;
+import static com.google.auto.common.MoreTypes.asTypeElement;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static java.util.stream.Collectors.joining;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.squareup.javapoet.ClassName;
+import dagger.hilt.processor.internal.AnnotationValues;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.ProcessorErrors;
+import dagger.hilt.processor.internal.Processors;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.ElementFilter;
+
+/** Metadata for types annotated with {@link dagger.hilt.DefineComponent}. */
+final class DefineComponentMetadatas {
+ static DefineComponentMetadatas create() {
+ return new DefineComponentMetadatas();
+ }
+
+ private final Map<Element, DefineComponentMetadata> metadatas = new HashMap<>();
+
+ private DefineComponentMetadatas() {}
+
+ /** Returns the metadata for an element annotated with {@link dagger.hilt.DefineComponent}. */
+ DefineComponentMetadata get(Element element) {
+ return get(element, new LinkedHashSet<>());
+ }
+
+ private DefineComponentMetadata get(Element element, LinkedHashSet<Element> childPath) {
+ if (!metadatas.containsKey(element)) {
+ metadatas.put(element, getUncached(element, childPath));
+ }
+ return metadatas.get(element);
+ }
+
+ private DefineComponentMetadata getUncached(
+ Element element, LinkedHashSet<Element> childPath) {
+ ProcessorErrors.checkState(
+ childPath.add(element),
+ element,
+ "@DefineComponent cycle: %s -> %s",
+ childPath.stream().map(Object::toString).collect(joining(" -> ")),
+ element);
+
+ ProcessorErrors.checkState(
+ Processors.hasAnnotation(element, ClassNames.DEFINE_COMPONENT),
+ element,
+ "%s, expected to be annotated with @DefineComponent. Found: %s",
+ element,
+ element.getAnnotationMirrors());
+
+ // TODO(bcorso): Allow abstract classes?
+ ProcessorErrors.checkState(
+ element.getKind().equals(ElementKind.INTERFACE),
+ element,
+ "@DefineComponent is only allowed on interfaces. Found: %s",
+ element);
+ TypeElement component = asType(element);
+
+ // TODO(bcorso): Allow extending interfaces?
+ ProcessorErrors.checkState(
+ component.getInterfaces().isEmpty(),
+ component,
+ "@DefineComponent %s, cannot extend a super class or interface. Found: %s",
+ component,
+ component.getInterfaces());
+
+ // TODO(bcorso): Allow type parameters?
+ ProcessorErrors.checkState(
+ component.getTypeParameters().isEmpty(),
+ component,
+ "@DefineComponent %s, cannot have type parameters.",
+ component.asType());
+
+ // TODO(bcorso): Allow non-static abstract methods (aka EntryPoints)?
+ List<ExecutableElement> nonStaticMethods =
+ ElementFilter.methodsIn(component.getEnclosedElements()).stream()
+ .filter(method -> !method.getModifiers().contains(STATIC))
+ .collect(Collectors.toList());
+ ProcessorErrors.checkState(
+ nonStaticMethods.isEmpty(),
+ component,
+ "@DefineComponent %s, cannot have non-static methods. Found: %s",
+ component,
+ nonStaticMethods);
+
+ // No need to check non-static fields since interfaces can't have them.
+
+ ImmutableList<TypeElement> scopes =
+ Processors.getScopeAnnotations(component).stream()
+ .map(AnnotationMirror::getAnnotationType)
+ .map(MoreTypes::asTypeElement)
+ .collect(toImmutableList());
+
+ ImmutableList<AnnotationMirror> aliasScopes =
+ Processors.getAnnotationsAnnotatedWith(component, ClassNames.ALIAS_OF);
+ ProcessorErrors.checkState(
+ aliasScopes.isEmpty(),
+ component,
+ "@DefineComponent %s, references invalid scope(s) annotated with @AliasOf. "
+ + "@DefineComponent scopes cannot be aliases of other scopes: %s",
+ component,
+ aliasScopes);
+
+ AnnotationMirror mirror =
+ Processors.getAnnotationMirror(component, ClassNames.DEFINE_COMPONENT);
+ AnnotationValue parentValue = getAnnotationElementAndValue(mirror, "parent").getValue();
+
+ ProcessorErrors.checkState(
+ // TODO(bcorso): Contribute a check to auto/common AnnotationValues.
+ !"<error>".contentEquals(parentValue.getValue().toString()),
+ component,
+ "@DefineComponent %s, references an invalid parent type: %s",
+ component,
+ mirror);
+
+ TypeElement parent = asTypeElement(AnnotationValues.getTypeMirror(parentValue));
+
+ ProcessorErrors.checkState(
+ ClassName.get(parent).equals(ClassNames.DEFINE_COMPONENT_NO_PARENT)
+ || Processors.hasAnnotation(parent, ClassNames.DEFINE_COMPONENT),
+ component,
+ "@DefineComponent %s, references a type not annotated with @DefineComponent: %s",
+ component,
+ parent);
+
+ Optional<DefineComponentMetadata> parentComponent =
+ ClassName.get(parent).equals(ClassNames.DEFINE_COMPONENT_NO_PARENT)
+ ? Optional.empty()
+ : Optional.of(get(parent, childPath));
+
+ ProcessorErrors.checkState(
+ parentComponent.isPresent()
+ || ClassName.get(component).equals(ClassNames.SINGLETON_COMPONENT),
+ component,
+ "@DefineComponent %s is missing a parent declaration.\n"
+ + "Please declare the parent, for example: @DefineComponent(parent ="
+ + " SingletonComponent.class)",
+ component);
+
+ return new AutoValue_DefineComponentMetadatas_DefineComponentMetadata(
+ component, scopes, parentComponent);
+ }
+
+ @AutoValue
+ abstract static class DefineComponentMetadata {
+
+ /** Returns the component annotated with {@link dagger.hilt.DefineComponent}. */
+ abstract TypeElement component();
+
+ /** Returns the scopes of the component. */
+ abstract ImmutableList<TypeElement> scopes();
+
+ /** Returns the parent component, if one exists. */
+ abstract Optional<DefineComponentMetadata> parentMetadata();
+
+ boolean isRoot() {
+ return !parentMetadata().isPresent();
+ }
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/definecomponent/DefineComponentProcessor.java b/java/dagger/hilt/processor/internal/definecomponent/DefineComponentProcessor.java
new file mode 100644
index 000000000..e0bfca930
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/definecomponent/DefineComponentProcessor.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.definecomponent;
+
+import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.TypeSpec;
+import dagger.hilt.processor.internal.BaseProcessor;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.Processors;
+import dagger.hilt.processor.internal.definecomponent.DefineComponentBuilderMetadatas.DefineComponentBuilderMetadata;
+import dagger.hilt.processor.internal.definecomponent.DefineComponentMetadatas.DefineComponentMetadata;
+import java.io.IOException;
+import java.util.Set;
+import javax.annotation.processing.Processor;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
+
+/**
+ * A processor for {@link dagger.hilt.DefineComponent} and {@link
+ * dagger.hilt.DefineComponent.Builder}.
+ */
+@IncrementalAnnotationProcessor(ISOLATING)
+@AutoService(Processor.class)
+public final class DefineComponentProcessor extends BaseProcessor {
+ private final DefineComponentMetadatas componentMetadatas = DefineComponentMetadatas.create();
+ private final DefineComponentBuilderMetadatas componentBuilderMetadatas =
+ DefineComponentBuilderMetadatas.create(componentMetadatas);
+
+ @Override
+ public Set<String> getSupportedAnnotationTypes() {
+ return ImmutableSet.of(
+ ClassNames.DEFINE_COMPONENT.toString(), ClassNames.DEFINE_COMPONENT_BUILDER.toString());
+ }
+
+ @Override
+ protected void processEach(TypeElement annotation, Element element) throws Exception {
+ if (ClassName.get(annotation).equals(ClassNames.DEFINE_COMPONENT)) {
+ // TODO(bcorso): For cycles we currently process each element in the cycle. We should skip
+ // processing of subsequent elements in a cycle, but this requires ensuring that the first
+ // element processed is always the same so that our failure tests are stable.
+ DefineComponentMetadata metadata = componentMetadatas.get(element);
+ generateFile("component", metadata.component());
+ } else if (ClassName.get(annotation).equals(ClassNames.DEFINE_COMPONENT_BUILDER)) {
+ DefineComponentBuilderMetadata metadata = componentBuilderMetadatas.get(element);
+ generateFile("builder", metadata.builder());
+ } else {
+ throw new AssertionError("Unhandled annotation type: " + annotation);
+ }
+ }
+
+ private void generateFile(String member, TypeElement typeElement) throws IOException {
+ TypeSpec.Builder builder =
+ TypeSpec.interfaceBuilder(Processors.getFullEnclosedName(typeElement))
+ .addOriginatingElement(typeElement)
+ .addAnnotation(
+ AnnotationSpec.builder(ClassNames.DEFINE_COMPONENT_CLASSES)
+ .addMember(member, "$S", typeElement.getQualifiedName())
+ .build());
+
+ Processors.addGeneratedAnnotation(builder, processingEnv, getClass());
+
+ JavaFile.builder(DefineComponents.AGGREGATING_PACKAGE, builder.build())
+ .build()
+ .writeTo(processingEnv.getFiler());
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/definecomponent/DefineComponents.java b/java/dagger/hilt/processor/internal/definecomponent/DefineComponents.java
new file mode 100644
index 000000000..0b330111a
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/definecomponent/DefineComponents.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.definecomponent;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationElementAndValue;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ListMultimap;
+import com.squareup.javapoet.ClassName;
+import dagger.hilt.processor.internal.AnnotationValues;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.ComponentDescriptor;
+import dagger.hilt.processor.internal.ComponentTree;
+import dagger.hilt.processor.internal.ProcessorErrors;
+import dagger.hilt.processor.internal.Processors;
+import dagger.hilt.processor.internal.definecomponent.DefineComponentBuilderMetadatas.DefineComponentBuilderMetadata;
+import dagger.hilt.processor.internal.definecomponent.DefineComponentMetadatas.DefineComponentMetadata;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
+
+/**
+ * A utility class for getting {@link DefineComponentMetadata} and {@link
+ * DefineComponentBuilderMetadata}.
+ */
+public final class DefineComponents {
+ static final String AGGREGATING_PACKAGE =
+ DefineComponents.class.getPackage().getName() + ".codegen";
+
+ public static DefineComponents create() {
+ return new DefineComponents();
+ }
+
+ private final Map<Element, ComponentDescriptor> componentDescriptors = new HashMap<>();
+ private final DefineComponentMetadatas componentMetadatas = DefineComponentMetadatas.create();
+ private final DefineComponentBuilderMetadatas componentBuilderMetadatas =
+ DefineComponentBuilderMetadatas.create(componentMetadatas);
+
+ private DefineComponents() {}
+
+ /** Returns the {@link ComponentDescriptor} for the given component element. */
+ // TODO(b/144940889): This descriptor doesn't contain the "creator" or the "installInName".
+ public ComponentDescriptor componentDescriptor(Element element) {
+ if (!componentDescriptors.containsKey(element)) {
+ componentDescriptors.put(element, uncachedComponentDescriptor(element));
+ }
+ return componentDescriptors.get(element);
+ }
+
+ private ComponentDescriptor uncachedComponentDescriptor(Element element) {
+ DefineComponentMetadata metadata = componentMetadatas.get(element);
+ ComponentDescriptor.Builder builder =
+ ComponentDescriptor.builder()
+ .component(ClassName.get(metadata.component()))
+ .scopes(metadata.scopes().stream().map(ClassName::get).collect(toImmutableSet()));
+
+
+ metadata.parentMetadata()
+ .map(DefineComponentMetadata::component)
+ .map(this::componentDescriptor)
+ .ifPresent(builder::parent);
+
+ return builder.build();
+ }
+
+ /** Returns the {@link ComponentTree} from the aggregated {@link ComponentDescriptor}s. */
+ public ComponentTree getComponentTree(Elements elements) {
+ AggregatedMetadata aggregatedMetadata =
+ AggregatedMetadata.from(elements, componentMetadatas, componentBuilderMetadatas);
+ ListMultimap<DefineComponentMetadata, DefineComponentBuilderMetadata> builderMultimap =
+ ArrayListMultimap.create();
+ aggregatedMetadata.builders()
+ .forEach(builder -> builderMultimap.put(builder.componentMetadata(), builder));
+
+ // Check that there are not multiple builders per component
+ for (DefineComponentMetadata componentMetadata : builderMultimap.keySet()) {
+ TypeElement component = componentMetadata.component();
+ ProcessorErrors.checkState(
+ builderMultimap.get(componentMetadata).size() <= 1,
+ component,
+ "Multiple @%s declarations are not allowed for @%s type, %s. Found: %s",
+ ClassNames.DEFINE_COMPONENT_BUILDER,
+ ClassNames.DEFINE_COMPONENT,
+ component,
+ builderMultimap.get(componentMetadata).stream()
+ .map(DefineComponentBuilderMetadata::builder)
+ .map(TypeElement::toString)
+ .sorted()
+ .collect(toImmutableList()));
+ }
+
+ // Now that we know there is at most 1 builder per component, convert the Multimap to Map.
+ Map<DefineComponentMetadata, DefineComponentBuilderMetadata> builderMap = new LinkedHashMap<>();
+ builderMultimap.entries().forEach(e -> builderMap.put(e.getKey(), e.getValue()));
+
+
+ return ComponentTree.from(aggregatedMetadata.components().stream()
+ .map(componentMetadata -> toComponentDescriptor(componentMetadata, builderMap))
+ .collect(toImmutableSet()));
+ }
+
+ private static ComponentDescriptor toComponentDescriptor(
+ DefineComponentMetadata componentMetadata,
+ Map<DefineComponentMetadata, DefineComponentBuilderMetadata> builderMap) {
+ ComponentDescriptor.Builder builder =
+ ComponentDescriptor.builder()
+ .component(ClassName.get(componentMetadata.component()))
+ .scopes(
+ componentMetadata.scopes().stream().map(ClassName::get).collect(toImmutableSet()));
+
+
+ if (builderMap.containsKey(componentMetadata)) {
+ builder.creator(ClassName.get(builderMap.get(componentMetadata).builder()));
+ }
+
+ componentMetadata
+ .parentMetadata()
+ .map(parent -> toComponentDescriptor(parent, builderMap))
+ .ifPresent(builder::parent);
+
+ return builder.build();
+ }
+
+ @AutoValue
+ abstract static class AggregatedMetadata {
+ /** Returns the aggregated metadata for {@link DefineComponentClasses#component()}. */
+ abstract ImmutableList<DefineComponentMetadata> components();
+
+ /** Returns the aggregated metadata for {@link DefineComponentClasses#builder()}. */
+ abstract ImmutableList<DefineComponentBuilderMetadata> builders();
+
+ static AggregatedMetadata from(
+ Elements elements,
+ DefineComponentMetadatas componentMetadatas,
+ DefineComponentBuilderMetadatas componentBuilderMetadatas) {
+ PackageElement packageElement = elements.getPackageElement(AGGREGATING_PACKAGE);
+
+ if (packageElement == null) {
+ return new AutoValue_DefineComponents_AggregatedMetadata(
+ ImmutableList.of(), ImmutableList.of());
+ }
+
+ ImmutableList.Builder<DefineComponentMetadata> components = ImmutableList.builder();
+ ImmutableList.Builder<DefineComponentBuilderMetadata> builders = ImmutableList.builder();
+ for (Element element : packageElement.getEnclosedElements()) {
+ ProcessorErrors.checkState(
+ MoreElements.isType(element),
+ element,
+ "Only types may be in package %s. Did you add custom code in the package?",
+ packageElement);
+
+ TypeElement typeElement = MoreElements.asType(element);
+ ProcessorErrors.checkState(
+ Processors.hasAnnotation(typeElement, ClassNames.DEFINE_COMPONENT_CLASSES),
+ typeElement,
+ "Class, %s, must be annotated with @%s. Found: %s.",
+ typeElement,
+ ClassNames.DEFINE_COMPONENT_CLASSES.simpleName(),
+ typeElement.getAnnotationMirrors());
+
+ Optional<TypeElement> component = defineComponentClass(elements, typeElement, "component");
+ Optional<TypeElement> builder = defineComponentClass(elements, typeElement, "builder");
+ ProcessorErrors.checkState(
+ component.isPresent() || builder.isPresent(),
+ typeElement,
+ "@DefineComponentClasses missing both `component` and `builder` members.");
+
+ component.map(componentMetadatas::get).ifPresent(components::add);
+ builder.map(componentBuilderMetadatas::get).ifPresent(builders::add);
+ }
+
+ return new AutoValue_DefineComponents_AggregatedMetadata(
+ components.build(), builders.build());
+ }
+
+ private static Optional<TypeElement> defineComponentClass(
+ Elements elements, Element element, String annotationMember) {
+ AnnotationMirror mirror =
+ Processors.getAnnotationMirror(element, ClassNames.DEFINE_COMPONENT_CLASSES);
+ AnnotationValue value = getAnnotationElementAndValue(mirror, annotationMember).getValue();
+ String className = AnnotationValues.getString(value);
+
+ if (className.isEmpty()) { // The default value.
+ return Optional.empty();
+ }
+
+ TypeElement type = elements.getTypeElement(className);
+ ProcessorErrors.checkState(
+ type != null,
+ element,
+ "%s.%s(), has invalid value: `%s`.",
+ ClassNames.DEFINE_COMPONENT_CLASSES.simpleName(),
+ annotationMember,
+ className);
+
+ return Optional.of(type);
+ }
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/disableinstallincheck/BUILD b/java/dagger/hilt/processor/internal/disableinstallincheck/BUILD
new file mode 100644
index 000000000..e3865a608
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/disableinstallincheck/BUILD
@@ -0,0 +1,49 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# A processor for @dagger.hilt.AliasOfProcessor.
+
+package(default_visibility = ["//:src"])
+
+java_plugin(
+ name = "processor",
+ generates_api = 1,
+ processor_class = "dagger.hilt.processor.internal.disableinstallincheck.DisableInstallInCheckProcessor",
+ visibility = [
+ "//java/dagger/hilt/migration:__pkg__",
+ ],
+ deps = [":processor_lib"],
+)
+
+java_library(
+ name = "processor_lib",
+ srcs = [
+ "DisableInstallInCheckProcessor.java",
+ ],
+ deps = [
+ "//java/dagger/hilt/processor/internal:base_processor",
+ "//java/dagger/hilt/processor/internal:classnames",
+ "//java/dagger/hilt/processor/internal:processor_errors",
+ "//java/dagger/hilt/processor/internal:processors",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/auto:service",
+ "@google_bazel_common//third_party/java/incap",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/processor/internal/disableinstallincheck/DisableInstallInCheckProcessor.java b/java/dagger/hilt/processor/internal/disableinstallincheck/DisableInstallInCheckProcessor.java
new file mode 100644
index 000000000..c51f350e9
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/disableinstallincheck/DisableInstallInCheckProcessor.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.disableinstallincheck;
+
+import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.ImmutableSet;
+import dagger.hilt.processor.internal.BaseProcessor;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.ProcessorErrors;
+import dagger.hilt.processor.internal.Processors;
+import javax.annotation.processing.Processor;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
+
+/** Processes the annotations annotated with {@link dagger.hilt.migration.DisableInstallInCheck} */
+@IncrementalAnnotationProcessor(ISOLATING)
+@AutoService(Processor.class)
+public final class DisableInstallInCheckProcessor extends BaseProcessor {
+ @Override
+ public ImmutableSet<String> getSupportedAnnotationTypes() {
+ return ImmutableSet.of(ClassNames.DISABLE_INSTALL_IN_CHECK.toString());
+ }
+
+ @Override
+ public void processEach(TypeElement annotation, Element element) {
+ ProcessorErrors.checkState(
+ Processors.hasAnnotation(element, ClassNames.MODULE),
+ element,
+ "@DisableInstallInCheck should only be used on modules. However, it was found annotating"
+ + " %s",
+ element);
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/generatesrootinput/BUILD b/java/dagger/hilt/processor/internal/generatesrootinput/BUILD
new file mode 100644
index 000000000..f24ffb716
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/generatesrootinput/BUILD
@@ -0,0 +1,65 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# A processor for @dagger.hilt.GeneratesRootInput.
+
+package(default_visibility = ["//:src"])
+
+java_plugin(
+ name = "processor",
+ generates_api = 1,
+ processor_class = "dagger.hilt.processor.internal.generatesrootinput.GeneratesRootInputProcessor",
+ deps = [":processor_lib"],
+)
+
+java_library(
+ name = "processor_lib",
+ srcs = [
+ "GeneratesRootInputProcessor.java",
+ "GeneratesRootInputPropagatedDataGenerator.java",
+ ],
+ deps = [
+ ":generates_root_inputs",
+ "//java/dagger/hilt/processor/internal:base_processor",
+ "//java/dagger/hilt/processor/internal:classnames",
+ "//java/dagger/hilt/processor/internal:processor_errors",
+ "//java/dagger/hilt/processor/internal:processors",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/auto:service",
+ "@google_bazel_common//third_party/java/incap",
+ "@google_bazel_common//third_party/java/javapoet",
+ ],
+)
+
+java_library(
+ name = "generates_root_inputs",
+ srcs = [
+ "GeneratesRootInputs.java",
+ ],
+ deps = [
+ "//java/dagger/hilt/processor/internal:classnames",
+ "//java/dagger/hilt/processor/internal:processor_errors",
+ "//java/dagger/hilt/processor/internal:processors",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/javapoet",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/processor/internal/generatesrootinput/GeneratesRootInputProcessor.java b/java/dagger/hilt/processor/internal/generatesrootinput/GeneratesRootInputProcessor.java
new file mode 100644
index 000000000..6588e09e4
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/generatesrootinput/GeneratesRootInputProcessor.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.generatesrootinput;
+
+import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.ImmutableSet;
+import dagger.hilt.processor.internal.BaseProcessor;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.ProcessorErrors;
+import java.util.Set;
+import javax.annotation.processing.Processor;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.TypeElement;
+import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
+
+/**
+ * Processes the annotations annotated with {@link dagger.hilt.GeneratesRootInput} which generate
+ * input for components and should be processed before component creation.
+ */
+@IncrementalAnnotationProcessor(ISOLATING)
+@AutoService(Processor.class)
+public final class GeneratesRootInputProcessor extends BaseProcessor {
+
+ @Override
+ public Set<String> getSupportedAnnotationTypes() {
+ return ImmutableSet.of(ClassNames.GENERATES_ROOT_INPUT.toString());
+ }
+
+ @Override
+ public void processEach(TypeElement annotation, Element element) throws Exception {
+ ProcessorErrors.checkState(
+ element.getKind().equals(ElementKind.ANNOTATION_TYPE),
+ element,
+ "%s should only annotate other annotations. However, it was found annotating %s",
+ annotation,
+ element);
+
+ new GeneratesRootInputPropagatedDataGenerator(this.getProcessingEnv(), element).generate();
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/generatesrootinput/GeneratesRootInputPropagatedDataGenerator.java b/java/dagger/hilt/processor/internal/generatesrootinput/GeneratesRootInputPropagatedDataGenerator.java
new file mode 100644
index 000000000..5c5389416
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/generatesrootinput/GeneratesRootInputPropagatedDataGenerator.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.generatesrootinput;
+
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.TypeSpec;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.Processors;
+import java.io.IOException;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Element;
+
+/** Generates resource files for {@link GeneratesRootInputs}. */
+final class GeneratesRootInputPropagatedDataGenerator {
+ private final ProcessingEnvironment processingEnv;
+ private final Element element;
+
+ GeneratesRootInputPropagatedDataGenerator(ProcessingEnvironment processingEnv, Element element) {
+ this.processingEnv = processingEnv;
+ this.element = element;
+ }
+
+ void generate() throws IOException {
+ TypeSpec.Builder generator =
+ TypeSpec.classBuilder(Processors.getFullEnclosedName(element))
+ .addOriginatingElement(element)
+ .addAnnotation(
+ AnnotationSpec.builder(ClassNames.GENERATES_ROOT_INPUT_PROPAGATED_DATA)
+ .addMember("value", "$T.class", element)
+ .build())
+ .addJavadoc(
+ "Generated class to"
+ + "get the list of annotations that generate input for root.\n");
+
+ Processors.addGeneratedAnnotation(generator, processingEnv, getClass());
+
+ JavaFile.builder(GeneratesRootInputs.AGGREGATING_PACKAGE, generator.build())
+ .build()
+ .writeTo(processingEnv.getFiler());
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/generatesrootinput/GeneratesRootInputs.java b/java/dagger/hilt/processor/internal/generatesrootinput/GeneratesRootInputs.java
new file mode 100644
index 000000000..139592e0a
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/generatesrootinput/GeneratesRootInputs.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.generatesrootinput;
+
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Suppliers.memoize;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.ProcessorErrors;
+import dagger.hilt.processor.internal.Processors;
+import java.util.List;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
+
+/** Extracts the list of annotations annotated with {@link dagger.hilt.GeneratesRootInput}. */
+public final class GeneratesRootInputs {
+ static final String AGGREGATING_PACKAGE =
+ GeneratesRootInputs.class.getPackage().getName() + ".codegen";
+
+ private final Elements elements;
+ private final Supplier<ImmutableList<ClassName>> generatesRootInputAnnotations =
+ memoize(() -> getAnnotationList());
+
+ public GeneratesRootInputs(ProcessingEnvironment processingEnvironment) {
+ this.elements = processingEnvironment.getElementUtils();
+ }
+
+ public ImmutableSet<Element> getElementsToWaitFor(RoundEnvironment roundEnv) {
+ // Processing can only take place after all dependent annotations have been processed
+ // Note: We start with ClassName rather than TypeElement because jdk8 does not treat type
+ // elements as equal across rounds. Thus, in order for RoundEnvironment#getElementsAnnotatedWith
+ // to work properly, we get new elements to ensure it works across rounds (See b/148693284).
+ return generatesRootInputAnnotations.get().stream()
+ .map(className -> elements.getTypeElement(className.toString()))
+ .filter(element -> element != null)
+ .flatMap(annotation -> roundEnv.getElementsAnnotatedWith(annotation).stream())
+ .collect(toImmutableSet());
+ }
+
+ private ImmutableList<ClassName> getAnnotationList() {
+ PackageElement packageElement = elements.getPackageElement(AGGREGATING_PACKAGE);
+
+ if (packageElement == null) {
+ return ImmutableList.of();
+ }
+
+ List<? extends Element> annotationElements = packageElement.getEnclosedElements();
+ checkState(!annotationElements.isEmpty(), "No elements Found in package %s.", packageElement);
+
+ ImmutableList.Builder<ClassName> builder = ImmutableList.builder();
+ for (Element element : annotationElements) {
+ ProcessorErrors.checkState(
+ element.getKind() == ElementKind.CLASS,
+ element,
+ "Only classes may be in package %s. Did you add custom code in the package?",
+ packageElement);
+
+ AnnotationMirror annotationMirror =
+ Processors.getAnnotationMirror(element, ClassNames.GENERATES_ROOT_INPUT_PROPAGATED_DATA);
+ ProcessorErrors.checkState(
+ annotationMirror != null,
+ element,
+ "Classes in package %s must be annotated with @%s: %s."
+ + " Found: %s. Files in this package are generated, did you add custom code in the"
+ + " package? ",
+ packageElement,
+ ClassNames.GENERATES_ROOT_INPUT_PROPAGATED_DATA,
+ element.getSimpleName(),
+ element.getAnnotationMirrors());
+
+ TypeElement annotation =
+ Processors.getAnnotationClassValue(elements, annotationMirror, "value");
+
+ builder.add(ClassName.get(annotation));
+ }
+ // This annotation was on Dagger so it couldn't be annotated with @GeneratesRootInput to be
+ // cultivated later. We have to manually add it to the list.
+ builder.add(ClassNames.PRODUCTION_COMPONENT);
+ return builder.build();
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/originatingelement/BUILD b/java/dagger/hilt/processor/internal/originatingelement/BUILD
new file mode 100644
index 000000000..50dbcf6b1
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/originatingelement/BUILD
@@ -0,0 +1,47 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# A processor for @dagger.hilt.codegen.OriginatingElement.
+
+package(default_visibility = ["//:src"])
+
+java_plugin(
+ name = "processor",
+ generates_api = 1,
+ processor_class = "dagger.hilt.processor.internal.originatingelement.OriginatingElementProcessor",
+ deps = [":processor_lib"],
+)
+
+java_library(
+ name = "processor_lib",
+ srcs = [
+ "OriginatingElementProcessor.java",
+ ],
+ deps = [
+ "//java/dagger/hilt/processor/internal:base_processor",
+ "//java/dagger/hilt/processor/internal:classnames",
+ "//java/dagger/hilt/processor/internal:processor_errors",
+ "//java/dagger/hilt/processor/internal:processors",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/auto:service",
+ "@google_bazel_common//third_party/java/incap",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/processor/internal/originatingelement/OriginatingElementProcessor.java b/java/dagger/hilt/processor/internal/originatingelement/OriginatingElementProcessor.java
new file mode 100644
index 000000000..723cf04ee
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/originatingelement/OriginatingElementProcessor.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.originatingelement;
+
+import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.service.AutoService;
+import com.google.common.collect.ImmutableSet;
+import dagger.hilt.processor.internal.BaseProcessor;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.ProcessorErrors;
+import dagger.hilt.processor.internal.Processors;
+import javax.annotation.processing.Processor;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
+
+/**
+ * Processes the annotations annotated with {@link dagger.hilt.codegen.OriginatingElement} to check
+ * that they're only used on top-level classes and the value passed is also a top-level class.
+ */
+@IncrementalAnnotationProcessor(ISOLATING)
+@AutoService(Processor.class)
+public final class OriginatingElementProcessor extends BaseProcessor {
+
+ @Override
+ public ImmutableSet<String> getSupportedAnnotationTypes() {
+ return ImmutableSet.of(ClassNames.ORIGINATING_ELEMENT.toString());
+ }
+
+ @Override
+ public void processEach(TypeElement annotation, Element element) throws Exception {
+ ProcessorErrors.checkState(
+ MoreElements.isType(element) && Processors.isTopLevel(element),
+ element,
+ "@%s should only be used to annotate top-level types, but found: %s",
+ annotation.getSimpleName(),
+ element);
+
+ TypeElement originatingElementValue =
+ Processors.getAnnotationClassValue(
+ getElementUtils(),
+ Processors.getAnnotationMirror(element, ClassNames.ORIGINATING_ELEMENT),
+ "topLevelClass");
+
+ // TODO(bcorso): ProcessorErrors should allow us to point to the annotation too.
+ ProcessorErrors.checkState(
+ Processors.isTopLevel(originatingElementValue),
+ element,
+ "@%s.topLevelClass value should be a top-level class, but found: %s",
+ annotation.getSimpleName(),
+ originatingElementValue);
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/root/BUILD b/java/dagger/hilt/processor/internal/root/BUILD
new file mode 100644
index 000000000..709eb7f5f
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/root/BUILD
@@ -0,0 +1,102 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Annotation processor for Hilt.
+
+package(default_visibility = ["//:src"])
+
+java_plugin(
+ name = "plugin",
+ generates_api = 1,
+ processor_class = "dagger.hilt.processor.internal.root.RootProcessor",
+ deps = [
+ ":processor_lib",
+ ],
+)
+
+java_library(
+ name = "processor_lib",
+ srcs = [
+ "RootFileFormatter.java",
+ "RootGenerator.java",
+ "RootProcessor.java",
+ "TestComponentDataGenerator.java",
+ "TestComponentDataSupplierGenerator.java",
+ "TestInjectorGenerator.java",
+ ],
+ deps = [
+ ":root_metadata",
+ ":root_type",
+ "//java/dagger/hilt/processor/internal:base_processor",
+ "//java/dagger/hilt/processor/internal:classnames",
+ "//java/dagger/hilt/processor/internal:component_descriptor",
+ "//java/dagger/hilt/processor/internal:component_names",
+ "//java/dagger/hilt/processor/internal:processor_errors",
+ "//java/dagger/hilt/processor/internal:processors",
+ "//java/dagger/hilt/processor/internal/aggregateddeps:component_dependencies",
+ "//java/dagger/hilt/processor/internal/definecomponent:define_components",
+ "//java/dagger/hilt/processor/internal/generatesrootinput:generates_root_inputs",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:graph",
+ "@google_bazel_common//third_party/java/auto:service",
+ "@google_bazel_common//third_party/java/incap",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
+
+java_library(
+ name = "root_metadata",
+ srcs = [
+ "Root.java",
+ "RootMetadata.java",
+ "TestRootMetadata.java",
+ ],
+ deps = [
+ ":root_type",
+ "//java/dagger/hilt/processor/internal:classnames",
+ "//java/dagger/hilt/processor/internal:component_descriptor",
+ "//java/dagger/hilt/processor/internal:kotlin",
+ "//java/dagger/hilt/processor/internal:processor_errors",
+ "//java/dagger/hilt/processor/internal:processors",
+ "//java/dagger/hilt/processor/internal/aggregateddeps:component_dependencies",
+ "//java/dagger/hilt/processor/internal/aliasof:alias_ofs",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/codegen/kotlin",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
+
+java_library(
+ name = "root_type",
+ srcs = ["RootType.java"],
+ deps = [
+ "//java/dagger/hilt/processor/internal:classnames",
+ "//java/dagger/hilt/processor/internal:processor_errors",
+ "//java/dagger/hilt/processor/internal:processors",
+ "@google_bazel_common//third_party/java/javapoet",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/processor/internal/root/Root.java b/java/dagger/hilt/processor/internal/root/Root.java
new file mode 100644
index 000000000..981636d62
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/root/Root.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.root;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.value.AutoValue;
+import com.squareup.javapoet.ClassName;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+/** Metadata for a root element that can trigger the {@link RootProcessor}. */
+@AutoValue
+abstract class Root {
+ /** Creates a {@plainlink Root root} for the given {@plainlink Element element}. */
+ static Root create(Element element, ProcessingEnvironment env) {
+ TypeElement rootElement = MoreElements.asType(element);
+ return new AutoValue_Root(RootType.of(env, rootElement), rootElement);
+ }
+
+ /** Returns the type of the root {@code element}. */
+ abstract RootType type();
+
+ /** Returns the root element that should be used with processing. */
+ abstract TypeElement element();
+
+ /** Returns the class name of the root element. */
+ ClassName classname() {
+ return ClassName.get(element());
+ }
+
+ @Override
+ public final String toString() {
+ return element().toString();
+ }
+
+ boolean isTestRoot() {
+ return type().isTestRoot();
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/root/RootFileFormatter.java b/java/dagger/hilt/processor/internal/root/RootFileFormatter.java
new file mode 100644
index 000000000..45cdd22f6
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/root/RootFileFormatter.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.root;
+
+import com.squareup.javapoet.JavaFile;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.regex.MatchResult;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.annotation.processing.Filer;
+import javax.lang.model.element.Element;
+import javax.tools.JavaFileObject;
+
+/**
+ * Typically we would just use {@code JavaFile#writeTo()} to write files. However, this formatter
+ * exists to add new lines inbetween interfaces. This can be important for classes with many
+ * interfaces (e.g. Dagger components) to avoid spamming the entire list of interfaces when
+ * reporting errors to the user.
+ *
+ * <p>See b/33108646.
+ */
+final class RootFileFormatter {
+ private static final Pattern CLASS_PATERN = Pattern.compile("(\\h*)(.*class.*implements)(.*\\{)");
+
+ /** Formats the {@link JavaFile} java source file. */
+ static void write(JavaFile javaFile, Filer filer) throws IOException {
+ String fileName =
+ javaFile.packageName.isEmpty()
+ ? javaFile.typeSpec.name
+ : javaFile.packageName + "." + javaFile.typeSpec.name;
+
+ Element[] originatingElements = javaFile.typeSpec.originatingElements.toArray(new Element[0]);
+
+ StringBuilder sb = new StringBuilder("");
+ javaFile.writeTo(sb);
+ String fileContent = formatInterfaces(sb.toString(), CLASS_PATERN);
+
+ JavaFileObject filerSourceFile = filer.createSourceFile(fileName, originatingElements);
+ try (Writer writer = filerSourceFile.openWriter()) {
+ writer.write(fileContent);
+ } catch (Exception e) {
+ try {
+ filerSourceFile.delete();
+ } catch (Exception ignored) {
+ // Nothing to do.
+ }
+ throw e;
+ }
+ }
+
+ private static String formatInterfaces(String content, Pattern pattern) {
+ Matcher matcher = pattern.matcher(content);
+ StringBuffer sb = new StringBuffer(content.length());
+ while (matcher.find()) {
+ MatchResult result = matcher.toMatchResult();
+ String spaces = result.group(1);
+ String prefix = result.group(2);
+ String interfaces = result.group(3);
+ String formattedInterfaces = formatInterfaces(spaces, interfaces);
+ matcher.appendReplacement(
+ sb, Matcher.quoteReplacement(spaces + prefix + formattedInterfaces));
+ }
+ matcher.appendTail(sb);
+ return sb.toString();
+ }
+
+ private static String formatInterfaces(String prefixSpaces, String interfaces) {
+ StringBuilder sb = new StringBuilder(interfaces);
+ String newLine = String.format("\n%s ", prefixSpaces);
+
+ // Add a line break after each interface so that there's only 1 interface per line.
+ int i = 0;
+ int bracketCount = 0;
+ while (i >= 0 && i < sb.length()) {
+ char c = sb.charAt(i++);
+ if (c == '<') {
+ bracketCount++;
+ } else if (c == '>') {
+ bracketCount--;
+ } else if (c == ',' && bracketCount == 0) {
+ sb.insert(i++, newLine);
+ }
+ }
+ return sb.toString();
+ }
+
+ private RootFileFormatter() {}
+}
diff --git a/java/dagger/hilt/processor/internal/root/RootGenerator.java b/java/dagger/hilt/processor/internal/root/RootGenerator.java
new file mode 100644
index 000000000..c2c55e6c4
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/root/RootGenerator.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.root;
+
+import static dagger.hilt.processor.internal.Processors.toClassNames;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.graph.GraphBuilder;
+import com.google.common.graph.Graphs;
+import com.google.common.graph.MutableGraph;
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeSpec;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.ComponentDescriptor;
+import dagger.hilt.processor.internal.ComponentGenerator;
+import dagger.hilt.processor.internal.ComponentNames;
+import dagger.hilt.processor.internal.ComponentTree;
+import dagger.hilt.processor.internal.Processors;
+import java.io.IOException;
+import java.util.Optional;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Modifier;
+
+/** Generates components and any other classes needed for a root. */
+final class RootGenerator {
+
+ static void generate(RootMetadata metadata, ProcessingEnvironment env) throws IOException {
+ new RootGenerator(
+ RootMetadata.copyWithNewTree(
+ metadata,
+ filterDescriptors(metadata.componentTree())),
+ env).generateComponents();
+ }
+
+ private final RootMetadata metadata;
+ private final ProcessingEnvironment env;
+ private final Root root;
+
+ private RootGenerator(RootMetadata metadata, ProcessingEnvironment env) {
+ this.metadata = metadata;
+ this.env = env;
+ this.root = metadata.root();
+ }
+
+ private void generateComponents() throws IOException {
+
+ // TODO(bcorso): Consider moving all of this logic into ComponentGenerator?
+ TypeSpec.Builder componentsWrapper =
+ TypeSpec.classBuilder(getComponentsWrapperClassName())
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .addMethod(MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE).build());
+
+ Processors.addGeneratedAnnotation(componentsWrapper, env, ClassNames.ROOT_PROCESSOR.toString());
+
+ ImmutableMap<ComponentDescriptor, ClassName> subcomponentBuilderModules =
+ subcomponentBuilderModules(componentsWrapper);
+
+ ComponentTree componentTree = metadata.componentTree();
+ for (ComponentDescriptor componentDescriptor : componentTree.getComponentDescriptors()) {
+ ImmutableSet<ClassName> modules =
+ ImmutableSet.<ClassName>builder()
+ .addAll(toClassNames(metadata.modules(componentDescriptor.component())))
+ .addAll(
+ componentTree.childrenOf(componentDescriptor).stream()
+ .map(subcomponentBuilderModules::get)
+ .collect(toImmutableSet()))
+ .build();
+
+ componentsWrapper.addType(
+ new ComponentGenerator(
+ env,
+ getComponentClassName(componentDescriptor),
+ root.element(),
+ Optional.empty(),
+ modules,
+ metadata.entryPoints(componentDescriptor.component()),
+ metadata.scopes(componentDescriptor.component()),
+ ImmutableList.of(),
+ componentAnnotation(componentDescriptor),
+ componentBuilder(componentDescriptor))
+ .generate().toBuilder().addModifiers(Modifier.STATIC).build());
+ }
+
+ RootFileFormatter.write(
+ JavaFile.builder(root.classname().packageName(), componentsWrapper.build()).build(),
+ env.getFiler());
+ }
+
+ private static ComponentTree filterDescriptors(ComponentTree componentTree) {
+ MutableGraph<ComponentDescriptor> graph =
+ GraphBuilder.from(componentTree.graph()).build();
+
+ componentTree.graph().nodes().forEach(graph::addNode);
+ componentTree.graph().edges().forEach(graph::putEdge);
+
+ // Remove components that do not have builders (besides the root component) since if
+ // we didn't find any builder class, then we don't need to generate the component
+ // since it would be inaccessible.
+ componentTree.getComponentDescriptors().stream()
+ .filter(descriptor -> !descriptor.isRoot() && !descriptor.creator().isPresent())
+ .forEach(graph::removeNode);
+
+ // The graph may still have nodes that are children of components that don't have builders,
+ // so we need to find reachable nodes from the root and create a new graph to remove those.
+ // We reuse the root from the original tree since it should not have been removed.
+ return ComponentTree.from(Graphs.reachableNodes(graph, componentTree.root()));
+ }
+
+ private ImmutableMap<ComponentDescriptor, ClassName> subcomponentBuilderModules(
+ TypeSpec.Builder componentsWrapper) throws IOException {
+ ImmutableMap.Builder<ComponentDescriptor, ClassName> modules = ImmutableMap.builder();
+ for (ComponentDescriptor descriptor : metadata.componentTree().getComponentDescriptors()) {
+ // Root component builders don't have subcomponent builder modules
+ if (!descriptor.isRoot() && descriptor.creator().isPresent()) {
+ ClassName component = getComponentClassName(descriptor);
+ ClassName builder = descriptor.creator().get();
+ ClassName module = component.peerClass(component.simpleName() + "BuilderModule");
+ componentsWrapper.addType(subcomponentBuilderModule(component, builder, module));
+ modules.put(descriptor, module);
+ }
+ }
+ return modules.build();
+ }
+
+ // Generates:
+ // @Module(subcomponents = FooSubcomponent.class)
+ // interface FooSubcomponentBuilderModule {
+ // @Binds FooSubcomponentInterfaceBuilder bind(FooSubcomponent.Builder builder);
+ // }
+ private TypeSpec subcomponentBuilderModule(
+ ClassName componentName, ClassName builderName, ClassName moduleName) throws IOException {
+ TypeSpec.Builder subcomponentBuilderModule =
+ TypeSpec.interfaceBuilder(moduleName)
+ .addOriginatingElement(root.element())
+ .addModifiers(ABSTRACT)
+ .addAnnotation(
+ AnnotationSpec.builder(ClassNames.MODULE)
+ .addMember("subcomponents", "$T.class", componentName)
+ .build())
+ .addAnnotation(ClassNames.DISABLE_INSTALL_IN_CHECK)
+ .addMethod(
+ MethodSpec.methodBuilder("bind")
+ .addModifiers(ABSTRACT, PUBLIC)
+ .addAnnotation(ClassNames.BINDS)
+ .returns(builderName)
+ .addParameter(componentName.nestedClass("Builder"), "builder")
+ .build());
+
+ Processors.addGeneratedAnnotation(
+ subcomponentBuilderModule, env, ClassNames.ROOT_PROCESSOR.toString());
+
+ return subcomponentBuilderModule.build();
+ }
+
+ private Optional<TypeSpec> componentBuilder(ComponentDescriptor descriptor) {
+ return descriptor
+ .creator()
+ .map(
+ creator ->
+ TypeSpec.interfaceBuilder("Builder")
+ .addOriginatingElement(root.element())
+ .addModifiers(STATIC, ABSTRACT)
+ .addSuperinterface(creator)
+ .addAnnotation(componentBuilderAnnotation(descriptor))
+ .build());
+ }
+
+ private ClassName componentAnnotation(ComponentDescriptor componentDescriptor) {
+ if (!componentDescriptor.isRoot()
+ ) {
+ return ClassNames.SUBCOMPONENT;
+ } else {
+ return ClassNames.COMPONENT;
+ }
+ }
+
+ private ClassName componentBuilderAnnotation(ComponentDescriptor componentDescriptor) {
+ if (componentDescriptor.isRoot()) {
+ return ClassNames.COMPONENT_BUILDER;
+ } else {
+ return ClassNames.SUBCOMPONENT_BUILDER;
+ }
+ }
+
+ private ClassName getPartialRootModuleClassName() {
+ return getComponentsWrapperClassName().nestedClass("PartialRootModule");
+ }
+
+ private ClassName getComponentsWrapperClassName() {
+ return ComponentNames.generatedComponentsWrapper(root.classname());
+ }
+
+ private ClassName getComponentClassName(ComponentDescriptor componentDescriptor) {
+ return ComponentNames.generatedComponent(root.classname(), componentDescriptor.component());
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/root/RootMetadata.java b/java/dagger/hilt/processor/internal/root/RootMetadata.java
new file mode 100644
index 000000000..4c43f1d61
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/root/RootMetadata.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.root;
+
+import static com.google.common.base.Suppliers.memoize;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.TypeName;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.ComponentDescriptor;
+import dagger.hilt.processor.internal.ComponentTree;
+import dagger.hilt.processor.internal.KotlinMetadataUtils;
+import dagger.hilt.processor.internal.Processors;
+import dagger.hilt.processor.internal.aggregateddeps.ComponentDependencies;
+import dagger.hilt.processor.internal.aliasof.AliasOfs;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import java.util.List;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+import javax.tools.Diagnostic;
+
+/** Contains metadata about the given hilt root. */
+public final class RootMetadata {
+ private static final ClassName APPLICATION_CONTEXT_MODULE =
+ ClassName.get("dagger.hilt.android.internal.modules", "ApplicationContextModule");
+
+ static RootMetadata create(
+ Root root,
+ ComponentTree componentTree,
+ ComponentDependencies deps,
+ ProcessingEnvironment env) {
+ RootMetadata metadata = new RootMetadata(root, componentTree, deps, env);
+ metadata.validate();
+ return metadata;
+ }
+
+ static RootMetadata copyWithNewTree(
+ RootMetadata other,
+ ComponentTree componentTree) {
+ return create(other.root, componentTree, other.deps, other.env);
+ }
+
+ private final Root root;
+ private final ProcessingEnvironment env;
+ private final Elements elements;
+ private final ComponentTree componentTree;
+ private final ComponentDependencies deps;
+ private final Supplier<ImmutableSetMultimap<ClassName, ClassName>> scopesByComponent =
+ memoize(this::getScopesByComponentUncached);
+ private final Supplier<TestRootMetadata> testRootMetadata =
+ memoize(this::testRootMetadataUncached);
+
+ private RootMetadata(
+ Root root,
+ ComponentTree componentTree,
+ ComponentDependencies deps,
+ ProcessingEnvironment env) {
+ this.root = root;
+ this.env = env;
+ this.elements = env.getElementUtils();
+ this.componentTree = componentTree;
+ this.deps = deps;
+ }
+
+ public Root root() {
+ return root;
+ }
+
+ public ComponentTree componentTree() {
+ return componentTree;
+ }
+
+ public ComponentDependencies deps() {
+ return deps;
+ }
+
+ public ImmutableSet<TypeElement> modules(ClassName componentName) {
+ return deps.modules().get(componentName, root.classname(), root.isTestRoot());
+ }
+
+ public ImmutableSet<TypeName> entryPoints(ClassName componentName) {
+ return ImmutableSet.<TypeName>builder()
+ .addAll(getUserDefinedEntryPoints(componentName))
+ .add(componentName)
+ .build();
+ }
+
+ public ImmutableSet<ClassName> scopes(ClassName componentName) {
+ return scopesByComponent.get().get(componentName);
+ }
+
+ /**
+ * Returns all modules in the given component that do not have accessible default constructors.
+ * Note that a non-static module nested in an outer class is considered to have no default
+ * constructors, since an instance of the outer class is needed to construct the module. This also
+ * filters out framework modules directly referenced by the codegen, since those are already known
+ * about and are specifically handled in the codegen.
+ */
+ public ImmutableSet<TypeElement> modulesThatDaggerCannotConstruct(ClassName componentName) {
+ return modules(componentName).stream()
+ .filter(module -> !daggerCanConstruct(module))
+ .filter(module -> !APPLICATION_CONTEXT_MODULE.equals(ClassName.get(module)))
+ .collect(toImmutableSet());
+ }
+
+ public TestRootMetadata testRootMetadata() {
+ return testRootMetadata.get();
+ }
+
+ public boolean waitForBindValue() {
+ return false;
+ }
+
+ private TestRootMetadata testRootMetadataUncached() {
+ return TestRootMetadata.of(env, root().element());
+ }
+
+ /**
+ * Validates that the {@link RootType} annotation is compatible with its {@link TypeElement} and
+ * {@link ComponentDependencies}.
+ */
+ private void validate() {
+
+ // Only test modules in the application component can be missing default constructor
+ for (ComponentDescriptor componentDescriptor : componentTree.getComponentDescriptors()) {
+ ClassName componentName = componentDescriptor.component();
+ for (TypeElement extraModule : modulesThatDaggerCannotConstruct(componentName)) {
+ if (root.type().isTestRoot() && !componentName.equals(ClassNames.SINGLETON_COMPONENT)) {
+ env.getMessager()
+ .printMessage(
+ Diagnostic.Kind.ERROR,
+ "[Hilt] All test modules (unless installed in ApplicationComponent) must use "
+ + "static provision methods or have a visible, no-arg constructor. Found: "
+ + extraModule.getQualifiedName(),
+ root.element());
+ } else if (!root.type().isTestRoot()) {
+ env.getMessager()
+ .printMessage(
+ Diagnostic.Kind.ERROR,
+ "[Hilt] All modules must be static and use static provision methods or have a "
+ + "visible, no-arg constructor. Found: "
+ + extraModule.getQualifiedName(),
+ root.element());
+ }
+ }
+ }
+ }
+
+ private ImmutableSet<TypeName> getUserDefinedEntryPoints(ClassName componentName) {
+ ImmutableSet.Builder<TypeName> entryPointSet = ImmutableSet.builder();
+ entryPointSet.add(ClassNames.GENERATED_COMPONENT);
+ for (TypeElement element :
+ deps.entryPoints().get(componentName, root.classname(), root.isTestRoot())) {
+ entryPointSet.add(ClassName.get(element));
+ }
+ return entryPointSet.build();
+ }
+
+ private ImmutableSetMultimap<ClassName, ClassName> getScopesByComponentUncached() {
+ ImmutableSetMultimap.Builder<ClassName, ClassName> builder = ImmutableSetMultimap.builder();
+
+ ImmutableSet<ClassName> defineComponentScopes =
+ componentTree.getComponentDescriptors().stream()
+ .flatMap(descriptor -> descriptor.scopes().stream())
+ .collect(toImmutableSet());
+
+ AliasOfs aliasOfs = new AliasOfs(env, defineComponentScopes);
+
+ for (ComponentDescriptor componentDescriptor : componentTree.getComponentDescriptors()) {
+ for (ClassName scope : componentDescriptor.scopes()) {
+ builder.put(componentDescriptor.component(), scope);
+ builder.putAll(componentDescriptor.component(), aliasOfs.getAliasesFor(scope));
+ }
+ }
+
+ return builder.build();
+ }
+
+ private static boolean daggerCanConstruct(TypeElement type) {
+ KotlinMetadataUtil metadataUtil = KotlinMetadataUtils.getMetadataUtil();
+ boolean isKotlinObject =
+ metadataUtil.isObjectClass(type) || metadataUtil.isCompanionObjectClass(type);
+ if (isKotlinObject) {
+ // Treat Kotlin object modules as if Dagger can construct them (it technically can't, but it
+ // doesn't need to as it can use them since all their provision methods are static).
+ return true;
+ }
+
+ return !isInnerClass(type)
+ && !hasNonDaggerAbstractMethod(type)
+ && (hasOnlyStaticProvides(type) || hasVisibleEmptyConstructor(type));
+ }
+
+ private static boolean isInnerClass(TypeElement type) {
+ return type.getNestingKind().isNested() && !type.getModifiers().contains(STATIC);
+ }
+
+ private static boolean hasNonDaggerAbstractMethod(TypeElement type) {
+ // TODO(erichang): Actually this isn't really supported b/28989613
+ return ElementFilter.methodsIn(type.getEnclosedElements()).stream()
+ .filter(method -> method.getModifiers().contains(ABSTRACT))
+ .anyMatch(method -> !Processors.hasDaggerAbstractMethodAnnotation(method));
+ }
+
+ private static boolean hasOnlyStaticProvides(TypeElement type) {
+ // TODO(erichang): Check for @Produces too when we have a producers story
+ return ElementFilter.methodsIn(type.getEnclosedElements()).stream()
+ .filter(method -> Processors.hasAnnotation(method, ClassNames.PROVIDES))
+ .allMatch(method -> method.getModifiers().contains(STATIC));
+ }
+
+ private static boolean hasVisibleEmptyConstructor(TypeElement type) {
+ List<ExecutableElement> constructors = ElementFilter.constructorsIn(type.getEnclosedElements());
+ return constructors.isEmpty()
+ || constructors.stream()
+ .filter(constructor -> constructor.getParameters().isEmpty())
+ .anyMatch(constructor -> !constructor.getModifiers().contains(PRIVATE));
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/root/RootProcessor.java b/java/dagger/hilt/processor/internal/root/RootProcessor.java
new file mode 100644
index 000000000..8f8c94874
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/root/RootProcessor.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.root;
+
+import static com.google.common.base.Preconditions.checkState;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.AGGREGATING;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.service.AutoService;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import dagger.hilt.processor.internal.BaseProcessor;
+import dagger.hilt.processor.internal.ComponentTree;
+import dagger.hilt.processor.internal.ProcessorErrors;
+import dagger.hilt.processor.internal.aggregateddeps.ComponentDependencies;
+import dagger.hilt.processor.internal.definecomponent.DefineComponents;
+import dagger.hilt.processor.internal.generatesrootinput.GeneratesRootInputs;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.Processor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
+
+/** Processor that outputs dagger components based on transitive build deps. */
+@IncrementalAnnotationProcessor(AGGREGATING)
+@AutoService(Processor.class)
+public final class RootProcessor extends BaseProcessor {
+ private final List<ClassName> rootNames = new ArrayList<>();
+ private final Set<ClassName> processed = new HashSet<>();
+ private boolean isTestEnv;
+ // TODO(bcorso): Consider using a Dagger component to create/scope these objects
+ private final DefineComponents defineComponents = DefineComponents.create();
+ private GeneratesRootInputs generatesRootInputs;
+
+ @Override
+ public synchronized void init(ProcessingEnvironment processingEnvironment) {
+ super.init(processingEnvironment);
+ generatesRootInputs = new GeneratesRootInputs(processingEnvironment);
+ }
+
+ @Override
+ public ImmutableSet<String> getSupportedAnnotationTypes() {
+ return ImmutableSet.<String>builder()
+ .addAll(
+ Arrays.stream(RootType.values())
+ .map(rootType -> rootType.className().toString())
+ .collect(toImmutableSet()))
+ .build();
+ }
+
+ @Override
+ public void processEach(TypeElement annotation, Element element) throws Exception {
+ TypeElement rootElement = MoreElements.asType(element);
+ boolean isTestRoot = RootType.of(getProcessingEnv(), rootElement).isTestRoot();
+ checkState(
+ rootNames.isEmpty() || isTestEnv == isTestRoot,
+ "Cannot mix test roots with non-test roots:"
+ + "\n\tNon-Test Roots: %s"
+ + "\n\tTest Roots: %s",
+ isTestRoot ? rootNames : rootElement,
+ isTestRoot ? rootElement : rootNames);
+ isTestEnv = isTestRoot;
+
+ rootNames.add(ClassName.get(rootElement));
+ if (isTestEnv) {
+ new TestInjectorGenerator(
+ getProcessingEnv(),
+ TestRootMetadata.of(getProcessingEnv(), rootElement)).generate();
+ } else {
+ ProcessorErrors.checkState(
+ rootNames.size() <= 1, element, "More than one root found: %s", rootNames);
+ }
+ }
+
+ @Override
+ public void postRoundProcess(RoundEnvironment roundEnv) throws Exception {
+ Set<Element> newElements = generatesRootInputs.getElementsToWaitFor(roundEnv);
+ if (!processed.isEmpty() ) {
+ checkState(
+ newElements.isEmpty(),
+ "Found extra modules after compilation: %s\n"
+ + "(If you are adding an annotation processor that generates root input for hilt, "
+ + "the annotation must be annotated with @dagger.hilt.GeneratesRootInput.\n)",
+ newElements);
+ }
+
+ if (!newElements.isEmpty()) {
+ // Skip further processing since there's new elements that generate root inputs in this round.
+ return;
+ }
+
+ ImmutableList<Root> rootsToProcess =
+ rootNames.stream()
+ .filter(rootName -> !processed.contains(rootName))
+ // We create a new root element each round to avoid the jdk8 bug where
+ // TypeElement.equals does not work for elements across processing rounds.
+ .map(rootName -> getElementUtils().getTypeElement(rootName.toString()))
+ .map(rootElement -> Root.create(rootElement, getProcessingEnv()))
+ .collect(toImmutableList());
+
+ if (rootsToProcess.isEmpty()) {
+ // Skip further processing since there's no roots that need processing.
+ return;
+ }
+
+ // TODO(bcorso): Currently, if there's an exception in any of the roots we stop processing
+ // all roots. We should consider if it's worth trying to continue processing for other
+ // roots. At the moment, I think it's rare that if one root failed the others would not.
+ try {
+ ComponentTree tree = defineComponents.getComponentTree(getElementUtils());
+ ComponentDependencies deps = ComponentDependencies.from(
+ tree.getComponentDescriptors(), getElementUtils());
+ ImmutableList<RootMetadata> rootMetadatas =
+ rootsToProcess.stream()
+ .map(root -> RootMetadata.create(root, tree, deps, getProcessingEnv()))
+ .collect(toImmutableList());
+
+ for (RootMetadata rootMetadata : rootMetadatas) {
+ setProcessingState(rootMetadata.root());
+ generateComponents(rootMetadata);
+ }
+
+ if (isTestEnv) {
+ generateTestComponentData(rootMetadatas);
+ }
+ } catch (Exception e) {
+ for (Root root : rootsToProcess) {
+ processed.add(root.classname());
+ }
+ throw e;
+ }
+ }
+
+ private void setProcessingState(Root root) {
+ processed.add(root.classname());
+ }
+
+ private void generateComponents(RootMetadata rootMetadata) throws IOException {
+ RootGenerator.generate(rootMetadata, getProcessingEnv());
+ }
+
+ private void generateTestComponentData(ImmutableList<RootMetadata> rootMetadatas)
+ throws IOException {
+ for (RootMetadata rootMetadata : rootMetadatas) {
+ // TODO(bcorso): Consider moving this check earlier into processEach.
+ TypeElement testElement = rootMetadata.testRootMetadata().testElement();
+ ProcessorErrors.checkState(
+ testElement.getModifiers().contains(PUBLIC),
+ testElement,
+ "Hilt tests must be public, but found: %s",
+ testElement);
+ new TestComponentDataGenerator(getProcessingEnv(), rootMetadata).generate();
+ }
+ new TestComponentDataSupplierGenerator(getProcessingEnv(), rootMetadatas).generate();
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/root/RootType.java b/java/dagger/hilt/processor/internal/root/RootType.java
new file mode 100644
index 000000000..3545231c7
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/root/RootType.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.root;
+
+import com.squareup.javapoet.ClassName;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.Processors;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.TypeElement;
+
+/** The valid root types for Hilt applications. */
+// TODO(erichang): Fix this class so we don't have to have placeholders
+ enum RootType {
+ ROOT(ClassNames.HILT_ANDROID_APP),
+
+ // Placeholder to make sure @HiltAndroidTest usages get processed
+ HILT_ANDROID_TEST_ROOT(ClassNames.HILT_ANDROID_TEST),
+
+ TEST_ROOT(ClassNames.INTERNAL_TEST_ROOT);
+
+ @SuppressWarnings("ImmutableEnumChecker")
+ private final ClassName annotation;
+
+ RootType(ClassName annotation) {
+ this.annotation = annotation;
+ }
+
+ public boolean isTestRoot() {
+ return this == TEST_ROOT;
+ }
+
+ public ClassName className() {
+ return annotation;
+ }
+
+ public static RootType of(ProcessingEnvironment env, TypeElement element) {
+ if (Processors.hasAnnotation(element, ClassNames.HILT_ANDROID_APP)) {
+ return ROOT;
+ } else if (Processors.hasAnnotation(element, ClassNames.HILT_ANDROID_TEST)) {
+ return TEST_ROOT;
+ } else if (Processors.hasAnnotation(element, ClassNames.INTERNAL_TEST_ROOT)) {
+ return TEST_ROOT;
+ }
+ throw new IllegalStateException("Unknown root type: " + element);
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/root/TestComponentDataGenerator.java b/java/dagger/hilt/processor/internal/root/TestComponentDataGenerator.java
new file mode 100644
index 000000000..284f8cdee
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/root/TestComponentDataGenerator.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.root;
+
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static java.util.stream.Collectors.joining;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+import static javax.lang.model.util.ElementFilter.constructorsIn;
+
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeSpec;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.ComponentNames;
+import dagger.hilt.processor.internal.Processors;
+import java.io.IOException;
+import java.util.List;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+
+/** Generates an implementation of {@link dagger.hilt.android.internal.TestComponentData}. */
+public final class TestComponentDataGenerator {
+ private final ProcessingEnvironment processingEnv;
+ private final RootMetadata rootMetadata;
+ private final ClassName name;
+
+ public TestComponentDataGenerator(
+ ProcessingEnvironment processingEnv,
+ RootMetadata rootMetadata) {
+ this.processingEnv = processingEnv;
+ this.rootMetadata = rootMetadata;
+ this.name =
+ Processors.append(
+ Processors.getEnclosedClassName(rootMetadata.testRootMetadata().testName()),
+ "_ComponentDataHolder");
+ }
+
+ /**
+ *
+ *
+ * <pre><code>{@code
+ * public final class FooTest_ComponentDataHolder {
+ * public static TestComponentData get() {
+ * return new TestComponentData(
+ * false, // waitForBindValue
+ * testInstance -> injectInternal(($1T) testInstance),
+ * Arrays.asList(FooTest.TestModule.class, ...),
+ * modules ->
+ * DaggerFooTest_ApplicationComponent.builder()
+ * .applicationContextModule(
+ * new ApplicationContextModule(ApplicationProvider.getApplicationContext()))
+ * .testModule((FooTest.TestModule) modules.get(FooTest.TestModule.class))
+ * .testModule(modules.containsKey(FooTest.TestModule.class)
+ * ? (FooTest.TestModule) modules.get(FooTest.TestModule.class)
+ * : ((TestInstace) testInstance).new TestModule())
+ * .build());
+ * }
+ * }
+ * }</code></pre>
+ */
+ public void generate() throws IOException {
+ TypeSpec.Builder generator =
+ TypeSpec.classBuilder(name)
+ .addModifiers(PUBLIC, FINAL)
+ .addMethod(MethodSpec.constructorBuilder().addModifiers(PRIVATE).build())
+ .addMethod(getMethod())
+ .addMethod(getTestInjectInternalMethod());
+
+ Processors.addGeneratedAnnotation(
+ generator, processingEnv, ClassNames.ROOT_PROCESSOR.toString());
+
+ JavaFile.builder(rootMetadata.testRootMetadata().testName().packageName(), generator.build())
+ .build()
+ .writeTo(processingEnv.getFiler());
+ }
+
+ private MethodSpec getMethod() {
+ TypeElement testElement = rootMetadata.testRootMetadata().testElement();
+ ClassName component =
+ ComponentNames.generatedComponent(
+ ClassName.get(testElement), ClassNames.SINGLETON_COMPONENT);
+ ImmutableSet<TypeElement> daggerRequiredModules =
+ rootMetadata.modulesThatDaggerCannotConstruct(ClassNames.SINGLETON_COMPONENT);
+ ImmutableSet<TypeElement> hiltRequiredModules =
+ daggerRequiredModules.stream()
+ .filter(module -> !canBeConstructedByHilt(module, testElement))
+ .collect(toImmutableSet());
+
+ return MethodSpec.methodBuilder("get")
+ .addModifiers(PUBLIC, STATIC)
+ .returns(ClassNames.TEST_COMPONENT_DATA)
+ .addStatement(
+ "return new $T($L, $L, $L, $L, $L)",
+ ClassNames.TEST_COMPONENT_DATA,
+ rootMetadata.waitForBindValue(),
+ CodeBlock.of("testInstance -> injectInternal(($1T) testInstance)", testElement),
+ getElementsListed(daggerRequiredModules),
+ getElementsListed(hiltRequiredModules),
+ CodeBlock.of(
+ "(modules, testInstance, autoAddModuleEnabled) -> $T.builder()\n"
+ + ".applicationContextModule(new $T($T.getApplicationContext()))\n"
+ + "$L"
+ + ".build()",
+ Processors.prepend(Processors.getEnclosedClassName(component), "Dagger"),
+ ClassNames.APPLICATION_CONTEXT_MODULE,
+ ClassNames.APPLICATION_PROVIDER,
+ daggerRequiredModules.stream()
+ .map(module -> getAddModuleStatement(module, testElement))
+ .collect(joining("\n"))))
+ .build();
+ }
+
+ /**
+ *
+ *
+ * <pre><code>
+ * .testModule(modules.get(FooTest.TestModule.class))
+ * </code></pre>
+ *
+ * <pre><code>
+ * .testModule(autoAddModuleEnabled
+ * ? ((FooTest) testInstance).new TestModule()
+ * : (FooTest.TestModule) modules.get(FooTest.TestModule.class))
+ * </code></pre>
+ */
+ private static String getAddModuleStatement(TypeElement module, TypeElement testElement) {
+ ClassName className = ClassName.get(module);
+ return canBeConstructedByHilt(module, testElement)
+ ? CodeBlock.of(
+ ".$1L(autoAddModuleEnabled\n"
+ // testInstance can never be null if we reach here, because this flag can be
+ // turned on only when testInstance is not null
+ + " ? (($3T) testInstance).new $4L()\n"
+ + " : ($2T) modules.get($2T.class))",
+ Processors.upperToLowerCamel(className.simpleName()),
+ className,
+ className.enclosingClassName(),
+ className.simpleName())
+ .toString()
+ : CodeBlock.of(
+ ".$1L(($2T) modules.get($2T.class))",
+ Processors.upperToLowerCamel(className.simpleName()),
+ className)
+ .toString();
+ }
+
+ private static boolean canBeConstructedByHilt(TypeElement module, TypeElement testElement) {
+ return hasOnlyAccessibleNoArgConstructor(module)
+ && module.getEnclosingElement().equals(testElement);
+ }
+
+ private static boolean hasOnlyAccessibleNoArgConstructor(TypeElement module) {
+ List<ExecutableElement> declaredConstructors = constructorsIn(module.getEnclosedElements());
+ return declaredConstructors.isEmpty()
+ || (declaredConstructors.size() == 1
+ && !declaredConstructors.get(0).getModifiers().contains(PRIVATE)
+ && declaredConstructors.get(0).getParameters().isEmpty());
+ }
+
+ /* Arrays.asList(FooTest.TestModule.class, ...) */
+ private static CodeBlock getElementsListed(ImmutableSet<TypeElement> modules) {
+ return modules.isEmpty()
+ ? CodeBlock.of("$T.emptySet()", ClassNames.COLLECTIONS)
+ : CodeBlock.of(
+ "new $T<>($T.asList($L))",
+ ClassNames.HASH_SET,
+ ClassNames.ARRAYS,
+ modules.stream()
+ .map(module -> CodeBlock.of("$T.class", module).toString())
+ .collect(joining(",")));
+ }
+
+ private MethodSpec getTestInjectInternalMethod() {
+ TypeElement testElement = rootMetadata.testRootMetadata().testElement();
+ ClassName testName = ClassName.get(testElement);
+ return MethodSpec.methodBuilder("injectInternal")
+ .addModifiers(PRIVATE, STATIC)
+ .addParameter(testName, "testInstance")
+ .addAnnotation(
+ AnnotationSpec.builder(SuppressWarnings.class)
+ .addMember("value", "$S", "unchecked")
+ .build())
+ .addStatement("$L.injectTest(testInstance)", getInjector(testElement))
+ .build();
+ }
+
+ private static CodeBlock getInjector(TypeElement testElement) {
+ return CodeBlock.of(
+ "(($T) (($T) $T.getApplicationContext()).generatedComponent())",
+ ClassNames.TEST_INJECTOR,
+ ClassNames.GENERATED_COMPONENT_MANAGER,
+ ClassNames.APPLICATION_PROVIDER);
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/root/TestComponentDataSupplierGenerator.java b/java/dagger/hilt/processor/internal/root/TestComponentDataSupplierGenerator.java
new file mode 100644
index 000000000..e5a83b63c
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/root/TestComponentDataSupplierGenerator.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.root;
+
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PROTECTED;
+import static javax.lang.model.element.Modifier.PUBLIC;
+
+import com.google.common.collect.ImmutableList;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.WildcardTypeName;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.Processors;
+import java.io.IOException;
+import javax.annotation.processing.ProcessingEnvironment;
+
+/** Generates an implementation of {@link dagger.hilt.android.internal.TestComponentDataSupplier} */
+public final class TestComponentDataSupplierGenerator {
+ private static final ClassName TEST_COMPONENT_DATA_SUPPLIER_IMPL =
+ ClassName.get("dagger.hilt.android.internal.testing", "TestComponentDataSupplierImpl");
+ private static final ParameterizedTypeName CLASS_TYPE =
+ ParameterizedTypeName.get(ClassNames.CLASS, WildcardTypeName.subtypeOf(TypeName.OBJECT));
+ private static final ParameterizedTypeName TEST_COMPONENT_DATA_MAP_TYPE =
+ ParameterizedTypeName.get(ClassNames.MAP, CLASS_TYPE, ClassNames.TEST_COMPONENT_DATA);
+
+ private final ProcessingEnvironment processingEnv;
+ private final ImmutableList<RootMetadata> rootMetadatas;
+
+ public TestComponentDataSupplierGenerator(
+ ProcessingEnvironment processingEnv,
+ ImmutableList<RootMetadata> rootMetadatas) {
+ this.processingEnv = processingEnv;
+ this.rootMetadatas = rootMetadatas;
+ }
+
+ /**
+ * <pre><code>{@code
+ * public final class TestComponentDataSupplierImpl extends TestComponentDataSupplier {
+ * private final Map<Class<?>, TestComponentData> testComponentDataMap = new HashMap<>();
+ *
+ * protected TestComponentDataSupplierImpl() {
+ * testComponentDataMap.put(FooTest.class, new FooTest_ComponentData());
+ * testComponentDataMap.put(BarTest.class, new BarTest_ComponentData());
+ * ...
+ * }
+ *
+ * @Override
+ * protected Map<Class<?>, TestComponentData> get() {
+ * return testComponentDataMap;
+ * }
+ * }
+ * }</code></pre>
+ */
+ public void generate() throws IOException {
+ TypeSpec.Builder generator =
+ TypeSpec.classBuilder(TEST_COMPONENT_DATA_SUPPLIER_IMPL)
+ .addModifiers(PUBLIC, FINAL)
+ .superclass(ClassNames.TEST_COMPONENT_DATA_SUPPLIER)
+ .addField(
+ FieldSpec.builder(
+ TEST_COMPONENT_DATA_MAP_TYPE, "testComponentDataMap", PRIVATE, FINAL)
+ .initializer("new $T<>($L)", ClassNames.HASH_MAP, rootMetadatas.size())
+ .build())
+ .addMethod(constructor())
+ .addMethod(getMethod());
+
+ Processors.addGeneratedAnnotation(
+ generator, processingEnv, ClassNames.ROOT_PROCESSOR.toString());
+
+ JavaFile.builder(TEST_COMPONENT_DATA_SUPPLIER_IMPL.packageName(), generator.build())
+ .build()
+ .writeTo(processingEnv.getFiler());
+ }
+
+
+ private MethodSpec constructor() {
+ MethodSpec.Builder constructor = MethodSpec.constructorBuilder();
+ for (RootMetadata rootMetadata : rootMetadatas) {
+ ClassName testName = rootMetadata.testRootMetadata().testName();
+ ClassName testComponentDataHolderName =
+ Processors.append(Processors.getEnclosedClassName(testName), "_ComponentDataHolder");
+ constructor.addStatement(
+ "testComponentDataMap.put($T.class, $T.get())",
+ testName,
+ testComponentDataHolderName);
+ }
+ return constructor.build();
+ }
+
+ private MethodSpec getMethod() {
+ return MethodSpec.methodBuilder("get")
+ .addAnnotation(Override.class)
+ .addModifiers(PROTECTED)
+ .returns(TEST_COMPONENT_DATA_MAP_TYPE)
+ .addStatement("return testComponentDataMap")
+ .build();
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/root/TestInjectorGenerator.java b/java/dagger/hilt/processor/internal/root/TestInjectorGenerator.java
new file mode 100644
index 000000000..fc97feda6
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/root/TestInjectorGenerator.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.root;
+
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.Processors;
+import java.io.IOException;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+
+/** Generates an entry point for a test. */
+public final class TestInjectorGenerator {
+ private final ProcessingEnvironment env;
+ private final TestRootMetadata metadata;
+
+ TestInjectorGenerator(ProcessingEnvironment env, TestRootMetadata metadata) {
+ this.env = env;
+ this.metadata = metadata;
+ }
+
+ // @GeneratedEntryPoint
+ // @InstallIn(SingletonComponent.class)
+ // public interface FooTest_GeneratedInjector extends TestInjector<FooTest> {}
+ public void generate() throws IOException {
+ TypeSpec.Builder builder =
+ TypeSpec.interfaceBuilder(metadata.testInjectorName())
+ .addOriginatingElement(metadata.testElement())
+ .addAnnotation(Processors.getOriginatingElementAnnotation(metadata.testElement()))
+ .addAnnotation(ClassNames.GENERATED_ENTRY_POINT)
+ .addAnnotation(
+ AnnotationSpec.builder(ClassNames.INSTALL_IN)
+ .addMember("value", "$T.class", installInComponent(metadata.testElement()))
+ .build())
+ .addModifiers(Modifier.PUBLIC)
+ .addSuperinterface(
+ ParameterizedTypeName.get(ClassNames.TEST_INJECTOR, metadata.testName()))
+ .addMethod(
+ MethodSpec.methodBuilder("injectTest")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
+ .addParameter(
+ metadata.testName(),
+ Processors.upperToLowerCamel(metadata.testName().simpleName()))
+ .build());
+
+ Processors.addGeneratedAnnotation(builder, env, getClass());
+
+ JavaFile.builder(metadata.testInjectorName().packageName(), builder.build())
+ .build()
+ .writeTo(env.getFiler());
+ }
+
+ private static ClassName installInComponent(TypeElement testElement) {
+ return ClassNames.SINGLETON_COMPONENT;
+ }
+}
diff --git a/java/dagger/hilt/processor/internal/root/TestRootMetadata.java b/java/dagger/hilt/processor/internal/root/TestRootMetadata.java
new file mode 100644
index 000000000..561beb789
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/root/TestRootMetadata.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.root;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.value.AutoValue;
+import com.squareup.javapoet.ClassName;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.ProcessorErrors;
+import dagger.hilt.processor.internal.Processors;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+/** Metadata class for {@code InternalTestRoot} annotated classes. */
+@AutoValue
+abstract class TestRootMetadata {
+
+ /** Returns the {@link TypeElement} for the test class. */
+ abstract TypeElement testElement();
+
+ /** Returns the {@link TypeElement} for the base application. */
+ abstract TypeElement baseElement();
+
+ /** Returns the {@link ClassName} for the test class. */
+ ClassName testName() {
+ return ClassName.get(testElement());
+ }
+
+ /** Returns the {@link ClassName} for the base application. */
+ ClassName baseAppName() {
+ return ClassName.get(baseElement());
+ }
+
+ /** The name of the generated Hilt test application class for the given test name. */
+ ClassName appName() {
+ return Processors.append(Processors.getEnclosedClassName(testName()), "_Application");
+ }
+
+ /** The name of the generated Hilt test application class for the given test name. */
+ ClassName testInjectorName() {
+ return Processors.append(Processors.getEnclosedClassName(testName()), "_GeneratedInjector");
+ }
+
+ static TestRootMetadata of(ProcessingEnvironment env, Element element) {
+
+ TypeElement testElement = MoreElements.asType(element);
+
+ TypeElement baseElement =
+ env.getElementUtils().getTypeElement(ClassNames.MULTI_DEX_APPLICATION.toString());
+ ProcessorErrors.checkState(
+ !Processors.hasAnnotation(element, ClassNames.ANDROID_ENTRY_POINT),
+ element,
+ "Tests cannot be annotated with @AndroidEntryPoint. Please use @HiltAndroidTest");
+
+ ProcessorErrors.checkState(
+ Processors.hasAnnotation(element, ClassNames.HILT_ANDROID_TEST),
+ element,
+ "Tests must be annotated with @HiltAndroidTest");
+
+ return new AutoValue_TestRootMetadata(testElement, baseElement);
+ }
+}
diff --git a/java/dagger/hilt/testing/BUILD b/java/dagger/hilt/testing/BUILD
new file mode 100644
index 000000000..3ea1c5f4b
--- /dev/null
+++ b/java/dagger/hilt/testing/BUILD
@@ -0,0 +1,47 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Description:
+# Testing libraries for Hilt.
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "package_info",
+ srcs = ["package-info.java"],
+ deps = [
+ "@google_bazel_common//third_party/java/jsr305_annotations",
+ ],
+)
+
+java_library(
+ name = "test_install_in",
+ testonly = 1,
+ srcs = ["TestInstallIn.java"],
+ exported_plugins = [
+ "//java/dagger/hilt/processor/internal/aggregateddeps:plugin",
+ ],
+ exports = [
+ "//java/dagger/hilt/processor/internal/aggregateddeps:annotation",
+ ],
+ deps = [
+ ":package_info",
+ "//java/dagger/hilt:generates_root_input",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/testing/TestInstallIn.java b/java/dagger/hilt/testing/TestInstallIn.java
new file mode 100644
index 000000000..dfe138988
--- /dev/null
+++ b/java/dagger/hilt/testing/TestInstallIn.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.testing;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import dagger.hilt.GeneratesRootInput;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that replaces one or more {@link dagger.hilt.InstallIn} modules with the annotated
+ * module in tests.
+ *
+ * <p>The annotated class must also be annotated with {@link dagger.Module}.
+ *
+ * <p>Example:
+ *
+ * <pre><code>
+ * // Replaces FooModule with FakeFooModule, and installs it into the same component as FooModule.
+ * {@literal @}Module
+ * {@literal @}TestInstallIn(components = SingletonComponent.class, replaces = FooModule.class)
+ * public final class FakeFooModule {
+ * {@literal @}Provides
+ * static Foo provideFoo() {
+ * return new FakeFoo();
+ * }
+ * }
+ * </code></pre>
+ *
+ * @see <a href="https://dagger.dev/hilt/modules">Hilt Modules</a>
+ */
+@Retention(CLASS)
+@Target({ElementType.TYPE})
+@GeneratesRootInput
+public @interface TestInstallIn {
+ /** Returns the component(s) into which the annotated module will be installed. */
+ Class<?>[] components();
+
+ /** Returns the {@link InstallIn} module(s) that the annotated class will replace in tests. */
+ Class<?>[] replaces();
+}
diff --git a/java/dagger/hilt/testing/package-info.java b/java/dagger/hilt/testing/package-info.java
new file mode 100644
index 000000000..5837343c9
--- /dev/null
+++ b/java/dagger/hilt/testing/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package contains APIs for writing tests with Hilt.
+ *
+ * @see <a href="https://dagger.dev/hilt/testing">Hilt Testing</a>
+ */
+@ParametersAreNonnullByDefault
+package dagger.hilt.testing;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/java/dagger/internal/ConfigureInitializationParameters.java b/java/dagger/internal/ConfigureInitializationParameters.java
deleted file mode 100644
index 1ca0fbb76..000000000
--- a/java/dagger/internal/ConfigureInitializationParameters.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2019 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal;
-
-import static java.lang.annotation.ElementType.METHOD;
-
-import java.lang.annotation.Target;
-
-/**
- * Annotates a {@code configureInitialization()} method with {@code ComponentRequirement}s that it
- * accepts as parameters.
- */
-@Target(METHOD)
-public @interface ConfigureInitializationParameters {
- /**
- * The list of parameters.
- *
- * Each value is a {@link dagger.internal.codegen.serialization.ComponentRequirementProto}
- * serialized in Base64.
- */
- String[] value() default {};
-}
diff --git a/java/dagger/internal/GenerationOptions.java b/java/dagger/internal/GenerationOptions.java
deleted file mode 100644
index 996cd1d89..000000000
--- a/java/dagger/internal/GenerationOptions.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Target;
-
-/**
- * Metadata annotation for base subcomponent implementations in ahead-of-time compilations. This
- * propagates any compiler options related to code generation so that later compilations can
- * recreate the model of the generated code of superclass implementations.
- */
-@Target(ElementType.TYPE)
-public @interface GenerationOptions {
- boolean fastInit();
-}
diff --git a/java/dagger/internal/InjectedFieldSignature.java b/java/dagger/internal/InjectedFieldSignature.java
new file mode 100644
index 000000000..55d3285c9
--- /dev/null
+++ b/java/dagger/internal/InjectedFieldSignature.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a {@link dagger.MembersInjector} method for injecting a field with the signature of the
+ * field intended to inject.
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.METHOD)
+public @interface InjectedFieldSignature {
+ String value();
+}
diff --git a/java/dagger/internal/MapBuilder.java b/java/dagger/internal/MapBuilder.java
index 25e2b5b86..12824d1a4 100644
--- a/java/dagger/internal/MapBuilder.java
+++ b/java/dagger/internal/MapBuilder.java
@@ -50,11 +50,9 @@ public final class MapBuilder<K, V> {
}
public Map<K, V> build() {
- switch (contributions.size()) {
- case 0:
- return Collections.emptyMap();
- default:
- return Collections.unmodifiableMap(contributions);
+ if (contributions.isEmpty()) {
+ return Collections.emptyMap();
}
+ return Collections.unmodifiableMap(contributions);
}
}
diff --git a/java/dagger/internal/MapFactory.java b/java/dagger/internal/MapFactory.java
index 8eb078312..39748c9ad 100644
--- a/java/dagger/internal/MapFactory.java
+++ b/java/dagger/internal/MapFactory.java
@@ -66,11 +66,13 @@ public final class MapFactory<K, V> extends AbstractMapFactory<K, V, V> {
super(size);
}
+ @Override
public Builder<K, V> put(K key, Provider<V> providerOfValue) {
super.put(key, providerOfValue);
return this;
}
+ @Override
public Builder<K, V> putAll(Provider<Map<K, V>> mapFactory) {
super.putAll(mapFactory);
return this;
diff --git a/java/dagger/internal/MissingBindingFactory.java b/java/dagger/internal/MissingBindingFactory.java
deleted file mode 100644
index 993d15047..000000000
--- a/java/dagger/internal/MissingBindingFactory.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal;
-
-/**
- * A {@link Factory} that always throws on calls to {@link Factory#get()}. This is necessary in
- * ahead-of-time subcomponents mode, where modifiable binding methods need to return a {@code
- * Provider<T>} to a framework instance initialization that is pruned and no longer in the binding
- * graph, but was present in a superclass implementation. This class fulfills that requirement but
- * is still practically unusable.
- */
-public final class MissingBindingFactory<T> implements Factory<T> {
- private static final MissingBindingFactory<Object> INSTANCE = new MissingBindingFactory<>();
-
- private MissingBindingFactory() {}
-
- @SuppressWarnings({"unchecked", "rawtypes"}) // safe covariant cast
- public static <T> Factory<T> create() {
- return (Factory) INSTANCE;
- }
-
- @Override
- public T get() {
- throw new AssertionError(
- "This binding is not part of the final binding graph. The key was requested by a binding "
- + "that was believed to possibly be part of the graph, but is no longer requested. "
- + "If this exception is thrown, it is the result of a Dagger bug.");
- }
-}
diff --git a/java/dagger/internal/ModifiableBinding.java b/java/dagger/internal/ModifiableBinding.java
deleted file mode 100644
index 1e658e42c..000000000
--- a/java/dagger/internal/ModifiableBinding.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2019 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal;
-
-import static java.lang.annotation.ElementType.METHOD;
-
-import java.lang.annotation.Target;
-
-/** Annotates methods that implement bindings that may be modified by subclass implementations. */
-@Target(METHOD)
-public @interface ModifiableBinding {
- /** {@code ModifiableBindingType} of the binding. */
- // TODO(ronshapiro): should this be a shared enum with dagger.internal.codegen?
- String modifiableBindingType();
-
- /** A {@link dagger.internal.codegen.serialization.BindingRequestProto} serialized in Base64. */
- String bindingRequest();
-
- /**
- * For a multibinding, the keys of all contributions it depends on in this implementation.
- */
- String[] multibindingContributions() default {};
-}
diff --git a/java/dagger/internal/ModifiableModule.java b/java/dagger/internal/ModifiableModule.java
deleted file mode 100644
index 983321e85..000000000
--- a/java/dagger/internal/ModifiableModule.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2019 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal;
-
-/**
- * Annotates methods that return {@linkplain dagger.Module modules} that may be modified by subclass
- * implementations.
- */
-public @interface ModifiableModule {
- /** The serialized {@code ComponentRequirement} of this method's module. */
- String value();
-}
diff --git a/java/dagger/internal/Preconditions.java b/java/dagger/internal/Preconditions.java
index 714a3535e..5c2d74067 100644
--- a/java/dagger/internal/Preconditions.java
+++ b/java/dagger/internal/Preconditions.java
@@ -51,6 +51,34 @@ public final class Preconditions {
}
/**
+ * Ensures that an object reference returned from a provides method is not null.
+ *
+ * @param reference an object reference
+ * @return the non-null reference that was validated
+ * @throws NullPointerException if {@code reference} is null
+ */
+ public static <T> T checkNotNullFromProvides(T reference) {
+ if (reference == null) {
+ throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method");
+ }
+ return reference;
+ }
+
+ /**
+ * Ensures that an object reference returned from a component method is not null.
+ *
+ * @param reference an object reference
+ * @return the non-null reference that was validated
+ * @throws NullPointerException if {@code reference} is null
+ */
+ public static <T> T checkNotNullFromComponent(T reference) {
+ if (reference == null) {
+ throw new NullPointerException("Cannot return null from a non-@Nullable component method");
+ }
+ return reference;
+ }
+
+ /**
* Ensures that an object reference passed as a parameter to the calling method is not null.
*
* @param reference an object reference
diff --git a/java/dagger/internal/SetBuilder.java b/java/dagger/internal/SetBuilder.java
index 41a2fc721..d65e7cb59 100644
--- a/java/dagger/internal/SetBuilder.java
+++ b/java/dagger/internal/SetBuilder.java
@@ -61,13 +61,12 @@ public final class SetBuilder<T> {
}
public Set<T> build() {
- switch (contributions.size()) {
- case 0:
- return Collections.emptySet();
- case 1:
- return Collections.singleton(contributions.get(0));
- default:
- return Collections.unmodifiableSet(new HashSet<>(contributions));
+ if (contributions.isEmpty()) {
+ return Collections.emptySet();
+ } else if (contributions.size() == 1) {
+ return Collections.singleton(contributions.get(0));
+ } else {
+ return Collections.unmodifiableSet(new HashSet<>(contributions));
}
}
}
diff --git a/java/dagger/internal/codegen/AnnotationCreatorGenerator.java b/java/dagger/internal/codegen/AnnotationCreatorGenerator.java
deleted file mode 100644
index 273f5526f..000000000
--- a/java/dagger/internal/codegen/AnnotationCreatorGenerator.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.squareup.javapoet.MethodSpec.constructorBuilder;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static com.squareup.javapoet.TypeSpec.classBuilder;
-import static dagger.internal.codegen.AnnotationExpression.createMethodName;
-import static dagger.internal.codegen.AnnotationExpression.getAnnotationCreatorClassName;
-import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PUBLIC;
-import static javax.lang.model.element.Modifier.STATIC;
-import static javax.lang.model.util.ElementFilter.methodsIn;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableList;
-import com.google.errorprone.annotations.CanIgnoreReturnValue;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.TypeName;
-import com.squareup.javapoet.TypeSpec;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import java.util.LinkedHashSet;
-import java.util.Optional;
-import java.util.Set;
-import javax.annotation.processing.Filer;
-import javax.inject.Inject;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.util.SimpleTypeVisitor6;
-
-/**
- * Generates classes that create annotation instances for an annotation type. The generated class
- * will have a private empty constructor, a static method that creates the annotation type itself,
- * and a static method that creates each annotation type that is nested in the top-level annotation
- * type.
- *
- * <p>So for an example annotation:
- *
- * <pre>
- * {@literal @interface} Foo {
- * String s();
- * int i();
- * Bar bar(); // an annotation defined elsewhere
- * }
- * </pre>
- *
- * the generated class will look like:
- *
- * <pre>
- * public final class FooCreator {
- * private FooCreator() {}
- *
- * public static Foo createFoo(String s, int i, Bar bar) { … }
- * public static Bar createBar(…) { … }
- * }
- * </pre>
- */
-class AnnotationCreatorGenerator extends SourceFileGenerator<TypeElement> {
- private static final ClassName AUTO_ANNOTATION =
- ClassName.get("com.google.auto.value", "AutoAnnotation");
-
- @Inject
- AnnotationCreatorGenerator(Filer filer, DaggerElements elements, SourceVersion sourceVersion) {
- super(filer, elements, sourceVersion);
- }
-
- @Override
- ClassName nameGeneratedType(TypeElement annotationType) {
- return getAnnotationCreatorClassName(annotationType);
- }
-
- @Override
- Element originatingElement(TypeElement annotationType) {
- return annotationType;
- }
-
- @Override
- Optional<TypeSpec.Builder> write(ClassName generatedTypeName, TypeElement annotationType) {
- TypeSpec.Builder annotationCreatorBuilder =
- classBuilder(generatedTypeName)
- .addModifiers(PUBLIC, FINAL)
- .addMethod(constructorBuilder().addModifiers(PRIVATE).build());
-
- for (TypeElement annotationElement : annotationsToCreate(annotationType)) {
- annotationCreatorBuilder.addMethod(buildCreateMethod(generatedTypeName, annotationElement));
- }
-
- return Optional.of(annotationCreatorBuilder);
- }
-
- private MethodSpec buildCreateMethod(ClassName generatedTypeName, TypeElement annotationElement) {
- String createMethodName = createMethodName(annotationElement);
- MethodSpec.Builder createMethod =
- methodBuilder(createMethodName)
- .addAnnotation(AUTO_ANNOTATION)
- .addModifiers(PUBLIC, STATIC)
- .returns(TypeName.get(annotationElement.asType()));
-
- ImmutableList.Builder<CodeBlock> parameters = ImmutableList.builder();
- for (ExecutableElement annotationMember : methodsIn(annotationElement.getEnclosedElements())) {
- String parameterName = annotationMember.getSimpleName().toString();
- TypeName parameterType = TypeName.get(annotationMember.getReturnType());
- createMethod.addParameter(parameterType, parameterName);
- parameters.add(CodeBlock.of("$L", parameterName));
- }
-
- ClassName autoAnnotationClass =
- generatedTypeName.peerClass(
- "AutoAnnotation_" + generatedTypeName.simpleName() + "_" + createMethodName);
- createMethod.addStatement(
- "return new $T($L)", autoAnnotationClass, makeParametersCodeBlock(parameters.build()));
- return createMethod.build();
- }
-
- /**
- * Returns the annotation types for which {@code @AutoAnnotation static Foo createFoo(…)} methods
- * should be written.
- */
- protected Set<TypeElement> annotationsToCreate(TypeElement annotationElement) {
- return nestedAnnotationElements(annotationElement, new LinkedHashSet<>());
- }
-
- @CanIgnoreReturnValue
- private static Set<TypeElement> nestedAnnotationElements(
- TypeElement annotationElement, Set<TypeElement> annotationElements) {
- if (annotationElements.add(annotationElement)) {
- for (ExecutableElement method : methodsIn(annotationElement.getEnclosedElements())) {
- TRAVERSE_NESTED_ANNOTATIONS.visit(method.getReturnType(), annotationElements);
- }
- }
- return annotationElements;
- }
-
- private static final SimpleTypeVisitor6<Void, Set<TypeElement>> TRAVERSE_NESTED_ANNOTATIONS =
- new SimpleTypeVisitor6<Void, Set<TypeElement>>() {
- @Override
- public Void visitDeclared(DeclaredType t, Set<TypeElement> p) {
- TypeElement typeElement = MoreTypes.asTypeElement(t);
- if (typeElement.getKind() == ElementKind.ANNOTATION_TYPE) {
- nestedAnnotationElements(typeElement, p);
- }
- return null;
- }
- };
-}
diff --git a/java/dagger/internal/codegen/AnnotationExpression.java b/java/dagger/internal/codegen/AnnotationExpression.java
deleted file mode 100644
index 8d729e274..000000000
--- a/java/dagger/internal/codegen/AnnotationExpression.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.AnnotationMirrors.getAnnotationValuesWithDefaults;
-import static dagger.internal.codegen.SourceFiles.classFileName;
-import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
-import static java.util.stream.Collectors.toList;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableList;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.TypeName;
-import java.util.List;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.ArrayType;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.SimpleAnnotationValueVisitor6;
-import javax.lang.model.util.SimpleTypeVisitor6;
-
-/**
- * Returns an expression creating an instance of the visited annotation type. Its parameter must be
- * a class as generated by {@link AnnotationCreatorGenerator}.
- *
- * <p>Note that {@link AnnotationValue#toString()} is the source-code representation of the value
- * <em>when used in an annotation</em>, which is not always the same as the representation needed
- * when creating the value in a method body.
- *
- * <p>For example, inside an annotation, a nested array of {@code int}s is simply {@code {1, 2, 3}},
- * but in code it would have to be {@code new int[] {1, 2, 3}}.
- */
-class AnnotationExpression extends SimpleAnnotationValueVisitor6<CodeBlock, AnnotationValue> {
-
- private final AnnotationMirror annotation;
- private final ClassName creatorClass;
-
- AnnotationExpression(AnnotationMirror annotation) {
- this.annotation = annotation;
- this.creatorClass =
- getAnnotationCreatorClassName(
- MoreTypes.asTypeElement(annotation.getAnnotationType()));
- }
-
- /**
- * Returns an expression that calls static methods on the annotation's creator class to create an
- * annotation instance equivalent the annotation passed to the constructor.
- */
- CodeBlock getAnnotationInstanceExpression() {
- return getAnnotationInstanceExpression(annotation);
- }
-
- private CodeBlock getAnnotationInstanceExpression(AnnotationMirror annotation) {
- return CodeBlock.of(
- "$T.$L($L)",
- creatorClass,
- createMethodName(
- MoreElements.asType(annotation.getAnnotationType().asElement())),
- makeParametersCodeBlock(
- getAnnotationValuesWithDefaults(annotation)
- .entrySet()
- .stream()
- .map(entry -> getValueExpression(entry.getKey().getReturnType(), entry.getValue()))
- .collect(toList())));
- }
-
- /**
- * Returns the name of the generated class that contains the static {@code create} methods for an
- * annotation type.
- */
- static ClassName getAnnotationCreatorClassName(TypeElement annotationType) {
- ClassName annotationTypeName = ClassName.get(annotationType);
- return annotationTypeName
- .topLevelClassName()
- .peerClass(classFileName(annotationTypeName) + "Creator");
- }
-
- static String createMethodName(TypeElement annotationType) {
- return "create" + annotationType.getSimpleName();
- }
-
- /**
- * Returns an expression that evaluates to a {@code value} of a given type on an {@code
- * annotation}.
- */
- CodeBlock getValueExpression(TypeMirror valueType, AnnotationValue value) {
- return ARRAY_LITERAL_PREFIX.visit(valueType, this.visit(value, value));
- }
-
- @Override
- public CodeBlock visitEnumConstant(VariableElement c, AnnotationValue p) {
- return CodeBlock.of("$T.$L", c.getEnclosingElement(), c.getSimpleName());
- }
-
- @Override
- public CodeBlock visitAnnotation(AnnotationMirror a, AnnotationValue p) {
- return getAnnotationInstanceExpression(a);
- }
-
- @Override
- public CodeBlock visitType(TypeMirror t, AnnotationValue p) {
- return CodeBlock.of("$T.class", t);
- }
-
- @Override
- public CodeBlock visitString(String s, AnnotationValue p) {
- return CodeBlock.of("$S", s);
- }
-
- @Override
- public CodeBlock visitByte(byte b, AnnotationValue p) {
- return CodeBlock.of("(byte) $L", b);
- }
-
- @Override
- public CodeBlock visitChar(char c, AnnotationValue p) {
- return CodeBlock.of("$L", p);
- }
-
- @Override
- public CodeBlock visitDouble(double d, AnnotationValue p) {
- return CodeBlock.of("$LD", d);
- }
-
- @Override
- public CodeBlock visitFloat(float f, AnnotationValue p) {
- return CodeBlock.of("$LF", f);
- }
-
- @Override
- public CodeBlock visitLong(long i, AnnotationValue p) {
- return CodeBlock.of("$LL", i);
- }
-
- @Override
- public CodeBlock visitShort(short s, AnnotationValue p) {
- return CodeBlock.of("(short) $L", s);
- }
-
- @Override
- protected CodeBlock defaultAction(Object o, AnnotationValue p) {
- return CodeBlock.of("$L", o);
- }
-
- @Override
- public CodeBlock visitArray(List<? extends AnnotationValue> values, AnnotationValue p) {
- ImmutableList.Builder<CodeBlock> codeBlocks = ImmutableList.builder();
- for (AnnotationValue value : values) {
- codeBlocks.add(this.visit(value, p));
- }
- return CodeBlock.of("{$L}", makeParametersCodeBlock(codeBlocks.build()));
- }
-
- /**
- * If the visited type is an array, prefixes the parameter code block with {@code new T[]}, where
- * {@code T} is the raw array component type.
- */
- private static final SimpleTypeVisitor6<CodeBlock, CodeBlock> ARRAY_LITERAL_PREFIX =
- new SimpleTypeVisitor6<CodeBlock, CodeBlock>() {
-
- @Override
- public CodeBlock visitArray(ArrayType t, CodeBlock p) {
- return CodeBlock.of("new $T[] $L", RAW_TYPE_NAME.visit(t.getComponentType()), p);
- }
-
- @Override
- protected CodeBlock defaultAction(TypeMirror e, CodeBlock p) {
- return p;
- }
- };
-
- /**
- * If the visited type is an array, returns the name of its raw component type; otherwise returns
- * the name of the type itself.
- */
- private static final SimpleTypeVisitor6<TypeName, Void> RAW_TYPE_NAME =
- new SimpleTypeVisitor6<TypeName, Void>() {
- @Override
- public TypeName visitDeclared(DeclaredType t, Void p) {
- return ClassName.get(MoreTypes.asTypeElement(t));
- }
-
- @Override
- protected TypeName defaultAction(TypeMirror e, Void p) {
- return TypeName.get(e);
- }
- };
-}
diff --git a/java/dagger/internal/codegen/AnnotationProtoConverter.java b/java/dagger/internal/codegen/AnnotationProtoConverter.java
deleted file mode 100644
index 282d8cacf..000000000
--- a/java/dagger/internal/codegen/AnnotationProtoConverter.java
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright (C) 2019 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.AnnotationMirrors.getAnnotationValuesWithDefaults;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.Maps.transformValues;
-import static dagger.internal.codegen.DaggerStreams.toImmutableList;
-import static javax.lang.model.util.ElementFilter.fieldsIn;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableList;
-import dagger.internal.codegen.serialization.AnnotationProto;
-import dagger.internal.codegen.serialization.AnnotationValueProto;
-import java.util.Collections;
-import java.util.List;
-import javax.inject.Inject;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.AnnotationValueVisitor;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.SimpleAnnotationValueVisitor8;
-
-/** Converts {@link AnnotationMirror}s to {@link AnnotationProto}s and vice-versa. */
-final class AnnotationProtoConverter {
- private final TypeProtoConverter typeProtoConverter;
-
- @Inject
- AnnotationProtoConverter(TypeProtoConverter typeProtoConverter) {
- this.typeProtoConverter = typeProtoConverter;
- }
-
- /** Translates an {@link AnnotationMirror} to a proto representation. */
- static AnnotationProto toProto(AnnotationMirror annotationMirror) {
- AnnotationProto.Builder builder = AnnotationProto.newBuilder();
- builder.setAnnotationType(TypeProtoConverter.toProto(annotationMirror.getAnnotationType()));
- getAnnotationValuesWithDefaults(annotationMirror)
- .forEach(
- (attribute, value) ->
- builder.putAllValues(
- Collections.singletonMap(
- attribute.getSimpleName().toString(), annotationValueProto(value))));
- return builder.build();
- }
-
- /** Creates an {@link AnnotationMirror} from its proto representation. */
- AnnotationMirror fromProto(AnnotationProto annotation) {
- return SimpleAnnotationMirror.of(
- MoreTypes.asTypeElement(typeProtoConverter.fromProto(annotation.getAnnotationType())),
- transformValues(annotation.getValues(), AnnotationValueFromProto::new));
- }
-
- private static final AnnotationValueVisitor<
- AnnotationValueProto.Builder, AnnotationValueProto.Builder>
- ANNOTATION_VALUE_TO_PROTO =
- new SimpleAnnotationValueVisitor8<
- AnnotationValueProto.Builder, AnnotationValueProto.Builder>() {
- @Override
- public AnnotationValueProto.Builder visitAnnotation(
- AnnotationMirror nestedAnnotation, AnnotationValueProto.Builder builder) {
- return builder
- .setNestedAnnotation(toProto(nestedAnnotation))
- .setKind(AnnotationValueProto.Kind.ANNOTATION);
- }
-
- @Override
- public AnnotationValueProto.Builder visitBoolean(
- boolean b, AnnotationValueProto.Builder builder) {
- return builder.setBooleanValue(b).setKind(AnnotationValueProto.Kind.BOOLEAN);
- }
-
- @Override
- public AnnotationValueProto.Builder visitChar(
- char c, AnnotationValueProto.Builder builder) {
- return builder
- .setStringValue(String.valueOf(c))
- .setKind(AnnotationValueProto.Kind.CHAR);
- }
-
- @Override
- public AnnotationValueProto.Builder visitByte(
- byte b, AnnotationValueProto.Builder builder) {
- return builder.setIntValue(b).setKind(AnnotationValueProto.Kind.BYTE);
- }
-
- @Override
- public AnnotationValueProto.Builder visitShort(
- short s, AnnotationValueProto.Builder builder) {
- return builder.setIntValue(s).setKind(AnnotationValueProto.Kind.SHORT);
- }
-
- @Override
- public AnnotationValueProto.Builder visitInt(
- int i, AnnotationValueProto.Builder builder) {
- return builder.setIntValue(i).setKind(AnnotationValueProto.Kind.INT);
- }
-
- @Override
- public AnnotationValueProto.Builder visitFloat(
- float f, AnnotationValueProto.Builder builder) {
- return builder.setFloatValue(f).setKind(AnnotationValueProto.Kind.FLOAT);
- }
-
- @Override
- public AnnotationValueProto.Builder visitLong(
- long l, AnnotationValueProto.Builder builder) {
- return builder.setLongValue(l).setKind(AnnotationValueProto.Kind.LONG);
- }
-
- @Override
- public AnnotationValueProto.Builder visitDouble(
- double d, AnnotationValueProto.Builder builder) {
- return builder.setDoubleValue(d).setKind(AnnotationValueProto.Kind.DOUBLE);
- }
-
- @Override
- public AnnotationValueProto.Builder visitString(
- String s, AnnotationValueProto.Builder builder) {
- return builder.setStringValue(s).setKind(AnnotationValueProto.Kind.STRING);
- }
-
- @Override
- public AnnotationValueProto.Builder visitType(
- TypeMirror t, AnnotationValueProto.Builder builder) {
- return builder
- .setClassLiteral(TypeProtoConverter.toProto(t))
- .setKind(AnnotationValueProto.Kind.CLASS_LITERAL);
- }
-
- @Override
- public AnnotationValueProto.Builder visitEnumConstant(
- VariableElement c, AnnotationValueProto.Builder builder) {
- return builder
- .setEnumType(TypeProtoConverter.toProto(c.asType()))
- .setEnumName(c.getSimpleName().toString())
- .setKind(AnnotationValueProto.Kind.ENUM);
- }
-
- @Override
- public AnnotationValueProto.Builder visitArray(
- List<? extends AnnotationValue> values, AnnotationValueProto.Builder builder) {
- values.forEach(value -> builder.addArrayValues(annotationValueProto(value)));
- return builder.setKind(AnnotationValueProto.Kind.ARRAY);
- }
-
- @Override
- public AnnotationValueProto.Builder visitUnknown(
- AnnotationValue av, AnnotationValueProto.Builder builder) {
- throw new UnsupportedOperationException(av.toString());
- }
- };
-
- /** Translates an {@link AnnotationValue} to a proto representation. */
- private static AnnotationValueProto annotationValueProto(AnnotationValue annotationValue) {
- return annotationValue
- .accept(ANNOTATION_VALUE_TO_PROTO, AnnotationValueProto.newBuilder())
- .build();
- }
-
- private class AnnotationValueFromProto implements AnnotationValue {
- private final AnnotationValueProto proto;
-
- AnnotationValueFromProto(AnnotationValueProto proto) {
- this.proto = proto;
- }
-
- @Override
- public Object getValue() {
- switch (proto.getKind()) {
- case BOOLEAN:
- return proto.getBooleanValue();
- case BYTE:
- return (byte) proto.getIntValue();
- case SHORT:
- return (short) proto.getIntValue();
- case CHAR:
- return getCharValue();
- case INT:
- return proto.getIntValue();
- case FLOAT:
- return proto.getFloatValue();
- case LONG:
- return proto.getLongValue();
- case DOUBLE:
- return proto.getDoubleValue();
- case STRING:
- return proto.getStringValue();
- case CLASS_LITERAL:
- return typeProtoConverter.fromProto(proto.getClassLiteral());
- case ENUM:
- return getEnumConstant();
- case ANNOTATION:
- return fromProto(proto.getNestedAnnotation());
- case ARRAY:
- return getArrayValues();
- case UNKNOWN:
- case UNRECOGNIZED:
- // fall through
- }
- throw new AssertionError(proto);
- }
-
- @Override
- public <R, P> R accept(AnnotationValueVisitor<R, P> visitor, P passedValue) {
- switch (proto.getKind()) {
- case BOOLEAN:
- return visitor.visitBoolean(proto.getBooleanValue(), passedValue);
- case BYTE:
- return visitor.visitByte((byte) proto.getIntValue(), passedValue);
- case SHORT:
- return visitor.visitShort((short) proto.getIntValue(), passedValue);
- case CHAR:
- return visitor.visitChar(getCharValue(), passedValue);
- case INT:
- return visitor.visitInt(proto.getIntValue(), passedValue);
- case FLOAT:
- return visitor.visitFloat(proto.getFloatValue(), passedValue);
- case LONG:
- return visitor.visitLong(proto.getLongValue(), passedValue);
- case DOUBLE:
- return visitor.visitDouble(proto.getDoubleValue(), passedValue);
- case STRING:
- return visitor.visitString((String) getValue(), passedValue);
- case CLASS_LITERAL:
- return visitor.visitType((TypeMirror) getValue(), passedValue);
- case ENUM:
- return visitor.visitEnumConstant((VariableElement) getValue(), passedValue);
- case ANNOTATION:
- return visitor.visitAnnotation((AnnotationMirror) getValue(), passedValue);
- case ARRAY:
- return visitor.visitArray(getArrayValues(), passedValue);
- case UNKNOWN:
- case UNRECOGNIZED:
- // fall through
- }
- throw new AssertionError(proto);
- }
-
- private char getCharValue() {
- checkState(proto.getKind().equals(AnnotationValueProto.Kind.CHAR));
- return proto.getStringValue().charAt(0);
- }
-
- private VariableElement getEnumConstant() {
- checkState(proto.getKind().equals(AnnotationValueProto.Kind.ENUM));
- TypeMirror enumType = typeProtoConverter.fromProto(proto.getEnumType());
- return fieldsIn(MoreTypes.asTypeElement(enumType).getEnclosedElements()).stream()
- .filter(value -> value.getSimpleName().contentEquals(proto.getEnumName()))
- .findFirst()
- .get();
- }
-
- private ImmutableList<AnnotationValue> getArrayValues() {
- checkState(proto.getKind().equals(AnnotationValueProto.Kind.ARRAY));
- return proto.getArrayValuesList().stream()
- .map(AnnotationValueFromProto::new)
- .collect(toImmutableList());
- }
- }
-}
diff --git a/java/dagger/internal/codegen/AnonymousProviderCreationExpression.java b/java/dagger/internal/codegen/AnonymousProviderCreationExpression.java
deleted file mode 100644
index fc30eaa70..000000000
--- a/java/dagger/internal/codegen/AnonymousProviderCreationExpression.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.BindingRequest.bindingRequest;
-import static dagger.internal.codegen.javapoet.CodeBlocks.anonymousProvider;
-import static dagger.model.RequestKind.INSTANCE;
-
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
-import dagger.internal.codegen.javapoet.Expression;
-
-/**
- * A {@link javax.inject.Provider} creation expression for an anonymous inner class whose
- * {@code get()} method returns the expression for an instance binding request for its key.
- */
-final class AnonymousProviderCreationExpression
- implements FrameworkInstanceCreationExpression {
- private final ContributionBinding binding;
- private final ComponentBindingExpressions componentBindingExpressions;
- private final ClassName requestingClass;
-
- AnonymousProviderCreationExpression(
- ContributionBinding binding,
- ComponentBindingExpressions componentBindingExpressions,
- ClassName requestingClass) {
- this.binding = binding;
- this.componentBindingExpressions = componentBindingExpressions;
- this.requestingClass = requestingClass;
- }
-
- @Override
- public CodeBlock creationExpression() {
- BindingRequest instanceExpressionRequest = bindingRequest(binding.key(), INSTANCE);
- Expression instanceExpression =
- componentBindingExpressions.getDependencyExpression(
- instanceExpressionRequest,
- // Not a real class name, but the actual requestingClass is an inner class within the
- // given class, not that class itself.
- requestingClass.nestedClass("Anonymous"));
- return anonymousProvider(instanceExpression);
- }
-}
diff --git a/java/dagger/internal/codegen/AnyBindingMethodValidator.java b/java/dagger/internal/codegen/AnyBindingMethodValidator.java
deleted file mode 100644
index bad9636a0..000000000
--- a/java/dagger/internal/codegen/AnyBindingMethodValidator.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
-import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
-import static java.util.stream.Collectors.joining;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import java.lang.annotation.Annotation;
-import java.util.HashMap;
-import java.util.Map;
-import javax.inject.Inject;
-import javax.lang.model.element.ExecutableElement;
-
-/** Validates any binding method. */
-final class AnyBindingMethodValidator {
-
- private final ImmutableMap<Class<? extends Annotation>, BindingMethodValidator> validators;
- private final Map<ExecutableElement, ValidationReport<ExecutableElement>> reports =
- new HashMap<>();
-
- @Inject
- AnyBindingMethodValidator(
- ImmutableMap<Class<? extends Annotation>, BindingMethodValidator> validators) {
- this.validators = validators;
- }
-
- /** Returns the binding method annotations considered by this validator. */
- ImmutableSet<Class<? extends Annotation>> methodAnnotations() {
- return validators.keySet();
- }
-
- /**
- * Returns {@code true} if {@code method} is annotated with at least one of {@link
- * #methodAnnotations()}.
- */
- boolean isBindingMethod(ExecutableElement method) {
- return isAnyAnnotationPresent(method, methodAnnotations());
- }
-
- /**
- * Returns a validation report for a method.
- *
- * <ul>
- * <li>Reports an error if {@code method} is annotated with more than one {@linkplain
- * #methodAnnotations() binding method annotation}.
- * <li>Validates {@code method} with the {@link BindingMethodValidator} for the single
- * {@linkplain #methodAnnotations() binding method annotation}.
- * </ul>
- *
- * @throws IllegalArgumentException if {@code method} is not annotated by any {@linkplain
- * #methodAnnotations() binding method annotation}
- */
- ValidationReport<ExecutableElement> validate(ExecutableElement method) {
- return reentrantComputeIfAbsent(reports, method, this::validateUncached);
- }
-
- /**
- * Returns {@code true} if {@code method} was already {@linkplain #validate(ExecutableElement)
- * validated}.
- */
- boolean wasAlreadyValidated(ExecutableElement method) {
- return reports.containsKey(method);
- }
-
- private ValidationReport<ExecutableElement> validateUncached(ExecutableElement method) {
- ValidationReport.Builder<ExecutableElement> report = ValidationReport.about(method);
- ImmutableSet<? extends Class<? extends Annotation>> bindingMethodAnnotations =
- methodAnnotations()
- .stream()
- .filter(annotation -> isAnnotationPresent(method, annotation))
- .collect(toImmutableSet());
- switch (bindingMethodAnnotations.size()) {
- case 0:
- throw new IllegalArgumentException(
- String.format("%s has no binding method annotation", method));
-
- case 1:
- report.addSubreport(
- validators.get(getOnlyElement(bindingMethodAnnotations)).validate(method));
- break;
-
- default:
- report.addError(
- String.format(
- "%s is annotated with more than one of (%s)",
- method.getSimpleName(),
- methodAnnotations().stream().map(Class::getCanonicalName).collect(joining(", "))),
- method);
- break;
- }
- return report.build();
- }
-}
diff --git a/java/dagger/internal/codegen/AssistedFactoryProcessingStep.java b/java/dagger/internal/codegen/AssistedFactoryProcessingStep.java
new file mode 100644
index 000000000..abc0436a0
--- /dev/null
+++ b/java/dagger/internal/codegen/AssistedFactoryProcessingStep.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.asType;
+import static com.google.auto.common.MoreTypes.asDeclared;
+import static com.google.auto.common.MoreTypes.asTypeElement;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.assistedInjectedConstructors;
+import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.INSTANCE_FACTORY;
+import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
+import static java.util.stream.Collectors.joining;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.TypeVariableName;
+import dagger.assisted.AssistedFactory;
+import dagger.internal.codegen.base.SourceFileGenerationException;
+import dagger.internal.codegen.base.SourceFileGenerator;
+import dagger.internal.codegen.binding.AssistedInjectionAnnotations;
+import dagger.internal.codegen.binding.AssistedInjectionAnnotations.AssistedFactoryMetadata;
+import dagger.internal.codegen.binding.AssistedInjectionAnnotations.AssistedParameter;
+import dagger.internal.codegen.binding.BindingFactory;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.validation.TypeCheckingProcessingStep;
+import dagger.internal.codegen.validation.ValidationReport;
+import java.lang.annotation.Annotation;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.Messager;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+/** An annotation processor for {@link dagger.assisted.AssistedFactory}-annotated types. */
+final class AssistedFactoryProcessingStep extends TypeCheckingProcessingStep<TypeElement> {
+ private final Messager messager;
+ private final Filer filer;
+ private final SourceVersion sourceVersion;
+ private final DaggerElements elements;
+ private final DaggerTypes types;
+ private final BindingFactory bindingFactory;
+
+ @Inject
+ AssistedFactoryProcessingStep(
+ Messager messager,
+ Filer filer,
+ SourceVersion sourceVersion,
+ DaggerElements elements,
+ DaggerTypes types,
+ BindingFactory bindingFactory) {
+ super(MoreElements::asType);
+ this.messager = messager;
+ this.filer = filer;
+ this.sourceVersion = sourceVersion;
+ this.elements = elements;
+ this.types = types;
+ this.bindingFactory = bindingFactory;
+ }
+
+ @Override
+ public ImmutableSet<Class<? extends Annotation>> annotations() {
+ return ImmutableSet.of(AssistedFactory.class);
+ }
+
+ @Override
+ protected void process(
+ TypeElement factory, ImmutableSet<Class<? extends Annotation>> annotations) {
+ ValidationReport<TypeElement> report = new AssistedFactoryValidator().validate(factory);
+ report.printMessagesTo(messager);
+ if (report.isClean()) {
+ try {
+ ProvisionBinding binding = bindingFactory.assistedFactoryBinding(factory, Optional.empty());
+ new AssistedFactoryImplGenerator().generate(binding);
+ } catch (SourceFileGenerationException e) {
+ e.printMessageTo(messager);
+ }
+ }
+ }
+
+ private final class AssistedFactoryValidator {
+ ValidationReport<TypeElement> validate(TypeElement factory) {
+ ValidationReport.Builder<TypeElement> report = ValidationReport.about(factory);
+
+ if (!factory.getModifiers().contains(ABSTRACT)) {
+ return report
+ .addError(
+ "The @AssistedFactory-annotated type must be either an abstract class or "
+ + "interface.",
+ factory)
+ .build();
+ }
+
+ if (factory.getNestingKind().isNested() && !factory.getModifiers().contains(STATIC)) {
+ report.addError("Nested @AssistedFactory-annotated types must be static. ", factory);
+ }
+
+ ImmutableSet<ExecutableElement> abstractFactoryMethods =
+ AssistedInjectionAnnotations.assistedFactoryMethods(factory, elements, types);
+
+ if (abstractFactoryMethods.isEmpty()) {
+ report.addError(
+ "The @AssistedFactory-annotated type is missing an abstract, non-default method "
+ + "whose return type matches the assisted injection type.",
+ factory);
+ }
+
+ for (ExecutableElement method : abstractFactoryMethods) {
+ ExecutableType methodType = types.resolveExecutableType(method, factory.asType());
+ if (!isAssistedInjectionType(methodType.getReturnType())) {
+ report.addError(
+ String.format(
+ "Invalid return type: %s. An assisted factory's abstract method must return a "
+ + "type with an @AssistedInject-annotated constructor.",
+ methodType.getReturnType()),
+ method);
+ }
+ if (!method.getTypeParameters().isEmpty()) {
+ report.addError(
+ "@AssistedFactory does not currently support type parameters in the creator "
+ + "method. See https://github.com/google/dagger/issues/2279",
+ method);
+ }
+ }
+
+ if (abstractFactoryMethods.size() > 1) {
+ report.addError(
+ "The @AssistedFactory-annotated type should contain a single abstract, non-default"
+ + " method but found multiple: "
+ + abstractFactoryMethods,
+ factory);
+ }
+
+ if (!report.build().isClean()) {
+ return report.build();
+ }
+
+ AssistedFactoryMetadata metadata =
+ AssistedFactoryMetadata.create(factory.asType(), elements, types);
+
+ // Note: We check uniqueness of the @AssistedInject constructor parameters in
+ // AssistedInjectProcessingStep. We need to check uniqueness for here too because we may
+ // have resolved some type parameters that were not resolved in the @AssistedInject type.
+ Set<AssistedParameter> uniqueAssistedParameters = new HashSet<>();
+ for (AssistedParameter assistedParameter : metadata.assistedFactoryAssistedParameters()) {
+ if (!uniqueAssistedParameters.add(assistedParameter)) {
+ report.addError(
+ "@AssistedFactory method has duplicate @Assisted types: " + assistedParameter,
+ assistedParameter.variableElement());
+ }
+ }
+
+ if (!ImmutableSet.copyOf(metadata.assistedInjectAssistedParameters())
+ .equals(ImmutableSet.copyOf(metadata.assistedFactoryAssistedParameters()))) {
+ report.addError(
+ String.format(
+ "The parameters in the factory method must match the @Assisted parameters in %s."
+ + "\n Actual: %s#%s"
+ + "\n Expected: %s#%s(%s)",
+ metadata.assistedInjectType(),
+ metadata.factory().getQualifiedName(),
+ metadata.factoryMethod(),
+ metadata.factory().getQualifiedName(),
+ metadata.factoryMethod().getSimpleName(),
+ metadata.assistedInjectAssistedParameters().stream()
+ .map(AssistedParameter::type)
+ .map(Object::toString)
+ .collect(joining(", "))),
+ metadata.factoryMethod());
+ }
+
+ return report.build();
+ }
+
+ private boolean isAssistedInjectionType(TypeMirror type) {
+ return type.getKind() == TypeKind.DECLARED
+ && AssistedInjectionAnnotations.isAssistedInjectionType(asTypeElement(type));
+ }
+ }
+
+ /** Generates an implementation of the {@link dagger.assisted.AssistedFactory}-annotated class. */
+ private final class AssistedFactoryImplGenerator extends SourceFileGenerator<ProvisionBinding> {
+ AssistedFactoryImplGenerator() {
+ super(filer, elements, sourceVersion);
+ }
+
+ @Override
+ public ClassName nameGeneratedType(ProvisionBinding binding) {
+ return generatedClassNameForBinding(binding);
+ }
+
+ @Override
+ public Element originatingElement(ProvisionBinding binding) {
+ return binding.bindingElement().get();
+ }
+
+ // For each @AssistedFactory-annotated type, we generates a class named "*_Impl" that implements
+ // that type.
+ //
+ // Note that this class internally delegates to the @AssistedInject generated class, which
+ // contains the actual implementation logic for creating the @AssistedInject type. The reason we
+ // need both of these generated classes is because while the @AssistedInject generated class
+ // knows how to create the @AssistedInject type, it doesn't know about all of the
+ // @AssistedFactory interfaces that it needs to extend when it's generated. Thus, the role of
+ // the @AssistedFactory generated class is purely to implement the @AssistedFactory type.
+ // Furthermore, while we could have put all of the logic into the @AssistedFactory generated
+ // class and not generate the @AssistedInject generated class, having the @AssistedInject
+ // generated class ensures we have proper accessibility to the @AssistedInject type, and reduces
+ // duplicate logic if there are multiple @AssistedFactory types for the same @AssistedInject
+ // type.
+ //
+ // Example:
+ // public class FooFactory_Impl implements FooFactory {
+ // private final Foo_Factory delegateFactory;
+ //
+ // FooFactory_Impl(Foo_Factory delegateFactory) {
+ // this.delegateFactory = delegateFactory;
+ // }
+ //
+ // @Override
+ // public Foo createFoo(AssistedDep assistedDep) {
+ // return delegateFactory.get(assistedDep);
+ // }
+ //
+ // public static Provider<FooFactory> create(Foo_Factory delegateFactory) {
+ // return InstanceFactory.create(new FooFactory_Impl(delegateFactory));
+ // }
+ // }
+ @Override
+ public Optional<TypeSpec.Builder> write(ProvisionBinding binding) {
+ TypeElement factory = asType(binding.bindingElement().get());
+
+ ClassName name = nameGeneratedType(binding);
+ TypeSpec.Builder builder =
+ TypeSpec.classBuilder(name)
+ .addModifiers(PUBLIC, FINAL)
+ .addTypeVariables(
+ factory.getTypeParameters().stream()
+ .map(TypeVariableName::get)
+ .collect(toImmutableList()));
+
+ if (factory.getKind() == ElementKind.INTERFACE) {
+ builder.addSuperinterface(factory.asType());
+ } else {
+ builder.superclass(factory.asType());
+ }
+
+ AssistedFactoryMetadata metadata =
+ AssistedFactoryMetadata.create(asDeclared(factory.asType()), elements, types);
+ ParameterSpec delegateFactoryParam =
+ ParameterSpec.builder(
+ delegateFactoryTypeName(metadata.assistedInjectType()), "delegateFactory")
+ .build();
+ builder
+ .addField(
+ FieldSpec.builder(delegateFactoryParam.type, delegateFactoryParam.name)
+ .addModifiers(PRIVATE, FINAL)
+ .build())
+ .addMethod(
+ MethodSpec.constructorBuilder()
+ .addParameter(delegateFactoryParam)
+ .addStatement("this.$1N = $1N", delegateFactoryParam)
+ .build())
+ .addMethod(
+ MethodSpec.overriding(metadata.factoryMethod(), metadata.factoryType(), types)
+ .addStatement(
+ "return $N.get($L)",
+ delegateFactoryParam,
+ // Use the order of the parameters from the @AssistedInject constructor but
+ // use the parameter names of the @AssistedFactory method.
+ metadata.assistedInjectAssistedParameters().stream()
+ .map(metadata.assistedFactoryAssistedParametersMap()::get)
+ .map(param -> CodeBlock.of("$L", param.getSimpleName()))
+ .collect(toParametersCodeBlock()))
+ .build())
+ .addMethod(
+ MethodSpec.methodBuilder("create")
+ .addModifiers(PUBLIC, STATIC)
+ .addParameter(delegateFactoryParam)
+ .addTypeVariables(
+ metadata.assistedInjectElement().getTypeParameters().stream()
+ .map(TypeVariableName::get)
+ .collect(toImmutableList()))
+ .returns(providerOf(TypeName.get(factory.asType())))
+ .addStatement(
+ "return $T.$Lcreate(new $T($N))",
+ INSTANCE_FACTORY,
+ // Java 7 type inference requires the method call provide the exact type here.
+ sourceVersion.compareTo(SourceVersion.RELEASE_7) <= 0
+ ? CodeBlock.of("<$T>", types.accessibleType(metadata.factoryType(), name))
+ : CodeBlock.of(""),
+ name,
+ delegateFactoryParam)
+ .build());
+ return Optional.of(builder);
+ }
+
+ /** Returns the generated factory {@link TypeName type} for an @AssistedInject constructor. */
+ private TypeName delegateFactoryTypeName(DeclaredType assistedInjectType) {
+ // The name of the generated factory for the assisted inject type,
+ // e.g. an @AssistedInject Foo(...) {...} constructor will generate a Foo_Factory class.
+ ClassName generatedFactoryClassName =
+ generatedClassNameForBinding(
+ bindingFactory.injectionBinding(
+ getOnlyElement(assistedInjectedConstructors(asTypeElement(assistedInjectType))),
+ Optional.empty()));
+
+ // Return the factory type resolved with the same type parameters as the assisted inject type.
+ return assistedInjectType.getTypeArguments().isEmpty()
+ ? generatedFactoryClassName
+ : ParameterizedTypeName.get(
+ generatedFactoryClassName,
+ assistedInjectType.getTypeArguments().stream()
+ .map(TypeName::get)
+ .collect(toImmutableList())
+ .toArray(new TypeName[0]));
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/AssistedInjectProcessingStep.java b/java/dagger/internal/codegen/AssistedInjectProcessingStep.java
new file mode 100644
index 000000000..4f2f5b7b7
--- /dev/null
+++ b/java/dagger/internal/codegen/AssistedInjectProcessingStep.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreTypes.asDeclared;
+import static com.google.common.base.Preconditions.checkState;
+import static dagger.internal.codegen.langmodel.DaggerElements.closestEnclosingTypeElement;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import dagger.assisted.AssistedInject;
+import dagger.internal.codegen.binding.AssistedInjectionAnnotations;
+import dagger.internal.codegen.binding.AssistedInjectionAnnotations.AssistedParameter;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.validation.TypeCheckingProcessingStep;
+import dagger.internal.codegen.validation.ValidationReport;
+import java.lang.annotation.Annotation;
+import java.util.HashSet;
+import java.util.Set;
+import javax.annotation.processing.Messager;
+import javax.inject.Inject;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.DeclaredType;
+
+/** An annotation processor for {@link dagger.assisted.AssistedInject}-annotated elements. */
+final class AssistedInjectProcessingStep extends TypeCheckingProcessingStep<ExecutableElement> {
+ private final DaggerTypes types;
+ private final Messager messager;
+
+ @Inject
+ AssistedInjectProcessingStep(DaggerTypes types, Messager messager) {
+ super(MoreElements::asExecutable);
+ this.types = types;
+ this.messager = messager;
+ }
+
+ @Override
+ public ImmutableSet<Class<? extends Annotation>> annotations() {
+ return ImmutableSet.of(AssistedInject.class);
+ }
+
+ @Override
+ protected void process(
+ ExecutableElement assistedInjectElement,
+ ImmutableSet<Class<? extends Annotation>> annotations) {
+ new AssistedInjectValidator().validate(assistedInjectElement).printMessagesTo(messager);
+ }
+
+ private final class AssistedInjectValidator {
+ ValidationReport<ExecutableElement> validate(ExecutableElement constructor) {
+ checkState(constructor.getKind() == ElementKind.CONSTRUCTOR);
+ ValidationReport.Builder<ExecutableElement> report = ValidationReport.about(constructor);
+
+ DeclaredType assistedInjectType =
+ asDeclared(closestEnclosingTypeElement(constructor).asType());
+ ImmutableList<AssistedParameter> assistedParameters =
+ AssistedInjectionAnnotations.assistedInjectAssistedParameters(assistedInjectType, types);
+
+ Set<AssistedParameter> uniqueAssistedParameters = new HashSet<>();
+ for (AssistedParameter assistedParameter : assistedParameters) {
+ if (!uniqueAssistedParameters.add(assistedParameter)) {
+ report.addError(
+ "@AssistedInject constructor has duplicate @Assisted type: " + assistedParameter,
+ assistedParameter.variableElement());
+ }
+ }
+
+ return report.build();
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/AssistedProcessingStep.java b/java/dagger/internal/codegen/AssistedProcessingStep.java
new file mode 100644
index 000000000..3173987e1
--- /dev/null
+++ b/java/dagger/internal/codegen/AssistedProcessingStep.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static dagger.internal.codegen.langmodel.DaggerElements.closestEnclosingTypeElement;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableSet;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedInject;
+import dagger.internal.codegen.binding.AssistedInjectionAnnotations;
+import dagger.internal.codegen.binding.InjectionAnnotations;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.validation.TypeCheckingProcessingStep;
+import dagger.internal.codegen.validation.ValidationReport;
+import java.lang.annotation.Annotation;
+import javax.annotation.processing.Messager;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+
+/**
+ * An annotation processor for {@link dagger.assisted.Assisted}-annotated types.
+ *
+ * <p>This processing step should run after {@link AssistedFactoryProcessingStep}.
+ */
+final class AssistedProcessingStep extends TypeCheckingProcessingStep<VariableElement> {
+ private final KotlinMetadataUtil kotlinMetadataUtil;
+ private final InjectionAnnotations injectionAnnotations;
+ private final DaggerElements elements;
+ private final DaggerTypes types;
+ private final Messager messager;
+
+ @Inject
+ AssistedProcessingStep(
+ KotlinMetadataUtil kotlinMetadataUtil,
+ InjectionAnnotations injectionAnnotations,
+ DaggerElements elements,
+ DaggerTypes types,
+ Messager messager) {
+ super(MoreElements::asVariable);
+ this.kotlinMetadataUtil = kotlinMetadataUtil;
+ this.injectionAnnotations = injectionAnnotations;
+ this.elements = elements;
+ this.types = types;
+ this.messager = messager;
+ }
+
+ @Override
+ public ImmutableSet<Class<? extends Annotation>> annotations() {
+ return ImmutableSet.of(Assisted.class);
+ }
+
+ @Override
+ protected void process(
+ VariableElement assisted, ImmutableSet<Class<? extends Annotation>> annotations) {
+ new AssistedValidator().validate(assisted).printMessagesTo(messager);
+ }
+
+ private final class AssistedValidator {
+ ValidationReport<VariableElement> validate(VariableElement assisted) {
+ ValidationReport.Builder<VariableElement> report = ValidationReport.about(assisted);
+
+ Element enclosingElement = assisted.getEnclosingElement();
+ if (!isAssistedInjectConstructor(enclosingElement)
+ && !isAssistedFactoryCreateMethod(enclosingElement)
+ // The generated java stubs for kotlin data classes contain a "copy" method that has
+ // the same parameters (and annotations) as the constructor, so just ignore it.
+ && !isKotlinDataClassCopyMethod(enclosingElement)) {
+ report.addError(
+ "@Assisted parameters can only be used within an @AssistedInject-annotated "
+ + "constructor.",
+ assisted);
+ }
+
+ injectionAnnotations
+ .getQualifiers(assisted)
+ .forEach(
+ qualifier ->
+ report.addError(
+ "Qualifiers cannot be used with @Assisted parameters.", assisted, qualifier));
+
+ return report.build();
+ }
+ }
+
+ private boolean isAssistedInjectConstructor(Element element) {
+ return element.getKind() == ElementKind.CONSTRUCTOR
+ && isAnnotationPresent(element, AssistedInject.class);
+ }
+
+ private boolean isAssistedFactoryCreateMethod(Element element) {
+ if (element.getKind() == ElementKind.METHOD) {
+ TypeElement enclosingElement = closestEnclosingTypeElement(element);
+ return AssistedInjectionAnnotations.isAssistedFactoryType(enclosingElement)
+ // This assumes we've already validated AssistedFactory and that a valid method exists.
+ && AssistedInjectionAnnotations.assistedFactoryMethod(enclosingElement, elements, types)
+ .equals(element);
+ }
+ return false;
+ }
+
+ private boolean isKotlinDataClassCopyMethod(Element element) {
+ // Note: This is a best effort. Technically, we could check the return type and parameters of
+ // the copy method to verify it's the one associated with the constructor, but I'd rather keep
+ // this simple to avoid encoding too many details of kapt's stubs. At worst, we'll be allowing
+ // an @Assisted annotation that has no affect, which is already true for many of Dagger's other
+ // annotations.
+ return element.getKind() == ElementKind.METHOD
+ && element.getSimpleName().contentEquals("copy")
+ && kotlinMetadataUtil.isDataClass(closestEnclosingTypeElement(element));
+ }
+}
diff --git a/java/dagger/internal/codegen/BUILD b/java/dagger/internal/codegen/BUILD
index 824a72ef6..be970be12 100644
--- a/java/dagger/internal/codegen/BUILD
+++ b/java/dagger/internal/codegen/BUILD
@@ -15,485 +15,106 @@
# Description:
# A JSR-330 compliant dependency injection system for android and java
-package(default_visibility = ["//:src"])
-
-load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX", "DOCLINT_REFERENCES")
-load("//tools:maven.bzl", "POM_VERSION", "pom_file")
-
-EXPERIMENTAL_VISUALIZER_SRCS = ["BindingNetworkVisualizer.java"]
-
-JAVAC_PLUGIN_MODULE_SRCS = ["JavacPluginModule.java"]
-
-KYTHE_SRCS = ["DaggerKythePlugin.java"]
-
-STATISTICS_COLLECTOR_SRCS = ["BindingGraphStatisticsCollector.java"]
-
-CODEGEN_SRCS = glob(
- ["*.java"],
- exclude = EXPERIMENTAL_VISUALIZER_SRCS + KYTHE_SRCS + STATISTICS_COLLECTOR_SRCS +
- JAVAC_PLUGIN_MODULE_SRCS,
-)
-
-CODEGEN_PLUGINS = [":bootstrap_compiler_plugin"]
-
-CODEGEN_SHARED_DEPS = [
- "@google_bazel_common//third_party/java/auto:service",
- "@google_bazel_common//third_party/java/auto:value",
- "@google_bazel_common//third_party/java/auto:common",
- "@google_bazel_common//third_party/java/checker_framework_annotations",
- "@google_bazel_common//third_party/java/error_prone:annotations",
- "@google_bazel_common//third_party/java/google_java_format",
- "@google_bazel_common//third_party/java/javapoet",
- "@bazel_tools//tools/jdk:langtools-neverlink",
- "@google_bazel_common//third_party/java/jsr250_annotations",
- "@google_bazel_common//third_party/java/jsr330_inject",
- "//java/dagger:core",
- "//java/dagger/internal/codegen/serialization",
- "//java/dagger/producers",
- "//java/dagger/model",
- "//java/dagger/spi",
- "//java/dagger/model:internal-proxies",
-]
-
-CODEGEN_DEPS = CODEGEN_SHARED_DEPS + [
- ":jdk-and-guava-extras",
- "@google_bazel_common//third_party/java/guava",
-]
-
-# Extra features for the JDK and Guava. This code is merged into both
-# the dagger-compiler and dagger-spi artifacts that are sent to Maven
-java_library(
- name = "jdk-and-guava-extras",
- srcs = [
- "DaggerGraphs.java",
- "DaggerStreams.java",
- "Optionals.java",
- ],
- plugins = CODEGEN_PLUGINS,
- tags = ["maven:merged"],
- deps = [
- "@google_bazel_common//third_party/java/guava",
- ],
+load("@rules_java//java:defs.bzl", "java_library", "java_plugin")
+load(
+ "//:build_defs.bzl",
+ "POM_VERSION",
)
+load("//tools:maven.bzl", "gen_maven_artifact")
-# Common types needed across all of the codegen package
-java_library(
- name = "base",
- srcs = [
- "AnnotationProtoConverter.java",
- "ClearableCache.java",
- "CompilerOptions.java",
- "ComponentAnnotation.java",
- "ContributionType.java",
- "DaggerStatistics.java",
- "DaggerStatisticsCollectingProcessingStep.java",
- "DaggerStatisticsCollector.java",
- "DaggerStatisticsRecorder.java",
- "DiagnosticFormatting.java",
- "ElementFormatter.java",
- "FeatureStatus.java",
- "Formatter.java",
- "ForwardingCompilerOptions.java",
- "FrameworkTypes.java",
- "InjectionAnnotations.java",
- "Keys.java",
- "MapKeyAccessibility.java",
- "MapType.java",
- "ModuleAnnotation.java",
- "MoreAnnotationMirrors.java",
- "MoreAnnotationValues.java",
- "MultibindingAnnotations.java",
- "OptionalType.java",
- "ProcessingEnvironmentCompilerOptions.java",
- "ProcessingOptions.java",
- "RequestKinds.java",
- "Scopes.java",
- "SetType.java",
- "SimpleAnnotationMirror.java",
- "SimpleTypeAnnotationValue.java",
- "SourceFileGenerationException.java", # Used in :writing and :processor
- "SourceFileGenerator.java", # Needed by InjectBindingRegistry in :binding and also :writing
- "TypeCheckingProcessingStep.java",
- "TypeProtoConverter.java",
- "UniqueNameSet.java",
- "Util.java",
- "ValidationType.java",
- "package-info.java",
- ],
- plugins = CODEGEN_PLUGINS,
- tags = ["maven:merged"],
- deps = CODEGEN_DEPS + [
- "//java/dagger/internal/codegen/javapoet",
- "//java/dagger/internal/codegen/langmodel",
- ],
-)
-
-# Classes that help to build a model of the binding graph
-java_library(
- name = "binding",
- srcs = [
- "AnnotationExpression.java",
- "Binding.java",
- "BindingDeclaration.java",
- "BindingDeclarationFormatter.java",
- "BindingFactory.java",
- "BindingGraph.java",
- "BindingGraphConverter.java",
- "BindingGraphFactory.java",
- "BindingNode.java",
- "BindingRequest.java",
- "BindingType.java",
- "BindsTypeChecker.java",
- "ChildFactoryMethodEdgeImpl.java",
- "ComponentCreatorAnnotation.java",
- "ComponentCreatorDescriptor.java",
- "ComponentCreatorKind.java",
- "ComponentDescriptor.java",
- "ComponentDescriptorFactory.java",
- "ComponentKind.java",
- "ComponentNodeImpl.java",
- "ComponentRequirement.java",
- "ComponentTreeTraverser.java",
- "ConfigurationAnnotations.java", # Uses ModuleDescriptors
- "ContributionBinding.java",
- "DelegateDeclaration.java",
- "DependencyEdgeImpl.java",
- "DependencyRequestFactory.java",
- "DependencyRequestFormatter.java",
- "DependencyVariableNamer.java", # Used by SourceFiles
- "ErrorMessages.java", # Consider splitting this up as it pulls in too much
- "FrameworkDependency.java",
- "FrameworkField.java", # Used by SourceFiles
- "FrameworkType.java",
- "FrameworkTypeMapper.java",
- "InjectBindingRegistry.java",
- "InjectionSiteFactory.java",
- "KeyFactory.java",
- "KeyVariableNamer.java", # needs ConfigurationAnnotations, SourceFiles
- "MapKeys.java",
- "MembersInjectionBinding.java",
- "MethodSignature.java",
- "MethodSignatureFormatter.java",
- "ModuleDescriptor.java",
- "ModuleKind.java",
- "MultibindingDeclaration.java",
- "OptionalBindingDeclaration.java",
- "ProductionBinding.java",
- "ProvisionBinding.java",
- "ResolvedBindings.java",
- "SourceFiles.java", # Consider splitting this up?
- "SubcomponentCreatorBindingEdgeImpl.java",
- "SubcomponentDeclaration.java",
- ],
- plugins = CODEGEN_PLUGINS,
- tags = ["maven:merged"],
- deps = CODEGEN_DEPS + [
- ":base",
- "//java/dagger/internal/codegen/langmodel",
- "//java/dagger/internal/codegen/javapoet",
- ],
-)
-
-# Code related to validating the user-written Dagger code
-java_library(
- name = "validation",
- srcs = [
- "AnyBindingMethodValidator.java",
- "BindingElementValidator.java",
- "BindingGraphPlugins.java",
- "BindingGraphValidator.java",
- "BindingMethodProcessingStep.java",
- "BindingMethodValidator.java",
- "BindsInstanceElementValidator.java",
- "BindsInstanceMethodValidator.java",
- "BindsInstanceParameterValidator.java",
- "BindsInstanceProcessingStep.java",
- "BindsMethodValidator.java",
- "BindsOptionalOfMethodValidator.java",
- "ComponentCreatorValidator.java",
- "ComponentDescriptorValidator.java",
- "ComponentHierarchyValidator.java",
- "ComponentValidator.java",
- "DependencyRequestValidator.java",
- "DiagnosticReporterFactory.java",
- "InjectValidator.java",
- "MapKeyValidator.java",
- "MembersInjectionValidator.java",
- "ModuleValidator.java",
- "MultibindingAnnotationsProcessingStep.java",
- "MultibindsMethodValidator.java",
- "ProducesMethodValidator.java",
- "ProvidesMethodValidator.java",
- "Validation.java",
- "ValidationReport.java",
- ],
- plugins = CODEGEN_PLUGINS,
- tags = ["maven:merged"],
- deps = CODEGEN_DEPS + [
- ":base",
- ":binding",
- "//java/dagger/internal/codegen/langmodel",
- ],
-)
-
-java_library(
- name = "binding_graph_validation",
- srcs = [
- "DependencyCycleValidator.java",
- "DependsOnProductionExecutorValidator.java",
- "DuplicateBindingsValidator.java",
- "IncompatiblyScopedBindingsValidator.java",
- "InjectBindingValidator.java",
- "MapMultibindingValidator.java",
- "MissingBindingValidator.java",
- "NullableBindingValidator.java",
- "ProvisionDependencyOnProducerBindingValidator.java",
- "SubcomponentFactoryMethodValidator.java",
- ],
- plugins = CODEGEN_PLUGINS,
- tags = ["maven:merged"],
- deps = CODEGEN_DEPS + [
- ":base",
- ":binding",
- ":validation",
- "//java/dagger/internal/codegen/langmodel",
- ],
-)
-
-# Classes that assemble the model of the generated code and write to the Filer
-java_library(
- name = "writing",
- srcs = [
- "AnnotationCreatorGenerator.java",
- "AnonymousProviderCreationExpression.java",
- "BindingExpression.java",
- "ComponentBindingExpressions.java",
- "ComponentCreatorImplementation.java",
- "ComponentImplementation.java",
- "ComponentInstanceBindingExpression.java",
- "ComponentMethodBindingExpression.java",
- "ComponentProvisionBindingExpression.java",
- "ComponentRequirementBindingExpression.java",
- "ComponentRequirementExpression.java",
- "ComponentRequirementExpressions.java",
- "DeferredModifiableBindingExpression.java",
- "DelegateBindingExpression.java",
- "DelegatingFrameworkInstanceCreationExpression.java",
- "DependencyMethodProducerCreationExpression.java",
- "DependencyMethodProviderCreationExpression.java",
- "DerivedFromFrameworkInstanceBindingExpression.java",
- "FactoryGenerator.java",
- "FrameworkFieldInitializer.java",
- "FrameworkInstanceBindingExpression.java",
- "FrameworkInstanceSupplier.java",
- "GenerationCompilerOptions.java",
- "GwtCompatibility.java",
- "HjarSourceFileGenerator.java",
- "ImmediateFutureBindingExpression.java",
- "InaccessibleMapKeyProxyGenerator.java",
- "InjectionMethod.java",
- "InjectionMethods.java",
- "InjectionOrProvisionProviderCreationExpression.java",
- "InnerSwitchingProviders.java",
- "InstanceFactoryCreationExpression.java",
- "MapBindingExpression.java",
- "MapFactoryCreationExpression.java",
- "MemberSelect.java",
- "MembersInjectionBindingExpression.java",
- "MembersInjectionMethods.java",
- "MembersInjectorGenerator.java",
- "MembersInjectorProviderCreationExpression.java",
- "MethodBindingExpression.java",
- "MissingBindingExpression.java",
- "ModifiableAbstractMethodBindingExpression.java",
- "ModifiableBindingExpressions.java",
- "ModifiableBindingMethods.java",
- "ModifiableBindingType.java",
- "ModifiableConcreteMethodBindingExpression.java",
- "ModuleConstructorProxyGenerator.java",
- "ModuleGenerator.java",
- "ModuleProxies.java",
- "MonitoringModuleGenerator.java",
- "MonitoringModuleProcessingStep.java",
- "MultibindingExpression.java",
- "MultibindingFactoryCreationExpression.java",
- "OptionalBindingExpression.java",
- "OptionalFactories.java",
- "OptionalFactoryInstanceCreationExpression.java",
- "ParentComponent.java",
- "PerComponentImplementation.java",
- "PerGeneratedFile.java",
- "PrivateMethodBindingExpression.java",
- "ProducerCreationExpression.java",
- "ProducerEntryPointView.java",
- "ProducerFactoryGenerator.java",
- "ProducerFromProviderCreationExpression.java",
- "ProducerNodeInstanceBindingExpression.java",
- "ProviderInstanceBindingExpression.java",
- "PrunedConcreteMethodBindingExpression.java",
- "SetBindingExpression.java",
- "SetFactoryCreationExpression.java",
- "SimpleInvocationBindingExpression.java",
- "SimpleMethodBindingExpression.java",
- "SubcomponentCreatorBindingExpression.java",
- "SubcomponentNames.java",
- "SwitchingProviders.java",
- "TopLevel.java",
- "UnwrappedMapKeyGenerator.java",
- ],
- plugins = CODEGEN_PLUGINS,
- tags = ["maven:merged"],
- deps = CODEGEN_DEPS + [
- ":base",
- ":binding",
- "//java/dagger/internal/codegen/javapoet",
- "//java/dagger/internal/codegen/langmodel",
- ],
-)
+package(default_visibility = ["//:src"])
-# The processor's "main", if you will
java_library(
name = "processor",
- srcs = [
- "BindingGraphValidationModule.java",
- "BindingMethodValidatorsModule.java",
- "ComponentCreatorImplementationFactory.java",
- "ComponentGenerator.java",
- "ComponentHjarProcessingStep.java",
- "ComponentImplementationBuilder.java",
- "ComponentImplementationFactory.java",
- "ComponentProcessingStep.java",
- "ComponentProcessor.java",
- "CurrentImplementationSubcomponent.java",
- "DeserializedComponentImplementationBuilder.java",
- "GenerationOptionsModule.java",
- "InjectBindingRegistryImpl.java",
- "InjectBindingRegistryModule.java",
- "InjectProcessingStep.java",
- "MapKeyProcessingStep.java",
- "ModuleProcessingStep.java",
- "ProcessingEnvironmentModule.java",
- "ProcessingRoundCacheModule.java",
- "SourceFileGeneratorsModule.java",
- "SpiModule.java",
- "SystemComponentsModule.java",
- "TopLevelImplementationComponent.java",
+ srcs = glob(
+ ["*.java"],
+ exclude = ["package-info.java"],
+ ),
+ plugins = [
+ "//java/dagger/internal/codegen/bootstrap",
],
- plugins = CODEGEN_PLUGINS,
tags = ["maven_coordinates=com.google.dagger:dagger-compiler:" + POM_VERSION],
- deps = CODEGEN_DEPS + [
- ":base",
- ":binding",
- ":binding_graph_validation",
- ":writing",
- ":validation",
- "//java/dagger/internal/codegen/javapoet",
- "//java/dagger/internal/codegen/langmodel",
- "@google_bazel_common//third_party/java/incap",
- ],
-)
-
-pom_file(
- name = "pom",
- artifact_id = "dagger-compiler",
- artifact_name = "Dagger Compiler",
- targets = [
- ":processor",
- ":base",
- ":binding",
- ":binding_graph_validation",
- ":writing",
- ":validation",
- "//java/dagger/internal/codegen/serialization",
- "//java/dagger/internal/codegen/javapoet",
- ],
-)
-
-java_library(
- name = "javac-plugin-module",
- srcs = JAVAC_PLUGIN_MODULE_SRCS,
- plugins = [":component-codegen"],
- visibility = ["//visibility:private"],
- deps = [
- ":base",
- ":binding",
- ":javac",
- ":processor",
- "//java/dagger:core",
- "//java/dagger/internal/codegen/langmodel",
+ exports = [
+ "@google_bazel_common//third_party/java/jsr250_annotations", # Export for @Generated
],
-)
-
-java_library(
- name = "kythe",
- srcs = KYTHE_SRCS,
- plugins = [":component-codegen"],
deps = [
- ":base",
- ":binding",
- ":javac",
- ":javac-plugin-module",
- ":kythe_plugin",
- ":processor",
+ ":package_info",
"//java/dagger:core",
+ "//java/dagger/internal/codegen/base",
+ "//java/dagger/internal/codegen/binding",
+ "//java/dagger/internal/codegen/bindinggraphvalidation",
+ "//java/dagger/internal/codegen/compileroption",
+ "//java/dagger/internal/codegen/componentgenerator",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/codegen/javapoet",
+ "//java/dagger/internal/codegen/kotlin",
"//java/dagger/internal/codegen/langmodel",
- "//java/dagger/model",
+ "//java/dagger/internal/codegen/validation",
+ "//java/dagger/internal/codegen/writing",
+ "//java/dagger/internal/guava:collect",
"//java/dagger/producers",
- "@google_bazel_common//third_party/java/auto:common",
+ "//java/dagger/spi",
"@google_bazel_common//third_party/java/auto:service",
- "@google_bazel_common//third_party/java/guava",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/error_prone:annotations",
+ "@google_bazel_common//third_party/java/google_java_format",
+ "@google_bazel_common//third_party/java/incap",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ "@maven//:com_google_auto_auto_common",
],
)
-# Replacement for @bazel_tools//third_party/java/jdk/langtools:javac, which seems to have gone away?
-java_import(
- name = "javac",
- jars = ["@bazel_tools//third_party/java/jdk/langtools:javac_jar"],
-)
-
-# A _deploy.jar consisting of the java_librarys in https://github.com/kythe/kythe needed to build a
-# Kythe plugin
-# TODO(ronshapiro): replace this with a http_archive of the next release in
-# https://github.com/kythe/kythe/releases
-java_import(
- name = "kythe_plugin",
- jars = ["kythe_plugin_deploy.jar"],
- neverlink = 1,
-)
-
-java_import(
- name = "bootstrap_compiler",
- jars = ["bootstrap_compiler_deploy.jar"],
- visibility = ["//visibility:private"],
-)
-
-java_plugin(
- name = "bootstrap_compiler_plugin",
- generates_api = 1,
- processor_class = "dagger.internal.codegen.ComponentProcessor",
- deps = [":bootstrap_compiler"],
-)
-
-load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
-
-javadoc_library(
- name = "codegen-javadoc",
- srcs = CODEGEN_SRCS,
- root_packages = ["dagger.internal.codegen"],
- deps = [":processor"],
+java_library(
+ name = "package_info",
+ srcs = ["package-info.java"],
+ tags = ["maven:merged"],
+ deps = ["@google_bazel_common//third_party/java/error_prone:annotations"],
)
-java_library(
- name = "check-package-javadoc",
- testonly = 1,
- srcs = CODEGEN_SRCS,
- javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
- plugins = CODEGEN_PLUGINS,
- deps = CODEGEN_DEPS + [
- "//java/dagger/internal/codegen/langmodel",
+gen_maven_artifact(
+ name = "artifact",
+ artifact_coordinates = "com.google.dagger:dagger-compiler:" + POM_VERSION,
+ artifact_name = "Dagger Compiler",
+ artifact_target = ":processor",
+ artifact_target_libs = [
+ ":package_info",
+ "//java/dagger/internal/codegen/base",
+ "//java/dagger/internal/codegen/base:shared",
+ "//java/dagger/internal/codegen/binding",
+ "//java/dagger/internal/codegen/bindinggraphvalidation",
+ "//java/dagger/internal/codegen/compileroption",
+ "//java/dagger/internal/codegen/componentgenerator",
"//java/dagger/internal/codegen/javapoet",
- "@google_bazel_common//third_party/java/incap",
- ],
+ "//java/dagger/internal/codegen/kotlin",
+ "//java/dagger/internal/codegen/langmodel",
+ "//java/dagger/internal/codegen/validation",
+ "//java/dagger/internal/codegen/writing",
+ "//java/dagger/model:internal-proxies",
+ ],
+ artifact_target_maven_deps = [
+ "com.google.auto:auto-common",
+ "com.google.code.findbugs:jsr305",
+ "com.google.dagger:dagger-producers",
+ "com.google.dagger:dagger-spi",
+ "com.google.dagger:dagger",
+ "com.google.googlejavaformat:google-java-format",
+ "com.google.guava:failureaccess",
+ "com.google.guava:guava",
+ "com.squareup:javapoet",
+ "javax.annotation:jsr250-api",
+ "javax.inject:javax.inject",
+ "net.ltgt.gradle.incap:incap",
+ "org.checkerframework:checker-compat-qual",
+ "org.jetbrains.kotlin:kotlin-stdlib",
+ "org.jetbrains.kotlinx:kotlinx-metadata-jvm",
+ ],
+ javadoc_root_packages = ["dagger.internal.codegen"],
+ # The javadocs should only include ComponentProcessor.java, since that is the only class used
+ # externally. Specifically, ComponentProcessor.forTesting() is required for testing SPI plugins.
+ javadoc_srcs = ["ComponentProcessor.java"],
+ shaded_deps = ["@maven//:com_google_auto_auto_common"],
+ shaded_rules = ["rule com.google.auto.common.** dagger.shaded.auto.common.@1"],
)
java_plugin(
@@ -509,19 +130,3 @@ java_plugin(
],
deps = [":processor"],
)
-
-java_library(
- name = "statistics",
- srcs = STATISTICS_COLLECTOR_SRCS,
- plugins = [":component-codegen"],
- deps = [
- ":base",
- ":binding",
- ":javac",
- ":javac-plugin-module",
- ":processor",
- "//java/dagger:core",
- "//java/dagger/model",
- "@google_bazel_common//third_party/java/error_prone:check_api",
- ],
-)
diff --git a/java/dagger/internal/codegen/Binding.java b/java/dagger/internal/codegen/Binding.java
deleted file mode 100644
index c0f6b71e3..000000000
--- a/java/dagger/internal/codegen/Binding.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Suppliers.memoize;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.DaggerStreams.toImmutableList;
-import static java.util.stream.Collectors.toSet;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.STATIC;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Supplier;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Multimaps;
-import com.google.common.collect.Sets;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.BindingKind;
-import dagger.model.DependencyRequest;
-import dagger.model.Key;
-import dagger.model.Scope;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.TypeParameterElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.SimpleTypeVisitor6;
-
-/**
- * An abstract type for classes representing a Dagger binding. Particularly, contains the {@link
- * Element} that generated the binding and the {@link DependencyRequest} instances that are required
- * to satisfy the binding, but leaves the specifics of the <i>mechanism</i> of the binding to the
- * subtypes.
- */
-abstract class Binding extends BindingDeclaration {
-
- /**
- * Returns {@code true} if using this binding requires an instance of the {@link
- * #contributingModule()}.
- */
- boolean requiresModuleInstance() {
- if (!bindingElement().isPresent() || !contributingModule().isPresent()) {
- return false;
- }
- Set<Modifier> modifiers = bindingElement().get().getModifiers();
- return !modifiers.contains(ABSTRACT) && !modifiers.contains(STATIC);
- }
-
- /**
- * Returns {@code true} if this binding may provide {@code null} instead of an instance of {@link
- * #key()}. Nullable bindings cannot be requested from {@linkplain DependencyRequest#isNullable()
- * non-nullable dependency requests}.
- */
- abstract boolean isNullable();
-
- /** The kind of binding this instance represents. */
- abstract BindingKind kind();
-
- /** The {@link BindingType} of this binding. */
- abstract BindingType bindingType();
-
- /** The {@link FrameworkType} of this binding. */
- final FrameworkType frameworkType() {
- return FrameworkType.forBindingType(bindingType());
- }
-
- /**
- * The explicit set of {@link DependencyRequest dependencies} required to satisfy this binding as
- * defined by the user-defined injection sites.
- */
- abstract ImmutableSet<DependencyRequest> explicitDependencies();
-
- /**
- * The set of {@link DependencyRequest dependencies} that are added by the framework rather than a
- * user-defined injection site. This returns an unmodifiable set.
- */
- // TODO(gak): this will eventually get changed to return a set of FrameworkDependency
- ImmutableSet<DependencyRequest> implicitDependencies() {
- return ImmutableSet.of();
- }
-
- private final Supplier<ImmutableSet<DependencyRequest>> dependencies =
- memoize(
- () -> {
- ImmutableSet<DependencyRequest> implicitDependencies = implicitDependencies();
- return ImmutableSet.copyOf(
- implicitDependencies.isEmpty()
- ? explicitDependencies()
- : Sets.union(implicitDependencies, explicitDependencies()));
- });
-
- /**
- * The set of {@link DependencyRequest dependencies} required to satisfy this binding. This is the
- * union of {@link #explicitDependencies()} and {@link #implicitDependencies()}. This returns an
- * unmodifiable set.
- */
- final ImmutableSet<DependencyRequest> dependencies() {
- return dependencies.get();
- }
-
- private final Supplier<ImmutableList<FrameworkDependency>> frameworkDependencies =
- memoize(
- () ->
- dependencyAssociations()
- .stream()
- .map(DependencyAssociation::frameworkDependency)
- .collect(toImmutableList()));
-
- /**
- * The framework dependencies of {@code binding}. There will be one element for each different
- * binding key in the <em>{@linkplain Binding#unresolved() unresolved}</em> version of {@code
- * binding}.
- *
- * <p>For example, given the following modules:
- *
- * <pre><code>
- * {@literal @Module} abstract class {@literal BaseModule<T>} {
- * {@literal @Provides} Foo provideFoo(T t, String string) {
- * return …;
- * }
- * }
- *
- * {@literal @Module} class StringModule extends {@literal BaseModule<String>} {}
- * </code></pre>
- *
- * Both dependencies of {@code StringModule.provideFoo} have the same binding key: {@code String}.
- * But there are still two dependencies, because in the unresolved binding they have different
- * binding keys:
- *
- * <dl>
- * <dt>{@code T}
- * <dd>{@code String t}
- * <dt>{@code String}
- * <dd>{@code String string}
- * </dl>
- *
- * <p>Note that the sets returned by this method when called on the same binding will be equal,
- * and their elements will be in the same order.
- */
- /* TODO(dpb): The stable-order postcondition is actually hard to verify in code for two equal
- * instances of Binding, because it really depends on the order of the binding's dependencies,
- * and two equal instances of Binding may have the same dependencies in a different order. */
- final ImmutableList<FrameworkDependency> frameworkDependencies() {
- return frameworkDependencies.get();
- }
-
- /**
- * Associates a {@link FrameworkDependency} with the set of {@link DependencyRequest} instances
- * that correlate for a binding.
- */
- @AutoValue
- abstract static class DependencyAssociation {
- abstract FrameworkDependency frameworkDependency();
-
- abstract ImmutableSet<DependencyRequest> dependencyRequests();
-
- static DependencyAssociation create(
- FrameworkDependency frameworkDependency, Iterable<DependencyRequest> dependencyRequests) {
- return new AutoValue_Binding_DependencyAssociation(
- frameworkDependency, ImmutableSet.copyOf(dependencyRequests));
- }
- }
-
- private final Supplier<ImmutableList<DependencyAssociation>> dependencyAssociations =
- memoize(
- () -> {
- FrameworkTypeMapper frameworkTypeMapper =
- FrameworkTypeMapper.forBindingType(bindingType());
- ImmutableList.Builder<DependencyAssociation> list = ImmutableList.builder();
- for (Set<DependencyRequest> requests : groupByUnresolvedKey()) {
- list.add(
- DependencyAssociation.create(
- FrameworkDependency.create(
- getOnlyElement(
- requests.stream().map(DependencyRequest::key).collect(toSet())),
- frameworkTypeMapper.getFrameworkType(requests)),
- requests));
- }
- return list.build();
- });
-
- /**
- * Returns the same {@link FrameworkDependency} instances from {@link #frameworkDependencies}, but
- * with the set of {@link DependencyRequest} instances with which each is associated.
- *
- * <p>Ths method returns a list of {@link Map.Entry entries} rather than a {@link Map} or {@link
- * com.google.common.collect.Multimap} because any given {@link FrameworkDependency} may appear
- * multiple times if the {@linkplain Binding#unresolved() unresolved} binding requires it. If that
- * distinction is not important, the entries can be merged into a single mapping.
- */
- final ImmutableList<DependencyAssociation> dependencyAssociations() {
- return dependencyAssociations.get();
- }
-
- private final Supplier<ImmutableMap<DependencyRequest, FrameworkDependency>>
- frameworkDependenciesMap =
- memoize(
- () -> {
- ImmutableMap.Builder<DependencyRequest, FrameworkDependency> frameworkDependencies =
- ImmutableMap.builder();
- for (DependencyAssociation dependencyAssociation : dependencyAssociations()) {
- for (DependencyRequest dependencyRequest :
- dependencyAssociation.dependencyRequests()) {
- frameworkDependencies.put(
- dependencyRequest, dependencyAssociation.frameworkDependency());
- }
- }
- return frameworkDependencies.build();
- });
-
- /**
- * Returns the mapping from each {@linkplain #dependencies dependency} to its associated {@link
- * FrameworkDependency}.
- */
- final ImmutableMap<DependencyRequest, FrameworkDependency>
- dependenciesToFrameworkDependenciesMap() {
- return frameworkDependenciesMap.get();
- }
-
- /**
- * Groups {@code binding}'s implicit dependencies by their binding key, using the dependency keys
- * from the {@link Binding#unresolved()} binding if it exists.
- */
- private ImmutableList<Set<DependencyRequest>> groupByUnresolvedKey() {
- ImmutableSetMultimap.Builder<Key, DependencyRequest> dependenciesByKeyBuilder =
- ImmutableSetMultimap.builder();
- Iterator<DependencyRequest> dependencies = dependencies().iterator();
- Binding unresolved = unresolved().isPresent() ? unresolved().get() : this;
- Iterator<DependencyRequest> unresolvedDependencies = unresolved.dependencies().iterator();
- while (dependencies.hasNext()) {
- dependenciesByKeyBuilder.put(unresolvedDependencies.next().key(), dependencies.next());
- }
- return ImmutableList.copyOf(
- Multimaps.asMap(
- dependenciesByKeyBuilder.orderValuesBy(SourceFiles.DEPENDENCY_ORDERING).build())
- .values());
- }
-
- /**
- * If this binding's key's type parameters are different from those of the
- * {@link #bindingTypeElement()}, this is the binding for the {@link #bindingTypeElement()}'s
- * unresolved type.
- */
- abstract Optional<? extends Binding> unresolved();
-
- Optional<Scope> scope() {
- return Optional.empty();
- }
-
- // TODO(sameb): Remove the TypeElement parameter and pull it from the TypeMirror.
- static boolean hasNonDefaultTypeParameters(
- TypeElement element, TypeMirror type, DaggerTypes types) {
- // If the element has no type parameters, nothing can be wrong.
- if (element.getTypeParameters().isEmpty()) {
- return false;
- }
-
- List<TypeMirror> defaultTypes = Lists.newArrayList();
- for (TypeParameterElement parameter : element.getTypeParameters()) {
- defaultTypes.add(parameter.asType());
- }
-
- List<TypeMirror> actualTypes =
- type.accept(
- new SimpleTypeVisitor6<List<TypeMirror>, Void>() {
- @Override
- protected List<TypeMirror> defaultAction(TypeMirror e, Void p) {
- return ImmutableList.of();
- }
-
- @Override
- public List<TypeMirror> visitDeclared(DeclaredType t, Void p) {
- return ImmutableList.<TypeMirror>copyOf(t.getTypeArguments());
- }
- },
- null);
-
- // The actual type parameter size can be different if the user is using a raw type.
- if (defaultTypes.size() != actualTypes.size()) {
- return true;
- }
-
- for (int i = 0; i < defaultTypes.size(); i++) {
- if (!types.isSameType(defaultTypes.get(i), actualTypes.get(i))) {
- return true;
- }
- }
- return false;
- }
-}
diff --git a/java/dagger/internal/codegen/BindingDeclaration.java b/java/dagger/internal/codegen/BindingDeclaration.java
deleted file mode 100644
index c9520cde0..000000000
--- a/java/dagger/internal/codegen/BindingDeclaration.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.model.BindingKind;
-import dagger.model.Key;
-import java.util.Optional;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-
-/** An object that declares or specifies a binding. */
-abstract class BindingDeclaration {
-
- /** The {@link Key} of this declaration. */
- abstract Key key();
-
- /**
- * The {@link Element} that declares this binding. Absent for {@linkplain BindingKind binding
- * kinds} that are not always declared by exactly one element.
- *
- * <p>For example, consider {@link BindingKind#MULTIBOUND_SET}. A component with many
- * {@code @IntoSet} bindings for the same key will have a synthetic binding that depends on all
- * contributions, but with no identifiying binding element. A {@code @Multibinds} method will also
- * contribute a synthetic binding, but since multiple {@code @Multibinds} methods can coexist in
- * the same component (and contribute to one single binding), it has no binding element.
- */
- // TODO(ronshapiro): examine whether this wildcard+bound have any benefit.
- // We never actually refer to the overridden bindingElement methods directly in a way which needs
- // anything more than an Element. Removing the wildcard would allow for simpler user-written code
- // when the binding element is passed to a method.
- abstract Optional<Element> bindingElement();
-
- /**
- * The type enclosing the {@link #bindingElement()}, or {@link Optional#empty()} if {@link
- * #bindingElement()} is empty.
- */
- final Optional<TypeElement> bindingTypeElement() {
- return bindingElement().map(DaggerElements::closestEnclosingTypeElement);
- }
-
- /**
- * The installed module class that contributed the {@link #bindingElement()}. May be a subclass of
- * the class that contains {@link #bindingElement()}. Absent if {@link #bindingElement()} is
- * empty.
- */
- abstract Optional<TypeElement> contributingModule();
-}
diff --git a/java/dagger/internal/codegen/BindingDeclarationFormatter.java b/java/dagger/internal/codegen/BindingDeclarationFormatter.java
deleted file mode 100644
index d8501651a..000000000
--- a/java/dagger/internal/codegen/BindingDeclarationFormatter.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.collect.Sets.immutableEnumSet;
-import static dagger.internal.codegen.DiagnosticFormatting.stripCommonTypePrefixes;
-import static dagger.internal.codegen.ElementFormatter.elementToString;
-import static javax.lang.model.element.ElementKind.PARAMETER;
-import static javax.lang.model.type.TypeKind.DECLARED;
-import static javax.lang.model.type.TypeKind.EXECUTABLE;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import javax.inject.Inject;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.TypeKind;
-
-/**
- * Formats a {@link BindingDeclaration} into a {@link String} suitable for use in error messages.
- */
-final class BindingDeclarationFormatter extends Formatter<BindingDeclaration> {
- private static final ImmutableSet<TypeKind> FORMATTABLE_ELEMENT_TYPE_KINDS =
- immutableEnumSet(EXECUTABLE, DECLARED);
-
- private final MethodSignatureFormatter methodSignatureFormatter;
-
- @Inject
- BindingDeclarationFormatter(MethodSignatureFormatter methodSignatureFormatter) {
- this.methodSignatureFormatter = methodSignatureFormatter;
- }
-
- /**
- * Returns {@code true} for declarations that this formatter can format. Specifically bindings
- * from subcomponent declarations or those with {@linkplain BindingDeclaration#bindingElement()
- * binding elements} that are methods, constructors, or types.
- */
- boolean canFormat(BindingDeclaration bindingDeclaration) {
- if (bindingDeclaration instanceof SubcomponentDeclaration) {
- return true;
- }
- if (bindingDeclaration.bindingElement().isPresent()) {
- Element bindingElement = bindingDeclaration.bindingElement().get();
- return bindingElement.getKind().equals(PARAMETER)
- || FORMATTABLE_ELEMENT_TYPE_KINDS.contains(bindingElement.asType().getKind());
- }
- // TODO(dpb): validate whether what this is doing is correct
- return false;
- }
-
- @Override
- public String format(BindingDeclaration bindingDeclaration) {
- if (bindingDeclaration instanceof SubcomponentDeclaration) {
- return formatSubcomponentDeclaration((SubcomponentDeclaration) bindingDeclaration);
- }
-
- if (bindingDeclaration.bindingElement().isPresent()) {
- Element bindingElement = bindingDeclaration.bindingElement().get();
- if (bindingElement.getKind().equals(PARAMETER)) {
- return elementToString(bindingElement);
- }
-
- switch (bindingElement.asType().getKind()) {
- case EXECUTABLE:
- return methodSignatureFormatter.format(
- MoreElements.asExecutable(bindingElement),
- bindingDeclaration
- .contributingModule()
- .map(module -> MoreTypes.asDeclared(module.asType())));
-
- case DECLARED:
- return stripCommonTypePrefixes(bindingElement.asType().toString());
-
- default:
- throw new IllegalArgumentException(
- "Formatting unsupported for element: " + bindingElement);
- }
- }
-
- return String.format(
- "Dagger-generated binding for %s",
- stripCommonTypePrefixes(bindingDeclaration.key().toString()));
- }
-
- private String formatSubcomponentDeclaration(SubcomponentDeclaration subcomponentDeclaration) {
- ImmutableList<TypeElement> moduleSubcomponents =
- subcomponentDeclaration.moduleAnnotation().subcomponents();
- int index = moduleSubcomponents.indexOf(subcomponentDeclaration.subcomponentType());
- StringBuilder annotationValue = new StringBuilder();
- if (moduleSubcomponents.size() != 1) {
- annotationValue.append("{");
- }
- annotationValue.append(
- formatArgumentInList(
- index,
- moduleSubcomponents.size(),
- subcomponentDeclaration.subcomponentType().getQualifiedName() + ".class"));
- if (moduleSubcomponents.size() != 1) {
- annotationValue.append("}");
- }
-
- return String.format(
- "@%s(subcomponents = %s) for %s",
- subcomponentDeclaration.moduleAnnotation().annotationClass().getSimpleName(),
- annotationValue,
- subcomponentDeclaration.contributingModule().get());
- }
-}
diff --git a/java/dagger/internal/codegen/BindingElementValidator.java b/java/dagger/internal/codegen/BindingElementValidator.java
deleted file mode 100644
index 00519123d..000000000
--- a/java/dagger/internal/codegen/BindingElementValidator.java
+++ /dev/null
@@ -1,383 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Verify.verifyNotNull;
-import static dagger.internal.codegen.InjectionAnnotations.getQualifiers;
-import static dagger.internal.codegen.MapKeys.getMapKeys;
-import static dagger.internal.codegen.Scopes.scopesOf;
-import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
-import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
-import static javax.lang.model.type.TypeKind.ARRAY;
-import static javax.lang.model.type.TypeKind.DECLARED;
-import static javax.lang.model.type.TypeKind.TYPEVAR;
-import static javax.lang.model.type.TypeKind.VOID;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.errorprone.annotations.FormatMethod;
-import dagger.MapKey;
-import dagger.Provides;
-import dagger.model.Key;
-import dagger.model.Scope;
-import dagger.multibindings.ElementsIntoSet;
-import dagger.multibindings.IntoMap;
-import dagger.producers.Produces;
-import java.lang.annotation.Annotation;
-import java.util.Formatter;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import javax.inject.Qualifier;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-
-/** A validator for elements that represent binding declarations. */
-abstract class BindingElementValidator<E extends Element> {
- private final Class<? extends Annotation> bindingAnnotation;
- private final AllowsMultibindings allowsMultibindings;
- private final AllowsScoping allowsScoping;
- private final Map<E, ValidationReport<E>> cache = new HashMap<>();
-
- /**
- * Creates a validator object.
- *
- * @param bindingAnnotation the annotation on an element that identifies it as a binding element
- */
- protected BindingElementValidator(
- Class<? extends Annotation> bindingAnnotation,
- AllowsMultibindings allowsMultibindings,
- AllowsScoping allowsScoping) {
- this.bindingAnnotation = bindingAnnotation;
- this.allowsMultibindings = allowsMultibindings;
- this.allowsScoping = allowsScoping;
- }
-
- /** Returns a {@link ValidationReport} for {@code element}. */
- final ValidationReport<E> validate(E element) {
- return reentrantComputeIfAbsent(cache, element, this::validateUncached);
- }
-
- private ValidationReport<E> validateUncached(E element) {
- return elementValidator(element).validate();
- }
-
- /**
- * Returns an error message of the form "&lt;{@link #bindingElements()}&gt; <i>rule</i>", where
- * <i>rule</i> comes from calling {@link String#format(String, Object...)} on {@code ruleFormat}
- * and the other arguments.
- */
- @FormatMethod
- protected final String bindingElements(String ruleFormat, Object... args) {
- return new Formatter().format("%s ", bindingElements()).format(ruleFormat, args).toString();
- }
-
- /**
- * The kind of elements that this validator validates. Should be plural. Used for error reporting.
- */
- protected abstract String bindingElements();
-
- /** The verb describing the {@link ElementValidator#bindingElementType()} in error messages. */
- // TODO(ronshapiro,dpb): improve the name of this method and it's documentation.
- protected abstract String bindingElementTypeVerb();
-
- /** The error message when a binding element has a bad type. */
- protected String badTypeMessage() {
- return bindingElements(
- "must %s a primitive, an array, a type variable, or a declared type",
- bindingElementTypeVerb());
- }
-
- /**
- * The error message when a the type for a binding element with {@link
- * ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES} is a not set type.
- */
- protected String elementsIntoSetNotASetMessage() {
- return bindingElements(
- "annotated with @ElementsIntoSet must %s a Set", bindingElementTypeVerb());
- }
-
- /**
- * The error message when a the type for a binding element with {@link
- * ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES} is a raw set.
- */
- protected String elementsIntoSetRawSetMessage() {
- return bindingElements(
- "annotated with @ElementsIntoSet cannot %s a raw Set", bindingElementTypeVerb());
- }
-
- /*** Returns an {@link ElementValidator} for validating the given {@code element}. */
- protected abstract ElementValidator elementValidator(E element);
-
- /** Validator for a single binding element. */
- protected abstract class ElementValidator {
- protected final E element;
- protected final ValidationReport.Builder<E> report;
-
- protected ElementValidator(E element) {
- this.element = element;
- this.report = ValidationReport.about(element);
- }
-
- /** Checks the element for validity. */
- private ValidationReport<E> validate() {
- checkType();
- checkQualifiers();
- checkMapKeys();
- checkMultibindings();
- checkScopes();
- checkAdditionalProperties();
- return report.build();
- }
-
- /** Check any additional properties of the element. Does nothing by default. */
- protected void checkAdditionalProperties() {}
-
- /**
- * The type declared by this binding element. This may differ from a binding's {@link
- * Key#type()}, for example in multibindings. An {@link Optional#empty()} return value indicates
- * that the contributed type is ambiguous or missing, i.e. a {@code @BindsInstance} method with
- * zero or many parameters.
- */
- // TODO(dpb): should this be an ImmutableList<TypeMirror>, with this class checking the size?
- protected abstract Optional<TypeMirror> bindingElementType();
-
- /**
- * Adds an error if the {@link #bindingElementType() binding element type} is not appropriate.
- *
- * <p>Adds an error if the type is not a primitive, array, declared type, or type variable.
- *
- * <p>If the binding is not a multibinding contribution, adds an error if the type is a
- * framework type.
- *
- * <p>If the element has {@link ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES}, adds an
- * error if the type is not a {@code Set<T>} for some {@code T}
- */
- protected void checkType() {
- switch (ContributionType.fromBindingElement(element)) {
- case UNIQUE:
- /* Validate that a unique binding is not attempting to bind a framework type. This
- * validation is only appropriate for unique bindings because multibindings may collect
- * framework types. E.g. Set<Provider<Foo>> is perfectly reasonable. */
- checkFrameworkType();
- // fall through
-
- case SET:
- case MAP:
- bindingElementType().ifPresent(type -> checkKeyType(type));
- break;
-
- case SET_VALUES:
- checkSetValuesType();
- }
- }
-
- /**
- * Adds an error if {@code keyType} is not a primitive, declared type, array, or type variable.
- */
- protected void checkKeyType(TypeMirror keyType) {
- TypeKind kind = keyType.getKind();
- if (kind.equals(VOID)) {
- report.addError(bindingElements("must %s a value (not void)", bindingElementTypeVerb()));
- } else if (!(kind.isPrimitive()
- || kind.equals(DECLARED)
- || kind.equals(ARRAY)
- || kind.equals(TYPEVAR))) {
- report.addError(badTypeMessage());
- }
- }
-
- /**
- * Adds an error if the type for an element with {@link ElementsIntoSet @ElementsIntoSet} or
- * {@code SET_VALUES} is not a a {@code Set<T>} for a reasonable {@code T}.
- */
- // TODO(gak): should we allow "covariant return" for set values?
- protected void checkSetValuesType() {
- bindingElementType().ifPresent(keyType -> checkSetValuesType(keyType));
- }
-
- /** Adds an error if {@code type} is not a {@code Set<T>} for a reasonable {@code T}. */
- protected final void checkSetValuesType(TypeMirror type) {
- if (!SetType.isSet(type)) {
- report.addError(elementsIntoSetNotASetMessage());
- } else {
- SetType setType = SetType.from(type);
- if (setType.isRawType()) {
- report.addError(elementsIntoSetRawSetMessage());
- } else {
- checkKeyType(setType.elementType());
- }
- }
- }
-
- /**
- * Adds an error if the element has more than one {@linkplain Qualifier qualifier} annotation.
- */
- private void checkQualifiers() {
- ImmutableSet<? extends AnnotationMirror> qualifiers = getQualifiers(element);
- if (qualifiers.size() > 1) {
- for (AnnotationMirror qualifier : qualifiers) {
- report.addError(
- bindingElements("may not use more than one @Qualifier"),
- element,
- qualifier);
- }
- }
- }
-
- /**
- * Adds an error if an {@link IntoMap @IntoMap} element doesn't have exactly one {@link
- * MapKey @MapKey} annotation, or if an element that is {@link IntoMap @IntoMap} has any.
- */
- private void checkMapKeys() {
- if (!allowsMultibindings.allowsMultibindings()) {
- return;
- }
- ImmutableSet<? extends AnnotationMirror> mapKeys = getMapKeys(element);
- if (ContributionType.fromBindingElement(element).equals(ContributionType.MAP)) {
- switch (mapKeys.size()) {
- case 0:
- report.addError(bindingElements("of type map must declare a map key"));
- break;
- case 1:
- break;
- default:
- report.addError(bindingElements("may not have more than one map key"));
- break;
- }
- } else if (!mapKeys.isEmpty()) {
- report.addError(bindingElements("of non map type cannot declare a map key"));
- }
- }
-
- /**
- * Adds errors if:
- *
- * <ul>
- * <li>the element doesn't allow {@linkplain MultibindingAnnotations multibinding annotations}
- * and has any
- * <li>the element does allow them but has more than one
- * <li>the element has a multibinding annotation and its {@link Provides} or {@link Produces}
- * annotation has a {@code type} parameter.
- * </ul>
- */
- private void checkMultibindings() {
- ImmutableSet<AnnotationMirror> multibindingAnnotations =
- MultibindingAnnotations.forElement(element);
-
- switch (allowsMultibindings) {
- case NO_MULTIBINDINGS:
- for (AnnotationMirror annotation : multibindingAnnotations) {
- report.addError(
- bindingElements("cannot have multibinding annotations"),
- element,
- annotation);
- }
- break;
-
- case ALLOWS_MULTIBINDINGS:
- if (multibindingAnnotations.size() > 1) {
- for (AnnotationMirror annotation : multibindingAnnotations) {
- report.addError(
- bindingElements("cannot have more than one multibinding annotation"),
- element,
- annotation);
- }
- }
- break;
- }
-
- // TODO(ronshapiro): move this into ProvidesMethodValidator
- if (bindingAnnotation.equals(Provides.class)) {
- AnnotationMirror bindingAnnotationMirror =
- getAnnotationMirror(element, bindingAnnotation).get();
- boolean usesProvidesType = false;
- for (ExecutableElement member : bindingAnnotationMirror.getElementValues().keySet()) {
- usesProvidesType |= member.getSimpleName().contentEquals("type");
- }
- if (usesProvidesType && !multibindingAnnotations.isEmpty()) {
- report.addError(
- "@Provides.type cannot be used with multibinding annotations", element);
- }
- }
- }
-
- /**
- * Adds an error if the element has a scope but doesn't allow scoping, or if it has more than
- * one {@linkplain Scope scope} annotation.
- */
- private void checkScopes() {
- ImmutableSet<Scope> scopes = scopesOf(element);
- String error = null;
- switch (allowsScoping) {
- case ALLOWS_SCOPING:
- if (scopes.size() <= 1) {
- return;
- }
- error = bindingElements("cannot use more than one @Scope");
- break;
- case NO_SCOPING:
- error = bindingElements("cannot be scoped");
- break;
- }
- verifyNotNull(error);
- for (Scope scope : scopes) {
- report.addError(error, element, scope.scopeAnnotation());
- }
- }
-
- /**
- * Adds an error if the {@link #bindingElementType() type} is a {@linkplain FrameworkTypes
- * framework type}.
- */
- private void checkFrameworkType() {
- if (bindingElementType().filter(FrameworkTypes::isFrameworkType).isPresent()) {
- report.addError(bindingElements("must not %s framework types", bindingElementTypeVerb()));
- }
- }
- }
-
- /** Whether to check multibinding annotations. */
- enum AllowsMultibindings {
- /**
- * This element disallows multibinding annotations, so don't bother checking for their validity.
- * {@link MultibindingAnnotationsProcessingStep} will add errors if the element has any
- * multibinding annotations.
- */
- NO_MULTIBINDINGS,
-
- /** This element allows multibinding annotations, so validate them. */
- ALLOWS_MULTIBINDINGS,
- ;
-
- private boolean allowsMultibindings() {
- return this == ALLOWS_MULTIBINDINGS;
- }
- }
-
- /** How to check scoping annotations. */
- enum AllowsScoping {
- /** This element disallows scoping, so check that no scope annotations are present. */
- NO_SCOPING,
-
- /** This element allows scoping, so validate that there's at most one scope annotation. */
- ALLOWS_SCOPING,
- ;
- }
-}
diff --git a/java/dagger/internal/codegen/BindingExpression.java b/java/dagger/internal/codegen/BindingExpression.java
deleted file mode 100644
index 65200f7a5..000000000
--- a/java/dagger/internal/codegen/BindingExpression.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
-
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
-import dagger.internal.codegen.javapoet.Expression;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import javax.lang.model.type.TypeMirror;
-
-/** A factory of code expressions used to access a single request for a binding in a component. */
-// TODO(user): Rename this to RequestExpression?
-abstract class BindingExpression {
-
- /**
- * Returns an expression that evaluates to the value of a request based on the given requesting
- * class.
- *
- * @param requestingClass the class that will contain the expression
- */
- abstract Expression getDependencyExpression(ClassName requestingClass);
-
- /**
- * Equivalent to {@link #getDependencyExpression} that is used only when the request is for an
- * implementation of a component method. By default, just delegates to {@link
- * #getDependencyExpression}.
- */
- Expression getDependencyExpressionForComponentMethod(
- ComponentMethodDescriptor componentMethod, ComponentImplementation component) {
- return getDependencyExpression(component.name());
- }
-
- /** Returns {@code true} if this binding expression should be encapsulated in a method. */
- boolean requiresMethodEncapsulation() {
- return false;
- }
-
- /**
- * Returns an expression for the implementation of a component method with the given request.
- *
- * @param component the component that will contain the implemented method
- */
- CodeBlock getComponentMethodImplementation(
- ComponentMethodDescriptor componentMethod, ComponentImplementation component) {
- // By default, just delegate to #getDependencyExpression().
- return CodeBlock.of(
- "return $L;",
- getDependencyExpressionForComponentMethod(componentMethod, component).codeBlock());
- }
-
- /**
- * Returns an expression for the implementation of a modifiable binding method for the given
- * component.
- */
- CodeBlock getModifiableBindingMethodImplementation(
- ModifiableBindingMethod modifiableBindingMethod,
- ComponentImplementation component,
- DaggerTypes types) {
- Expression dependencyExpression = getDependencyExpression(component.name());
-
- // It's possible to have a case where a modifiable component method delegates to another
- // binding method from an enclosing class that is not itself a component method. In that case,
- // the enclosing class's method may return a publicly accessible type, but the nested class will
- // have a return type that is defined by the component method. In that case, a downcast is
- // necessary so that the return statement is valid.
- //
- // E.g.:
- //
- // public class DaggerAncestor implements Ancestor {
- // protected Object packagePrivateModifiable() { ... }
- //
- // protected class LeafImpl extends DaggerLeaf {
- // @Override
- // public final PackagePrivateModifiable componentMethod() {
- // return (PackagePrivateModifiable) DaggerAncestor.this.packagePrivateModifiable();
- // }
- // }
- // }
- //
- // DaggerAncestor.packagePrivateModifiable returns Object even though the actual instance's type
- // is PackagePrivateModifiable. So a cast is necessary.
- //
- // This isn't necessary for getComponentMethodImplementation() because that's only used for
- // non-modifiable bindings
- TypeMirror returnType = modifiableBindingMethod.returnType();
- if (!types.isAssignable(dependencyExpression.type(), returnType)
- && isTypeAccessibleFrom(returnType, component.name().packageName())) {
- dependencyExpression = dependencyExpression.castTo(returnType);
- }
-
- return CodeBlock.of("return $L;", dependencyExpression.codeBlock());
- }
-}
diff --git a/java/dagger/internal/codegen/BindingFactory.java b/java/dagger/internal/codegen/BindingFactory.java
deleted file mode 100644
index 564b412ec..000000000
--- a/java/dagger/internal/codegen/BindingFactory.java
+++ /dev/null
@@ -1,508 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.auto.common.MoreTypes.asDeclared;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.Binding.hasNonDefaultTypeParameters;
-import static dagger.internal.codegen.ComponentDescriptor.isComponentProductionMethod;
-import static dagger.internal.codegen.ConfigurationAnnotations.getNullableType;
-import static dagger.internal.codegen.ContributionBinding.bindingKindForMultibindingKey;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.InjectionAnnotations.getQualifier;
-import static dagger.internal.codegen.MapKeys.getMapKey;
-import static dagger.internal.codegen.MoreAnnotationMirrors.wrapOptionalInEquivalence;
-import static dagger.internal.codegen.Scopes.uniqueScopeOf;
-import static dagger.model.BindingKind.BOUND_INSTANCE;
-import static dagger.model.BindingKind.COMPONENT;
-import static dagger.model.BindingKind.COMPONENT_DEPENDENCY;
-import static dagger.model.BindingKind.COMPONENT_PRODUCTION;
-import static dagger.model.BindingKind.COMPONENT_PROVISION;
-import static dagger.model.BindingKind.DELEGATE;
-import static dagger.model.BindingKind.INJECTION;
-import static dagger.model.BindingKind.MEMBERS_INJECTOR;
-import static dagger.model.BindingKind.OPTIONAL;
-import static dagger.model.BindingKind.PRODUCTION;
-import static dagger.model.BindingKind.PROVISION;
-import static dagger.model.BindingKind.SUBCOMPONENT_CREATOR;
-import static javax.lang.model.element.ElementKind.CONSTRUCTOR;
-import static javax.lang.model.element.ElementKind.METHOD;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSortedSet;
-import com.google.common.collect.Iterables;
-import dagger.Module;
-import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
-import dagger.internal.codegen.ProductionBinding.ProductionKind;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.DependencyRequest;
-import dagger.model.Key;
-import dagger.model.RequestKind;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
-import java.util.Optional;
-import java.util.function.BiFunction;
-import javax.inject.Inject;
-import javax.inject.Provider;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeMirror;
-
-/** A factory for {@link Binding} objects. */
-final class BindingFactory {
- private final DaggerTypes types;
- private final KeyFactory keyFactory;
- private final DependencyRequestFactory dependencyRequestFactory;
- private final InjectionSiteFactory injectionSiteFactory;
- private final DaggerElements elements;
-
- @Inject
- BindingFactory(
- DaggerTypes types,
- DaggerElements elements,
- KeyFactory keyFactory,
- DependencyRequestFactory dependencyRequestFactory,
- InjectionSiteFactory injectionSiteFactory) {
- this.types = types;
- this.elements = elements;
- this.keyFactory = keyFactory;
- this.dependencyRequestFactory = dependencyRequestFactory;
- this.injectionSiteFactory = injectionSiteFactory;
- }
-
- /**
- * Returns an {@link dagger.model.BindingKind#INJECTION} binding.
- *
- * @param constructorElement the {@code @Inject}-annotated constructor
- * @param resolvedType the parameterized type if the constructor is for a generic class and the
- * binding should be for the parameterized type
- */
- // TODO(dpb): See if we can just pass the parameterized type and not also the constructor.
- ProvisionBinding injectionBinding(
- ExecutableElement constructorElement, Optional<TypeMirror> resolvedType) {
- checkArgument(constructorElement.getKind().equals(CONSTRUCTOR));
- checkArgument(isAnnotationPresent(constructorElement, Inject.class));
- checkArgument(!getQualifier(constructorElement).isPresent());
-
- ExecutableType constructorType = MoreTypes.asExecutable(constructorElement.asType());
- DeclaredType constructedType =
- MoreTypes.asDeclared(constructorElement.getEnclosingElement().asType());
- // If the class this is constructing has some type arguments, resolve everything.
- if (!constructedType.getTypeArguments().isEmpty() && resolvedType.isPresent()) {
- DeclaredType resolved = MoreTypes.asDeclared(resolvedType.get());
- // Validate that we're resolving from the correct type.
- checkState(
- types.isSameType(types.erasure(resolved), types.erasure(constructedType)),
- "erased expected type: %s, erased actual type: %s",
- types.erasure(resolved),
- types.erasure(constructedType));
- constructorType = MoreTypes.asExecutable(types.asMemberOf(resolved, constructorElement));
- constructedType = resolved;
- }
-
- Key key = keyFactory.forInjectConstructorWithResolvedType(constructedType);
- ImmutableSet<DependencyRequest> provisionDependencies =
- dependencyRequestFactory.forRequiredResolvedVariables(
- constructorElement.getParameters(), constructorType.getParameterTypes());
-
- ProvisionBinding.Builder builder =
- ProvisionBinding.builder()
- .contributionType(ContributionType.UNIQUE)
- .bindingElement(constructorElement)
- .key(key)
- .provisionDependencies(provisionDependencies)
- .injectionSites(injectionSiteFactory.getInjectionSites(constructedType))
- .kind(INJECTION)
- .scope(uniqueScopeOf(constructorElement.getEnclosingElement()));
-
- TypeElement bindingTypeElement = MoreElements.asType(constructorElement.getEnclosingElement());
- if (hasNonDefaultTypeParameters(bindingTypeElement, key.type(), types)) {
- builder.unresolved(injectionBinding(constructorElement, Optional.empty()));
- }
- return builder.build();
- }
-
- /**
- * Returns a {@link dagger.model.BindingKind#PROVISION} binding for a {@code @Provides}-annotated
- * method.
- *
- * @param contributedBy the installed module that declares or inherits the method
- */
- ProvisionBinding providesMethodBinding(
- ExecutableElement providesMethod, TypeElement contributedBy) {
- return setMethodBindingProperties(
- ProvisionBinding.builder(),
- providesMethod,
- contributedBy,
- keyFactory.forProvidesMethod(providesMethod, contributedBy),
- this::providesMethodBinding)
- .kind(PROVISION)
- .scope(uniqueScopeOf(providesMethod))
- .nullableType(getNullableType(providesMethod))
- .build();
- }
-
- /**
- * Returns a {@link dagger.model.BindingKind#PRODUCTION} binding for a {@code @Produces}-annotated
- * method.
- *
- * @param contributedBy the installed module that declares or inherits the method
- */
- ProductionBinding producesMethodBinding(
- ExecutableElement producesMethod, TypeElement contributedBy) {
- // TODO(beder): Add nullability checking with Java 8.
- ProductionBinding.Builder builder =
- setMethodBindingProperties(
- ProductionBinding.builder(),
- producesMethod,
- contributedBy,
- keyFactory.forProducesMethod(producesMethod, contributedBy),
- this::producesMethodBinding)
- .kind(PRODUCTION)
- .productionKind(ProductionKind.fromProducesMethod(producesMethod))
- .thrownTypes(producesMethod.getThrownTypes())
- .executorRequest(dependencyRequestFactory.forProductionImplementationExecutor())
- .monitorRequest(dependencyRequestFactory.forProductionComponentMonitor());
- return builder.build();
- }
-
- private <C extends ContributionBinding, B extends ContributionBinding.Builder<C, B>>
- B setMethodBindingProperties(
- B builder,
- ExecutableElement method,
- TypeElement contributedBy,
- Key key,
- BiFunction<ExecutableElement, TypeElement, C> create) {
- checkArgument(method.getKind().equals(METHOD));
- ExecutableType methodType =
- MoreTypes.asExecutable(
- types.asMemberOf(MoreTypes.asDeclared(contributedBy.asType()), method));
- if (!types.isSameType(methodType, method.asType())) {
- builder.unresolved(create.apply(method, MoreElements.asType(method.getEnclosingElement())));
- }
- return builder
- .contributionType(ContributionType.fromBindingElement(method))
- .bindingElement(method)
- .contributingModule(contributedBy)
- .key(key)
- .dependencies(
- dependencyRequestFactory.forRequiredResolvedVariables(
- method.getParameters(), methodType.getParameterTypes()))
- .wrappedMapKeyAnnotation(wrapOptionalInEquivalence(getMapKey(method)));
- }
-
- /**
- * Returns a {@link dagger.model.BindingKind#MULTIBOUND_MAP} or {@link
- * dagger.model.BindingKind#MULTIBOUND_SET} binding given a set of multibinding contribution
- * bindings.
- *
- * @param key a key that may be satisfied by a multibinding
- */
- ContributionBinding syntheticMultibinding(
- Key key, Iterable<ContributionBinding> multibindingContributions) {
- ContributionBinding.Builder<?, ?> builder =
- multibindingRequiresProduction(key, multibindingContributions)
- ? ProductionBinding.builder()
- : ProvisionBinding.builder();
- return builder
- .contributionType(ContributionType.UNIQUE)
- .key(key)
- .dependencies(
- dependencyRequestFactory.forMultibindingContributions(key, multibindingContributions))
- .kind(bindingKindForMultibindingKey(key))
- .build();
- }
-
- private boolean multibindingRequiresProduction(
- Key key, Iterable<ContributionBinding> multibindingContributions) {
- if (MapType.isMap(key)) {
- MapType mapType = MapType.from(key);
- if (mapType.valuesAreTypeOf(Producer.class) || mapType.valuesAreTypeOf(Produced.class)) {
- return true;
- }
- } else if (SetType.isSet(key) && SetType.from(key).elementsAreTypeOf(Produced.class)) {
- return true;
- }
- return Iterables.any(
- multibindingContributions, binding -> binding.bindingType().equals(BindingType.PRODUCTION));
- }
-
- /** Returns a {@link dagger.model.BindingKind#COMPONENT} binding for the component. */
- ProvisionBinding componentBinding(TypeElement componentDefinitionType) {
- checkNotNull(componentDefinitionType);
- return ProvisionBinding.builder()
- .contributionType(ContributionType.UNIQUE)
- .bindingElement(componentDefinitionType)
- .key(keyFactory.forType(componentDefinitionType.asType()))
- .kind(COMPONENT)
- .build();
- }
-
- /**
- * Returns a {@link dagger.model.BindingKind#COMPONENT_DEPENDENCY} binding for a component's
- * dependency.
- */
- ProvisionBinding componentDependencyBinding(ComponentRequirement dependency) {
- checkNotNull(dependency);
- return ProvisionBinding.builder()
- .contributionType(ContributionType.UNIQUE)
- .bindingElement(dependency.typeElement())
- .key(keyFactory.forType(dependency.type()))
- .kind(COMPONENT_DEPENDENCY)
- .build();
- }
-
- /**
- * Returns a {@link dagger.model.BindingKind#COMPONENT_PROVISION} or {@link
- * dagger.model.BindingKind#COMPONENT_PRODUCTION} binding for a method on a component's
- * dependency.
- *
- * @param componentDescriptor the component with the dependency, not the dependency that has the
- * method
- */
- ContributionBinding componentDependencyMethodBinding(
- ComponentDescriptor componentDescriptor, ExecutableElement dependencyMethod) {
- checkArgument(dependencyMethod.getKind().equals(METHOD));
- checkArgument(dependencyMethod.getParameters().isEmpty());
- ContributionBinding.Builder<?, ?> builder;
- if (componentDescriptor.isProduction()
- && isComponentProductionMethod(elements, dependencyMethod)) {
- builder =
- ProductionBinding.builder()
- .key(keyFactory.forProductionComponentMethod(dependencyMethod))
- .kind(COMPONENT_PRODUCTION)
- .thrownTypes(dependencyMethod.getThrownTypes());
- } else {
- builder =
- ProvisionBinding.builder()
- .key(keyFactory.forComponentMethod(dependencyMethod))
- .nullableType(getNullableType(dependencyMethod))
- .kind(COMPONENT_PROVISION)
- .scope(uniqueScopeOf(dependencyMethod));
- }
- return builder
- .contributionType(ContributionType.UNIQUE)
- .bindingElement(dependencyMethod)
- .build();
- }
-
- /**
- * Returns a {@link dagger.model.BindingKind#BOUND_INSTANCE} binding for a
- * {@code @BindsInstance}-annotated builder setter method or factory method parameter.
- */
- ProvisionBinding boundInstanceBinding(ComponentRequirement requirement, Element element) {
- checkArgument(element instanceof VariableElement || element instanceof ExecutableElement);
- VariableElement parameterElement =
- element instanceof VariableElement
- ? MoreElements.asVariable(element)
- : getOnlyElement(MoreElements.asExecutable(element).getParameters());
- return ProvisionBinding.builder()
- .contributionType(ContributionType.UNIQUE)
- .bindingElement(element)
- .key(requirement.key().get())
- .nullableType(getNullableType(parameterElement))
- .kind(BOUND_INSTANCE)
- .build();
- }
-
- /**
- * Returns a {@link dagger.model.BindingKind#SUBCOMPONENT_CREATOR} binding declared by a component
- * method that returns a subcomponent builder. Use {{@link
- * #subcomponentCreatorBinding(ImmutableSet)}} for bindings declared using {@link
- * Module#subcomponents()}.
- *
- * @param component the component that declares or inherits the method
- */
- ProvisionBinding subcomponentCreatorBinding(
- ExecutableElement subcomponentCreatorMethod, TypeElement component) {
- checkArgument(subcomponentCreatorMethod.getKind().equals(METHOD));
- checkArgument(subcomponentCreatorMethod.getParameters().isEmpty());
- Key key =
- keyFactory.forSubcomponentCreatorMethod(
- subcomponentCreatorMethod, asDeclared(component.asType()));
- return ProvisionBinding.builder()
- .contributionType(ContributionType.UNIQUE)
- .bindingElement(subcomponentCreatorMethod)
- .key(key)
- .kind(SUBCOMPONENT_CREATOR)
- .build();
- }
-
- /**
- * Returns a {@link dagger.model.BindingKind#SUBCOMPONENT_CREATOR} binding declared using {@link
- * Module#subcomponents()}.
- */
- ProvisionBinding subcomponentCreatorBinding(
- ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations) {
- SubcomponentDeclaration subcomponentDeclaration = subcomponentDeclarations.iterator().next();
- return ProvisionBinding.builder()
- .contributionType(ContributionType.UNIQUE)
- .key(subcomponentDeclaration.key())
- .kind(SUBCOMPONENT_CREATOR)
- .build();
- }
-
- /**
- * Returns a {@link dagger.model.BindingKind#DELEGATE} binding.
- *
- * @param delegateDeclaration the {@code @Binds}-annotated declaration
- * @param actualBinding the binding that satisfies the {@code @Binds} declaration
- */
- ContributionBinding delegateBinding(
- DelegateDeclaration delegateDeclaration, ContributionBinding actualBinding) {
- switch (actualBinding.bindingType()) {
- case PRODUCTION:
- return buildDelegateBinding(
- ProductionBinding.builder().nullableType(actualBinding.nullableType()),
- delegateDeclaration,
- Producer.class);
-
- case PROVISION:
- return buildDelegateBinding(
- ProvisionBinding.builder()
- .scope(uniqueScopeOf(delegateDeclaration.bindingElement().get()))
- .nullableType(actualBinding.nullableType()),
- delegateDeclaration,
- Provider.class);
-
- case MEMBERS_INJECTION: // fall-through to throw
- }
- throw new AssertionError("bindingType: " + actualBinding);
- }
-
- /**
- * Returns a {@link dagger.model.BindingKind#DELEGATE} binding used when there is no binding that
- * satisfies the {@code @Binds} declaration.
- */
- ContributionBinding unresolvedDelegateBinding(DelegateDeclaration delegateDeclaration) {
- return buildDelegateBinding(
- ProvisionBinding.builder().scope(uniqueScopeOf(delegateDeclaration.bindingElement().get())),
- delegateDeclaration,
- Provider.class);
- }
-
- private ContributionBinding buildDelegateBinding(
- ContributionBinding.Builder<?, ?> builder,
- DelegateDeclaration delegateDeclaration,
- Class<?> frameworkType) {
- return builder
- .contributionType(delegateDeclaration.contributionType())
- .bindingElement(delegateDeclaration.bindingElement().get())
- .contributingModule(delegateDeclaration.contributingModule().get())
- .key(keyFactory.forDelegateBinding(delegateDeclaration, frameworkType))
- .dependencies(delegateDeclaration.delegateRequest())
- .wrappedMapKeyAnnotation(delegateDeclaration.wrappedMapKey())
- .kind(DELEGATE)
- .build();
- }
-
- /**
- * Returns an {@link dagger.model.BindingKind#OPTIONAL} binding for {@code key}.
- *
- * @param requestKind the kind of request for the optional binding
- * @param underlyingKeyBindings the possibly empty set of bindings that exist in the component for
- * the underlying (non-optional) key
- */
- ContributionBinding syntheticOptionalBinding(
- Key key, RequestKind requestKind, ResolvedBindings underlyingKeyBindings) {
- ContributionBinding.Builder<?, ?> builder =
- syntheticOptionalBindingBuilder(requestKind, underlyingKeyBindings)
- .contributionType(ContributionType.UNIQUE)
- .key(key)
- .kind(OPTIONAL);
- if (!underlyingKeyBindings.isEmpty()) {
- builder.dependencies(
- dependencyRequestFactory.forSyntheticPresentOptionalBinding(key, requestKind));
- }
- return builder.build();
- }
-
- private ContributionBinding.Builder<?, ?> syntheticOptionalBindingBuilder(
- RequestKind requestKind, ResolvedBindings underlyingKeyBindings) {
- return !underlyingKeyBindings.isEmpty()
- && (underlyingKeyBindings.bindingTypes().contains(BindingType.PRODUCTION)
- || requestKind.equals(RequestKind.PRODUCER) // handles producerFromProvider cases
- || requestKind.equals(RequestKind.PRODUCED)) // handles producerFromProvider cases
- ? ProductionBinding.builder()
- : ProvisionBinding.builder();
- }
-
- /** Returns a {@link dagger.model.BindingKind#MEMBERS_INJECTOR} binding. */
- ProvisionBinding membersInjectorBinding(
- Key key, MembersInjectionBinding membersInjectionBinding) {
- return ProvisionBinding.builder()
- .key(key)
- .contributionType(ContributionType.UNIQUE)
- .kind(MEMBERS_INJECTOR)
- .bindingElement(MoreTypes.asTypeElement(membersInjectionBinding.key().type()))
- .provisionDependencies(membersInjectionBinding.dependencies())
- .injectionSites(membersInjectionBinding.injectionSites())
- .build();
- }
-
- /**
- * Returns a {@link dagger.model.BindingKind#MEMBERS_INJECTION} binding.
- *
- * @param resolvedType if {@code declaredType} is a generic class and {@code resolvedType} is a
- * parameterization of that type, the returned binding will be for the resolved type
- */
- // TODO(dpb): See if we can just pass one nongeneric/parameterized type.
- MembersInjectionBinding membersInjectionBinding(
- DeclaredType declaredType, Optional<TypeMirror> resolvedType) {
- // If the class this is injecting has some type arguments, resolve everything.
- if (!declaredType.getTypeArguments().isEmpty() && resolvedType.isPresent()) {
- DeclaredType resolved = asDeclared(resolvedType.get());
- // Validate that we're resolving from the correct type.
- checkState(
- types.isSameType(types.erasure(resolved), types.erasure(declaredType)),
- "erased expected type: %s, erased actual type: %s",
- types.erasure(resolved),
- types.erasure(declaredType));
- declaredType = resolved;
- }
- ImmutableSortedSet<InjectionSite> injectionSites =
- injectionSiteFactory.getInjectionSites(declaredType);
- ImmutableSet<DependencyRequest> dependencies =
- injectionSites
- .stream()
- .flatMap(injectionSite -> injectionSite.dependencies().stream())
- .collect(toImmutableSet());
-
- Key key = keyFactory.forMembersInjectedType(declaredType);
- TypeElement typeElement = MoreElements.asType(declaredType.asElement());
- return new AutoValue_MembersInjectionBinding(
- key,
- dependencies,
- typeElement,
- hasNonDefaultTypeParameters(typeElement, key.type(), types)
- ? Optional.of(
- membersInjectionBinding(asDeclared(typeElement.asType()), Optional.empty()))
- : Optional.empty(),
- injectionSites);
- }
-}
diff --git a/java/dagger/internal/codegen/BindingGraph.java b/java/dagger/internal/codegen/BindingGraph.java
deleted file mode 100644
index 2f548b2b7..000000000
--- a/java/dagger/internal/codegen/BindingGraph.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkState;
-import static dagger.internal.codegen.DaggerStreams.presentValues;
-import static dagger.internal.codegen.DaggerStreams.stream;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-
-import com.google.auto.value.AutoValue;
-import com.google.auto.value.extension.memoized.Memoized;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multimaps;
-import com.google.common.graph.Traverser;
-import dagger.Subcomponent;
-import dagger.model.Key;
-import dagger.model.RequestKind;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-
-/** The canonical representation of a full-resolved graph. */
-@AutoValue
-abstract class BindingGraph {
- abstract ComponentDescriptor componentDescriptor();
-
- /**
- * The resolved bindings for all {@link ContributionBinding}s in this graph, keyed by {@link Key}.
- */
- // TODO(ronshapiro): when MembersInjectionBinding no longer extends Binding, rename this to
- // bindings()
- abstract ImmutableMap<Key, ResolvedBindings> contributionBindings();
-
- /**
- * The resolved bindings for all {@link MembersInjectionBinding}s in this graph, keyed by {@link
- * Key}.
- */
- abstract ImmutableMap<Key, ResolvedBindings> membersInjectionBindings();
-
- /**
- * Returns the {@link ResolvedBindings resolved bindings} instance for {@code
- * bindingExpressionKey}. If the bindings will be used for members injection, a {@link
- * ResolvedBindings} with {@linkplain #membersInjectionBindings() members injection bindings} will
- * be returned, otherwise a {@link ResolvedBindings} with {@link #contributionBindings()} will be
- * returned.
- */
- final ResolvedBindings resolvedBindings(BindingRequest request) {
- return request.isRequestKind(RequestKind.MEMBERS_INJECTION)
- ? membersInjectionBindings().get(request.key())
- : contributionBindings().get(request.key());
- }
-
- final Iterable<ResolvedBindings> resolvedBindings() {
- // Don't return an immutable collection - this is only ever used for looping over all bindings
- // in the graph. Copying is wasteful, especially if is a hashing collection, since the values
- // should all, by definition, be distinct.
- // TODO(dpb): consider inlining this to callers and removing this.
- return Iterables.concat(membersInjectionBindings().values(), contributionBindings().values());
- }
-
- abstract ImmutableList<BindingGraph> subgraphs();
-
- /**
- * The type that defines the component for this graph.
- *
- * @see ComponentDescriptor#typeElement()
- */
- TypeElement componentTypeElement() {
- return componentDescriptor().typeElement();
- }
-
- /**
- * Returns the set of modules that are owned by this graph regardless of whether or not any of
- * their bindings are used in this graph. For graphs representing top-level {@link
- * dagger.Component components}, this set will be the same as {@linkplain
- * ComponentDescriptor#modules() the component's transitive modules}. For {@linkplain Subcomponent
- * subcomponents}, this set will be the transitive modules that are not owned by any of their
- * ancestors.
- */
- abstract ImmutableSet<ModuleDescriptor> ownedModules();
-
- @Memoized
- ImmutableSet<TypeElement> ownedModuleTypes() {
- return FluentIterable.from(ownedModules()).transform(ModuleDescriptor::moduleElement).toSet();
- }
-
- /**
- * Returns the factory method for this subcomponent, if it exists.
- *
- * <p>This factory method is the one defined in the parent component's interface.
- *
- * <p>In the example below, the {@link BindingGraph#factoryMethod} for {@code ChildComponent}
- * would return the {@link ExecutableElement}: {@code childComponent(ChildModule1)} .
- *
- * <pre><code>
- * {@literal @Component}
- * interface ParentComponent {
- * ChildComponent childComponent(ChildModule1 childModule);
- * }
- * </code></pre>
- */
- // TODO(b/73294201): Consider returning the resolved ExecutableType for the factory method.
- abstract Optional<ExecutableElement> factoryMethod();
-
- /**
- * Returns a map between the {@linkplain ComponentRequirement component requirement} and the
- * corresponding {@link VariableElement} for each module parameter in the {@linkplain
- * BindingGraph#factoryMethod factory method}.
- */
- // TODO(dpb): Consider disallowing modules if none of their bindings are used.
- ImmutableMap<ComponentRequirement, VariableElement> factoryMethodParameters() {
- checkState(factoryMethod().isPresent());
- ImmutableMap.Builder<ComponentRequirement, VariableElement> builder = ImmutableMap.builder();
- for (VariableElement parameter : factoryMethod().get().getParameters()) {
- builder.put(ComponentRequirement.forModule(parameter.asType()), parameter);
- }
- return builder.build();
- }
-
- private static final Traverser<BindingGraph> SUBGRAPH_TRAVERSER =
- Traverser.forTree(BindingGraph::subgraphs);
-
- /**
- * The types for which the component needs instances.
- *
- * <ul>
- * <li>component dependencies
- * <li>{@linkplain #ownedModules() owned modules} with concrete instance bindings that are used
- * in the graph
- * <li>bound instances
- * </ul>
- */
- @Memoized
- ImmutableSet<ComponentRequirement> componentRequirements() {
- ImmutableSet<TypeElement> requiredModules = requiredModuleElements();
- ImmutableSet.Builder<ComponentRequirement> requirements = ImmutableSet.builder();
- componentDescriptor().requirements().stream()
- .filter(
- requirement ->
- !requirement.kind().isModule()
- || requiredModules.contains(requirement.typeElement()))
- .forEach(requirements::add);
- if (factoryMethod().isPresent()) {
- requirements.addAll(factoryMethodParameters().keySet());
- }
- return requirements.build();
- }
-
- private ImmutableSet<TypeElement> requiredModuleElements() {
- return stream(SUBGRAPH_TRAVERSER.depthFirstPostOrder(this))
- .flatMap(graph -> graph.contributionBindings().values().stream())
- .flatMap(bindings -> bindings.contributionBindings().stream())
- .map(ContributionBinding::contributingModule)
- .distinct()
- .flatMap(presentValues())
- .filter(ownedModuleTypes()::contains)
- .collect(toImmutableSet());
- }
-
- /** Returns the {@link ComponentDescriptor}s for this component and its subcomponents. */
- ImmutableSet<ComponentDescriptor> componentDescriptors() {
- return FluentIterable.from(SUBGRAPH_TRAVERSER.depthFirstPreOrder(this))
- .transform(BindingGraph::componentDescriptor)
- .toSet();
- }
-
- /**
- * {@code true} if this graph contains all bindings installed in the component; {@code false} if
- * it contains only those bindings that are reachable from at least one entry point.
- */
- abstract boolean isFullBindingGraph();
-
- @Memoized
- @Override
- public abstract int hashCode();
-
- @Override // Suppresses ErrorProne warning that hashCode was overridden w/o equals
- public abstract boolean equals(Object other);
-
- static BindingGraph create(
- ComponentDescriptor componentDescriptor,
- Map<Key, ResolvedBindings> resolvedContributionBindingsMap,
- Map<Key, ResolvedBindings> resolvedMembersInjectionBindings,
- List<BindingGraph> subgraphs,
- Set<ModuleDescriptor> ownedModules,
- Optional<ExecutableElement> factoryMethod,
- boolean isFullBindingGraph) {
- checkForDuplicates(subgraphs);
- return new AutoValue_BindingGraph(
- componentDescriptor,
- ImmutableMap.copyOf(resolvedContributionBindingsMap),
- ImmutableMap.copyOf(resolvedMembersInjectionBindings),
- ImmutableList.copyOf(subgraphs),
- ImmutableSet.copyOf(ownedModules),
- factoryMethod,
- isFullBindingGraph);
- }
-
- private static final void checkForDuplicates(Iterable<BindingGraph> graphs) {
- Map<TypeElement, Collection<BindingGraph>> duplicateGraphs =
- Maps.filterValues(
- Multimaps.index(graphs, graph -> graph.componentDescriptor().typeElement()).asMap(),
- overlapping -> overlapping.size() > 1);
- if (!duplicateGraphs.isEmpty()) {
- throw new IllegalArgumentException("Expected no duplicates: " + duplicateGraphs);
- }
- }
-}
diff --git a/java/dagger/internal/codegen/BindingGraphConverter.java b/java/dagger/internal/codegen/BindingGraphConverter.java
deleted file mode 100644
index a2cc7990f..000000000
--- a/java/dagger/internal/codegen/BindingGraphConverter.java
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreTypes.asTypeElement;
-import static dagger.internal.codegen.BindingRequest.bindingRequest;
-import static dagger.internal.codegen.DaggerGraphs.unreachableNodes;
-import static dagger.model.BindingKind.SUBCOMPONENT_CREATOR;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.graph.MutableNetwork;
-import com.google.common.graph.Network;
-import com.google.common.graph.NetworkBuilder;
-import dagger.model.BindingGraph.ComponentNode;
-import dagger.model.BindingGraph.DependencyEdge;
-import dagger.model.BindingGraph.Edge;
-import dagger.model.BindingGraph.MissingBinding;
-import dagger.model.BindingGraph.Node;
-import dagger.model.BindingGraphProxies;
-import dagger.model.ComponentPath;
-import dagger.model.DependencyRequest;
-import javax.inject.Inject;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.TypeMirror;
-
-/** Converts {@link dagger.internal.codegen.BindingGraph}s to {@link dagger.model.BindingGraph}s. */
-final class BindingGraphConverter {
- private final BindingDeclarationFormatter bindingDeclarationFormatter;
-
- @Inject
- BindingGraphConverter(BindingDeclarationFormatter bindingDeclarationFormatter) {
- this.bindingDeclarationFormatter = bindingDeclarationFormatter;
- }
-
- /**
- * Creates the external {@link dagger.model.BindingGraph} representing the given internal {@link
- * dagger.internal.codegen.BindingGraph}.
- */
- dagger.model.BindingGraph convert(BindingGraph bindingGraph) {
- Traverser traverser = new Traverser(bindingGraph);
- traverser.traverseComponents();
-
- // When bindings are copied down into child graphs because they transitively depend on local
- // multibindings or optional bindings, the parent-owned binding is still there. If that
- // parent-owned binding is not reachable from its component, it doesn't need to be in the graph
- // because it will never be used. So remove all nodes that are not reachable from the root
- // component—unless we're converting a full binding graph.
- if (!bindingGraph.isFullBindingGraph()) {
- unreachableNodes(traverser.network.asGraph(), rootComponentNode(traverser.network))
- .forEach(traverser.network::removeNode);
- }
-
- return BindingGraphProxies.bindingGraph(traverser.network, bindingGraph.isFullBindingGraph());
- }
-
- // TODO(dpb): Example of BindingGraph logic applied to derived networks.
- private ComponentNode rootComponentNode(Network<Node, Edge> network) {
- return (ComponentNode)
- Iterables.find(
- network.nodes(),
- node -> node instanceof ComponentNode && node.componentPath().atRoot());
- }
-
- private final class Traverser extends ComponentTreeTraverser {
- private final MutableNetwork<Node, Edge> network =
- NetworkBuilder.directed().allowsParallelEdges(true).allowsSelfLoops(true).build();
- private final boolean isRootSubcomponent;
- private final boolean isFullBindingGraph;
-
- private final ComponentPath rootComponentPath;
- private ComponentNode parentComponent;
- private ComponentNode currentComponent;
-
- Traverser(BindingGraph graph) {
- super(graph);
- rootComponentPath = ComponentPath.create(ImmutableList.of(graph.componentTypeElement()));
- isRootSubcomponent = graph.componentDescriptor().isSubcomponent();
- isFullBindingGraph = graph.isFullBindingGraph();
- }
-
- @Override
- protected void visitComponent(BindingGraph graph) {
- ComponentNode grandparentComponent = parentComponent;
- parentComponent = currentComponent;
- currentComponent = ComponentNodeImpl.create(componentPath(), graph.componentDescriptor());
-
- network.addNode(currentComponent);
-
- for (ResolvedBindings resolvedBindings : graph.resolvedBindings()) {
- for (BindingNode binding : bindingNodes(resolvedBindings)) {
- addBinding(binding);
- if (binding.kind().equals(SUBCOMPONENT_CREATOR)
- && binding.componentPath().equals(currentComponent.componentPath())) {
- network.addEdge(
- binding,
- subcomponentNode(binding.key().type(), graph),
- new SubcomponentCreatorBindingEdgeImpl(
- resolvedBindings.subcomponentDeclarations()));
- }
- }
- }
-
- super.visitComponent(graph);
-
- currentComponent = parentComponent;
- parentComponent = grandparentComponent;
- }
-
- @Override
- protected void visitEntryPoint(DependencyRequest entryPoint, BindingGraph graph) {
- addDependencyEdges(currentComponent, entryPoint);
- super.visitEntryPoint(entryPoint, graph);
- }
-
- @Override
- protected void visitSubcomponentFactoryMethod(
- BindingGraph graph, BindingGraph parent, ExecutableElement factoryMethod) {
- network.addEdge(
- parentComponent, currentComponent, new ChildFactoryMethodEdgeImpl(factoryMethod));
- super.visitSubcomponentFactoryMethod(graph, parent, factoryMethod);
- }
-
- /**
- * Adds a {@link dagger.model.BindingGraph.DependencyEdge} from a node to the binding(s) that
- * satisfy a dependency request.
- */
- private void addDependencyEdges(Node source, DependencyRequest dependencyRequest) {
- ResolvedBindings dependencies = resolvedDependencies(source, dependencyRequest);
- if (dependencies.isEmpty()) {
- addDependencyEdge(source, dependencyRequest, missingBindingNode(dependencies));
- } else {
- for (BindingNode dependency : bindingNodes(dependencies)) {
- addDependencyEdge(source, dependencyRequest, dependency);
- }
- }
- }
-
- private void addDependencyEdge(
- Node source, DependencyRequest dependencyRequest, Node dependency) {
- network.addNode(dependency);
- if (!hasDependencyEdge(source, dependency, dependencyRequest)) {
- network.addEdge(
- source,
- dependency,
- new DependencyEdgeImpl(dependencyRequest, source instanceof ComponentNode));
- }
- }
-
- private boolean hasDependencyEdge(
- Node source, Node dependency, DependencyRequest dependencyRequest) {
- // An iterative approach is used instead of a Stream because this method is called in a hot
- // loop, and the Stream calculates the size of network.edgesConnecting(), which is slow. This
- // seems to be because caculating the edges connecting two nodes in a Network that supports
- // parallel edges is must check the equality of many nodes, and BindingNode's equality
- // semantics drag in the equality of many other expensive objects
- for (Edge edge : network.edgesConnecting(source, dependency)) {
- if (edge instanceof DependencyEdge) {
- if (((DependencyEdge) edge).dependencyRequest().equals(dependencyRequest)) {
- return true;
- }
- }
- }
- return false;
- }
-
- private ResolvedBindings resolvedDependencies(
- Node source, DependencyRequest dependencyRequest) {
- return graphForAncestor(source.componentPath().currentComponent())
- .resolvedBindings(bindingRequest(dependencyRequest));
- }
-
- /** Adds a binding and all its dependencies. */
- private void addBinding(BindingNode binding) {
- network.addNode(binding);
- for (DependencyRequest dependencyRequest : binding.dependencies()) {
- addDependencyEdges(binding, dependencyRequest);
- }
- }
-
- private ImmutableSet<BindingNode> bindingNodes(ResolvedBindings resolvedBindings) {
- ImmutableSet.Builder<BindingNode> bindingNodes = ImmutableSet.builder();
- resolvedBindings
- .allBindings()
- .asMap()
- .forEach(
- (component, bindings) -> {
- for (Binding binding : bindings) {
- bindingNodes.add(bindingNode(resolvedBindings, binding, component));
- }
- });
- return bindingNodes.build();
- }
-
- private BindingNode bindingNode(
- ResolvedBindings resolvedBindings, Binding binding, TypeElement owningComponent) {
- return BindingNode.create(
- pathFromRootToAncestor(owningComponent),
- binding,
- resolvedBindings.multibindingDeclarations(),
- resolvedBindings.optionalBindingDeclarations(),
- resolvedBindings.subcomponentDeclarations(),
- bindingDeclarationFormatter);
- }
-
- private MissingBinding missingBindingNode(ResolvedBindings dependencies) {
- // TODO(b/117833324): Revisit whether limiting missing binding nodes to the root component is
- // necessary to limit the amount of missing binding nodes in the network, or if perhaps *all*
- // missing binding nodes should be structured this way.
- return BindingGraphProxies.missingBindingNode(
- isRootSubcomponent && !isFullBindingGraph ? rootComponentPath : componentPath(),
- dependencies.key());
- }
-
- private ComponentNode subcomponentNode(TypeMirror subcomponentBuilderType, BindingGraph graph) {
- TypeElement subcomponentBuilderElement = asTypeElement(subcomponentBuilderType);
- ComponentDescriptor subcomponent =
- graph.componentDescriptor().getChildComponentWithBuilderType(subcomponentBuilderElement);
- return ComponentNodeImpl.create(
- componentPath().childPath(subcomponent.typeElement()), subcomponent);
- }
- }
-}
diff --git a/java/dagger/internal/codegen/BindingGraphFactory.java b/java/dagger/internal/codegen/BindingGraphFactory.java
deleted file mode 100644
index d96da8a74..000000000
--- a/java/dagger/internal/codegen/BindingGraphFactory.java
+++ /dev/null
@@ -1,1061 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreTypes.isType;
-import static com.google.auto.common.MoreTypes.isTypeOf;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.collect.Iterables.isEmpty;
-import static dagger.internal.codegen.ComponentDescriptor.isComponentContributionMethod;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.RequestKinds.getRequestKind;
-import static dagger.internal.codegen.SourceFiles.generatedMonitoringModuleName;
-import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
-import static dagger.model.BindingKind.DELEGATE;
-import static dagger.model.BindingKind.INJECTION;
-import static dagger.model.BindingKind.OPTIONAL;
-import static dagger.model.BindingKind.SUBCOMPONENT_CREATOR;
-import static dagger.model.RequestKind.MEMBERS_INJECTION;
-import static java.util.function.Predicate.isEqual;
-import static javax.lang.model.util.ElementFilter.methodsIn;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Multimaps;
-import com.google.common.collect.Sets;
-import dagger.MembersInjector;
-import dagger.Reusable;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.model.DependencyRequest;
-import dagger.model.Key;
-import dagger.model.Scope;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
-import dagger.producers.internal.ProductionExecutorModule;
-import java.util.ArrayDeque;
-import java.util.Collection;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Queue;
-import java.util.Set;
-import java.util.function.Function;
-import javax.inject.Inject;
-import javax.inject.Provider;
-import javax.inject.Singleton;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-
-/** A factory for {@link BindingGraph} objects. */
-@Singleton
-final class BindingGraphFactory implements ClearableCache {
- private final DaggerElements elements;
- private final InjectBindingRegistry injectBindingRegistry;
- private final KeyFactory keyFactory;
- private final BindingFactory bindingFactory;
- private final ModuleDescriptor.Factory moduleDescriptorFactory;
- private final Map<Key, ImmutableSet<Key>> keysMatchingRequestCache = new HashMap<>();
-
- @Inject
- BindingGraphFactory(
- DaggerElements elements,
- InjectBindingRegistry injectBindingRegistry,
- KeyFactory keyFactory,
- BindingFactory bindingFactory,
- ModuleDescriptor.Factory moduleDescriptorFactory) {
- this.elements = elements;
- this.injectBindingRegistry = injectBindingRegistry;
- this.keyFactory = keyFactory;
- this.bindingFactory = bindingFactory;
- this.moduleDescriptorFactory = moduleDescriptorFactory;
- }
-
- /**
- * Creates a binding graph for a component.
- *
- * @param createFullBindingGraph if {@code true}, the binding graph will include all bindings;
- * otherwise it will include only bindings reachable from at least one entry point
- */
- BindingGraph create(ComponentDescriptor componentDescriptor, boolean createFullBindingGraph) {
- return create(Optional.empty(), componentDescriptor, createFullBindingGraph);
- }
-
- private BindingGraph create(
- Optional<Resolver> parentResolver,
- ComponentDescriptor componentDescriptor,
- boolean createFullBindingGraph) {
- ImmutableSet.Builder<ContributionBinding> explicitBindingsBuilder = ImmutableSet.builder();
- ImmutableSet.Builder<DelegateDeclaration> delegatesBuilder = ImmutableSet.builder();
- ImmutableSet.Builder<OptionalBindingDeclaration> optionalsBuilder = ImmutableSet.builder();
-
- if (componentDescriptor.isRealComponent()) {
- // binding for the component itself
- explicitBindingsBuilder.add(
- bindingFactory.componentBinding(componentDescriptor.typeElement()));
- }
-
- // Collect Component dependencies.
- for (ComponentRequirement dependency : componentDescriptor.dependencies()) {
- explicitBindingsBuilder.add(bindingFactory.componentDependencyBinding(dependency));
- List<ExecutableElement> dependencyMethods =
- methodsIn(elements.getAllMembers(dependency.typeElement()));
- for (ExecutableElement method : dependencyMethods) {
- // MembersInjection methods aren't "provided" explicitly, so ignore them.
- if (isComponentContributionMethod(elements, method)) {
- explicitBindingsBuilder.add(
- bindingFactory.componentDependencyMethodBinding(componentDescriptor, method));
- }
- }
- }
-
- // Collect bindings on the creator.
- componentDescriptor
- .creatorDescriptor()
- .ifPresent(
- creatorDescriptor ->
- creatorDescriptor.boundInstanceRequirements().stream()
- .map(
- requirement ->
- bindingFactory.boundInstanceBinding(
- requirement, creatorDescriptor.elementForRequirement(requirement)))
- .forEach(explicitBindingsBuilder::add));
-
- componentDescriptor
- .childComponentsDeclaredByBuilderEntryPoints()
- .forEach(
- (builderEntryPoint, childComponent) -> {
- if (!componentDescriptor
- .childComponentsDeclaredByModules()
- .contains(childComponent)) {
- explicitBindingsBuilder.add(
- bindingFactory.subcomponentCreatorBinding(
- builderEntryPoint.methodElement(), componentDescriptor.typeElement()));
- }
- });
-
- ImmutableSet.Builder<MultibindingDeclaration> multibindingDeclarations = ImmutableSet.builder();
- ImmutableSet.Builder<SubcomponentDeclaration> subcomponentDeclarations = ImmutableSet.builder();
-
- // Collect transitive module bindings and multibinding declarations.
- for (ModuleDescriptor moduleDescriptor : modules(componentDescriptor, parentResolver)) {
- explicitBindingsBuilder.addAll(moduleDescriptor.bindings());
- multibindingDeclarations.addAll(moduleDescriptor.multibindingDeclarations());
- subcomponentDeclarations.addAll(moduleDescriptor.subcomponentDeclarations());
- delegatesBuilder.addAll(moduleDescriptor.delegateDeclarations());
- optionalsBuilder.addAll(moduleDescriptor.optionalDeclarations());
- }
-
- final Resolver requestResolver =
- new Resolver(
- parentResolver,
- componentDescriptor,
- indexBindingDeclarationsByKey(explicitBindingsBuilder.build()),
- indexBindingDeclarationsByKey(multibindingDeclarations.build()),
- indexBindingDeclarationsByKey(subcomponentDeclarations.build()),
- indexBindingDeclarationsByKey(delegatesBuilder.build()),
- indexBindingDeclarationsByKey(optionalsBuilder.build()));
-
- componentDescriptor.entryPointMethods().stream()
- .map(method -> method.dependencyRequest().get())
- .forEach(
- entryPoint -> {
- if (entryPoint.kind().equals(MEMBERS_INJECTION)) {
- requestResolver.resolveMembersInjection(entryPoint.key());
- } else {
- requestResolver.resolve(entryPoint.key());
- }
- });
-
- if (createFullBindingGraph) {
- // Resolve the keys for all bindings in all modules, stripping any multibinding contribution
- // identifier so that the multibinding itself is resolved.
- modules(componentDescriptor, parentResolver).stream()
- .flatMap(module -> module.allBindingKeys().stream())
- .map(key -> key.toBuilder().multibindingContributionIdentifier(Optional.empty()).build())
- .forEach(requestResolver::resolve);
- }
-
- // Resolve all bindings for subcomponents, creating subgraphs for all subcomponents that have
- // been detected during binding resolution. If a binding for a subcomponent is never resolved,
- // no BindingGraph will be created for it and no implementation will be generated. This is
- // done in a queue since resolving one subcomponent might resolve a key for a subcomponent
- // from a parent graph. This is done until no more new subcomponents are resolved.
- Set<ComponentDescriptor> resolvedSubcomponents = new HashSet<>();
- ImmutableList.Builder<BindingGraph> subgraphs = ImmutableList.builder();
- for (ComponentDescriptor subcomponent :
- Iterables.consumingIterable(requestResolver.subcomponentsToResolve)) {
- if (resolvedSubcomponents.add(subcomponent)) {
- subgraphs.add(create(Optional.of(requestResolver), subcomponent, createFullBindingGraph));
- }
- }
-
- return BindingGraph.create(
- componentDescriptor,
- requestResolver.getResolvedContributionBindings(),
- requestResolver.getResolvedMembersInjectionBindings(),
- subgraphs.build(),
- requestResolver.getOwnedModules(),
- requestResolver.getFactoryMethod(),
- createFullBindingGraph);
- }
-
- /**
- * Returns all the modules that should be installed in the component. For production components
- * and production subcomponents that have a parent that is not a production component or
- * subcomponent, also includes the production monitoring module for the component and the
- * production executor module.
- */
- private ImmutableSet<ModuleDescriptor> modules(
- ComponentDescriptor componentDescriptor, Optional<Resolver> parentResolver) {
- return shouldIncludeImplicitProductionModules(componentDescriptor, parentResolver)
- ? new ImmutableSet.Builder<ModuleDescriptor>()
- .addAll(componentDescriptor.modules())
- .add(descriptorForMonitoringModule(componentDescriptor.typeElement()))
- .add(descriptorForProductionExecutorModule())
- .build()
- : componentDescriptor.modules();
- }
-
- private boolean shouldIncludeImplicitProductionModules(
- ComponentDescriptor component, Optional<Resolver> parentResolver) {
- return component.isProduction()
- && ((!component.isSubcomponent() && component.isRealComponent())
- || (parentResolver.isPresent()
- && !parentResolver.get().componentDescriptor.isProduction()));
- }
-
- /**
- * Returns a descriptor for a generated module that handles monitoring for production components.
- * This module is generated in the {@link MonitoringModuleProcessingStep}.
- *
- * @throws TypeNotPresentException if the module has not been generated yet. This will cause the
- * processor to retry in a later processing round.
- */
- private ModuleDescriptor descriptorForMonitoringModule(TypeElement componentDefinitionType) {
- return moduleDescriptorFactory.create(
- elements.checkTypePresent(
- generatedMonitoringModuleName(componentDefinitionType).toString()));
- }
-
- /** Returns a descriptor {@link ProductionExecutorModule}. */
- private ModuleDescriptor descriptorForProductionExecutorModule() {
- return moduleDescriptorFactory.create(elements.getTypeElement(ProductionExecutorModule.class));
- }
-
- /** Indexes {@code bindingDeclarations} by {@link BindingDeclaration#key()}. */
- private static <T extends BindingDeclaration>
- ImmutableSetMultimap<Key, T> indexBindingDeclarationsByKey(Iterable<T> declarations) {
- return ImmutableSetMultimap.copyOf(Multimaps.index(declarations, BindingDeclaration::key));
- }
-
- @Override
- public void clearCache() {
- keysMatchingRequestCache.clear();
- }
-
- private final class Resolver {
- final Optional<Resolver> parentResolver;
- final ComponentDescriptor componentDescriptor;
- final ImmutableSetMultimap<Key, ContributionBinding> explicitBindings;
- final ImmutableSet<ContributionBinding> explicitBindingsSet;
- final ImmutableSetMultimap<Key, ContributionBinding> explicitMultibindings;
- final ImmutableSetMultimap<Key, MultibindingDeclaration> multibindingDeclarations;
- final ImmutableSetMultimap<Key, SubcomponentDeclaration> subcomponentDeclarations;
- final ImmutableSetMultimap<Key, DelegateDeclaration> delegateDeclarations;
- final ImmutableSetMultimap<Key, OptionalBindingDeclaration> optionalBindingDeclarations;
- final ImmutableSetMultimap<Key, DelegateDeclaration> delegateMultibindingDeclarations;
- final Map<Key, ResolvedBindings> resolvedContributionBindings = new LinkedHashMap<>();
- final Map<Key, ResolvedBindings> resolvedMembersInjectionBindings = new LinkedHashMap<>();
- final Deque<Key> cycleStack = new ArrayDeque<>();
- final Map<Key, Boolean> keyDependsOnLocalBindingsCache = new HashMap<>();
- final Map<Binding, Boolean> bindingDependsOnLocalBindingsCache = new HashMap<>();
- final Queue<ComponentDescriptor> subcomponentsToResolve = new ArrayDeque<>();
-
- Resolver(
- Optional<Resolver> parentResolver,
- ComponentDescriptor componentDescriptor,
- ImmutableSetMultimap<Key, ContributionBinding> explicitBindings,
- ImmutableSetMultimap<Key, MultibindingDeclaration> multibindingDeclarations,
- ImmutableSetMultimap<Key, SubcomponentDeclaration> subcomponentDeclarations,
- ImmutableSetMultimap<Key, DelegateDeclaration> delegateDeclarations,
- ImmutableSetMultimap<Key, OptionalBindingDeclaration> optionalBindingDeclarations) {
- this.parentResolver = parentResolver;
- this.componentDescriptor = checkNotNull(componentDescriptor);
- this.explicitBindings = checkNotNull(explicitBindings);
- this.explicitBindingsSet = ImmutableSet.copyOf(explicitBindings.values());
- this.multibindingDeclarations = checkNotNull(multibindingDeclarations);
- this.subcomponentDeclarations = checkNotNull(subcomponentDeclarations);
- this.delegateDeclarations = checkNotNull(delegateDeclarations);
- this.optionalBindingDeclarations = checkNotNull(optionalBindingDeclarations);
- this.explicitMultibindings = multibindingContributionsByMultibindingKey(explicitBindingsSet);
- this.delegateMultibindingDeclarations =
- multibindingContributionsByMultibindingKey(delegateDeclarations.values());
- subcomponentsToResolve.addAll(
- componentDescriptor.childComponentsDeclaredByFactoryMethods().values());
- subcomponentsToResolve.addAll(
- componentDescriptor.childComponentsDeclaredByBuilderEntryPoints().values());
- }
-
- /** Returns the optional factory method for this component. */
- Optional<ExecutableElement> getFactoryMethod() {
- return parentResolver
- .flatMap(
- parent ->
- parent.componentDescriptor.getFactoryMethodForChildComponent(componentDescriptor))
- .map(method -> method.methodElement());
- }
-
- /**
- * Returns the resolved contribution bindings for the given {@link Key}:
- *
- * <ul>
- * <li>All explicit bindings for:
- * <ul>
- * <li>the requested key
- * <li>{@code Set<T>} if the requested key's type is {@code Set<Produced<T>>}
- * <li>{@code Map<K, Provider<V>>} if the requested key's type is {@code Map<K,
- * Producer<V>>}.
- * </ul>
- * <li>A synthetic binding that depends on {@code Map<K, Producer<V>>} if the requested key's
- * type is {@code Map<K, V>} and there are some explicit bindings for {@code Map<K,
- * Producer<V>>}.
- * <li>A synthetic binding that depends on {@code Map<K, Provider<V>>} if the requested key's
- * type is {@code Map<K, V>} and there are some explicit bindings for {@code Map<K,
- * Provider<V>>} but no explicit bindings for {@code Map<K, Producer<V>>}.
- * <li>An implicit {@link Inject @Inject}-annotated constructor binding if there is one and
- * there are no explicit bindings or synthetic bindings.
- * </ul>
- */
- ResolvedBindings lookUpBindings(Key requestKey) {
- Set<ContributionBinding> bindings = new LinkedHashSet<>();
- bindings.addAll(getExplicitBindings(requestKey));
-
- ImmutableSet<ContributionBinding> multibindingContributions =
- getAllMatchingBindingDeclarations(requestKey, this::getExplicitMultibindings);
- ImmutableSet<MultibindingDeclaration> multibindingDeclarations =
- getAllMatchingBindingDeclarations(requestKey, this::getMultibindingDeclarations);
-
- syntheticMultibinding(requestKey, multibindingContributions, multibindingDeclarations)
- .ifPresent(bindings::add);
-
- ImmutableSet<OptionalBindingDeclaration> optionalBindingDeclarations =
- getAllMatchingBindingDeclarations(requestKey, this::getOptionalBindingDeclarations);
- syntheticOptionalBinding(requestKey, optionalBindingDeclarations).ifPresent(bindings::add);
-
- ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations =
- getSubcomponentDeclarations(requestKey);
- syntheticSubcomponentBuilderBinding(subcomponentDeclarations)
- .ifPresent(
- binding -> {
- bindings.add(binding);
- addSubcomponentToOwningResolver(binding);
- });
-
- if (isType(requestKey.type()) && isTypeOf(MembersInjector.class, requestKey.type())) {
- injectBindingRegistry
- .getOrFindMembersInjectorProvisionBinding(requestKey)
- .ifPresent(bindings::add);
- }
-
- // If there are no bindings, add the implicit @Inject-constructed binding if there is one.
- if (bindings.isEmpty()) {
- injectBindingRegistry.getOrFindProvisionBinding(requestKey)
- .filter(binding -> !isIncorrectlyScopedInPartialGraph(binding))
- .ifPresent(bindings::add);
- }
-
- return ResolvedBindings.forContributionBindings(
- requestKey,
- indexBindingsByOwningComponent(requestKey, ImmutableSet.copyOf(bindings)),
- multibindingDeclarations,
- subcomponentDeclarations,
- optionalBindingDeclarations);
- }
-
- /**
- * Returns true if this binding graph resolution is for a partial graph and the {@code @Inject}
- * binding's scope doesn't match any of the components in the current component ancestry. If so,
- * the binding is not owned by any of the currently known components, and will be owned by a
- * future ancestor (or, if never owned, will result in an incompatibly scoped binding error at
- * the root component).
- */
- private boolean isIncorrectlyScopedInPartialGraph(ProvisionBinding binding) {
- checkArgument(binding.kind().equals(INJECTION));
- Resolver owningResolver = getOwningResolver(binding).orElse(this);
- ComponentDescriptor owningComponent = owningResolver.componentDescriptor;
- return rootComponent().isSubcomponent()
- && binding.scope().isPresent()
- && !binding.scope().get().isReusable()
- && !owningComponent.scopes().contains(binding.scope().get());
- }
-
- private ComponentDescriptor rootComponent() {
- return parentResolver.map(Resolver::rootComponent).orElse(componentDescriptor);
- }
-
- /** Returns the resolved members injection bindings for the given {@link Key}. */
- ResolvedBindings lookUpMembersInjectionBinding(Key requestKey) {
- // no explicit deps for members injection, so just look it up
- Optional<MembersInjectionBinding> binding =
- injectBindingRegistry.getOrFindMembersInjectionBinding(requestKey);
- return binding.isPresent()
- ? ResolvedBindings.forMembersInjectionBinding(
- requestKey, componentDescriptor, binding.get())
- : ResolvedBindings.noBindings(requestKey);
- }
-
- /**
- * When a binding is resolved for a {@link SubcomponentDeclaration}, adds corresponding {@link
- * ComponentDescriptor subcomponent} to a queue in the owning component's resolver. The queue
- * will be used to detect which subcomponents need to be resolved.
- */
- private void addSubcomponentToOwningResolver(ProvisionBinding subcomponentCreatorBinding) {
- checkArgument(subcomponentCreatorBinding.kind().equals(SUBCOMPONENT_CREATOR));
- Resolver owningResolver = getOwningResolver(subcomponentCreatorBinding).get();
-
- TypeElement builderType = MoreTypes.asTypeElement(subcomponentCreatorBinding.key().type());
- owningResolver.subcomponentsToResolve.add(
- owningResolver.componentDescriptor.getChildComponentWithBuilderType(builderType));
- }
-
- /**
- * Profiling has determined that computing the keys matching {@code requestKey} has measurable
- * performance impact. It is called repeatedly (at least 3 times per key resolved per {@link
- * BindingGraph}. {@code javac}'s name-checking performance seems suboptimal (converting byte
- * strings to Strings repeatedly), and the matching keys creations relies on that. This also
- * ensures that the resulting keys have their hash codes cached on successive calls to this
- * method.
- *
- * <p>This caching may become obsolete if:
- *
- * <ul>
- * <li>We decide to intern all {@link Key} instances
- * <li>We fix javac's name-checking peformance (though we may want to keep this for older
- * javac users)
- * </ul>
- */
- private ImmutableSet<Key> keysMatchingRequest(Key requestKey) {
- return keysMatchingRequestCache.computeIfAbsent(
- requestKey, this::keysMatchingRequestUncached);
- }
-
- private ImmutableSet<Key> keysMatchingRequestUncached(Key requestKey) {
- ImmutableSet.Builder<Key> keys = ImmutableSet.builder();
- keys.add(requestKey);
- keyFactory.unwrapSetKey(requestKey, Produced.class).ifPresent(keys::add);
- keyFactory.rewrapMapKey(requestKey, Producer.class, Provider.class).ifPresent(keys::add);
- keyFactory.rewrapMapKey(requestKey, Provider.class, Producer.class).ifPresent(keys::add);
- keys.addAll(keyFactory.implicitFrameworkMapKeys(requestKey));
- return keys.build();
- }
-
- /**
- * Returns a synthetic binding that depends on individual multibinding contributions.
- *
- * <p>If there are no {@code multibindingContributions} or {@code multibindingDeclarations},
- * returns {@link Optional#empty()}.
- *
- * <p>If there are production {@code multibindingContributions} or the request is for any of the
- * following types, returns a {@link ProductionBinding}.
- *
- * <ul>
- * <li>{@code Set<Produced<T>>}
- * <li>{@code Map<K, Producer<V>>}
- * <li>{@code Map<K, Produced<V>>}
- * </ul>
- *
- * Otherwise, returns a {@link ProvisionBinding}.
- */
- private Optional<ContributionBinding> syntheticMultibinding(
- Key key,
- Iterable<ContributionBinding> multibindingContributions,
- Iterable<MultibindingDeclaration> multibindingDeclarations) {
- return isEmpty(multibindingContributions) && isEmpty(multibindingDeclarations)
- ? Optional.empty()
- : Optional.of(bindingFactory.syntheticMultibinding(key, multibindingContributions));
- }
-
- private Optional<ProvisionBinding> syntheticSubcomponentBuilderBinding(
- ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations) {
- return subcomponentDeclarations.isEmpty()
- ? Optional.empty()
- : Optional.of(bindingFactory.subcomponentCreatorBinding(subcomponentDeclarations));
- }
-
- /**
- * Returns a synthetic binding for {@code @Qualifier Optional<Type>} if there are any {@code
- * optionalBindingDeclarations}.
- *
- * <p>If there are no bindings for the underlying key (the key for dependency requests for
- * {@code Type}), returns a provision binding that always returns {@link Optional#empty()}.
- *
- * <p>If there are any production bindings for the underlying key, returns a production binding.
- * Otherwise returns a provision binding.
- */
- private Optional<ContributionBinding> syntheticOptionalBinding(
- Key key, ImmutableSet<OptionalBindingDeclaration> optionalBindingDeclarations) {
- return optionalBindingDeclarations.isEmpty()
- ? Optional.empty()
- : Optional.of(
- bindingFactory.syntheticOptionalBinding(
- key,
- getRequestKind(OptionalType.from(key).valueType()),
- lookUpBindings(keyFactory.unwrapOptional(key).get())));
- }
-
- private ImmutableSet<ContributionBinding> createDelegateBindings(
- ImmutableSet<DelegateDeclaration> delegateDeclarations) {
- ImmutableSet.Builder<ContributionBinding> builder = ImmutableSet.builder();
- for (DelegateDeclaration delegateDeclaration : delegateDeclarations) {
- builder.add(createDelegateBinding(delegateDeclaration));
- }
- return builder.build();
- }
-
- /**
- * Creates one (and only one) delegate binding for a delegate declaration, based on the resolved
- * bindings of the right-hand-side of a {@link dagger.Binds} method. If there are duplicate
- * bindings for the dependency key, there should still be only one binding for the delegate key.
- */
- private ContributionBinding createDelegateBinding(DelegateDeclaration delegateDeclaration) {
- Key delegateKey = delegateDeclaration.delegateRequest().key();
- if (cycleStack.contains(delegateKey)) {
- return bindingFactory.unresolvedDelegateBinding(delegateDeclaration);
- }
-
- ResolvedBindings resolvedDelegate;
- try {
- cycleStack.push(delegateKey);
- resolvedDelegate = lookUpBindings(delegateKey);
- } finally {
- cycleStack.pop();
- }
- if (resolvedDelegate.contributionBindings().isEmpty()) {
- // This is guaranteed to result in a missing binding error, so it doesn't matter if the
- // binding is a Provision or Production, except if it is a @IntoMap method, in which
- // case the key will be of type Map<K, Provider<V>>, which will be "upgraded" into a
- // Map<K, Producer<V>> if it's requested in a ProductionComponent. This may result in a
- // strange error, that the RHS needs to be provided with an @Inject or @Provides
- // annotated method, but a user should be able to figure out if a @Produces annotation
- // is needed.
- // TODO(gak): revisit how we model missing delegates if/when we clean up how we model
- // binding declarations
- return bindingFactory.unresolvedDelegateBinding(delegateDeclaration);
- }
- // It doesn't matter which of these is selected, since they will later on produce a
- // duplicate binding error.
- ContributionBinding explicitDelegate =
- resolvedDelegate.contributionBindings().iterator().next();
- return bindingFactory.delegateBinding(delegateDeclaration, explicitDelegate);
- }
-
- // TODO(dpb,ronshapiro): requestKey appears to be interchangeable with each binding's .key(),
- // but should it? We're currently conflating the two all over the place and it would be good
- // to unify, or if it's necessary, clarify why with docs+tests. Specifically, should we also
- // be checking these for keysMatchingRequest?
- private ImmutableSetMultimap<TypeElement, ContributionBinding> indexBindingsByOwningComponent(
- Key requestKey, Iterable<? extends ContributionBinding> bindings) {
- ImmutableSetMultimap.Builder<TypeElement, ContributionBinding> index =
- ImmutableSetMultimap.builder();
- for (ContributionBinding binding : bindings) {
- index.put(getOwningComponent(requestKey, binding), binding);
- }
- return index.build();
- }
-
- /**
- * Returns the component that should contain the framework field for {@code binding}.
- *
- * <p>If {@code binding} is either not bound in an ancestor component or depends transitively on
- * bindings in this component, returns this component.
- *
- * <p>Otherwise, resolves {@code request} in this component's parent in order to resolve any
- * multibinding contributions in the parent, and returns the parent-resolved {@link
- * ResolvedBindings#owningComponent(ContributionBinding)}.
- */
- private TypeElement getOwningComponent(Key requestKey, ContributionBinding binding) {
- if (isResolvedInParent(requestKey, binding)
- && !new LocalDependencyChecker().dependsOnLocalBindings(binding)) {
- ResolvedBindings parentResolvedBindings =
- parentResolver.get().resolvedContributionBindings.get(requestKey);
- return parentResolvedBindings.owningComponent(binding);
- } else {
- return componentDescriptor.typeElement();
- }
- }
-
- /**
- * Returns {@code true} if {@code binding} is owned by an ancestor. If so, {@linkplain #resolve
- * resolves} the {@link Key} in this component's parent. Don't resolve directly in the owning
- * component in case it depends on multibindings in any of its descendants.
- */
- private boolean isResolvedInParent(Key requestKey, ContributionBinding binding) {
- Optional<Resolver> owningResolver = getOwningResolver(binding);
- if (owningResolver.isPresent() && !owningResolver.get().equals(this)) {
- parentResolver.get().resolve(requestKey);
- return true;
- } else {
- return false;
- }
- }
-
- private Optional<Resolver> getOwningResolver(ContributionBinding binding) {
- // TODO(ronshapiro): extract the different pieces of this method into their own methods
- if ((binding.scope().isPresent() && binding.scope().get().isProductionScope())
- || binding.bindingType().equals(BindingType.PRODUCTION)) {
- for (Resolver requestResolver : getResolverLineage()) {
- // Resolve @Inject @ProductionScope bindings at the highest production component.
- if (binding.kind().equals(INJECTION)
- && requestResolver.componentDescriptor.isProduction()) {
- return Optional.of(requestResolver);
- }
-
- // Resolve explicit @Produces and @ProductionScope bindings at the highest component that
- // installs the binding.
- if (requestResolver.containsExplicitBinding(binding)) {
- return Optional.of(requestResolver);
- }
- }
- }
-
- if (binding.scope().isPresent() && binding.scope().get().isReusable()) {
- for (Resolver requestResolver : getResolverLineage().reverse()) {
- // If a @Reusable binding was resolved in an ancestor, use that component.
- ResolvedBindings resolvedBindings =
- requestResolver.resolvedContributionBindings.get(binding.key());
- if (resolvedBindings != null
- && resolvedBindings.contributionBindings().contains(binding)) {
- return Optional.of(requestResolver);
- }
- }
- // If a @Reusable binding was not resolved in any ancestor, resolve it here.
- return Optional.empty();
- }
-
- for (Resolver requestResolver : getResolverLineage().reverse()) {
- if (requestResolver.containsExplicitBinding(binding)) {
- return Optional.of(requestResolver);
- }
- }
-
- // look for scope separately. we do this for the case where @Singleton can appear twice
- // in the † compatibility mode
- Optional<Scope> bindingScope = binding.scope();
- if (bindingScope.isPresent()) {
- for (Resolver requestResolver : getResolverLineage().reverse()) {
- if (requestResolver.componentDescriptor.scopes().contains(bindingScope.get())) {
- return Optional.of(requestResolver);
- }
- }
- }
- return Optional.empty();
- }
-
- private boolean containsExplicitBinding(ContributionBinding binding) {
- return explicitBindingsSet.contains(binding)
- || resolverContainsDelegateDeclarationForBinding(binding)
- || subcomponentDeclarations.containsKey(binding.key());
- }
-
- /** Returns true if {@code binding} was installed in a module in this resolver's component. */
- private boolean resolverContainsDelegateDeclarationForBinding(ContributionBinding binding) {
- return binding.kind().equals(DELEGATE)
- && delegateDeclarations.get(binding.key()).stream()
- .anyMatch(
- declaration ->
- declaration.contributingModule().equals(binding.contributingModule())
- && declaration.bindingElement().equals(binding.bindingElement()));
- }
-
- /** Returns the resolver lineage from parent to child. */
- private ImmutableList<Resolver> getResolverLineage() {
- ImmutableList.Builder<Resolver> resolverList = ImmutableList.builder();
- for (Optional<Resolver> currentResolver = Optional.of(this);
- currentResolver.isPresent();
- currentResolver = currentResolver.get().parentResolver) {
- resolverList.add(currentResolver.get());
- }
- return resolverList.build().reverse();
- }
-
- /**
- * For all {@linkplain #keysMatchingRequest(Key) keys matching {@code requestKey}}, applies
- * {@code getDeclarationsPerKey} and collects the values into an {@link ImmutableSet}.
- */
- private <T extends BindingDeclaration> ImmutableSet<T> getAllMatchingBindingDeclarations(
- Key requestKey, Function<Key, Collection<T>> getDeclarationsPerKey) {
- return keysMatchingRequest(requestKey)
- .stream()
- .flatMap(key -> getDeclarationsPerKey.apply(key).stream())
- .collect(toImmutableSet());
- }
-
- /**
- * Returns the explicit {@link ContributionBinding}s that match the {@code key} from this and
- * all ancestor resolvers.
- */
- private ImmutableSet<ContributionBinding> getExplicitBindings(Key key) {
- ImmutableSet.Builder<ContributionBinding> bindings = ImmutableSet.builder();
- for (Resolver resolver : getResolverLineage()) {
- bindings.addAll(resolver.getLocalExplicitBindings(key));
- }
- return bindings.build();
- }
-
- /**
- * Returns the explicit {@link ContributionBinding}s that match the {@code key} from this
- * resolver.
- */
- private ImmutableSet<ContributionBinding> getLocalExplicitBindings(Key key) {
- return new ImmutableSet.Builder<ContributionBinding>()
- .addAll(explicitBindings.get(key))
- // @Binds @IntoMap declarations have key Map<K, V>, unlike @Provides @IntoMap or @Produces
- // @IntoMap, which have Map<K, Provider/Producer<V>> keys. So unwrap the key's type's
- // value type if it's a Map<K, Provider/Producer<V>> before looking in
- // delegateDeclarations. createDelegateBindings() will create bindings with the properly
- // wrapped key type.
- .addAll(
- createDelegateBindings(delegateDeclarations.get(keyFactory.unwrapMapValueType(key))))
- .build();
- }
-
- /**
- * Returns the explicit multibinding contributions that contribute to the map or set requested
- * by {@code key} from this and all ancestor resolvers.
- */
- private ImmutableSet<ContributionBinding> getExplicitMultibindings(Key key) {
- ImmutableSet.Builder<ContributionBinding> multibindings = ImmutableSet.builder();
- for (Resolver resolver : getResolverLineage()) {
- multibindings.addAll(resolver.getLocalExplicitMultibindings(key));
- }
- return multibindings.build();
- }
-
- /**
- * Returns the explicit multibinding contributions that contribute to the map or set requested
- * by {@code key} from this resolver.
- */
- private ImmutableSet<ContributionBinding> getLocalExplicitMultibindings(Key key) {
- ImmutableSet.Builder<ContributionBinding> multibindings = ImmutableSet.builder();
- multibindings.addAll(explicitMultibindings.get(key));
- if (!MapType.isMap(key)
- || MapType.from(key).isRawType()
- || MapType.from(key).valuesAreFrameworkType()) {
- // @Binds @IntoMap declarations have key Map<K, V>, unlike @Provides @IntoMap or @Produces
- // @IntoMap, which have Map<K, Provider/Producer<V>> keys. So unwrap the key's type's
- // value type if it's a Map<K, Provider/Producer<V>> before looking in
- // delegateMultibindingDeclarations. createDelegateBindings() will create bindings with the
- // properly wrapped key type.
- multibindings.addAll(
- createDelegateBindings(
- delegateMultibindingDeclarations.get(keyFactory.unwrapMapValueType(key))));
- }
- return multibindings.build();
- }
-
- /**
- * Returns the {@link MultibindingDeclaration}s that match the {@code key} from this and all
- * ancestor resolvers.
- */
- private ImmutableSet<MultibindingDeclaration> getMultibindingDeclarations(Key key) {
- ImmutableSet.Builder<MultibindingDeclaration> multibindingDeclarations =
- ImmutableSet.builder();
- for (Resolver resolver : getResolverLineage()) {
- multibindingDeclarations.addAll(resolver.multibindingDeclarations.get(key));
- }
- return multibindingDeclarations.build();
- }
-
- /**
- * Returns the {@link SubcomponentDeclaration}s that match the {@code key} from this and all
- * ancestor resolvers.
- */
- private ImmutableSet<SubcomponentDeclaration> getSubcomponentDeclarations(Key key) {
- ImmutableSet.Builder<SubcomponentDeclaration> subcomponentDeclarations =
- ImmutableSet.builder();
- for (Resolver resolver : getResolverLineage()) {
- subcomponentDeclarations.addAll(resolver.subcomponentDeclarations.get(key));
- }
- return subcomponentDeclarations.build();
- }
- /**
- * Returns the {@link OptionalBindingDeclaration}s that match the {@code key} from this and all
- * ancestor resolvers.
- */
- private ImmutableSet<OptionalBindingDeclaration> getOptionalBindingDeclarations(Key key) {
- Optional<Key> unwrapped = keyFactory.unwrapOptional(key);
- if (!unwrapped.isPresent()) {
- return ImmutableSet.of();
- }
- ImmutableSet.Builder<OptionalBindingDeclaration> declarations = ImmutableSet.builder();
- for (Resolver resolver : getResolverLineage()) {
- declarations.addAll(resolver.optionalBindingDeclarations.get(unwrapped.get()));
- }
- return declarations.build();
- }
-
- /**
- * Returns the {@link ResolvedBindings} for {@code key} that was resolved in this resolver or an
- * ancestor resolver. Only checks for {@link ContributionBinding}s as {@link
- * MembersInjectionBinding}s are not inherited.
- */
- private Optional<ResolvedBindings> getPreviouslyResolvedBindings(Key key) {
- Optional<ResolvedBindings> result =
- Optional.ofNullable(resolvedContributionBindings.get(key));
- if (result.isPresent()) {
- return result;
- } else if (parentResolver.isPresent()) {
- return parentResolver.get().getPreviouslyResolvedBindings(key);
- } else {
- return Optional.empty();
- }
- }
-
- private void resolveMembersInjection(Key key) {
- ResolvedBindings bindings = lookUpMembersInjectionBinding(key);
- resolveDependencies(bindings);
- resolvedMembersInjectionBindings.put(key, bindings);
- }
-
- void resolve(Key key) {
- // If we find a cycle, stop resolving. The original request will add it with all of the
- // other resolved deps.
- if (cycleStack.contains(key)) {
- return;
- }
-
- // If the binding was previously resolved in this (sub)component, don't resolve it again.
- if (resolvedContributionBindings.containsKey(key)) {
- return;
- }
-
- /*
- * If the binding was previously resolved in an ancestor component, then we may be able to
- * avoid resolving it here and just depend on the ancestor component resolution.
- *
- * 1. If it depends transitively on multibinding contributions or optional bindings with
- * bindings from this subcomponent, then we have to resolve it in this subcomponent so
- * that it sees the local bindings.
- *
- * 2. If there are any explicit bindings in this component, they may conflict with those in
- * the ancestor component, so resolve them here so that conflicts can be caught.
- */
- if (getPreviouslyResolvedBindings(key).isPresent()) {
- /* Resolve in the parent in case there are multibinding contributions or conflicts in some
- * component between this one and the previously-resolved one. */
- parentResolver.get().resolve(key);
- if (!new LocalDependencyChecker().dependsOnLocalBindings(key)
- && getLocalExplicitBindings(key).isEmpty()) {
- /* Cache the inherited parent component's bindings in case resolving at the parent found
- * bindings in some component between this one and the previously-resolved one. */
- resolvedContributionBindings.put(key, getPreviouslyResolvedBindings(key).get());
- return;
- }
- }
-
- cycleStack.push(key);
- try {
- ResolvedBindings bindings = lookUpBindings(key);
- resolvedContributionBindings.put(key, bindings);
- resolveDependencies(bindings);
- } finally {
- cycleStack.pop();
- }
- }
-
- /**
- * {@link #resolve(Key) Resolves} each of the dependencies of the bindings owned by this
- * component.
- */
- private void resolveDependencies(ResolvedBindings resolvedBindings) {
- for (Binding binding : resolvedBindings.bindingsOwnedBy(componentDescriptor)) {
- for (DependencyRequest dependency : binding.dependencies()) {
- resolve(dependency.key());
- }
- }
- }
-
- /**
- * Returns all of the {@link ResolvedBindings} for {@link ContributionBinding}s from this and
- * all ancestor resolvers, indexed by {@link ResolvedBindings#key()}.
- */
- Map<Key, ResolvedBindings> getResolvedContributionBindings() {
- Map<Key, ResolvedBindings> bindings = new LinkedHashMap<>();
- parentResolver.ifPresent(parent -> bindings.putAll(parent.getResolvedContributionBindings()));
- bindings.putAll(resolvedContributionBindings);
- return bindings;
- }
-
- /**
- * Returns all of the {@link ResolvedBindings} for {@link MembersInjectionBinding} from this
- * resolvers, indexed by {@link ResolvedBindings#key()}.
- */
- ImmutableMap<Key, ResolvedBindings> getResolvedMembersInjectionBindings() {
- return ImmutableMap.copyOf(resolvedMembersInjectionBindings);
- }
-
- ImmutableSet<ModuleDescriptor> getInheritedModules() {
- return parentResolver.isPresent()
- ? Sets.union(
- parentResolver.get().getInheritedModules(),
- parentResolver.get().componentDescriptor.modules())
- .immutableCopy()
- : ImmutableSet.<ModuleDescriptor>of();
- }
-
- ImmutableSet<ModuleDescriptor> getOwnedModules() {
- return Sets.difference(componentDescriptor.modules(), getInheritedModules()).immutableCopy();
- }
-
- private final class LocalDependencyChecker {
- private final Set<Object> cycleChecker = new HashSet<>();
-
- /**
- * Returns {@code true} if any of the bindings resolved for {@code key} are multibindings with
- * contributions declared within this component's modules or optional bindings with present
- * values declared within this component's modules, or if any of its unscoped dependencies
- * depend on such bindings.
- *
- * <p>We don't care about scoped dependencies because they will never depend on bindings from
- * subcomponents.
- *
- * @throws IllegalArgumentException if {@link #getPreviouslyResolvedBindings(Key)} is empty
- */
- boolean dependsOnLocalBindings(Key key) {
- // Don't recur infinitely if there are valid cycles in the dependency graph.
- // http://b/23032377
- if (!cycleChecker.add(key)) {
- return false;
- }
- return reentrantComputeIfAbsent(
- keyDependsOnLocalBindingsCache, key, this::dependsOnLocalBindingsUncached);
- }
-
- private boolean dependsOnLocalBindingsUncached(Key key) {
- checkArgument(
- getPreviouslyResolvedBindings(key).isPresent(),
- "no previously resolved bindings in %s for %s",
- Resolver.this,
- key);
- ResolvedBindings previouslyResolvedBindings = getPreviouslyResolvedBindings(key).get();
- if (hasLocalMultibindingContributions(key)
- || hasLocalOptionalBindingContribution(previouslyResolvedBindings)) {
- return true;
- }
-
- for (Binding binding : previouslyResolvedBindings.bindings()) {
- if (dependsOnLocalBindings(binding)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Returns {@code true} if {@code binding} is unscoped (or has {@link Reusable @Reusable}
- * scope) and depends on multibindings with contributions declared within this component's
- * modules, or if any of its unscoped or {@link Reusable @Reusable} scoped dependencies depend
- * on such local multibindings.
- *
- * <p>We don't care about non-reusable scoped dependencies because they will never depend on
- * multibindings with contributions from subcomponents.
- */
- boolean dependsOnLocalBindings(Binding binding) {
- if (!cycleChecker.add(binding)) {
- return false;
- }
- return reentrantComputeIfAbsent(
- bindingDependsOnLocalBindingsCache, binding, this::dependsOnLocalBindingsUncached);
- }
-
- private boolean dependsOnLocalBindingsUncached(Binding binding) {
- if ((!binding.scope().isPresent() || binding.scope().get().isReusable())
- // TODO(beder): Figure out what happens with production subcomponents.
- && !binding.bindingType().equals(BindingType.PRODUCTION)) {
- for (DependencyRequest dependency : binding.dependencies()) {
- if (dependsOnLocalBindings(dependency.key())) {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
- * Returns {@code true} if there is at least one multibinding contribution declared within
- * this component's modules that matches the key.
- */
- private boolean hasLocalMultibindingContributions(Key requestKey) {
- return keysMatchingRequest(requestKey)
- .stream()
- .anyMatch(key -> !getLocalExplicitMultibindings(key).isEmpty());
- }
-
- /**
- * Returns {@code true} if there is a contribution in this component for an {@code
- * Optional<Foo>} key that has not been contributed in a parent.
- */
- private boolean hasLocalOptionalBindingContribution(ResolvedBindings resolvedBindings) {
- if (resolvedBindings
- .contributionBindings()
- .stream()
- .map(ContributionBinding::kind)
- .anyMatch(isEqual(OPTIONAL))) {
- return !getLocalExplicitBindings(keyFactory.unwrapOptional(resolvedBindings.key()).get())
- .isEmpty();
- } else {
- // If a parent contributes a @Provides Optional<Foo> binding and a child has a
- // @BindsOptionalOf Foo method, the two should conflict, even if there is no binding for
- // Foo on its own
- return !getOptionalBindingDeclarations(resolvedBindings.key()).isEmpty();
- }
- }
- }
- }
-
- /**
- * A multimap of those {@code declarations} that are multibinding contribution declarations,
- * indexed by the key of the set or map to which they contribute.
- */
- static <T extends BindingDeclaration>
- ImmutableSetMultimap<Key, T> multibindingContributionsByMultibindingKey(
- Iterable<T> declarations) {
- ImmutableSetMultimap.Builder<Key, T> builder = ImmutableSetMultimap.builder();
- for (T declaration : declarations) {
- if (declaration.key().multibindingContributionIdentifier().isPresent()) {
- builder.put(
- declaration
- .key()
- .toBuilder()
- .multibindingContributionIdentifier(Optional.empty())
- .build(),
- declaration);
- }
- }
- return builder.build();
- }
-}
diff --git a/java/dagger/internal/codegen/BindingGraphPlugins.java b/java/dagger/internal/codegen/BindingGraphPlugins.java
deleted file mode 100644
index e2c38120d..000000000
--- a/java/dagger/internal/codegen/BindingGraphPlugins.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.spi.BindingGraphPlugin;
-import java.util.Map;
-import java.util.Set;
-import javax.annotation.processing.Filer;
-import javax.inject.Inject;
-
-/** Initializes {@link BindingGraphPlugin}s. */
-final class BindingGraphPlugins {
- private final ImmutableSet<BindingGraphPlugin> plugins;
- private final Filer filer;
- private final DaggerTypes types;
- private final DaggerElements elements;
- private final Map<String, String> processingOptions;
-
- @Inject
- BindingGraphPlugins(
- @Validation Set<BindingGraphPlugin> validationPlugins,
- ImmutableSet<BindingGraphPlugin> externalPlugins,
- Filer filer,
- DaggerTypes types,
- DaggerElements elements,
- @ProcessingOptions Map<String, String> processingOptions) {
- this.plugins = Sets.union(validationPlugins, externalPlugins).immutableCopy();
- this.filer = filer;
- this.types = types;
- this.elements = elements;
- this.processingOptions = processingOptions;
- }
-
- /** Returns {@link BindingGraphPlugin#supportedOptions()} from all the plugins. */
- ImmutableSet<String> allSupportedOptions() {
- return plugins.stream()
- .flatMap(plugin -> plugin.supportedOptions().stream())
- .collect(toImmutableSet());
- }
-
- /** Initializes the plugins. */
- // TODO(ronshapiro): Should we validate the uniqueness of plugin names?
- void initializePlugins() {
- plugins.forEach(this::initializePlugin);
- }
-
- private void initializePlugin(BindingGraphPlugin plugin) {
- plugin.initFiler(filer);
- plugin.initTypes(types);
- plugin.initElements(elements);
- Set<String> supportedOptions = plugin.supportedOptions();
- if (!supportedOptions.isEmpty()) {
- plugin.initOptions(Maps.filterKeys(processingOptions, supportedOptions::contains));
- }
- }
-}
diff --git a/java/dagger/internal/codegen/BindingGraphStatisticsCollector.java b/java/dagger/internal/codegen/BindingGraphStatisticsCollector.java
deleted file mode 100644
index 129647fff..000000000
--- a/java/dagger/internal/codegen/BindingGraphStatisticsCollector.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2019 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.errorprone.util.ASTHelpers.getSymbol;
-import static dagger.internal.codegen.ComponentAnnotation.rootComponentAnnotation;
-
-import com.google.errorprone.VisitorState;
-import com.google.errorprone.bugpatterns.BugChecker;
-import com.google.errorprone.bugpatterns.BugChecker.ClassTreeMatcher;
-import com.google.errorprone.matchers.Description;
-import com.sun.source.tree.ClassTree;
-import com.sun.tools.javac.code.Symbol.ClassSymbol;
-import com.sun.tools.javac.util.Context;
-import dagger.BindsInstance;
-import dagger.Component;
-import dagger.model.BindingGraph;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/** A {@link BugChecker} that collects statistics derived from a {@link BindingGraph}. */
-public abstract class BindingGraphStatisticsCollector extends BugChecker
- implements ClassTreeMatcher {
- private BindingGraphConverter bindingGraphConverter;
- private BindingGraphFactory bindingGraphFactory;
- private ComponentDescriptorFactory componentDescriptorFactory;
- private boolean isInjected;
-
- @Singleton
- @Component(modules = JavacPluginModule.class)
- interface Injector {
- void inject(BindingGraphStatisticsCollector collector);
-
- @Component.Factory
- interface Factory {
- Injector create(@BindsInstance Context context);
- }
- }
-
- // BugCheckers must have no-arg constructors, so we'll use method injection instead.
- @Inject
- void inject(
- BindingGraphConverter bindingGraphConverter,
- BindingGraphFactory bindingGraphFactory,
- ComponentDescriptorFactory componentDescriptorFactory) {
- this.bindingGraphConverter = bindingGraphConverter;
- this.bindingGraphFactory = bindingGraphFactory;
- this.componentDescriptorFactory = componentDescriptorFactory;
- }
-
- @Override
- public final Description matchClass(ClassTree tree, VisitorState state) {
- injectIfNecessary(state.context);
-
- ClassSymbol symbol = getSymbol(tree);
- rootComponentAnnotation(symbol)
- .map(annotation -> createBindingGraph(symbol))
- .ifPresent(graph -> visitBindingGraph(graph, state));
-
- return Description.NO_MATCH;
- }
-
- private BindingGraph createBindingGraph(ClassSymbol component) {
- return bindingGraphConverter.convert(
- bindingGraphFactory.create(
- componentDescriptorFactory.rootComponentDescriptor(component), false));
- }
-
- /** Visits a {@link BindingGraph} and emits stats to a {@link VisitorState}. */
- protected abstract void visitBindingGraph(BindingGraph graph, VisitorState state);
-
- private void injectIfNecessary(Context context) {
- if (isInjected) {
- return;
- }
- DaggerBindingGraphStatisticsCollector_Injector.factory().create(context).inject(this);
- isInjected = true;
- }
-}
diff --git a/java/dagger/internal/codegen/BindingGraphValidationModule.java b/java/dagger/internal/codegen/BindingGraphValidationModule.java
deleted file mode 100644
index 63e1fa255..000000000
--- a/java/dagger/internal/codegen/BindingGraphValidationModule.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import dagger.Binds;
-import dagger.Module;
-import dagger.multibindings.IntoSet;
-import dagger.spi.BindingGraphPlugin;
-
-/** Binds the set of {@link BindingGraphPlugin}s used to implement Dagger validation. */
-@Module
-interface BindingGraphValidationModule {
-
- @Binds
- @IntoSet
- @Validation
- BindingGraphPlugin dependencyCycle(DependencyCycleValidator validation);
-
- @Binds
- @IntoSet
- @Validation
- BindingGraphPlugin dependsOnProductionExecutor(DependsOnProductionExecutorValidator validation);
-
- @Binds
- @IntoSet
- @Validation
- BindingGraphPlugin duplicateBindings(DuplicateBindingsValidator validation);
-
- @Binds
- @IntoSet
- @Validation
- BindingGraphPlugin incompatiblyScopedBindings(IncompatiblyScopedBindingsValidator validation);
-
- @Binds
- @IntoSet
- @Validation
- BindingGraphPlugin injectBinding(InjectBindingValidator validation);
-
- @Binds
- @IntoSet
- @Validation
- BindingGraphPlugin mapMultibinding(MapMultibindingValidator validation);
-
- @Binds
- @IntoSet
- @Validation
- BindingGraphPlugin missingBinding(MissingBindingValidator validation);
-
- @Binds
- @IntoSet
- @Validation
- BindingGraphPlugin nullableBinding(NullableBindingValidator validation);
-
- @Binds
- @IntoSet
- @Validation
- BindingGraphPlugin provisionDependencyOnProducerBinding(
- ProvisionDependencyOnProducerBindingValidator validation);
-
- @Binds
- @IntoSet
- @Validation
- BindingGraphPlugin subcomponentFactoryMethod(SubcomponentFactoryMethodValidator validation);
-}
diff --git a/java/dagger/internal/codegen/BindingGraphValidator.java b/java/dagger/internal/codegen/BindingGraphValidator.java
deleted file mode 100644
index df17b15b5..000000000
--- a/java/dagger/internal/codegen/BindingGraphValidator.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static javax.tools.Diagnostic.Kind.ERROR;
-
-import com.google.common.collect.ImmutableSet;
-import dagger.internal.codegen.DiagnosticReporterFactory.DiagnosticReporterImpl;
-import dagger.model.BindingGraph;
-import dagger.spi.BindingGraphPlugin;
-import java.util.Set;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/** Validates a {@link BindingGraph}. */
-@Singleton
-final class BindingGraphValidator {
- private final ImmutableSet<BindingGraphPlugin> validationPlugins;
- private final ImmutableSet<BindingGraphPlugin> externalPlugins;
- private final DiagnosticReporterFactory diagnosticReporterFactory;
-
- @Inject
- BindingGraphValidator(
- @Validation Set<BindingGraphPlugin> validationPlugins,
- ImmutableSet<BindingGraphPlugin> externalPlugins,
- DiagnosticReporterFactory diagnosticReporterFactory) {
- this.validationPlugins = ImmutableSet.copyOf(validationPlugins);
- this.externalPlugins = ImmutableSet.copyOf(externalPlugins);
- this.diagnosticReporterFactory = checkNotNull(diagnosticReporterFactory);
- }
-
- /** Returns {@code true} if no errors are reported for {@code graph}. */
- boolean isValid(BindingGraph graph) {
- return isValid(validationPlugins, graph) && isValid(externalPlugins, graph);
- }
-
- private boolean isValid(ImmutableSet<BindingGraphPlugin> plugins, BindingGraph graph) {
- boolean isValid = true;
- for (BindingGraphPlugin plugin : plugins) {
- DiagnosticReporterImpl reporter = diagnosticReporterFactory.reporter(graph, plugin);
- plugin.visitGraph(graph, reporter);
- if (reporter.reportedDiagnosticKinds().contains(ERROR)) {
- isValid = false;
- }
- }
- return isValid;
- }
-}
diff --git a/java/dagger/internal/codegen/BindingMethodProcessingStep.java b/java/dagger/internal/codegen/BindingMethodProcessingStep.java
deleted file mode 100644
index e6c4f8ed3..000000000
--- a/java/dagger/internal/codegen/BindingMethodProcessingStep.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-import com.google.auto.common.MoreElements;
-import com.google.common.collect.ImmutableSet;
-import java.lang.annotation.Annotation;
-import java.util.Set;
-import javax.annotation.processing.Messager;
-import javax.inject.Inject;
-import javax.lang.model.element.ExecutableElement;
-
-/** A step that validates all binding methods that were not validated while processing modules. */
-final class BindingMethodProcessingStep extends TypeCheckingProcessingStep<ExecutableElement> {
-
- private final Messager messager;
- private final AnyBindingMethodValidator anyBindingMethodValidator;
-
- @Inject
- BindingMethodProcessingStep(
- Messager messager, AnyBindingMethodValidator anyBindingMethodValidator) {
- super(MoreElements::asExecutable);
- this.messager = messager;
- this.anyBindingMethodValidator = anyBindingMethodValidator;
- }
-
- @Override
- public Set<? extends Class<? extends Annotation>> annotations() {
- return anyBindingMethodValidator.methodAnnotations();
- }
-
- @Override
- protected void process(
- ExecutableElement method, ImmutableSet<Class<? extends Annotation>> annotations) {
- checkArgument(
- anyBindingMethodValidator.isBindingMethod(method),
- "%s is not annotated with any of %s",
- method,
- annotations());
- if (!anyBindingMethodValidator.wasAlreadyValidated(method)) {
- anyBindingMethodValidator.validate(method).printMessagesTo(messager);
- }
- }
-}
diff --git a/java/dagger/internal/codegen/BindingMethodValidator.java b/java/dagger/internal/codegen/BindingMethodValidator.java
deleted file mode 100644
index 21c05ccd2..000000000
--- a/java/dagger/internal/codegen/BindingMethodValidator.java
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
-import static java.util.stream.Collectors.joining;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.PRIVATE;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.errorprone.annotations.FormatMethod;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import java.lang.annotation.Annotation;
-import java.util.Optional;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.TypeMirror;
-
-/** A validator for methods that represent binding declarations. */
-abstract class BindingMethodValidator extends BindingElementValidator<ExecutableElement> {
-
- private final DaggerElements elements;
- private final DaggerTypes types;
- private final DependencyRequestValidator dependencyRequestValidator;
- private final Class<? extends Annotation> methodAnnotation;
- private final ImmutableSet<? extends Class<? extends Annotation>> enclosingElementAnnotations;
- private final Abstractness abstractness;
- private final ExceptionSuperclass exceptionSuperclass;
-
- /**
- * Creates a validator object.
- *
- * @param methodAnnotation the annotation on a method that identifies it as a binding method
- * @param enclosingElementAnnotation the method must be declared in a class or interface annotated
- * with this annotation
- */
- protected BindingMethodValidator(
- DaggerElements elements,
- DaggerTypes types,
- DependencyRequestValidator dependencyRequestValidator,
- Class<? extends Annotation> methodAnnotation,
- Class<? extends Annotation> enclosingElementAnnotation,
- Abstractness abstractness,
- ExceptionSuperclass exceptionSuperclass,
- AllowsMultibindings allowsMultibindings,
- AllowsScoping allowsScoping) {
- this(
- elements,
- types,
- methodAnnotation,
- ImmutableSet.of(enclosingElementAnnotation),
- dependencyRequestValidator,
- abstractness,
- exceptionSuperclass,
- allowsMultibindings,
- allowsScoping);
- }
-
- /**
- * Creates a validator object.
- *
- * @param methodAnnotation the annotation on a method that identifies it as a binding method
- * @param enclosingElementAnnotations the method must be declared in a class or interface
- * annotated with one of these annotations
- */
- protected BindingMethodValidator(
- DaggerElements elements,
- DaggerTypes types,
- Class<? extends Annotation> methodAnnotation,
- Iterable<? extends Class<? extends Annotation>> enclosingElementAnnotations,
- DependencyRequestValidator dependencyRequestValidator,
- Abstractness abstractness,
- ExceptionSuperclass exceptionSuperclass,
- AllowsMultibindings allowsMultibindings,
- AllowsScoping allowsScoping) {
- super(methodAnnotation, allowsMultibindings, allowsScoping);
- this.elements = elements;
- this.types = types;
- this.methodAnnotation = methodAnnotation;
- this.enclosingElementAnnotations = ImmutableSet.copyOf(enclosingElementAnnotations);
- this.dependencyRequestValidator = dependencyRequestValidator;
- this.abstractness = abstractness;
- this.exceptionSuperclass = exceptionSuperclass;
- }
-
- /** The annotation that identifies binding methods validated by this object. */
- final Class<? extends Annotation> methodAnnotation() {
- return methodAnnotation;
- }
-
- /**
- * Returns an error message of the form "@<i>annotation</i> methods <i>rule</i>", where
- * <i>rule</i> comes from calling {@link String#format(String, Object...)} on {@code ruleFormat}
- * and the other arguments.
- */
- @FormatMethod
- protected final String bindingMethods(String ruleFormat, Object... args) {
- return bindingElements(ruleFormat, args);
- }
-
- @Override
- protected final String bindingElements() {
- return String.format("@%s methods", methodAnnotation.getSimpleName());
- }
-
- @Override
- protected final String bindingElementTypeVerb() {
- return "return";
- }
-
- /** Abstract validator for individual binding method elements. */
- protected abstract class MethodValidator extends ElementValidator {
- protected MethodValidator(ExecutableElement element) {
- super(element);
- }
-
- @Override
- protected final Optional<TypeMirror> bindingElementType() {
- return Optional.of(element.getReturnType());
- }
-
- @Override
- protected final void checkAdditionalProperties() {
- checkEnclosingElement();
- checkTypeParameters();
- checkNotPrivate();
- checkAbstractness();
- checkThrows();
- checkParameters();
- checkAdditionalMethodProperties();
- }
-
- /** Checks additional properties of the binding method. */
- protected void checkAdditionalMethodProperties() {}
-
- /**
- * Adds an error if the method is not declared in a class or interface annotated with one of the
- * {@link #enclosingElementAnnotations}.
- */
- private void checkEnclosingElement() {
- if (!isAnyAnnotationPresent(
- element.getEnclosingElement(), enclosingElementAnnotations)) {
- report.addError(
- bindingMethods(
- "can only be present within a @%s",
- enclosingElementAnnotations.stream()
- .map(Class::getSimpleName)
- .collect(joining(" or @"))));
- }
- }
-
- /** Adds an error if the method is generic. */
- private void checkTypeParameters() {
- if (!element.getTypeParameters().isEmpty()) {
- report.addError(bindingMethods("may not have type parameters"));
- }
- }
-
- /** Adds an error if the method is private. */
- private void checkNotPrivate() {
- if (element.getModifiers().contains(PRIVATE)) {
- report.addError(bindingMethods("cannot be private"));
- }
- }
-
- /** Adds an error if the method is abstract but must not be, or is not and must be. */
- private void checkAbstractness() {
- boolean isAbstract = element.getModifiers().contains(ABSTRACT);
- switch (abstractness) {
- case MUST_BE_ABSTRACT:
- if (!isAbstract) {
- report.addError(bindingMethods("must be abstract"));
- }
- break;
-
- case MUST_BE_CONCRETE:
- if (isAbstract) {
- report.addError(bindingMethods("cannot be abstract"));
- }
- }
- }
-
- /**
- * Adds an error if the method declares throws anything but an {@link Error} or an appropriate
- * subtype of {@link Exception}.
- */
- private void checkThrows() {
- exceptionSuperclass.checkThrows(BindingMethodValidator.this, element, report);
- }
-
- /** Adds errors for the method parameters. */
- protected void checkParameters() {
- for (VariableElement parameter : element.getParameters()) {
- checkParameter(parameter);
- }
- }
-
- /**
- * Adds errors for a method parameter. This implementation reports an error if the parameter has
- * more than one qualifier.
- */
- protected void checkParameter(VariableElement parameter) {
- dependencyRequestValidator.validateDependencyRequest(report, parameter, parameter.asType());
- }
- }
-
- /** An abstract/concrete restriction on methods. */
- protected enum Abstractness {
- MUST_BE_ABSTRACT,
- MUST_BE_CONCRETE
- }
-
- /**
- * The exception class that all {@code throws}-declared throwables must extend, other than {@link
- * Error}.
- */
- protected enum ExceptionSuperclass {
- /** Methods may not declare any throwable types. */
- NO_EXCEPTIONS {
- @Override
- protected String errorMessage(BindingMethodValidator validator) {
- return validator.bindingMethods("may not throw");
- }
-
- @Override
- protected void checkThrows(
- BindingMethodValidator validator,
- ExecutableElement element,
- ValidationReport.Builder<ExecutableElement> report) {
- if (!element.getThrownTypes().isEmpty()) {
- report.addError(validator.bindingMethods("may not throw"));
- return;
- }
- }
- },
-
- /** Methods may throw checked or unchecked exceptions or errors. */
- EXCEPTION(Exception.class) {
- @Override
- protected String errorMessage(BindingMethodValidator validator) {
- return validator.bindingMethods(
- "may only throw unchecked exceptions or exceptions subclassing Exception");
- }
- },
-
- /** Methods may throw unchecked exceptions or errors. */
- RUNTIME_EXCEPTION(RuntimeException.class) {
- @Override
- protected String errorMessage(BindingMethodValidator validator) {
- return validator.bindingMethods("may only throw unchecked exceptions");
- }
- },
- ;
-
- private final Class<? extends Exception> superclass;
-
- ExceptionSuperclass() {
- this(null);
- }
-
- ExceptionSuperclass(Class<? extends Exception> superclass) {
- this.superclass = superclass;
- }
-
- /**
- * Adds an error if the method declares throws anything but an {@link Error} or an appropriate
- * subtype of {@link Exception}.
- *
- * <p>This method is overridden in {@link #NO_EXCEPTIONS}.
- */
- protected void checkThrows(
- BindingMethodValidator validator,
- ExecutableElement element,
- ValidationReport.Builder<ExecutableElement> report) {
- TypeMirror exceptionSupertype = validator.elements.getTypeElement(superclass).asType();
- TypeMirror errorType = validator.elements.getTypeElement(Error.class).asType();
- for (TypeMirror thrownType : element.getThrownTypes()) {
- if (!validator.types.isSubtype(thrownType, exceptionSupertype)
- && !validator.types.isSubtype(thrownType, errorType)) {
- report.addError(errorMessage(validator));
- break;
- }
- }
- }
-
- protected abstract String errorMessage(BindingMethodValidator validator);
- }
-}
diff --git a/java/dagger/internal/codegen/BindingMethodValidatorsModule.java b/java/dagger/internal/codegen/BindingMethodValidatorsModule.java
deleted file mode 100644
index 28a272d10..000000000
--- a/java/dagger/internal/codegen/BindingMethodValidatorsModule.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.collect.Maps.uniqueIndex;
-
-import com.google.common.collect.ImmutableMap;
-import dagger.Binds;
-import dagger.Module;
-import dagger.Provides;
-import dagger.multibindings.IntoSet;
-import java.lang.annotation.Annotation;
-import java.util.Set;
-
-/**
- * Binds each {@link BindingMethodValidator} into a map, keyed by {@link
- * BindingMethodValidator#methodAnnotation()}.
- */
-@Module
-interface BindingMethodValidatorsModule {
- @Provides
- static ImmutableMap<Class<? extends Annotation>, BindingMethodValidator> indexValidators(
- Set<BindingMethodValidator> validators) {
- return uniqueIndex(validators, BindingMethodValidator::methodAnnotation);
- }
-
- @Binds
- @IntoSet
- BindingMethodValidator provides(ProvidesMethodValidator validator);
-
- @Binds
- @IntoSet
- BindingMethodValidator produces(ProducesMethodValidator validator);
-
- @Binds
- @IntoSet
- BindingMethodValidator binds(BindsMethodValidator validator);
-
- @Binds
- @IntoSet
- BindingMethodValidator multibinds(MultibindsMethodValidator validator);
-
- @Binds
- @IntoSet
- BindingMethodValidator bindsOptionalOf(BindsOptionalOfMethodValidator validator);
-}
diff --git a/java/dagger/internal/codegen/BindingNode.java b/java/dagger/internal/codegen/BindingNode.java
deleted file mode 100644
index a7da0920c..000000000
--- a/java/dagger/internal/codegen/BindingNode.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static dagger.internal.codegen.BindingType.PRODUCTION;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import dagger.BindsOptionalOf;
-import dagger.Module;
-import dagger.model.BindingKind;
-import dagger.model.ComponentPath;
-import dagger.model.DependencyRequest;
-import dagger.model.Key;
-import dagger.model.Scope;
-import dagger.multibindings.Multibinds;
-import java.util.Optional;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-
-/**
- * An implementation of {@link dagger.model.Binding} that also exposes {@link BindingDeclaration}s
- * associated with the binding.
- */
-// TODO(dpb): Consider a supertype of dagger.model.Binding that dagger.internal.codegen.Binding
-// could also implement.
-@AutoValue
-abstract class BindingNode implements dagger.model.Binding {
- static BindingNode create(
- ComponentPath component,
- Binding delegate,
- ImmutableSet<MultibindingDeclaration> multibindingDeclarations,
- ImmutableSet<OptionalBindingDeclaration> optionalBindingDeclarations,
- ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations,
- BindingDeclarationFormatter bindingDeclarationFormatter) {
- BindingNode node =
- new AutoValue_BindingNode(
- component,
- delegate,
- multibindingDeclarations,
- optionalBindingDeclarations,
- subcomponentDeclarations);
- node.bindingDeclarationFormatter = checkNotNull(bindingDeclarationFormatter);
- return node;
- }
-
- private BindingDeclarationFormatter bindingDeclarationFormatter;
-
- abstract Binding delegate();
-
- abstract ImmutableSet<MultibindingDeclaration> multibindingDeclarations();
-
- abstract ImmutableSet<OptionalBindingDeclaration> optionalBindingDeclarations();
-
- abstract ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations();
-
- /**
- * The {@link Element}s (other than the binding's {@link #bindingElement()}) that are associated
- * with the binding.
- *
- * <ul>
- * <li>{@linkplain BindsOptionalOf optional binding} declarations
- * <li>{@linkplain Module#subcomponents() module subcomponent} declarations
- * <li>{@linkplain Multibinds multibinding} declarations
- * </ul>
- */
- final Iterable<BindingDeclaration> associatedDeclarations() {
- return Iterables.concat(
- multibindingDeclarations(), optionalBindingDeclarations(), subcomponentDeclarations());
- }
-
- @Override
- public Key key() {
- return delegate().key();
- }
-
- @Override
- public ImmutableSet<DependencyRequest> dependencies() {
- return delegate().dependencies();
- }
-
- @Override
- public Optional<Element> bindingElement() {
- return delegate().bindingElement();
- }
-
- @Override
- public Optional<TypeElement> contributingModule() {
- return delegate().contributingModule();
- }
-
- @Override
- public boolean requiresModuleInstance() {
- return delegate().requiresModuleInstance();
- }
-
- @Override
- public Optional<Scope> scope() {
- return delegate().scope();
- }
-
- @Override
- public boolean isNullable() {
- return delegate().isNullable();
- }
-
- @Override
- public boolean isProduction() {
- return delegate().bindingType().equals(PRODUCTION);
- }
-
- @Override
- public BindingKind kind() {
- return delegate().kind();
- }
-
- @Override
- public final String toString() {
- return bindingDeclarationFormatter.format(delegate());
- }
-}
diff --git a/java/dagger/internal/codegen/BindingRequest.java b/java/dagger/internal/codegen/BindingRequest.java
deleted file mode 100644
index 27067aa27..000000000
--- a/java/dagger/internal/codegen/BindingRequest.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.RequestKinds.requestType;
-
-import com.google.auto.value.AutoValue;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.internal.codegen.serialization.BindingRequestProto;
-import dagger.internal.codegen.serialization.FrameworkTypeWrapper;
-import dagger.internal.codegen.serialization.RequestKindWrapper;
-import dagger.model.DependencyRequest;
-import dagger.model.Key;
-import dagger.model.RequestKind;
-import java.util.Optional;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * A request for a binding, which may be in the form of a request for a dependency to pass to a
- * constructor or module method ({@link RequestKind}) or an internal request for a framework
- * instance ({@link FrameworkType}).
- */
-@AutoValue
-abstract class BindingRequest {
- /** Creates a {@link BindingRequest} for the given {@link DependencyRequest}. */
- static BindingRequest bindingRequest(DependencyRequest dependencyRequest) {
- return bindingRequest(dependencyRequest.key(), dependencyRequest.kind());
- }
-
- /**
- * Creates a {@link BindingRequest} for a normal dependency request for the given {@link Key} and
- * {@link RequestKind}.
- */
- static BindingRequest bindingRequest(Key key, RequestKind requestKind) {
- // When there's a request that has a 1:1 mapping to a FrameworkType, the request should be
- // associated with that FrameworkType as well, because we want to ensure that if a request
- // comes in for that as a dependency first and as a framework instance later, they resolve to
- // the same binding expression.
- // TODO(cgdecker): Instead of doing this, make ComponentBindingExpressions create a
- // BindingExpression for the RequestKind that simply delegates to the BindingExpression for the
- // FrameworkType. Then there are separate BindingExpressions, but we don't end up doing weird
- // things like creating two fields when there should only be one.
- return new AutoValue_BindingRequest(
- key, Optional.of(requestKind), FrameworkType.forRequestKind(requestKind));
- }
-
- /**
- * Creates a {@link BindingRequest} for a request for a framework instance for the given {@link
- * Key} with the given {@link FrameworkType}.
- */
- static BindingRequest bindingRequest(Key key, FrameworkType frameworkType) {
- return new AutoValue_BindingRequest(
- key, frameworkType.requestKind(), Optional.of(frameworkType));
- }
-
- /** Creates a {@link BindingRequest} for the given {@link FrameworkDependency}. */
- static BindingRequest bindingRequest(FrameworkDependency frameworkDependency) {
- return bindingRequest(frameworkDependency.key(), frameworkDependency.frameworkType());
- }
-
- /** Returns the {@link Key} for the requested binding. */
- abstract Key key();
-
- /** Returns the request kind associated with this request, if any. */
- abstract Optional<RequestKind> requestKind();
-
- /** Returns the framework type associated with this request, if any. */
- abstract Optional<FrameworkType> frameworkType();
-
- /** Returns whether this request is of the given kind. */
- final boolean isRequestKind(RequestKind requestKind) {
- return requestKind.equals(requestKind().orElse(null));
- }
-
- final TypeMirror requestedType(TypeMirror contributedType, DaggerTypes types) {
- if (requestKind().isPresent()) {
- return requestType(requestKind().get(), contributedType, types);
- }
- return types.wrapType(contributedType, frameworkType().get().frameworkClass());
- }
-
- /** Returns a name that can be used for the kind of request this is. */
- final String kindName() {
- Object requestKindObject =
- requestKind().isPresent()
- ? requestKind().get()
- : frameworkType().get().frameworkClass().getSimpleName();
- return requestKindObject.toString();
- }
-
- /** Returns {@code true} if this request can be satisfied by a production binding. */
- final boolean canBeSatisfiedByProductionBinding() {
- if (requestKind().isPresent()) {
- return RequestKinds.canBeSatisfiedByProductionBinding(requestKind().get());
- }
- return frameworkType().get().equals(FrameworkType.PRODUCER_NODE);
- }
-
- /** Creates a proto representation of this binding request. */
- BindingRequestProto toProto() {
- BindingRequestProto.Builder builder =
- BindingRequestProto.newBuilder().setKey(KeyFactory.toProto(key()));
- if (frameworkType().isPresent()) {
- builder.setFrameworkType(
- FrameworkTypeWrapper.FrameworkType.valueOf(frameworkType().get().name()));
- } else {
- builder.setRequestKind(RequestKindWrapper.RequestKind.valueOf(requestKind().get().name()));
- }
- return builder.build();
- }
-}
diff --git a/java/dagger/internal/codegen/BindingType.java b/java/dagger/internal/codegen/BindingType.java
deleted file mode 100644
index 37109c73b..000000000
--- a/java/dagger/internal/codegen/BindingType.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import dagger.MembersInjector;
-
-/** Whether a binding or declaration is for provision, production, or a {@link MembersInjector}. */
-enum BindingType {
- /** A binding with this type is a {@link ProvisionBinding}. */
- PROVISION,
-
- /** A binding with this type is a {@link MembersInjectionBinding}. */
- MEMBERS_INJECTION,
-
- /** A binding with this type is a {@link ProductionBinding}. */
- PRODUCTION,
-}
diff --git a/java/dagger/internal/codegen/BindsInstanceElementValidator.java b/java/dagger/internal/codegen/BindsInstanceElementValidator.java
deleted file mode 100644
index 9249c8ec5..000000000
--- a/java/dagger/internal/codegen/BindsInstanceElementValidator.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import dagger.BindsInstance;
-import javax.lang.model.element.Element;
-
-abstract class BindsInstanceElementValidator<E extends Element> extends BindingElementValidator<E> {
- BindsInstanceElementValidator() {
- super(BindsInstance.class, AllowsMultibindings.NO_MULTIBINDINGS, AllowsScoping.NO_SCOPING);
- }
-
- @Override
- protected final String bindingElements() {
- // Even though @BindsInstance may be placed on methods, the subject of errors is the
- // parameter
- return "@BindsInstance parameters";
- }
-
- @Override
- protected final String bindingElementTypeVerb() {
- return "be";
- }
-}
diff --git a/java/dagger/internal/codegen/BindsInstanceMethodValidator.java b/java/dagger/internal/codegen/BindsInstanceMethodValidator.java
deleted file mode 100644
index 1a491c76e..000000000
--- a/java/dagger/internal/codegen/BindsInstanceMethodValidator.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.ComponentAnnotation.anyComponentAnnotation;
-import static dagger.internal.codegen.ModuleAnnotation.moduleAnnotation;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-
-import com.google.auto.common.MoreElements;
-import java.util.List;
-import java.util.Optional;
-import javax.inject.Inject;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.TypeMirror;
-
-final class BindsInstanceMethodValidator extends BindsInstanceElementValidator<ExecutableElement> {
- @Inject
- BindsInstanceMethodValidator() {}
-
- @Override
- protected ElementValidator elementValidator(ExecutableElement element) {
- return new Validator(element);
- }
-
- private class Validator extends ElementValidator {
- Validator(ExecutableElement element) {
- super(element);
- }
-
- @Override
- protected void checkAdditionalProperties() {
- if (!element.getModifiers().contains(ABSTRACT)) {
- report.addError("@BindsInstance methods must be abstract");
- }
- if (element.getParameters().size() != 1) {
- report.addError(
- "@BindsInstance methods should have exactly one parameter for the bound type");
- }
- TypeElement enclosingType = MoreElements.asType(element.getEnclosingElement());
- moduleAnnotation(enclosingType)
- .ifPresent(moduleAnnotation -> report.addError(didYouMeanBinds(moduleAnnotation)));
- anyComponentAnnotation(enclosingType)
- .ifPresent(
- componentAnnotation ->
- report.addError(
- String.format(
- "@BindsInstance methods should not be included in @%1$ss. "
- + "Did you mean to put it in a @%1$s.Builder?",
- componentAnnotation.simpleName())));
- }
-
- @Override
- protected Optional<TypeMirror> bindingElementType() {
- List<? extends VariableElement> parameters =
- MoreElements.asExecutable(element).getParameters();
- return parameters.size() == 1
- ? Optional.of(getOnlyElement(parameters).asType())
- : Optional.empty();
- }
- }
-
- private static String didYouMeanBinds(ModuleAnnotation moduleAnnotation) {
- return String.format(
- "@BindsInstance methods should not be included in @%ss. Did you mean @Binds?",
- moduleAnnotation.annotationClass().getSimpleName());
- }
-}
diff --git a/java/dagger/internal/codegen/BindsInstanceParameterValidator.java b/java/dagger/internal/codegen/BindsInstanceParameterValidator.java
deleted file mode 100644
index b2dc8d878..000000000
--- a/java/dagger/internal/codegen/BindsInstanceParameterValidator.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static javax.lang.model.element.ElementKind.METHOD;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.type.TypeKind.DECLARED;
-import static javax.lang.model.type.TypeKind.TYPEVAR;
-
-import com.google.auto.common.MoreElements;
-import java.util.Optional;
-import javax.inject.Inject;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-
-final class BindsInstanceParameterValidator extends BindsInstanceElementValidator<VariableElement> {
- @Inject
- BindsInstanceParameterValidator() {}
-
- @Override
- protected ElementValidator elementValidator(VariableElement element) {
- return new Validator(element);
- }
-
- private class Validator extends ElementValidator {
- Validator(VariableElement element) {
- super(element);
- }
-
- @Override
- protected void checkAdditionalProperties() {
- Element enclosing = element.getEnclosingElement();
- if (!enclosing.getKind().equals(METHOD)) {
- report.addError(
- "@BindsInstance should only be applied to methods or parameters of methods");
- return;
- }
-
- ExecutableElement method = MoreElements.asExecutable(enclosing);
- if (!method.getModifiers().contains(ABSTRACT)) {
- report.addError("@BindsInstance parameters may only be used in abstract methods");
- }
-
- TypeKind returnKind = method.getReturnType().getKind();
- if (!(returnKind.equals(DECLARED) || returnKind.equals(TYPEVAR))) {
- report.addError(
- "@BindsInstance parameters may not be used in methods with a void, array or primitive "
- + "return type");
- }
- }
-
- @Override
- protected Optional<TypeMirror> bindingElementType() {
- return Optional.of(element.asType());
- }
- }
-}
diff --git a/java/dagger/internal/codegen/BindsInstanceProcessingStep.java b/java/dagger/internal/codegen/BindsInstanceProcessingStep.java
deleted file mode 100644
index 4c222a9ac..000000000
--- a/java/dagger/internal/codegen/BindsInstanceProcessingStep.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreElements;
-import com.google.common.collect.ImmutableSet;
-import dagger.BindsInstance;
-import java.lang.annotation.Annotation;
-import java.util.Set;
-import javax.annotation.processing.Messager;
-import javax.inject.Inject;
-import javax.lang.model.element.Element;
-
-/**
- * Processing step that validates that the {@code BindsInstance} annotation is applied to the
- * correct elements.
- */
-final class BindsInstanceProcessingStep extends TypeCheckingProcessingStep<Element> {
- private final BindsInstanceMethodValidator methodValidator;
- private final BindsInstanceParameterValidator parameterValidator;
- private final Messager messager;
-
- @Inject
- BindsInstanceProcessingStep(
- BindsInstanceMethodValidator methodValidator,
- BindsInstanceParameterValidator parameterValidator,
- Messager messager) {
- super(element -> element);
- this.methodValidator = methodValidator;
- this.parameterValidator = parameterValidator;
- this.messager = messager;
- }
-
- @Override
- public Set<? extends Class<? extends Annotation>> annotations() {
- return ImmutableSet.of(BindsInstance.class);
- }
-
- @Override
- protected void process(Element element, ImmutableSet<Class<? extends Annotation>> annotations) {
- switch (element.getKind()) {
- case PARAMETER:
- parameterValidator.validate(MoreElements.asVariable(element)).printMessagesTo(messager);
- break;
- case METHOD:
- methodValidator.validate(MoreElements.asExecutable(element)).printMessagesTo(messager);
- break;
- default:
- throw new AssertionError(element);
- }
- }
-}
diff --git a/java/dagger/internal/codegen/BindsMethodValidator.java b/java/dagger/internal/codegen/BindsMethodValidator.java
deleted file mode 100644
index e198c3af3..000000000
--- a/java/dagger/internal/codegen/BindsMethodValidator.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.BindingElementValidator.AllowsMultibindings.ALLOWS_MULTIBINDINGS;
-import static dagger.internal.codegen.BindingElementValidator.AllowsScoping.ALLOWS_SCOPING;
-import static dagger.internal.codegen.BindingMethodValidator.Abstractness.MUST_BE_ABSTRACT;
-import static dagger.internal.codegen.BindingMethodValidator.ExceptionSuperclass.RUNTIME_EXCEPTION;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableSet;
-import dagger.Binds;
-import dagger.Module;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.producers.ProducerModule;
-import javax.inject.Inject;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.TypeMirror;
-
-/** A validator for {@link Binds} methods. */
-final class BindsMethodValidator extends BindingMethodValidator {
- private final DaggerTypes types;
- private final BindsTypeChecker bindsTypeChecker;
-
- @Inject
- BindsMethodValidator(
- DaggerElements elements,
- DaggerTypes types,
- DependencyRequestValidator dependencyRequestValidator) {
- super(
- elements,
- types,
- Binds.class,
- ImmutableSet.of(Module.class, ProducerModule.class),
- dependencyRequestValidator,
- MUST_BE_ABSTRACT,
- RUNTIME_EXCEPTION,
- ALLOWS_MULTIBINDINGS,
- ALLOWS_SCOPING);
- this.types = types;
- this.bindsTypeChecker = new BindsTypeChecker(types, elements);
- }
-
- @Override
- protected ElementValidator elementValidator(ExecutableElement element) {
- return new Validator(element);
- }
-
- private class Validator extends MethodValidator {
- Validator(ExecutableElement element) {
- super(element);
- }
-
- @Override
- protected void checkParameters() {
- if (element.getParameters().size() != 1) {
- report.addError(
- bindingMethods(
- "must have exactly one parameter, whose type is assignable to the return type"));
- } else {
- super.checkParameters();
- }
- }
-
- @Override
- protected void checkParameter(VariableElement parameter) {
- super.checkParameter(parameter);
- TypeMirror leftHandSide = boxIfNecessary(element.getReturnType());
- TypeMirror rightHandSide = parameter.asType();
- ContributionType contributionType = ContributionType.fromBindingElement(element);
- if (contributionType.equals(ContributionType.SET_VALUES) && !SetType.isSet(leftHandSide)) {
- report.addError(
- "@Binds @ElementsIntoSet methods must return a Set and take a Set parameter");
- }
-
- if (!bindsTypeChecker.isAssignable(rightHandSide, leftHandSide, contributionType)) {
- // TODO(ronshapiro): clarify this error message for @ElementsIntoSet cases, where the
- // right-hand-side might not be assignable to the left-hand-side, but still compatible with
- // Set.addAll(Collection<? extends E>)
- report.addError("@Binds methods' parameter type must be assignable to the return type");
- }
- }
-
- private TypeMirror boxIfNecessary(TypeMirror maybePrimitive) {
- if (maybePrimitive.getKind().isPrimitive()) {
- return types.boxedClass(MoreTypes.asPrimitiveType(maybePrimitive)).asType();
- }
- return maybePrimitive;
- }
- }
-}
diff --git a/java/dagger/internal/codegen/BindsOptionalOfMethodValidator.java b/java/dagger/internal/codegen/BindsOptionalOfMethodValidator.java
deleted file mode 100644
index e1c9d735f..000000000
--- a/java/dagger/internal/codegen/BindsOptionalOfMethodValidator.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.BindingElementValidator.AllowsMultibindings.NO_MULTIBINDINGS;
-import static dagger.internal.codegen.BindingElementValidator.AllowsScoping.NO_SCOPING;
-import static dagger.internal.codegen.BindingMethodValidator.Abstractness.MUST_BE_ABSTRACT;
-import static dagger.internal.codegen.BindingMethodValidator.ExceptionSuperclass.NO_EXCEPTIONS;
-import static dagger.internal.codegen.InjectionAnnotations.getQualifiers;
-import static dagger.internal.codegen.InjectionAnnotations.injectedConstructors;
-import static dagger.internal.codegen.Keys.isValidImplicitProvisionKey;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableSet;
-import dagger.BindsOptionalOf;
-import dagger.Module;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.producers.ProducerModule;
-import javax.inject.Inject;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.type.TypeMirror;
-
-/** A validator for {@link BindsOptionalOf} methods. */
-final class BindsOptionalOfMethodValidator extends BindingMethodValidator {
-
- private final DaggerTypes types;
-
- @Inject
- BindsOptionalOfMethodValidator(
- DaggerElements elements,
- DaggerTypes types,
- DependencyRequestValidator dependencyRequestValidator) {
- super(
- elements,
- types,
- BindsOptionalOf.class,
- ImmutableSet.of(Module.class, ProducerModule.class),
- dependencyRequestValidator,
- MUST_BE_ABSTRACT,
- NO_EXCEPTIONS,
- NO_MULTIBINDINGS,
- NO_SCOPING);
- this.types = types;
- }
-
- @Override
- protected ElementValidator elementValidator(ExecutableElement element) {
- return new Validator(element);
- }
-
- private class Validator extends MethodValidator {
- Validator(ExecutableElement element) {
- super(element);
- }
-
- @Override
- protected void checkKeyType(TypeMirror keyType) {
- super.checkKeyType(keyType);
- if (isValidImplicitProvisionKey(
- getQualifiers(element).stream().findFirst(), keyType, types)
- && !injectedConstructors(MoreElements.asType(MoreTypes.asDeclared(keyType).asElement()))
- .isEmpty()) {
- report.addError(
- "@BindsOptionalOf methods cannot return unqualified types that have an @Inject-"
- + "annotated constructor because those are always present");
- }
- }
-
- @Override
- protected void checkParameters() {
- if (!element.getParameters().isEmpty()) {
- report.addError("@BindsOptionalOf methods cannot have parameters");
- }
- }
- }
-}
diff --git a/java/dagger/internal/codegen/BindsTypeChecker.java b/java/dagger/internal/codegen/BindsTypeChecker.java
deleted file mode 100644
index acecc9e23..000000000
--- a/java/dagger/internal/codegen/BindsTypeChecker.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.collect.Iterables.getOnlyElement;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableList;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import java.util.Map;
-import java.util.Set;
-import javax.inject.Inject;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * Checks the assignability of one type to another, given a {@link ContributionType} context. This
- * is used by {@link BindsMethodValidator} to validate that the right-hand-side of a {@link
- * dagger.Binds} method is valid, as well as in {@link DelegateBindingExpression} when the
- * right-hand-side in generated code might be an erased type due to accessibility.
- */
-final class BindsTypeChecker {
- private final DaggerTypes types;
- private final DaggerElements elements;
-
- @Inject
- BindsTypeChecker(DaggerTypes types, DaggerElements elements) {
- this.types = types;
- this.elements = elements;
- }
-
- /**
- * Checks the assignability of {@code rightHandSide} to {@code leftHandSide} given a {@link
- * ContributionType} context.
- */
- boolean isAssignable(
- TypeMirror rightHandSide, TypeMirror leftHandSide, ContributionType contributionType) {
- return types.isAssignable(rightHandSide, desiredAssignableType(leftHandSide, contributionType));
- }
-
- private TypeMirror desiredAssignableType(
- TypeMirror leftHandSide, ContributionType contributionType) {
- switch (contributionType) {
- case UNIQUE:
- return leftHandSide;
- case SET:
- DeclaredType parameterizedSetType = types.getDeclaredType(setElement(), leftHandSide);
- return methodParameterType(parameterizedSetType, "add");
- case SET_VALUES:
- return methodParameterType(MoreTypes.asDeclared(leftHandSide), "addAll");
- case MAP:
- DeclaredType parameterizedMapType =
- types.getDeclaredType(mapElement(), unboundedWildcard(), leftHandSide);
- return methodParameterTypes(parameterizedMapType, "put").get(1);
- }
- throw new AssertionError("Unknown contribution type: " + contributionType);
- }
-
- private ImmutableList<TypeMirror> methodParameterTypes(DeclaredType type, String methodName) {
- ImmutableList.Builder<ExecutableElement> methodsForName = ImmutableList.builder();
- for (ExecutableElement method :
- // type.asElement().getEnclosedElements() is not used because some non-standard JDKs (e.g.
- // J2CL) don't redefine Set.add() (whose only purpose of being redefined in the standard JDK
- // is documentation, and J2CL's implementation doesn't declare docs for JDK types).
- // MoreElements.getLocalAndInheritedMethods ensures that the method will always be present.
- MoreElements.getLocalAndInheritedMethods(MoreTypes.asTypeElement(type), types, elements)) {
- if (method.getSimpleName().contentEquals(methodName)) {
- methodsForName.add(method);
- }
- }
- ExecutableElement method = getOnlyElement(methodsForName.build());
- return ImmutableList.copyOf(
- MoreTypes.asExecutable(types.asMemberOf(type, method)).getParameterTypes());
- }
-
- private TypeMirror methodParameterType(DeclaredType type, String methodName) {
- return getOnlyElement(methodParameterTypes(type, methodName));
- }
-
- private TypeElement setElement() {
- return elements.getTypeElement(Set.class);
- }
-
- private TypeElement mapElement() {
- return elements.getTypeElement(Map.class);
- }
-
- private TypeMirror unboundedWildcard() {
- return types.getWildcardType(null, null);
- }
-}
diff --git a/java/dagger/internal/codegen/ChildFactoryMethodEdgeImpl.java b/java/dagger/internal/codegen/ChildFactoryMethodEdgeImpl.java
deleted file mode 100644
index a5e021942..000000000
--- a/java/dagger/internal/codegen/ChildFactoryMethodEdgeImpl.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.ElementFormatter.elementToString;
-
-import dagger.model.BindingGraph.ChildFactoryMethodEdge;
-import javax.lang.model.element.ExecutableElement;
-
-/** An implementation of {@link ChildFactoryMethodEdge}. */
-final class ChildFactoryMethodEdgeImpl implements ChildFactoryMethodEdge {
-
- private final ExecutableElement factoryMethod;
-
- ChildFactoryMethodEdgeImpl(ExecutableElement factoryMethod) {
- this.factoryMethod = factoryMethod;
- }
-
- @Override
- public ExecutableElement factoryMethod() {
- return factoryMethod;
- }
-
- @Override
- public String toString() {
- return elementToString(factoryMethod);
- }
-}
diff --git a/java/dagger/internal/codegen/ClearableCache.java b/java/dagger/internal/codegen/ClearableCache.java
deleted file mode 100644
index 66ce3ef82..000000000
--- a/java/dagger/internal/codegen/ClearableCache.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2019 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-/** A cache of objects that can be cleared. */
-interface ClearableCache {
- /** Releases cached references. */
- void clearCache();
-}
diff --git a/java/dagger/internal/codegen/CompilerOptions.java b/java/dagger/internal/codegen/CompilerOptions.java
deleted file mode 100644
index bc3cbf8f8..000000000
--- a/java/dagger/internal/codegen/CompilerOptions.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import com.squareup.javapoet.AnnotationSpec;
-import dagger.internal.GenerationOptions;
-import javax.lang.model.element.TypeElement;
-import javax.tools.Diagnostic;
-
-/** A collection of options that dictate how the compiler will run. */
-abstract class CompilerOptions {
- abstract boolean usesProducers();
-
- /**
- * Returns true if the fast initialization flag, {@code fastInit}, is enabled.
- *
- * <p>If enabled, the generated code will attempt to optimize for fast component initialization.
- * This is done by reducing the number of factory classes loaded during initialization and the
- * number of eagerly initialized fields at the cost of potential memory leaks and higher
- * per-provision instantiation time.
- */
- abstract boolean fastInit();
-
- abstract boolean formatGeneratedSource();
-
- abstract boolean writeProducerNameInToken();
-
- abstract Diagnostic.Kind nullableValidationKind();
-
- final boolean doCheckForNulls() {
- return nullableValidationKind().equals(Diagnostic.Kind.ERROR);
- }
-
- abstract Diagnostic.Kind privateMemberValidationKind();
-
- abstract Diagnostic.Kind staticMemberValidationKind();
-
- /**
- * If {@code true}, Dagger will generate factories and components even if some members-injected
- * types have {@code private} or {@code static} {@code @Inject}-annotated members.
- *
- * <p>This should only ever be enabled by the TCK tests. Disabling this validation could lead to
- * generating code that does not compile.
- */
- abstract boolean ignorePrivateAndStaticInjectionForComponent();
-
- abstract ValidationType scopeCycleValidationType();
-
- abstract boolean warnIfInjectionFactoryNotGeneratedUpstream();
-
- abstract boolean headerCompilation();
-
- abstract boolean aheadOfTimeSubcomponents();
-
- /**
- * Enables a testing configuration where all superclass {@link ComponentImplementation}s are
- * derived from their serialized forms.
- */
- abstract boolean forceUseSerializedComponentImplementations();
-
- /**
- * If {@code true}, in {@link #aheadOfTimeSubcomponents()} mode, Dagger will emit metadata
- * annotations to deserialize aspects of the {@link ComponentImplementation}.
- *
- * This should only be disabled in compile-testing tests that want to ignore the annotations when
- * asserting on generated source.
- */
- abstract boolean emitModifiableMetadataAnnotations();
-
- abstract boolean useGradleIncrementalProcessing();
-
- /**
- * Returns the validation that should be done for the full binding graph for the element.
- *
- * @throws IllegalArgumentException if {@code element} is not a module or (sub)component
- */
- abstract ValidationType fullBindingGraphValidationType(TypeElement element);
-
- abstract Diagnostic.Kind moduleHasDifferentScopesDiagnosticKind();
-
- abstract ValidationType explicitBindingConflictsWithInjectValidationType();
-
- /**
- * Creates a new {@link CompilerOptions} from the serialized {@link GenerationOptions} of a base
- * component implementation.
- */
- final CompilerOptions withGenerationOptions(GenerationOptions generationOptions) {
- return new ForwardingCompilerOptions(this) {
- @Override
- public boolean fastInit() {
- return generationOptions.fastInit();
- }
- };
- }
-
- /**
- * Returns a {@link GenerationOptions} annotation that serializes any options for this compilation
- * that should be reused in future compilations.
- */
- final AnnotationSpec toGenerationOptionsAnnotation() {
- return AnnotationSpec.builder(GenerationOptions.class)
- .addMember("fastInit", "$L", fastInit())
- .build();
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentAnnotation.java b/java/dagger/internal/codegen/ComponentAnnotation.java
deleted file mode 100644
index a8f2ece61..000000000
--- a/java/dagger/internal/codegen/ComponentAnnotation.java
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Copyright (C) 2019 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
-import static com.google.auto.common.MoreTypes.asTypeElements;
-import static com.google.auto.common.MoreTypes.isTypeOf;
-import static dagger.internal.codegen.DaggerStreams.toImmutableList;
-import static dagger.internal.codegen.MoreAnnotationValues.asAnnotationValues;
-import static dagger.internal.codegen.langmodel.DaggerElements.getAnyAnnotation;
-
-import com.google.auto.value.AutoValue;
-import com.google.auto.value.extension.memoized.Memoized;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import dagger.Component;
-import dagger.Subcomponent;
-import dagger.producers.ProducerModule;
-import dagger.producers.ProductionComponent;
-import dagger.producers.ProductionSubcomponent;
-import java.lang.annotation.Annotation;
-import java.util.Collection;
-import java.util.Optional;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * A {@code @Component}, {@code @Subcomponent}, {@code @ProductionComponent}, or
- * {@code @ProductionSubcomponent} annotation, or a {@code @Module} or {@code @ProducerModule}
- * annotation that is being treated as a component annotation when validating full binding graphs
- * for modules.
- */
-abstract class ComponentAnnotation {
- /** The root component annotation types. */
- private static final ImmutableSet<Class<? extends Annotation>> ROOT_COMPONENT_ANNOTATIONS =
- ImmutableSet.of(Component.class, ProductionComponent.class);
-
- /** The subcomponent annotation types. */
- private static final ImmutableSet<Class<? extends Annotation>> SUBCOMPONENT_ANNOTATIONS =
- ImmutableSet.of(Subcomponent.class, ProductionSubcomponent.class);
-
- /** All component annotation types. */
- private static final ImmutableSet<Class<? extends Annotation>> ALL_COMPONENT_ANNOTATIONS =
- ImmutableSet.<Class<? extends Annotation>>builder()
- .addAll(ROOT_COMPONENT_ANNOTATIONS)
- .addAll(SUBCOMPONENT_ANNOTATIONS)
- .build();
-
- /** The annotation itself. */
- abstract AnnotationMirror annotation();
-
- /** The simple name of the annotation type. */
- String simpleName() {
- return MoreAnnotationMirrors.simpleName(annotation()).toString();
- }
-
- /**
- * Returns {@code true} if the annotation is a {@code @Subcomponent} or
- * {@code @ProductionSubcomponent}.
- */
- abstract boolean isSubcomponent();
-
- /**
- * Returns {@code true} if the annotation is a {@code @ProductionComponent},
- * {@code @ProductionSubcomponent}, or {@code @ProducerModule}.
- */
- abstract boolean isProduction();
-
- /**
- * Returns {@code true} if the annotation is a real component annotation and not a module
- * annotation.
- */
- abstract boolean isRealComponent();
-
- /** The values listed as {@code dependencies}. */
- abstract ImmutableList<AnnotationValue> dependencyValues();
-
- /** The types listed as {@code dependencies}. */
- ImmutableList<TypeMirror> dependencyTypes() {
- return dependencyValues().stream().map(MoreAnnotationValues::asType).collect(toImmutableList());
- }
-
- /**
- * The types listed as {@code dependencies}.
- *
- * @throws IllegalArgumentException if any of {@link #dependencyTypes()} are error types
- */
- ImmutableList<TypeElement> dependencies() {
- return asTypeElements(dependencyTypes()).asList();
- }
-
- /** The values listed as {@code modules}. */
- abstract ImmutableList<AnnotationValue> moduleValues();
-
- /** The types listed as {@code modules}. */
- ImmutableList<TypeMirror> moduleTypes() {
- return moduleValues().stream().map(MoreAnnotationValues::asType).collect(toImmutableList());
- }
-
- /**
- * The types listed as {@code modules}.
- *
- * @throws IllegalArgumentException if any of {@link #moduleTypes()} are error types
- */
- ImmutableSet<TypeElement> modules() {
- return asTypeElements(moduleTypes());
- }
-
- protected final ImmutableList<AnnotationValue> getAnnotationValues(String parameterName) {
- return asAnnotationValues(getAnnotationValue(annotation(), parameterName));
- }
-
- /**
- * Returns an object representing a root component annotation, not a subcomponent annotation, if
- * one is present on {@code typeElement}.
- */
- static Optional<ComponentAnnotation> rootComponentAnnotation(TypeElement typeElement) {
- return anyComponentAnnotation(typeElement, ROOT_COMPONENT_ANNOTATIONS);
- }
-
- /**
- * Returns an object representing a subcomponent annotation, if one is present on {@code
- * typeElement}.
- */
- static Optional<ComponentAnnotation> subcomponentAnnotation(TypeElement typeElement) {
- return anyComponentAnnotation(typeElement, SUBCOMPONENT_ANNOTATIONS);
- }
-
- /**
- * Returns an object representing a root component or subcomponent annotation, if one is present
- * on {@code typeElement}.
- */
- static Optional<ComponentAnnotation> anyComponentAnnotation(TypeElement typeElement) {
- return anyComponentAnnotation(typeElement, ALL_COMPONENT_ANNOTATIONS);
- }
-
- private static Optional<ComponentAnnotation> anyComponentAnnotation(
- TypeElement typeElement, Collection<Class<? extends Annotation>> annotations) {
- return getAnyAnnotation(typeElement, annotations).map(ComponentAnnotation::componentAnnotation);
- }
-
- /** Returns {@code true} if the argument is a component annotation. */
- static boolean isComponentAnnotation(AnnotationMirror annotation) {
- return ALL_COMPONENT_ANNOTATIONS.stream()
- .anyMatch(annotationClass -> isTypeOf(annotationClass, annotation.getAnnotationType()));
- }
-
- /** Creates an object representing a component or subcomponent annotation. */
- static ComponentAnnotation componentAnnotation(AnnotationMirror annotation) {
- RealComponentAnnotation.Builder annotationBuilder =
- RealComponentAnnotation.builder().annotation(annotation);
-
- if (isTypeOf(Component.class, annotation.getAnnotationType())) {
- return annotationBuilder.isProduction(false).isSubcomponent(false).build();
- }
- if (isTypeOf(Subcomponent.class, annotation.getAnnotationType())) {
- return annotationBuilder.isProduction(false).isSubcomponent(true).build();
- }
- if (isTypeOf(ProductionComponent.class, annotation.getAnnotationType())) {
- return annotationBuilder.isProduction(true).isSubcomponent(false).build();
- }
- if (isTypeOf(ProductionSubcomponent.class, annotation.getAnnotationType())) {
- return annotationBuilder.isProduction(true).isSubcomponent(true).build();
- }
- throw new IllegalArgumentException(
- annotation
- + " must be a Component, Subcomponent, ProductionComponent, "
- + "or ProductionSubcomponent annotation");
- }
-
- /** Creates a fictional component annotation representing a module. */
- static ComponentAnnotation fromModuleAnnotation(ModuleAnnotation moduleAnnotation) {
- return new AutoValue_ComponentAnnotation_FictionalComponentAnnotation(moduleAnnotation);
- }
-
- /** The root component annotation types. */
- static ImmutableSet<Class<? extends Annotation>> rootComponentAnnotations() {
- return ROOT_COMPONENT_ANNOTATIONS;
- }
-
- /** The subcomponent annotation types. */
- static ImmutableSet<Class<? extends Annotation>> subcomponentAnnotations() {
- return SUBCOMPONENT_ANNOTATIONS;
- }
-
- /** All component annotation types. */
- static ImmutableSet<Class<? extends Annotation>> allComponentAnnotations() {
- return ALL_COMPONENT_ANNOTATIONS;
- }
-
- /**
- * An actual component annotation.
- *
- * @see FictionalComponentAnnotation
- */
- @AutoValue
- abstract static class RealComponentAnnotation extends ComponentAnnotation {
-
- @Override
- @Memoized
- ImmutableList<AnnotationValue> dependencyValues() {
- return isSubcomponent() ? ImmutableList.of() : getAnnotationValues("dependencies");
- }
-
- @Override
- @Memoized
- ImmutableList<TypeMirror> dependencyTypes() {
- return super.dependencyTypes();
- }
-
- @Override
- @Memoized
- ImmutableList<TypeElement> dependencies() {
- return super.dependencies();
- }
-
- @Override
- boolean isRealComponent() {
- return true;
- }
-
- @Override
- @Memoized
- ImmutableList<AnnotationValue> moduleValues() {
- return getAnnotationValues("modules");
- }
-
- @Override
- @Memoized
- ImmutableList<TypeMirror> moduleTypes() {
- return super.moduleTypes();
- }
-
- @Override
- @Memoized
- ImmutableSet<TypeElement> modules() {
- return super.modules();
- }
-
- static Builder builder() {
- return new AutoValue_ComponentAnnotation_RealComponentAnnotation.Builder();
- }
-
- @AutoValue.Builder
- interface Builder {
- Builder annotation(AnnotationMirror annotation);
-
- Builder isSubcomponent(boolean isSubcomponent);
-
- Builder isProduction(boolean isProduction);
-
- RealComponentAnnotation build();
- }
- }
-
- /**
- * A fictional component annotation used to represent modules or other collections of bindings as
- * a component.
- */
- @AutoValue
- abstract static class FictionalComponentAnnotation extends ComponentAnnotation {
-
- @Override
- AnnotationMirror annotation() {
- return moduleAnnotation().annotation();
- }
-
- @Override
- boolean isSubcomponent() {
- return false;
- }
-
- @Override
- boolean isProduction() {
- return moduleAnnotation().annotationClass().equals(ProducerModule.class);
- }
-
- @Override
- boolean isRealComponent() {
- return false;
- }
-
- @Override
- ImmutableList<AnnotationValue> dependencyValues() {
- return ImmutableList.of();
- }
-
- @Override
- ImmutableList<AnnotationValue> moduleValues() {
- return moduleAnnotation().includesAsAnnotationValues();
- }
-
- @Override
- @Memoized
- ImmutableList<TypeMirror> moduleTypes() {
- return super.moduleTypes();
- }
-
- @Override
- @Memoized
- ImmutableSet<TypeElement> modules() {
- return super.modules();
- }
-
- abstract ModuleAnnotation moduleAnnotation();
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentBindingExpressions.java b/java/dagger/internal/codegen/ComponentBindingExpressions.java
deleted file mode 100644
index 37cf1e8fa..000000000
--- a/java/dagger/internal/codegen/ComponentBindingExpressions.java
+++ /dev/null
@@ -1,713 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Verify.verify;
-import static dagger.internal.codegen.BindingRequest.bindingRequest;
-import static dagger.internal.codegen.BindingType.MEMBERS_INJECTION;
-import static dagger.internal.codegen.DelegateBindingExpression.isBindsScopeStrongerThanDependencyScope;
-import static dagger.internal.codegen.MemberSelect.staticFactoryCreation;
-import static dagger.internal.codegen.RequestKinds.isDerivedFromProvider;
-import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
-import static dagger.internal.codegen.javapoet.TypeNames.DOUBLE_CHECK;
-import static dagger.internal.codegen.javapoet.TypeNames.SINGLE_CHECK;
-import static dagger.internal.codegen.langmodel.Accessibility.isRawTypeAccessible;
-import static dagger.internal.codegen.langmodel.Accessibility.isRawTypePubliclyAccessible;
-import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
-import static dagger.model.BindingKind.DELEGATE;
-import static dagger.model.BindingKind.MULTIBOUND_MAP;
-import static dagger.model.BindingKind.MULTIBOUND_SET;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableList;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.MethodSpec;
-import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
-import dagger.internal.codegen.MethodBindingExpression.MethodImplementationStrategy;
-import dagger.internal.codegen.javapoet.Expression;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.DependencyRequest;
-import dagger.model.RequestKind;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import javax.inject.Inject;
-import javax.inject.Provider;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.type.TypeMirror;
-
-/** A central repository of code expressions used to access any binding available to a component. */
-@PerComponentImplementation
-final class ComponentBindingExpressions {
- // TODO(dpb,ronshapiro): refactor this and ComponentRequirementExpressions into a
- // HierarchicalComponentMap<K, V>, or perhaps this use a flattened ImmutableMap, built from its
- // parents? If so, maybe make BindingExpression.Factory create it.
-
- private final Optional<ComponentBindingExpressions> parent;
- private final BindingGraph graph;
- private final ComponentImplementation componentImplementation;
- private final ComponentRequirementExpressions componentRequirementExpressions;
- private final OptionalFactories optionalFactories;
- private final DaggerTypes types;
- private final DaggerElements elements;
- private final SourceVersion sourceVersion;
- private final CompilerOptions compilerOptions;
- private final MembersInjectionMethods membersInjectionMethods;
- private final InnerSwitchingProviders innerSwitchingProviders;
- private final ModifiableBindingExpressions modifiableBindingExpressions;
- private final Map<BindingRequest, BindingExpression> expressions = new HashMap<>();
-
- @Inject
- ComponentBindingExpressions(
- @ParentComponent Optional<ComponentBindingExpressions> parent,
- BindingGraph graph,
- ComponentImplementation componentImplementation,
- ComponentRequirementExpressions componentRequirementExpressions,
- OptionalFactories optionalFactories,
- DaggerTypes types,
- DaggerElements elements,
- SourceVersion sourceVersion,
- @GenerationCompilerOptions CompilerOptions compilerOptions) {
- this.parent = parent;
- this.graph = graph;
- this.componentImplementation = componentImplementation;
- this.componentRequirementExpressions = checkNotNull(componentRequirementExpressions);
- this.optionalFactories = checkNotNull(optionalFactories);
- this.types = checkNotNull(types);
- this.elements = checkNotNull(elements);
- this.sourceVersion = checkNotNull(sourceVersion);
- this.compilerOptions = checkNotNull(compilerOptions);
- this.membersInjectionMethods =
- new MembersInjectionMethods(componentImplementation, this, graph, elements, types);
- this.innerSwitchingProviders =
- new InnerSwitchingProviders(componentImplementation, this, types);
- this.modifiableBindingExpressions =
- new ModifiableBindingExpressions(
- parent.map(cbe -> cbe.modifiableBindingExpressions),
- this,
- graph,
- componentImplementation,
- compilerOptions,
- types);
- }
-
- /* Returns the {@link ModifiableBindingExpressions} for this component. */
- ModifiableBindingExpressions modifiableBindingExpressions() {
- return modifiableBindingExpressions;
- }
-
- /**
- * Returns an expression that evaluates to the value of a binding request for a binding owned by
- * this component or an ancestor.
- *
- * @param requestingClass the class that will contain the expression
- * @throws IllegalStateException if there is no binding expression that satisfies the request
- */
- Expression getDependencyExpression(BindingRequest request, ClassName requestingClass) {
- return getBindingExpression(request).getDependencyExpression(requestingClass);
- }
-
- /**
- * Equivalent to {@link #getDependencyExpression(BindingRequest, ClassName)} that is used only
- * when the request is for implementation of a component method.
- *
- * @throws IllegalStateException if there is no binding expression that satisfies the request
- */
- Expression getDependencyExpressionForComponentMethod(
- BindingRequest request,
- ComponentMethodDescriptor componentMethod,
- ComponentImplementation componentImplementation) {
- return getBindingExpression(request)
- .getDependencyExpressionForComponentMethod(componentMethod, componentImplementation);
- }
-
- /**
- * Returns the {@link CodeBlock} for the method arguments used with the factory {@code create()}
- * method for the given {@link ContributionBinding binding}.
- */
- CodeBlock getCreateMethodArgumentsCodeBlock(ContributionBinding binding) {
- return makeParametersCodeBlock(getCreateMethodArgumentsCodeBlocks(binding));
- }
-
- private ImmutableList<CodeBlock> getCreateMethodArgumentsCodeBlocks(ContributionBinding binding) {
- ImmutableList.Builder<CodeBlock> arguments = ImmutableList.builder();
-
- if (binding.requiresModuleInstance()) {
- arguments.add(
- componentRequirementExpressions.getExpressionDuringInitialization(
- ComponentRequirement.forModule(binding.contributingModule().get().asType()),
- componentImplementation.name()));
- }
-
- binding.frameworkDependencies().stream()
- .map(BindingRequest::bindingRequest)
- .map(request -> getDependencyExpression(request, componentImplementation.name()))
- .map(Expression::codeBlock)
- .forEach(arguments::add);
-
- return arguments.build();
- }
-
- /**
- * Returns an expression that evaluates to the value of a dependency request, for passing to a
- * binding method, an {@code @Inject}-annotated constructor or member, or a proxy for one.
- *
- * <p>If the method is a generated static {@link InjectionMethods injection method}, each
- * parameter will be {@link Object} if the dependency's raw type is inaccessible. If that is the
- * case for this dependency, the returned expression will use a cast to evaluate to the raw type.
- *
- * @param requestingClass the class that will contain the expression
- */
- Expression getDependencyArgumentExpression(
- DependencyRequest dependencyRequest, ClassName requestingClass) {
-
- TypeMirror dependencyType = dependencyRequest.key().type();
- BindingRequest bindingRequest = bindingRequest(dependencyRequest);
- Expression dependencyExpression = getDependencyExpression(bindingRequest, requestingClass);
-
- if (compilerOptions.aheadOfTimeSubcomponents()) {
- TypeMirror requestedType =
- bindingRequest.requestedType(dependencyRequest.key().type(), types);
- // If dependencyExpression.type() has been erased to it's publicly accessible type in AOT,
- // we must sometimes cast the expression so that it is usable in the current component. To do
- // so, we check that without the cast the assignment would fail, that argument to this proxy
- // method erased the type, and that the raw type of the requested type is actually accessible
- // in the current class so that the cast is valid.
- if (!types.isAssignable(dependencyExpression.type(), requestedType)
- && !isRawTypePubliclyAccessible(requestedType)
- && isRawTypeAccessible(requestedType, requestingClass.packageName())) {
- return dependencyExpression.castTo(types.erasure(requestedType));
- }
- }
-
- if (dependencyRequest.kind().equals(RequestKind.INSTANCE)
- && !isTypeAccessibleFrom(dependencyType, requestingClass.packageName())
- && isRawTypeAccessible(dependencyType, requestingClass.packageName())) {
- return dependencyExpression.castTo(types.erasure(dependencyType));
- }
-
- return dependencyExpression;
- }
-
- /** Returns the implementation of a component method. */
- MethodSpec getComponentMethod(ComponentMethodDescriptor componentMethod) {
- checkArgument(componentMethod.dependencyRequest().isPresent());
- BindingRequest request = bindingRequest(componentMethod.dependencyRequest().get());
- MethodSpec.Builder method =
- MethodSpec.overriding(
- componentMethod.methodElement(),
- MoreTypes.asDeclared(graph.componentTypeElement().asType()),
- types);
- // Even though this is not used if the method is abstract, we need to invoke the binding
- // expression in order for the side of effect of the method being added to the
- // ComponentImplementation
- CodeBlock methodBody =
- getBindingExpression(request)
- .getComponentMethodImplementation(componentMethod, componentImplementation);
- if (!componentImplementation.superclassImplementation().isPresent()
- && !modifiableBindingExpressions
- .getModifiableBindingType(request)
- .hasBaseClassImplementation()
- && !componentImplementation.getModifiableBindingMethod(request).isPresent()) {
- return method.addModifiers(ABSTRACT).build();
- }
- return method.addCode(methodBody).build();
- }
-
- /** Returns the {@link BindingExpression} for the given {@link BindingRequest}. */
- BindingExpression getBindingExpression(BindingRequest request) {
- if (expressions.containsKey(request)) {
- return expressions.get(request);
- }
- Optional<BindingExpression> expression =
- modifiableBindingExpressions.maybeCreateModifiableBindingExpression(request);
- if (!expression.isPresent()) {
- ResolvedBindings resolvedBindings = graph.resolvedBindings(request);
- if (resolvedBindings != null
- && !resolvedBindings.bindingsOwnedBy(graph.componentDescriptor()).isEmpty()) {
- expression = Optional.of(createBindingExpression(resolvedBindings, request));
- }
- }
- if (!expression.isPresent()
- && compilerOptions.aheadOfTimeSubcomponents()
- && request.requestKind().isPresent()
- && isDerivedFromProvider(request.requestKind().get())) {
- RequestKind requestKind = request.requestKind().get();
- expression =
- Optional.of(
- new DerivedFromFrameworkInstanceBindingExpression(
- request.key(), FrameworkType.PROVIDER, requestKind, this, types));
- }
-
- if (expression.isPresent()) {
- expressions.put(request, expression.get());
- return expression.get();
- }
- checkArgument(parent.isPresent(), "no expression found for %s", request);
- return parent.get().getBindingExpression(request);
- }
-
- /** Creates a binding expression. */
- BindingExpression createBindingExpression(
- ResolvedBindings resolvedBindings, BindingRequest request) {
- switch (resolvedBindings.bindingType()) {
- case MEMBERS_INJECTION:
- checkArgument(request.isRequestKind(RequestKind.MEMBERS_INJECTION));
- return new MembersInjectionBindingExpression(resolvedBindings, membersInjectionMethods);
-
- case PROVISION:
- return provisionBindingExpression(resolvedBindings, request);
-
- case PRODUCTION:
- return productionBindingExpression(resolvedBindings, request);
- }
- throw new AssertionError(resolvedBindings);
- }
-
- /**
- * Returns a binding expression that uses a {@link javax.inject.Provider} for provision bindings
- * or a {@link dagger.producers.Producer} for production bindings.
- */
- private BindingExpression frameworkInstanceBindingExpression(ResolvedBindings resolvedBindings) {
- // TODO(user): Consider merging the static factory creation logic into CreationExpressions?
- Optional<MemberSelect> staticMethod =
- useStaticFactoryCreation(resolvedBindings.contributionBinding())
- ? staticFactoryCreation(resolvedBindings)
- : Optional.empty();
- FrameworkInstanceCreationExpression frameworkInstanceCreationExpression =
- resolvedBindings.scope().isPresent()
- ? scope(resolvedBindings, frameworkInstanceCreationExpression(resolvedBindings))
- : frameworkInstanceCreationExpression(resolvedBindings);
- FrameworkInstanceSupplier frameworkInstanceSupplier =
- staticMethod.isPresent()
- ? staticMethod::get
- : new FrameworkFieldInitializer(
- componentImplementation, resolvedBindings, frameworkInstanceCreationExpression);
-
- switch (resolvedBindings.bindingType()) {
- case PROVISION:
- return new ProviderInstanceBindingExpression(
- resolvedBindings, frameworkInstanceSupplier, types, elements);
- case PRODUCTION:
- return new ProducerNodeInstanceBindingExpression(
- resolvedBindings, frameworkInstanceSupplier, types, elements, componentImplementation);
- default:
- throw new AssertionError("invalid binding type: " + resolvedBindings.bindingType());
- }
- }
-
- private FrameworkInstanceCreationExpression scope(
- ResolvedBindings resolvedBindings, FrameworkInstanceCreationExpression unscoped) {
- return () ->
- CodeBlock.of(
- "$T.provider($L)",
- resolvedBindings.scope().get().isReusable() ? SINGLE_CHECK : DOUBLE_CHECK,
- unscoped.creationExpression());
- }
-
- /**
- * Returns a creation expression for a {@link javax.inject.Provider} for provision bindings or a
- * {@link dagger.producers.Producer} for production bindings.
- */
- private FrameworkInstanceCreationExpression frameworkInstanceCreationExpression(
- ResolvedBindings resolvedBindings) {
- checkArgument(!resolvedBindings.bindingType().equals(MEMBERS_INJECTION));
- ContributionBinding binding = resolvedBindings.contributionBinding();
- switch (binding.kind()) {
- case COMPONENT:
- // The cast can be removed when we drop java 7 source support
- return new InstanceFactoryCreationExpression(
- () -> CodeBlock.of("($T) this", binding.key().type()));
-
- case BOUND_INSTANCE:
- return instanceFactoryCreationExpression(
- binding, ComponentRequirement.forBoundInstance(binding));
-
- case COMPONENT_DEPENDENCY:
- return instanceFactoryCreationExpression(
- binding, ComponentRequirement.forDependency(binding.key().type()));
-
- case COMPONENT_PROVISION:
- return new DependencyMethodProviderCreationExpression(
- binding,
- componentImplementation,
- componentRequirementExpressions,
- compilerOptions,
- graph);
-
- case SUBCOMPONENT_CREATOR:
- return new AnonymousProviderCreationExpression(
- binding, this, componentImplementation.name());
-
- case INJECTION:
- case PROVISION:
- return new InjectionOrProvisionProviderCreationExpression(binding, this);
-
- case COMPONENT_PRODUCTION:
- return new DependencyMethodProducerCreationExpression(
- binding, componentImplementation, componentRequirementExpressions, graph);
-
- case PRODUCTION:
- return new ProducerCreationExpression(binding, this);
-
- case MULTIBOUND_SET:
- return new SetFactoryCreationExpression(binding, componentImplementation, this, graph);
-
- case MULTIBOUND_MAP:
- return new MapFactoryCreationExpression(
- binding, componentImplementation, this, graph, elements);
-
- case DELEGATE:
- return new DelegatingFrameworkInstanceCreationExpression(
- binding, componentImplementation, this);
-
- case OPTIONAL:
- return new OptionalFactoryInstanceCreationExpression(
- optionalFactories, binding, componentImplementation, this);
-
- case MEMBERS_INJECTOR:
- return new MembersInjectorProviderCreationExpression((ProvisionBinding) binding, this);
-
- default:
- throw new AssertionError(binding);
- }
- }
-
- private InstanceFactoryCreationExpression instanceFactoryCreationExpression(
- ContributionBinding binding, ComponentRequirement componentRequirement) {
- return new InstanceFactoryCreationExpression(
- binding.nullableType().isPresent(),
- () ->
- componentRequirementExpressions.getExpressionDuringInitialization(
- componentRequirement, componentImplementation.name()));
- }
-
- /** Returns a binding expression for a provision binding. */
- private BindingExpression provisionBindingExpression(
- ResolvedBindings resolvedBindings, BindingRequest request) {
- if (!request.requestKind().isPresent()) {
- verify(
- request.frameworkType().get().equals(FrameworkType.PRODUCER_NODE),
- "expected a PRODUCER_NODE: %s",
- request);
- return producerFromProviderBindingExpression(resolvedBindings);
- }
- RequestKind requestKind = request.requestKind().get();
- switch (requestKind) {
- case INSTANCE:
- return instanceBindingExpression(resolvedBindings);
-
- case PROVIDER:
- return providerBindingExpression(resolvedBindings);
-
- case LAZY:
- case PRODUCED:
- case PROVIDER_OF_LAZY:
- return new DerivedFromFrameworkInstanceBindingExpression(
- resolvedBindings.key(), FrameworkType.PROVIDER, requestKind, this, types);
-
- case PRODUCER:
- return producerFromProviderBindingExpression(resolvedBindings);
-
- case FUTURE:
- return new ImmediateFutureBindingExpression(resolvedBindings, this, types, sourceVersion);
-
- case MEMBERS_INJECTION:
- throw new IllegalArgumentException();
- }
-
- throw new AssertionError();
- }
-
- /** Returns a binding expression for a production binding. */
- private BindingExpression productionBindingExpression(
- ResolvedBindings resolvedBindings, BindingRequest request) {
- if (request.frameworkType().isPresent()) {
- return frameworkInstanceBindingExpression(resolvedBindings);
- } else {
- // If no FrameworkType is present, a RequestKind is guaranteed to be present.
- RequestKind requestKind = request.requestKind().get();
- return new DerivedFromFrameworkInstanceBindingExpression(
- resolvedBindings.key(), FrameworkType.PRODUCER_NODE, requestKind, this, types);
- }
- }
-
- /**
- * Returns a binding expression for {@link RequestKind#PROVIDER} requests.
- *
- * <p>{@code @Binds} bindings that don't {@linkplain #needsCaching(ResolvedBindings) need to be
- * cached} can use a {@link DelegateBindingExpression}.
- *
- * <p>In fastInit mode, use an {@link InnerSwitchingProviders inner switching provider} unless
- * that provider's case statement will simply call {@code get()} on another {@link Provider} (in
- * which case, just use that Provider directly).
- *
- * <p>Otherwise, return a {@link FrameworkInstanceBindingExpression}.
- */
- private BindingExpression providerBindingExpression(ResolvedBindings resolvedBindings) {
- if (resolvedBindings.contributionBinding().kind().equals(DELEGATE)
- && !needsCaching(resolvedBindings)) {
- return new DelegateBindingExpression(
- resolvedBindings, RequestKind.PROVIDER, this, types, elements);
- } else if (compilerOptions.fastInit()
- && frameworkInstanceCreationExpression(resolvedBindings).useInnerSwitchingProvider()
- && !(instanceBindingExpression(resolvedBindings)
- instanceof DerivedFromFrameworkInstanceBindingExpression)) {
- return wrapInMethod(
- resolvedBindings,
- bindingRequest(resolvedBindings.key(), RequestKind.PROVIDER),
- innerSwitchingProviders.newBindingExpression(resolvedBindings.contributionBinding()));
- }
- return frameworkInstanceBindingExpression(resolvedBindings);
- }
-
- /**
- * Returns a binding expression that uses a {@link dagger.producers.Producer} field for a
- * provision binding.
- */
- private FrameworkInstanceBindingExpression producerFromProviderBindingExpression(
- ResolvedBindings resolvedBindings) {
- checkArgument(resolvedBindings.bindingType().equals(BindingType.PROVISION));
- return new ProducerNodeInstanceBindingExpression(
- resolvedBindings,
- new FrameworkFieldInitializer(
- componentImplementation,
- resolvedBindings,
- new ProducerFromProviderCreationExpression(
- resolvedBindings.contributionBinding(), componentImplementation, this)),
- types,
- elements,
- componentImplementation);
- }
-
- /**
- * Returns a binding expression for {@link RequestKind#INSTANCE} requests.
- *
- * <p>If there is a direct expression (not calling {@link Provider#get()}) we can use for an
- * instance of this binding, return it, wrapped in a method if the binding {@linkplain
- * #needsCaching(ResolvedBindings) needs to be cached} or the expression has dependencies.
- *
- * <p>In fastInit mode, we can use direct expressions unless the binding needs to be cached.
- */
- private BindingExpression instanceBindingExpression(ResolvedBindings resolvedBindings) {
- Optional<BindingExpression> maybeDirectInstanceExpression =
- unscopedDirectInstanceExpression(resolvedBindings);
- if (canUseDirectInstanceExpression(resolvedBindings)
- && maybeDirectInstanceExpression.isPresent()) {
- BindingExpression directInstanceExpression = maybeDirectInstanceExpression.get();
- return directInstanceExpression.requiresMethodEncapsulation()
- || needsCaching(resolvedBindings)
- ? wrapInMethod(
- resolvedBindings,
- bindingRequest(resolvedBindings.key(), RequestKind.INSTANCE),
- directInstanceExpression)
- : directInstanceExpression;
- }
- return new DerivedFromFrameworkInstanceBindingExpression(
- resolvedBindings.key(), FrameworkType.PROVIDER, RequestKind.INSTANCE, this, types);
- }
-
- /**
- * Returns an unscoped binding expression for an {@link RequestKind#INSTANCE} that does not call
- * {@code get()} on its provider, if there is one.
- */
- private Optional<BindingExpression> unscopedDirectInstanceExpression(
- ResolvedBindings resolvedBindings) {
- switch (resolvedBindings.contributionBinding().kind()) {
- case DELEGATE:
- return Optional.of(
- new DelegateBindingExpression(
- resolvedBindings, RequestKind.INSTANCE, this, types, elements));
-
- case COMPONENT:
- return Optional.of(
- new ComponentInstanceBindingExpression(
- resolvedBindings, componentImplementation.name()));
-
- case COMPONENT_DEPENDENCY:
- return Optional.of(
- new ComponentRequirementBindingExpression(
- resolvedBindings,
- ComponentRequirement.forDependency(resolvedBindings.key().type()),
- componentRequirementExpressions));
-
- case COMPONENT_PROVISION:
- return Optional.of(
- new ComponentProvisionBindingExpression(
- resolvedBindings, graph, componentRequirementExpressions, compilerOptions));
-
- case SUBCOMPONENT_CREATOR:
- return Optional.of(
- new SubcomponentCreatorBindingExpression(
- resolvedBindings,
- componentImplementation.getSubcomponentCreatorSimpleName(resolvedBindings.key())));
-
- case MULTIBOUND_SET:
- return Optional.of(
- new SetBindingExpression(
- resolvedBindings, componentImplementation, graph, this, types, elements));
-
- case MULTIBOUND_MAP:
- return Optional.of(
- new MapBindingExpression(
- resolvedBindings, componentImplementation, graph, this, types, elements));
-
- case OPTIONAL:
- return Optional.of(
- new OptionalBindingExpression(resolvedBindings, this, types, sourceVersion));
-
- case BOUND_INSTANCE:
- return Optional.of(
- new ComponentRequirementBindingExpression(
- resolvedBindings,
- ComponentRequirement.forBoundInstance(resolvedBindings.contributionBinding()),
- componentRequirementExpressions));
-
- case INJECTION:
- case PROVISION:
- return Optional.of(
- new SimpleMethodBindingExpression(
- resolvedBindings,
- compilerOptions,
- this,
- membersInjectionMethods,
- componentRequirementExpressions,
- types,
- elements,
- sourceVersion));
-
- case MEMBERS_INJECTOR:
- return Optional.empty();
-
- case MEMBERS_INJECTION:
- case COMPONENT_PRODUCTION:
- case PRODUCTION:
- throw new IllegalArgumentException(
- resolvedBindings.contributionBinding().kind().toString());
- }
- throw new AssertionError();
- }
-
- /**
- * Returns {@code true} if the binding should use the static factory creation strategy.
- *
- * <p>In default mode, we always use the static factory creation strategy. In fastInit mode, we
- * prefer to use a SwitchingProvider instead of static factories in order to reduce class loading;
- * however, we allow static factories that can reused across multiple bindings, e.g. {@code
- * MapFactory} or {@code SetFactory}.
- */
- private boolean useStaticFactoryCreation(ContributionBinding binding) {
- return !compilerOptions.fastInit()
- || binding.kind().equals(MULTIBOUND_MAP)
- || binding.kind().equals(MULTIBOUND_SET);
- }
-
- /**
- * Returns {@code true} if we can use a direct (not {@code Provider.get()}) expression for this
- * binding. If the binding doesn't {@linkplain #needsCaching(ResolvedBindings) need to be cached},
- * we can.
- *
- * <p>In fastInit mode, we can use a direct expression even if the binding {@linkplain
- * #needsCaching(ResolvedBindings) needs to be cached}.
- */
- private boolean canUseDirectInstanceExpression(ResolvedBindings resolvedBindings) {
- return !needsCaching(resolvedBindings) || compilerOptions.fastInit();
- }
-
- /**
- * Returns a binding expression that uses a given one as the body of a method that users call. If
- * a component provision method matches it, it will be the method implemented. If it does not
- * match a component provision method and the binding is modifiable, then a new public modifiable
- * binding method will be written. If the binding doesn't match a component method and is not
- * modifiable, then a new private method will be written.
- */
- BindingExpression wrapInMethod(
- ResolvedBindings resolvedBindings,
- BindingRequest request,
- BindingExpression bindingExpression) {
- // If we've already wrapped the expression, then use the delegate.
- if (bindingExpression instanceof MethodBindingExpression) {
- return bindingExpression;
- }
-
- MethodImplementationStrategy methodImplementationStrategy =
- methodImplementationStrategy(resolvedBindings, request);
- Optional<ComponentMethodDescriptor> matchingComponentMethod =
- graph.componentDescriptor().firstMatchingComponentMethod(request);
-
- if (modifiableBindingExpressions.getModifiableBindingType(request).isModifiable()
- && (componentImplementation.superclassImplementation().isPresent()
- || !matchingComponentMethod.isPresent())) {
- return modifiableBindingExpressions.wrapInModifiableMethodBindingExpression(
- request, resolvedBindings, methodImplementationStrategy, bindingExpression);
- } else if (matchingComponentMethod.isPresent()) {
- ComponentMethodDescriptor componentMethod = matchingComponentMethod.get();
- return new ComponentMethodBindingExpression(
- request,
- resolvedBindings,
- methodImplementationStrategy,
- bindingExpression,
- componentImplementation,
- componentMethod,
- types);
- } else {
- return new PrivateMethodBindingExpression(
- request,
- resolvedBindings,
- methodImplementationStrategy,
- bindingExpression,
- componentImplementation,
- types);
- }
- }
-
- private MethodImplementationStrategy methodImplementationStrategy(
- ResolvedBindings resolvedBindings, BindingRequest request) {
- if (compilerOptions.fastInit()) {
- if (request.isRequestKind(RequestKind.PROVIDER)) {
- return MethodImplementationStrategy.SINGLE_CHECK;
- } else if (request.isRequestKind(RequestKind.INSTANCE) && needsCaching(resolvedBindings)) {
- return resolvedBindings.scope().get().isReusable()
- ? MethodImplementationStrategy.SINGLE_CHECK
- : MethodImplementationStrategy.DOUBLE_CHECK;
- }
- }
- return MethodImplementationStrategy.SIMPLE;
- }
-
- /**
- * Returns {@code true} if the component needs to make sure the provided value is cached.
- *
- * <p>The component needs to cache the value for scoped bindings except for {@code @Binds}
- * bindings whose scope is no stronger than their delegate's.
- */
- private boolean needsCaching(ResolvedBindings resolvedBindings) {
- if (!resolvedBindings.scope().isPresent()) {
- return false;
- }
- if (resolvedBindings.contributionBinding().kind().equals(DELEGATE)) {
- return isBindsScopeStrongerThanDependencyScope(resolvedBindings, graph);
- }
- return true;
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentCreatorAnnotation.java b/java/dagger/internal/codegen/ComponentCreatorAnnotation.java
deleted file mode 100644
index 31bbfab34..000000000
--- a/java/dagger/internal/codegen/ComponentCreatorAnnotation.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2019 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.common.base.Ascii.toUpperCase;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.DaggerStreams.valuesOf;
-import static java.util.stream.Collectors.mapping;
-
-import com.google.common.collect.ImmutableSet;
-import dagger.Component;
-import dagger.Subcomponent;
-import dagger.producers.ProductionComponent;
-import dagger.producers.ProductionSubcomponent;
-import java.lang.annotation.Annotation;
-import java.util.stream.Collector;
-import java.util.stream.Stream;
-import javax.lang.model.element.TypeElement;
-
-/** Simple representation of a component creator annotation type. */
-enum ComponentCreatorAnnotation {
- COMPONENT_BUILDER(Component.Builder.class),
- COMPONENT_FACTORY(Component.Factory.class),
- SUBCOMPONENT_BUILDER(Subcomponent.Builder.class),
- SUBCOMPONENT_FACTORY(Subcomponent.Factory.class),
- PRODUCTION_COMPONENT_BUILDER(ProductionComponent.Builder.class),
- PRODUCTION_COMPONENT_FACTORY(ProductionComponent.Factory.class),
- PRODUCTION_SUBCOMPONENT_BUILDER(ProductionSubcomponent.Builder.class),
- PRODUCTION_SUBCOMPONENT_FACTORY(ProductionSubcomponent.Factory.class),
- ;
-
- private final Class<? extends Annotation> annotation;
- private final ComponentCreatorKind creatorKind;
- private final Class<? extends Annotation> componentAnnotation;
-
- ComponentCreatorAnnotation(Class<? extends Annotation> annotation) {
- this.annotation = annotation;
- this.creatorKind = ComponentCreatorKind.valueOf(toUpperCase(annotation.getSimpleName()));
- this.componentAnnotation = (Class<? extends Annotation>) annotation.getEnclosingClass();
- }
-
- /** The actual annotation type. */
- Class<? extends Annotation> annotation() {
- return annotation;
- }
-
- /** The component annotation type that encloses this creator annotation type. */
- final Class<? extends Annotation> componentAnnotation() {
- return componentAnnotation;
- }
-
- /** Returns {@code true} if the creator annotation is for a subcomponent. */
- final boolean isSubcomponentCreatorAnnotation() {
- return componentAnnotation().getSimpleName().endsWith("Subcomponent");
- }
-
- /**
- * Returns {@code true} if the creator annotation is for a production component or subcomponent.
- */
- final boolean isProductionCreatorAnnotation() {
- return componentAnnotation().getSimpleName().startsWith("Production");
- }
-
- /** The creator kind the annotation is associated with. */
- // TODO(dpb): Remove ComponentCreatorKind.
- ComponentCreatorKind creatorKind() {
- return creatorKind;
- }
-
- @Override
- public final String toString() {
- return annotation().getName();
- }
-
- /** Returns all component creator annotations. */
- static ImmutableSet<Class<? extends Annotation>> allCreatorAnnotations() {
- return stream().collect(toAnnotationClasses());
- }
-
- /** Returns all root component creator annotations. */
- static ImmutableSet<Class<? extends Annotation>> rootComponentCreatorAnnotations() {
- return stream()
- .filter(
- componentCreatorAnnotation ->
- !componentCreatorAnnotation.isSubcomponentCreatorAnnotation())
- .collect(toAnnotationClasses());
- }
-
- /** Returns all subcomponent creator annotations. */
- static ImmutableSet<Class<? extends Annotation>> subcomponentCreatorAnnotations() {
- return stream()
- .filter(
- componentCreatorAnnotation ->
- componentCreatorAnnotation.isSubcomponentCreatorAnnotation())
- .collect(toAnnotationClasses());
- }
-
- /** Returns all production component creator annotations. */
- static ImmutableSet<Class<? extends Annotation>> productionCreatorAnnotations() {
- return stream()
- .filter(
- componentCreatorAnnotation ->
- componentCreatorAnnotation.isProductionCreatorAnnotation())
- .collect(toAnnotationClasses());
- }
-
- /** Returns the legal creator annotations for the given {@code componentAnnotation}. */
- static ImmutableSet<Class<? extends Annotation>> creatorAnnotationsFor(
- ComponentAnnotation componentAnnotation) {
- return stream()
- .filter(
- creatorAnnotation ->
- creatorAnnotation
- .componentAnnotation()
- .getSimpleName()
- .equals(componentAnnotation.simpleName()))
- .collect(toAnnotationClasses());
- }
-
- /** Returns all creator annotations present on the given {@code type}. */
- static ImmutableSet<ComponentCreatorAnnotation> getCreatorAnnotations(TypeElement type) {
- return stream()
- .filter(cca -> isAnnotationPresent(type, cca.annotation()))
- .collect(toImmutableSet());
- }
-
- private static Stream<ComponentCreatorAnnotation> stream() {
- return valuesOf(ComponentCreatorAnnotation.class);
- }
-
- private static Collector<ComponentCreatorAnnotation, ?, ImmutableSet<Class<? extends Annotation>>>
- toAnnotationClasses() {
- return mapping(ComponentCreatorAnnotation::annotation, toImmutableSet());
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentCreatorDescriptor.java b/java/dagger/internal/codegen/ComponentCreatorDescriptor.java
deleted file mode 100644
index f6ec7a23b..000000000
--- a/java/dagger/internal/codegen/ComponentCreatorDescriptor.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.auto.common.MoreTypes.asTypeElement;
-import static com.google.common.base.Verify.verify;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.ComponentCreatorAnnotation.getCreatorAnnotations;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.ModuleAnnotation.moduleAnnotation;
-
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.auto.value.extension.memoized.Memoized;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
-import dagger.BindsInstance;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.DependencyRequest;
-import java.util.List;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * A descriptor for a component <i>creator</i> type: that is, a type annotated with
- * {@code @Component.Builder} (or one of the corresponding production or subcomponent versions).
- */
-@AutoValue
-abstract class ComponentCreatorDescriptor {
-
- /** Returns the annotation marking this creator. */
- abstract ComponentCreatorAnnotation annotation();
-
- /** The kind of this creator. */
- final ComponentCreatorKind kind() {
- return annotation().creatorKind();
- }
-
- /** The annotated creator type. */
- abstract TypeElement typeElement();
-
- /** The method that creates and returns a component instance. */
- abstract ExecutableElement factoryMethod();
-
- /**
- * Multimap of component requirements to setter methods that set that requirement.
- *
- * <p>In a valid creator, there will be exactly one element per component requirement, so this
- * method should only be called when validating the descriptor.
- */
- abstract ImmutableSetMultimap<ComponentRequirement, ExecutableElement> unvalidatedSetterMethods();
-
- /**
- * Multimap of component requirements to factory method parameters that set that requirement.
- *
- * <p>In a valid creator, there will be exactly one element per component requirement, so this
- * method should only be called when validating the descriptor.
- */
- abstract ImmutableSetMultimap<ComponentRequirement, VariableElement>
- unvalidatedFactoryParameters();
-
- /**
- * Multimap of component requirements to elements (methods or parameters) that set that
- * requirement.
- *
- * <p>In a valid creator, there will be exactly one element per component requirement, so this
- * method should only be called when validating the descriptor.
- */
- final ImmutableSetMultimap<ComponentRequirement, Element> unvalidatedRequirementElements() {
- // ComponentCreatorValidator ensures that there are either setter methods or factory method
- // parameters, but not both, so we can cheat a little here since we know that only one of
- // the two multimaps will be non-empty.
- return ImmutableSetMultimap.copyOf( // no actual copy
- unvalidatedSetterMethods().isEmpty()
- ? unvalidatedFactoryParameters()
- : unvalidatedSetterMethods());
- }
-
- /**
- * Map of component requirements to elements (setter methods or factory method parameters) that
- * set them.
- */
- @Memoized
- ImmutableMap<ComponentRequirement, Element> requirementElements() {
- return flatten(unvalidatedRequirementElements());
- }
-
- /** Map of component requirements to setter methods for those requirements. */
- @Memoized
- ImmutableMap<ComponentRequirement, ExecutableElement> setterMethods() {
- return flatten(unvalidatedSetterMethods());
- }
-
- /** Map of component requirements to factory method parameters for those requirements. */
- @Memoized
- ImmutableMap<ComponentRequirement, VariableElement> factoryParameters() {
- return flatten(unvalidatedFactoryParameters());
- }
-
- private static <K, V> ImmutableMap<K, V> flatten(Multimap<K, V> multimap) {
- return ImmutableMap.copyOf(
- Maps.transformValues(multimap.asMap(), values -> getOnlyElement(values)));
- }
-
- /** Returns the set of component requirements this creator allows the user to set. */
- final ImmutableSet<ComponentRequirement> userSettableRequirements() {
- // Note: they should have been validated at the point this is used, so this set is valid.
- return unvalidatedRequirementElements().keySet();
- }
-
- /** Returns the set of requirements for modules and component dependencies for this creator. */
- final ImmutableSet<ComponentRequirement> moduleAndDependencyRequirements() {
- return userSettableRequirements().stream()
- .filter(requirement -> !requirement.isBoundInstance())
- .collect(toImmutableSet());
- }
-
- /** Returns the set of bound instance requirements for this creator. */
- final ImmutableSet<ComponentRequirement> boundInstanceRequirements() {
- return userSettableRequirements().stream()
- .filter(ComponentRequirement::isBoundInstance)
- .collect(toImmutableSet());
- }
-
- /** Returns the element in this creator that sets the given {@code requirement}. */
- final Element elementForRequirement(ComponentRequirement requirement) {
- return requirementElements().get(requirement);
- }
-
- /** Creates a new {@link ComponentCreatorDescriptor} for the given creator {@code type}. */
- static ComponentCreatorDescriptor create(
- DeclaredType type,
- DaggerElements elements,
- DaggerTypes types,
- DependencyRequestFactory dependencyRequestFactory) {
- TypeElement typeElement = asTypeElement(type);
- TypeMirror componentType = typeElement.getEnclosingElement().asType();
-
- ImmutableSetMultimap.Builder<ComponentRequirement, ExecutableElement> setterMethods =
- ImmutableSetMultimap.builder();
-
- ExecutableElement factoryMethod = null;
- for (ExecutableElement method : elements.getUnimplementedMethods(typeElement)) {
- ExecutableType resolvedMethodType = MoreTypes.asExecutable(types.asMemberOf(type, method));
-
- if (types.isSubtype(componentType, resolvedMethodType.getReturnType())) {
- factoryMethod = method;
- } else {
- VariableElement parameter = getOnlyElement(method.getParameters());
- TypeMirror parameterType = getOnlyElement(resolvedMethodType.getParameterTypes());
- setterMethods.put(
- requirement(method, parameter, parameterType, dependencyRequestFactory, method),
- method);
- }
- }
- verify(factoryMethod != null); // validation should have ensured this.
-
- ImmutableSetMultimap.Builder<ComponentRequirement, VariableElement> factoryParameters =
- ImmutableSetMultimap.builder();
-
- ExecutableType resolvedFactoryMethodType =
- MoreTypes.asExecutable(types.asMemberOf(type, factoryMethod));
- List<? extends VariableElement> parameters = factoryMethod.getParameters();
- List<? extends TypeMirror> parameterTypes = resolvedFactoryMethodType.getParameterTypes();
- for (int i = 0; i < parameters.size(); i++) {
- VariableElement parameter = parameters.get(i);
- TypeMirror parameterType = parameterTypes.get(i);
- factoryParameters.put(
- requirement(factoryMethod, parameter, parameterType, dependencyRequestFactory, parameter),
- parameter);
- }
-
- // Validation should have ensured exactly one creator annotation is present on the type.
- ComponentCreatorAnnotation annotation = getOnlyElement(getCreatorAnnotations(typeElement));
- return new AutoValue_ComponentCreatorDescriptor(
- annotation, typeElement, factoryMethod, setterMethods.build(), factoryParameters.build());
- }
-
- private static ComponentRequirement requirement(
- ExecutableElement method,
- VariableElement parameter,
- TypeMirror type,
- DependencyRequestFactory dependencyRequestFactory,
- Element elementForVariableName) {
- if (isAnnotationPresent(method, BindsInstance.class)
- || isAnnotationPresent(parameter, BindsInstance.class)) {
- DependencyRequest request =
- dependencyRequestFactory.forRequiredResolvedVariable(parameter, type);
- String variableName = elementForVariableName.getSimpleName().toString();
- return ComponentRequirement.forBoundInstance(
- request.key(), request.isNullable(), variableName);
- }
-
- return moduleAnnotation(asTypeElement(type)).isPresent()
- ? ComponentRequirement.forModule(type)
- : ComponentRequirement.forDependency(type);
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentCreatorImplementation.java b/java/dagger/internal/codegen/ComponentCreatorImplementation.java
deleted file mode 100644
index a5eb68034..000000000
--- a/java/dagger/internal/codegen/ComponentCreatorImplementation.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.collect.ImmutableMap;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.FieldSpec;
-import com.squareup.javapoet.TypeSpec;
-
-/** The implementation of a component creator type. */
-@AutoValue
-abstract class ComponentCreatorImplementation {
-
- /** Creates a new {@link ComponentCreatorImplementation}. */
- static ComponentCreatorImplementation create(
- TypeSpec spec,
- ClassName name,
- ImmutableMap<ComponentRequirement, FieldSpec> fields) {
- return new AutoValue_ComponentCreatorImplementation(spec, name, fields);
- }
-
- /** The type spec for the creator implementation. */
- abstract TypeSpec spec();
-
- /** The name of the creator implementation class. */
- abstract ClassName name();
-
- /**
- * All fields that are present in this implementation or its supertype.
- *
- * <p>In the case of ahead-of-time subcomponents, not all fields will necessarily be passed to
- * the component's constructor (because, for example, it turns out that a particular module that
- * the creator can set is actually inherited from an ancestor module).
- */
- abstract ImmutableMap<ComponentRequirement, FieldSpec> fields();
-}
diff --git a/java/dagger/internal/codegen/ComponentCreatorImplementationFactory.java b/java/dagger/internal/codegen/ComponentCreatorImplementationFactory.java
deleted file mode 100644
index 4f06b900a..000000000
--- a/java/dagger/internal/codegen/ComponentCreatorImplementationFactory.java
+++ /dev/null
@@ -1,589 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreTypes.asDeclared;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static com.squareup.javapoet.MethodSpec.constructorBuilder;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static com.squareup.javapoet.TypeSpec.classBuilder;
-import static dagger.internal.codegen.SourceFiles.simpleVariableName;
-import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
-import static dagger.internal.codegen.javapoet.TypeSpecs.addSupertype;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PROTECTED;
-import static javax.lang.model.element.Modifier.PUBLIC;
-import static javax.lang.model.element.Modifier.STATIC;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.FieldSpec;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.ParameterSpec;
-import com.squareup.javapoet.TypeName;
-import com.squareup.javapoet.TypeSpec;
-import dagger.internal.Preconditions;
-import dagger.internal.codegen.ComponentRequirement.NullPolicy;
-import dagger.internal.codegen.javapoet.TypeNames;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import java.util.Optional;
-import java.util.Set;
-import javax.inject.Inject;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeKind;
-
-/** Factory for creating {@link ComponentCreatorImplementation} instances. */
-final class ComponentCreatorImplementationFactory {
-
- private final DaggerElements elements;
- private final DaggerTypes types;
-
- @Inject
- ComponentCreatorImplementationFactory(DaggerElements elements, DaggerTypes types) {
- this.elements = elements;
- this.types = types;
- }
-
- /** Returns a new creator implementation for the given component, if necessary. */
- Optional<ComponentCreatorImplementation> create(
- ComponentImplementation componentImplementation, Optional<BindingGraph> graph) {
- if (!componentImplementation.componentDescriptor().hasCreator()) {
- return Optional.empty();
- }
-
- Optional<ComponentCreatorDescriptor> creatorDescriptor =
- componentImplementation.componentDescriptor().creatorDescriptor();
-
- if (componentImplementation.isAbstract()
- && (hasNoSetterMethods(creatorDescriptor)
- || componentImplementation.superclassImplementation().isPresent())) {
- // 1. Factory-like creators (those with no setter methods) are only generated in concrete
- // components, because they only have a factory method and the factory method must call
- // a concrete component's constructor.
- // 2. The component builder in ahead-of-time mode is generated with the base subcomponent
- // implementation, with the exception of the build method since that requires invoking the
- // constructor of a concrete component implementation. Intermediate component
- // implementations, because they still can't invoke the eventual constructor and have no
- // additional extensions to the builder, can ignore generating a builder implementation.
- return Optional.empty();
- }
-
- Builder builder =
- creatorDescriptor.isPresent()
- ? new BuilderForCreatorDescriptor(
- componentImplementation, creatorDescriptor.get(), graph)
- : new BuilderForGeneratedRootComponentBuilder(componentImplementation);
- return Optional.of(builder.build());
- }
-
- private static boolean hasNoSetterMethods(
- Optional<ComponentCreatorDescriptor> creatorDescriptor) {
- return creatorDescriptor.filter(descriptor -> descriptor.setterMethods().isEmpty()).isPresent();
- }
-
- /** Base class for building a creator implementation. */
- private abstract class Builder {
- final ComponentImplementation componentImplementation;
- final ClassName className;
- final TypeSpec.Builder classBuilder;
-
- private ImmutableMap<ComponentRequirement, FieldSpec> fields;
-
- Builder(ComponentImplementation componentImplementation) {
- this.componentImplementation = componentImplementation;
- this.className = componentImplementation.getCreatorName();
- this.classBuilder = classBuilder(className);
- }
-
- /** Builds the {@link ComponentCreatorImplementation}. */
- ComponentCreatorImplementation build() {
- setModifiers();
- setSupertype();
- this.fields = getOrAddFields();
- addConstructor();
- addSetterMethods();
- addFactoryMethod();
- return ComponentCreatorImplementation.create(classBuilder.build(), className, fields);
- }
-
- /** Returns the descriptor for the component. */
- final ComponentDescriptor componentDescriptor() {
- return componentImplementation.componentDescriptor();
- }
-
- /**
- * The set of requirements that must be passed to the component's constructor in the order
- * they must be passed.
- */
- final ImmutableSet<ComponentRequirement> componentConstructorRequirements() {
- return componentImplementation.requirements();
- }
-
- /** Returns the requirements that have setter methods on the creator type. */
- abstract ImmutableSet<ComponentRequirement> setterMethods();
-
- /**
- * Returns the component requirements that have factory method parameters, mapped to the name
- * for that parameter.
- */
- abstract ImmutableMap<ComponentRequirement, String> factoryMethodParameters();
-
- /**
- * The {@link ComponentRequirement}s that this creator allows users to set. Values are a status
- * for each requirement indicating what's needed for that requirement in the implementation
- * class currently being generated.
- */
- abstract ImmutableMap<ComponentRequirement, RequirementStatus> userSettableRequirements();
-
- /**
- * Component requirements that are both settable by the creator and needed to construct the
- * component.
- */
- private Set<ComponentRequirement> neededUserSettableRequirements() {
- return Sets.intersection(
- userSettableRequirements().keySet(), componentConstructorRequirements());
- }
-
- private void setModifiers() {
- visibility().ifPresent(classBuilder::addModifiers);
- if (!componentImplementation.isNested()) {
- classBuilder.addModifiers(STATIC);
- }
- classBuilder.addModifiers(componentImplementation.isAbstract() ? ABSTRACT : FINAL);
- }
-
- /** Returns the visibility modifier the generated class should have, if any. */
- protected abstract Optional<Modifier> visibility();
-
- /** Sets the superclass being extended or interface being implemented for this creator. */
- protected abstract void setSupertype();
-
- /** Adds a constructor for the creator type, if needed. */
- protected abstract void addConstructor();
-
- private ImmutableMap<ComponentRequirement, FieldSpec> getOrAddFields() {
- // If a base implementation is present, any fields are already defined there and don't need to
- // be created in this implementation.
- return componentImplementation
- .baseCreatorImplementation()
- .map(ComponentCreatorImplementation::fields)
- .orElseGet(this::addFields);
- }
-
- private ImmutableMap<ComponentRequirement, FieldSpec> addFields() {
- // Fields in an abstract creator class need to be visible from subclasses.
- Modifier modifier = componentImplementation.isAbstract() ? PROTECTED : PRIVATE;
- UniqueNameSet fieldNames = new UniqueNameSet();
- ImmutableMap<ComponentRequirement, FieldSpec> result =
- Maps.toMap(
- Sets.intersection(neededUserSettableRequirements(), setterMethods()),
- requirement ->
- FieldSpec.builder(
- TypeName.get(requirement.type()),
- fieldNames.getUniqueName(requirement.variableName()),
- modifier)
- .build());
- classBuilder.addFields(result.values());
- return result;
- }
-
- private void addSetterMethods() {
- Maps.filterKeys(userSettableRequirements(), setterMethods()::contains)
- .forEach(
- (requirement, status) ->
- createSetterMethod(requirement, status).ifPresent(classBuilder::addMethod));
- }
-
- /** Creates a new setter method builder, with no method body, for the given requirement. */
- protected abstract MethodSpec.Builder setterMethodBuilder(ComponentRequirement requirement);
-
- private Optional<MethodSpec> createSetterMethod(
- ComponentRequirement requirement, RequirementStatus status) {
- switch (status) {
- case NEEDED:
- return Optional.of(normalSetterMethod(requirement));
- case UNNEEDED:
- return Optional.of(noopSetterMethod(requirement));
- case UNSETTABLE_REPEATED_MODULE:
- return Optional.of(repeatedModuleSetterMethod(requirement));
- case IMPLEMENTED_IN_SUPERTYPE:
- return Optional.empty();
- }
- throw new AssertionError();
- }
-
- private MethodSpec normalSetterMethod(ComponentRequirement requirement) {
- MethodSpec.Builder method = setterMethodBuilder(requirement);
- ParameterSpec parameter = parameter(method.build());
- method.addStatement(
- "this.$N = $L",
- fields.get(requirement),
- requirement.nullPolicy(elements, types).equals(NullPolicy.ALLOW)
- ? CodeBlock.of("$N", parameter)
- : CodeBlock.of("$T.checkNotNull($N)", Preconditions.class, parameter));
- return maybeReturnThis(method);
- }
-
- private MethodSpec noopSetterMethod(ComponentRequirement requirement) {
- MethodSpec.Builder method = setterMethodBuilder(requirement);
- ParameterSpec parameter = parameter(method.build());
- method
- .addAnnotation(Deprecated.class)
- .addJavadoc(
- "@deprecated This module is declared, but an instance is not used in the component. "
- + "This method is a no-op. For more, see https://dagger.dev/unused-modules.\n")
- .addStatement("$T.checkNotNull($N)", Preconditions.class, parameter);
- return maybeReturnThis(method);
- }
-
- private MethodSpec repeatedModuleSetterMethod(ComponentRequirement requirement) {
- return setterMethodBuilder(requirement)
- .addStatement(
- "throw new $T($T.format($S, $T.class.getCanonicalName()))",
- UnsupportedOperationException.class,
- String.class,
- "%s cannot be set because it is inherited from the enclosing component",
- TypeNames.rawTypeName(TypeName.get(requirement.type())))
- .build();
- }
-
- private ParameterSpec parameter(MethodSpec method) {
- return getOnlyElement(method.parameters);
- }
-
- private MethodSpec maybeReturnThis(MethodSpec.Builder method) {
- MethodSpec built = method.build();
- return built.returnType.equals(TypeName.VOID)
- ? built
- : method.addStatement("return this").build();
- }
-
- private void addFactoryMethod() {
- if (!componentImplementation.isAbstract()) {
- classBuilder.addMethod(factoryMethod());
- }
- }
-
- MethodSpec factoryMethod() {
- MethodSpec.Builder factoryMethod = factoryMethodBuilder();
- factoryMethod
- .returns(ClassName.get(componentDescriptor().typeElement()))
- .addModifiers(PUBLIC);
-
- ImmutableMap<ComponentRequirement, String> factoryMethodParameters =
- factoryMethodParameters();
- userSettableRequirements()
- .keySet()
- .forEach(
- requirement -> {
- if (fields.containsKey(requirement)
- && componentConstructorRequirements().contains(requirement)) {
- // In AOT mode, there can be a field for a requirement even if the component's
- // constructor doesn't need it, because the base class for the creator was created
- // before the final graph for the component was known.
- FieldSpec field = fields.get(requirement);
- addNullHandlingForField(requirement, field, factoryMethod);
- } else if (factoryMethodParameters.containsKey(requirement)) {
- String parameterName = factoryMethodParameters.get(requirement);
- addNullHandlingForParameter(requirement, parameterName, factoryMethod);
- }
- });
- factoryMethod.addStatement(
- "return new $T($L)",
- componentImplementation.name(),
- componentConstructorArgs(factoryMethodParameters));
- return factoryMethod.build();
- }
-
- private void addNullHandlingForField(
- ComponentRequirement requirement, FieldSpec field, MethodSpec.Builder factoryMethod) {
- switch (requirement.nullPolicy(elements, types)) {
- case NEW:
- checkState(requirement.kind().isModule());
- factoryMethod
- .beginControlFlow("if ($N == null)", field)
- .addStatement("this.$N = $L", field, newModuleInstance(requirement))
- .endControlFlow();
- break;
- case THROW:
- // TODO(cgdecker,ronshapiro): ideally this should use the key instead of a class for
- // @BindsInstance requirements, but that's not easily proguardable.
- factoryMethod.addStatement(
- "$T.checkBuilderRequirement($N, $T.class)",
- Preconditions.class,
- field,
- TypeNames.rawTypeName(field.type));
- break;
- case ALLOW:
- break;
- }
- }
-
- private void addNullHandlingForParameter(
- ComponentRequirement requirement, String parameter, MethodSpec.Builder factoryMethod) {
- if (!requirement.nullPolicy(elements, types).equals(NullPolicy.ALLOW)) {
- // Factory method parameters are always required unless they are a nullable
- // binds-instance (i.e. ALLOW)
- factoryMethod.addStatement("$T.checkNotNull($L)", Preconditions.class, parameter);
- }
- }
-
- /** Returns a builder for the creator's factory method. */
- protected abstract MethodSpec.Builder factoryMethodBuilder();
-
- private CodeBlock componentConstructorArgs(
- ImmutableMap<ComponentRequirement, String> factoryMethodParameters) {
- return componentConstructorRequirements().stream()
- .map(
- requirement -> {
- if (fields.containsKey(requirement)) {
- return CodeBlock.of("$N", fields.get(requirement));
- } else if (factoryMethodParameters.containsKey(requirement)) {
- return CodeBlock.of("$L", factoryMethodParameters.get(requirement));
- } else {
- return newModuleInstance(requirement);
- }
- })
- .collect(toParametersCodeBlock());
- }
-
- private CodeBlock newModuleInstance(ComponentRequirement requirement) {
- checkArgument(requirement.kind().isModule()); // this should be guaranteed to be true here
- return ModuleProxies.newModuleInstance(requirement.typeElement(), className, elements);
- }
- }
-
- /** Builder for a creator type defined by a {@code ComponentCreatorDescriptor}. */
- private final class BuilderForCreatorDescriptor extends Builder {
- final ComponentCreatorDescriptor creatorDescriptor;
- private final Optional<BindingGraph> graph;
-
- BuilderForCreatorDescriptor(
- ComponentImplementation componentImplementation,
- ComponentCreatorDescriptor creatorDescriptor,
- Optional<BindingGraph> graph) {
- super(componentImplementation);
- this.creatorDescriptor = creatorDescriptor;
- this.graph = graph;
- }
-
- @Override
- protected ImmutableMap<ComponentRequirement, RequirementStatus> userSettableRequirements() {
- return Maps.toMap(creatorDescriptor.userSettableRequirements(), this::requirementStatus);
- }
-
- @Override
- protected Optional<Modifier> visibility() {
- if (componentImplementation.isAbstract()) {
- // The component creator class of a top-level component implementation in ahead-of-time
- // subcomponents mode must be public, not protected, because the creator's subclass will
- // be a sibling of the component subclass implementation, not nested.
- return Optional.of(componentImplementation.isNested() ? PROTECTED : PUBLIC);
- }
- return Optional.of(PRIVATE);
- }
-
- @Override
- protected void setSupertype() {
- if (componentImplementation.baseCreatorImplementation().isPresent()) {
- // If an abstract base implementation for this creator exists, extend that class.
- classBuilder.superclass(componentImplementation.baseCreatorImplementation().get().name());
- } else {
- addSupertype(classBuilder, creatorDescriptor.typeElement());
- }
- }
-
- @Override
- protected void addConstructor() {
- // Just use the implicit no-arg public constructor.
- }
-
- @Override
- protected ImmutableSet<ComponentRequirement> setterMethods() {
- return ImmutableSet.copyOf(creatorDescriptor.setterMethods().keySet());
- }
-
- @Override
- protected ImmutableMap<ComponentRequirement, String> factoryMethodParameters() {
- return ImmutableMap.copyOf(
- Maps.transformValues(
- creatorDescriptor.factoryParameters(),
- element -> element.getSimpleName().toString()));
- }
-
- private DeclaredType creatorType() {
- return asDeclared(creatorDescriptor.typeElement().asType());
- }
-
- @Override
- protected MethodSpec.Builder factoryMethodBuilder() {
- return MethodSpec.overriding(creatorDescriptor.factoryMethod(), creatorType(), types);
- }
-
- private RequirementStatus requirementStatus(ComponentRequirement requirement) {
- // In ahead-of-time subcomponents mode, all builder methods are defined at the base
- // implementation. The only case where a method needs to be overridden is for a repeated
- // module, which is unknown at the point when a base implementation is generated. We do this
- // at the root for simplicity (and as an aside, repeated modules are never used in google
- // as of 11/28/18, and thus the additional cost of including these methods at the root is
- // negligible).
- if (isRepeatedModule(requirement)) {
- return RequirementStatus.UNSETTABLE_REPEATED_MODULE;
- }
-
- if (hasBaseCreatorImplementation()) {
- return RequirementStatus.IMPLEMENTED_IN_SUPERTYPE;
- }
-
- return componentConstructorRequirements().contains(requirement)
- ? RequirementStatus.NEEDED
- : RequirementStatus.UNNEEDED;
- }
-
- /**
- * Returns whether the given requirement is for a repeat of a module inherited from an ancestor
- * component. This creator is not allowed to set such a module.
- */
- final boolean isRepeatedModule(ComponentRequirement requirement) {
- return !componentConstructorRequirements().contains(requirement)
- && !isOwnedModule(requirement);
- }
-
- /**
- * Returns whether the given {@code requirement} is for a module type owned by the component.
- */
- private boolean isOwnedModule(ComponentRequirement requirement) {
- return graph.map(g -> g.ownedModuleTypes().contains(requirement.typeElement())).orElse(true);
- }
-
- private boolean hasBaseCreatorImplementation() {
- return !componentImplementation.isAbstract()
- && componentImplementation.baseImplementation().isPresent();
- }
-
- @Override
- protected MethodSpec.Builder setterMethodBuilder(ComponentRequirement requirement) {
- ExecutableElement supertypeMethod = creatorDescriptor.setterMethods().get(requirement);
- MethodSpec.Builder method = MethodSpec.overriding(supertypeMethod, creatorType(), types);
- if (!supertypeMethod.getReturnType().getKind().equals(TypeKind.VOID)) {
- // Take advantage of covariant returns so that we don't have to worry about type variables
- method.returns(className);
- }
- return method;
- }
- }
-
- /**
- * Builder for a component builder class that is automatically generated for a root component that
- * does not have its own user-defined creator type (i.e. a {@code ComponentCreatorDescriptor}).
- */
- private final class BuilderForGeneratedRootComponentBuilder extends Builder {
- BuilderForGeneratedRootComponentBuilder(ComponentImplementation componentImplementation) {
- super(componentImplementation);
- }
-
- @Override
- protected ImmutableMap<ComponentRequirement, RequirementStatus> userSettableRequirements() {
- return Maps.toMap(
- setterMethods(),
- requirement ->
- componentConstructorRequirements().contains(requirement)
- ? RequirementStatus.NEEDED
- : RequirementStatus.UNNEEDED);
- }
-
- @Override
- protected Optional<Modifier> visibility() {
- return componentImplementation
- .componentDescriptor()
- .typeElement()
- .getModifiers()
- .contains(PUBLIC) ? Optional.of(PUBLIC) : Optional.empty();
- }
-
- @Override
- protected void setSupertype() {
- // There's never a supertype for a root component auto-generated builder type.
- }
-
- @Override
- protected void addConstructor() {
- classBuilder.addMethod(constructorBuilder().addModifiers(PRIVATE).build());
- }
-
- @Override
- protected ImmutableSet<ComponentRequirement> setterMethods() {
- return componentDescriptor().dependenciesAndConcreteModules();
- }
-
- @Override
- protected ImmutableMap<ComponentRequirement, String> factoryMethodParameters() {
- return ImmutableMap.of();
- }
-
- @Override
- protected MethodSpec.Builder factoryMethodBuilder() {
- return methodBuilder("build");
- }
-
- @Override
- protected MethodSpec.Builder setterMethodBuilder(ComponentRequirement requirement) {
- String name = simpleVariableName(requirement.typeElement());
- return methodBuilder(name)
- .addModifiers(PUBLIC)
- .addParameter(TypeName.get(requirement.type()), name)
- .returns(className);
- }
- }
-
- /** Enumeration of statuses a component requirement may have in a creator. */
- enum RequirementStatus {
- /** An instance is needed to create the component. */
- NEEDED,
-
- /**
- * An instance is not needed to create the component, but the requirement is for a module owned
- * by the component. Setting the requirement is a no-op and any setter method should be marked
- * deprecated on the generated type as a warning to the user.
- */
- UNNEEDED,
-
- /**
- * The requirement may not be set in this creator because the module it is for is already
- * inherited from an ancestor component. Any setter method for it should throw an exception.
- */
- UNSETTABLE_REPEATED_MODULE,
-
- /**
- * The requirement is settable by the creator, but the setter method implementation already
- * exists in a supertype.
- */
- IMPLEMENTED_IN_SUPERTYPE,
- ;
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentCreatorKind.java b/java/dagger/internal/codegen/ComponentCreatorKind.java
deleted file mode 100644
index dc203de2a..000000000
--- a/java/dagger/internal/codegen/ComponentCreatorKind.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2019 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.CaseFormat.UPPER_CAMEL;
-import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
-
-import com.google.common.base.Ascii;
-
-/** Enumeration of the different kinds of component creators. */
-enum ComponentCreatorKind {
- /** {@code @Component.Builder} or one of its subcomponent/production variants. */
- BUILDER,
-
- /** {@code @Component.Factory} or one of its subcomponent/production variants. */
- FACTORY,
- ;
-
- /** Name to use as (or as part of) a type name for a creator of this kind. */
- String typeName() {
- return UPPER_UNDERSCORE.to(UPPER_CAMEL, name());
- }
-
- /** Name to use for a component's static method returning a creator of this kind. */
- String methodName() {
- return Ascii.toLowerCase(name());
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentCreatorValidator.java b/java/dagger/internal/codegen/ComponentCreatorValidator.java
deleted file mode 100644
index c55dadd80..000000000
--- a/java/dagger/internal/codegen/ComponentCreatorValidator.java
+++ /dev/null
@@ -1,390 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.ComponentCreatorAnnotation.getCreatorAnnotations;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.STATIC;
-import static javax.lang.model.util.ElementFilter.methodsIn;
-
-import com.google.auto.common.MoreElements;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ObjectArrays;
-import dagger.BindsInstance;
-import dagger.internal.codegen.ErrorMessages.ComponentCreatorMessages;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import java.util.List;
-import java.util.Set;
-import javax.inject.Inject;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.ElementFilter;
-
-/** Validates types annotated with component creator annotations. */
-final class ComponentCreatorValidator {
-
- private final DaggerElements elements;
- private final DaggerTypes types;
-
- @Inject
- ComponentCreatorValidator(DaggerElements elements, DaggerTypes types) {
- this.elements = elements;
- this.types = types;
- }
-
- /** Validates that the given {@code type} is potentially a valid component creator type. */
- public ValidationReport<TypeElement> validate(TypeElement type) {
- ValidationReport.Builder<TypeElement> report = ValidationReport.about(type);
-
- ImmutableSet<ComponentCreatorAnnotation> creatorAnnotations = getCreatorAnnotations(type);
- if (!validateOnlyOneCreatorAnnotation(creatorAnnotations, report)) {
- return report.build();
- }
-
- // Note: there's more validation in ComponentDescriptorValidator:
- // - to make sure the setter methods/factory parameters mirror the deps
- // - to make sure each type or key is set by only one method or parameter
- ElementValidator validator =
- new ElementValidator(type, report, getOnlyElement(creatorAnnotations));
- return validator.validate();
- }
-
- private boolean validateOnlyOneCreatorAnnotation(
- ImmutableSet<ComponentCreatorAnnotation> creatorAnnotations,
- ValidationReport.Builder<?> report) {
- // creatorAnnotations should never be empty because this should only ever be called for
- // types that have been found to have some creator annotation
- if (creatorAnnotations.size() > 1) {
- String error =
- "May not have more than one component Factory or Builder annotation on a type"
- + ": found "
- + creatorAnnotations;
- report.addError(error);
- return false;
- }
-
- return true;
- }
-
- /**
- * Validator for a single {@link TypeElement} that is annotated with a {@code Builder} or {@code
- * Factory} annotation.
- */
- private final class ElementValidator {
- private final TypeElement type;
- private final Element component;
- private final ValidationReport.Builder<TypeElement> report;
- private final ComponentCreatorAnnotation annotation;
- private final ComponentCreatorMessages messages;
-
- private ElementValidator(
- TypeElement type,
- ValidationReport.Builder<TypeElement> report,
- ComponentCreatorAnnotation annotation) {
- this.type = type;
- this.component = type.getEnclosingElement();
- this.report = report;
- this.annotation = annotation;
- this.messages = ErrorMessages.creatorMessagesFor(annotation);
- }
-
- /** Validates the creator type. */
- final ValidationReport<TypeElement> validate() {
- if (!isAnnotationPresent(component, annotation.componentAnnotation())) {
- report.addError(messages.mustBeInComponent());
- }
-
- // If the type isn't a class or interface, don't validate anything else since the rest of the
- // messages will be bogus.
- if (!validateIsClassOrInterface()) {
- return report.build();
- }
-
- validateTypeRequirements();
- switch (annotation.creatorKind()) {
- case FACTORY:
- validateFactory();
- break;
- case BUILDER:
- validateBuilder();
- }
-
- return report.build();
- }
-
- /** Validates that the type is a class or interface type and returns true if it is. */
- private boolean validateIsClassOrInterface() {
- switch (type.getKind()) {
- case CLASS:
- validateConstructor();
- return true;
- case INTERFACE:
- return true;
- default:
- report.addError(messages.mustBeClassOrInterface());
- }
- return false;
- }
-
- private void validateConstructor() {
- List<? extends Element> allElements = type.getEnclosedElements();
- List<ExecutableElement> constructors = ElementFilter.constructorsIn(allElements);
-
- boolean valid = true;
- if (constructors.size() != 1) {
- valid = false;
- } else {
- ExecutableElement constructor = getOnlyElement(constructors);
- valid =
- constructor.getParameters().isEmpty() && !constructor.getModifiers().contains(PRIVATE);
- }
-
- if (!valid) {
- report.addError(messages.invalidConstructor());
- }
- }
-
- /** Validates basic requirements about the type that are common to both creator kinds. */
- private void validateTypeRequirements() {
- if (!type.getTypeParameters().isEmpty()) {
- report.addError(messages.generics());
- }
-
- Set<Modifier> modifiers = type.getModifiers();
- if (modifiers.contains(PRIVATE)) {
- report.addError(messages.isPrivate());
- }
- if (!modifiers.contains(STATIC)) {
- report.addError(messages.mustBeStatic());
- }
- // Note: Must be abstract, so no need to check for final.
- if (!modifiers.contains(ABSTRACT)) {
- report.addError(messages.mustBeAbstract());
- }
- }
-
- private void validateBuilder() {
- ExecutableElement buildMethod = null;
- for (ExecutableElement method : elements.getUnimplementedMethods(type)) {
- switch (method.getParameters().size()) {
- case 0: // If this is potentially a build() method, validate it returns the correct type.
- if (validateFactoryMethodReturnType(method)) {
- if (buildMethod != null) {
- // If we found more than one build-like method, fail.
- error(
- method,
- messages.twoFactoryMethods(),
- messages.inheritedTwoFactoryMethods(),
- buildMethod);
- }
- }
- // We set the buildMethod regardless of the return type to reduce error spam.
- buildMethod = method;
- break;
-
- case 1: // If this correctly had one parameter, make sure the return types are valid.
- validateSetterMethod(method);
- break;
-
- default: // more than one parameter
- error(
- method,
- messages.setterMethodsMustTakeOneArg(),
- messages.inheritedSetterMethodsMustTakeOneArg());
- break;
- }
- }
-
- if (buildMethod == null) {
- report.addError(messages.missingFactoryMethod());
- } else {
- validateNotGeneric(buildMethod);
- }
- }
-
- private void validateSetterMethod(ExecutableElement method) {
- TypeMirror returnType = types.resolveExecutableType(method, type.asType()).getReturnType();
- if (returnType.getKind() != TypeKind.VOID && !types.isSubtype(type.asType(), returnType)) {
- error(
- method,
- messages.setterMethodsMustReturnVoidOrBuilder(),
- messages.inheritedSetterMethodsMustReturnVoidOrBuilder());
- }
-
- validateNotGeneric(method);
-
- VariableElement parameter = method.getParameters().get(0);
-
- boolean methodIsBindsInstance = isAnnotationPresent(method, BindsInstance.class);
- boolean parameterIsBindsInstance = isAnnotationPresent(parameter, BindsInstance.class);
- boolean bindsInstance = methodIsBindsInstance || parameterIsBindsInstance;
-
- if (methodIsBindsInstance && parameterIsBindsInstance) {
- error(
- method,
- messages.bindsInstanceNotAllowedOnBothSetterMethodAndParameter(),
- messages.inheritedBindsInstanceNotAllowedOnBothSetterMethodAndParameter());
- }
-
- if (!bindsInstance && parameter.asType().getKind().isPrimitive()) {
- error(
- method,
- messages.nonBindsInstanceParametersMayNotBePrimitives(),
- messages.inheritedNonBindsInstanceParametersMayNotBePrimitives());
- }
- }
-
- private void validateFactory() {
- ImmutableList<ExecutableElement> abstractMethods =
- elements.getUnimplementedMethods(type).asList();
- switch (abstractMethods.size()) {
- case 0:
- report.addError(messages.missingFactoryMethod());
- return;
- case 1:
- break; // good
- default:
- error(
- abstractMethods.get(1),
- messages.twoFactoryMethods(),
- messages.inheritedTwoFactoryMethods(),
- abstractMethods.get(0));
- return;
- }
-
- validateFactoryMethod(getOnlyElement(abstractMethods));
- }
-
- /** Validates that the given {@code method} is a valid component factory method. */
- private void validateFactoryMethod(ExecutableElement method) {
- validateNotGeneric(method);
-
- if (!validateFactoryMethodReturnType(method)) {
- // If we can't determine that the single method is a valid factory method, don't bother
- // validating its parameters.
- return;
- }
-
- for (VariableElement parameter : method.getParameters()) {
- if (!isAnnotationPresent(parameter, BindsInstance.class)
- && parameter.asType().getKind().isPrimitive()) {
- error(
- method,
- messages.nonBindsInstanceParametersMayNotBePrimitives(),
- messages.inheritedNonBindsInstanceParametersMayNotBePrimitives());
- }
- }
- }
-
- /**
- * Validates that the factory method that actually returns a new component instance. Returns
- * true if the return type was valid.
- */
- private boolean validateFactoryMethodReturnType(ExecutableElement method) {
- TypeMirror returnType = types.resolveExecutableType(method, type.asType()).getReturnType();
-
- if (!types.isSubtype(component.asType(), returnType)) {
- error(
- method,
- messages.factoryMethodMustReturnComponentType(),
- messages.inheritedFactoryMethodMustReturnComponentType());
- return false;
- }
-
- if (isAnnotationPresent(method, BindsInstance.class)) {
- error(
- method,
- messages.factoryMethodMayNotBeAnnotatedWithBindsInstance(),
- messages.inheritedFactoryMethodMayNotBeAnnotatedWithBindsInstance());
- return false;
- }
-
- TypeElement componentType = MoreElements.asType(component);
- if (!types.isSameType(componentType.asType(), returnType)) {
- ImmutableSet<ExecutableElement> methodsOnlyInComponent =
- methodsOnlyInComponent(componentType);
- if (!methodsOnlyInComponent.isEmpty()) {
- report.addWarning(
- messages.factoryMethodReturnsSupertypeWithMissingMethods(
- componentType, type, returnType, method, methodsOnlyInComponent),
- method);
- }
- }
- return true;
- }
-
- /**
- * Generates one of two error messages. If the method is enclosed in the subject, we target the
- * error to the method itself. Otherwise we target the error to the subject and list the method
- * as an argument. (Otherwise we have no way of knowing if the method is being compiled in this
- * pass too, so javac might not be able to pinpoint it's line of code.)
- */
- /*
- * For Component.Builder, the prototypical example would be if someone had:
- * libfoo: interface SharedBuilder { void badSetter(A a, B b); }
- * libbar: BarComponent { BarBuilder extends SharedBuilder } }
- * ... the compiler only validates BarBuilder when compiling libbar, but it fails because
- * of libfoo's SharedBuilder (which could have been compiled in a previous pass).
- * So we can't point to SharedBuilder#badSetter as the subject of the BarBuilder validation
- * failure.
- *
- * This check is a little more strict than necessary -- ideally we'd check if method's enclosing
- * class was included in this compile run. But that's hard, and this is close enough.
- */
- private void error(
- ExecutableElement method,
- String enclosedError,
- String inheritedError,
- Object... extraArgs) {
- if (method.getEnclosingElement().equals(type)) {
- report.addError(String.format(enclosedError, extraArgs), method);
- } else {
- report.addError(String.format(inheritedError, ObjectArrays.concat(extraArgs, method)));
- }
- }
-
- /** Validates that the given {@code method} is not generic. * */
- private void validateNotGeneric(ExecutableElement method) {
- if (!method.getTypeParameters().isEmpty()) {
- error(
- method,
- messages.methodsMayNotHaveTypeParameters(),
- messages.inheritedMethodsMayNotHaveTypeParameters());
- }
- }
-
- /**
- * Returns all methods defind in {@code componentType} which are not inherited from a supertype.
- */
- private ImmutableSet<ExecutableElement> methodsOnlyInComponent(TypeElement componentType) {
- // TODO(ronshapiro): Ideally this shouldn't return methods which are redeclared from a
- // supertype, but do not change the return type. We don't have a good/simple way of checking
- // that, and it doesn't seem likely, so the warning won't be too bad.
- return ImmutableSet.copyOf(methodsIn(componentType.getEnclosedElements()));
- }
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentDescriptor.java b/java/dagger/internal/codegen/ComponentDescriptor.java
deleted file mode 100644
index 769cc4c3d..000000000
--- a/java/dagger/internal/codegen/ComponentDescriptor.java
+++ /dev/null
@@ -1,376 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static dagger.internal.codegen.DaggerStreams.toImmutableMap;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.langmodel.DaggerTypes.isFutureType;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.type.TypeKind.VOID;
-
-import com.google.auto.value.AutoValue;
-import com.google.auto.value.extension.memoized.Memoized;
-import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
-import com.google.common.collect.ImmutableBiMap;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Maps;
-import com.google.errorprone.annotations.CanIgnoreReturnValue;
-import com.google.errorprone.annotations.CheckReturnValue;
-import dagger.Component;
-import dagger.Module;
-import dagger.Subcomponent;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.DependencyRequest;
-import dagger.model.Scope;
-import dagger.producers.CancellationPolicy;
-import dagger.producers.ProductionComponent;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.stream.Stream;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * A component declaration.
- *
- * <p>Represents one type annotated with {@code @Component}, {@code Subcomponent},
- * {@code @ProductionComponent}, or {@code @ProductionSubcomponent}.
- *
- * <p>When validating bindings installed in modules, a {@link ComponentDescriptor} can also
- * represent a synthetic component for the module, where there is an entry point for each binding in
- * the module.
- */
-@AutoValue
-abstract class ComponentDescriptor {
- /** The annotation that specifies that {@link #typeElement()} is a component. */
- abstract ComponentAnnotation annotation();
-
- /** Returns {@code true} if this is a subcomponent. */
- final boolean isSubcomponent() {
- return annotation().isSubcomponent();
- }
-
- /**
- * Returns {@code true} if this is a production component or subcomponent, or a
- * {@code @ProducerModule} when doing module binding validation.
- */
- final boolean isProduction() {
- return annotation().isProduction();
- }
-
- /**
- * Returns {@code true} if this is a real component, and not a fictional one used to validate
- * module bindings.
- */
- final boolean isRealComponent() {
- return annotation().isRealComponent();
- }
-
- /**
- * The element that defines the component. This is the element to which the {@link #annotation()}
- * was applied.
- */
- abstract TypeElement typeElement();
-
- /**
- * The set of component dependencies listed in {@link Component#dependencies} or {@link
- * ProductionComponent#dependencies()}.
- */
- abstract ImmutableSet<ComponentRequirement> dependencies();
-
- /** The non-abstract {@link #modules()} and the {@link #dependencies()}. */
- final ImmutableSet<ComponentRequirement> dependenciesAndConcreteModules() {
- return Stream.concat(
- moduleTypes().stream()
- .filter(dep -> !dep.getModifiers().contains(ABSTRACT))
- .map(module -> ComponentRequirement.forModule(module.asType())),
- dependencies().stream())
- .collect(toImmutableSet());
- }
-
- /**
- * The {@link ModuleDescriptor modules} declared in {@link Component#modules()} and reachable by
- * traversing {@link Module#includes()}.
- */
- abstract ImmutableSet<ModuleDescriptor> modules();
-
- /** The types of the {@link #modules()}. */
- final ImmutableSet<TypeElement> moduleTypes() {
- return modules().stream().map(ModuleDescriptor::moduleElement).collect(toImmutableSet());
- }
-
- /**
- * The types for which the component will need instances if all of its bindings are used. For the
- * types the component will need in a given binding graph, use {@link
- * BindingGraph#componentRequirements()}.
- *
- * <ul>
- * <li>{@linkplain #modules()} modules} with concrete instance bindings
- * <li>Bound instances
- * <li>{@linkplain #dependencies() dependencies}
- * </ul>
- */
- @Memoized
- ImmutableSet<ComponentRequirement> requirements() {
- ImmutableSet.Builder<ComponentRequirement> requirements = ImmutableSet.builder();
- modules().stream()
- .filter(
- module ->
- module.bindings().stream().anyMatch(ContributionBinding::requiresModuleInstance))
- .map(module -> ComponentRequirement.forModule(module.moduleElement().asType()))
- .forEach(requirements::add);
- requirements.addAll(dependencies());
- requirements.addAll(
- creatorDescriptor()
- .map(ComponentCreatorDescriptor::boundInstanceRequirements)
- .orElse(ImmutableSet.of()));
- return requirements.build();
- }
-
- /**
- * This component's {@linkplain #dependencies() dependencies} keyed by each provision or
- * production method defined by that dependency. Note that the dependencies' types are not simply
- * the enclosing type of the method; a method may be declared by a supertype of the actual
- * dependency.
- */
- abstract ImmutableMap<ExecutableElement, ComponentRequirement> dependenciesByDependencyMethod();
-
- /** The {@linkplain #dependencies() component dependency} that defines a method. */
- final ComponentRequirement getDependencyThatDefinesMethod(Element method) {
- checkArgument(
- method instanceof ExecutableElement, "method must be an executable element: %s", method);
- return checkNotNull(
- dependenciesByDependencyMethod().get(method), "no dependency implements %s", method);
- }
-
- /**
- * The scopes of the component.
- */
- abstract ImmutableSet<Scope> scopes();
-
- /**
- * All {@link Subcomponent}s which are direct children of this component. This includes
- * subcomponents installed from {@link Module#subcomponents()} as well as subcomponent {@linkplain
- * #childComponentsDeclaredByFactoryMethods() factory methods} and {@linkplain
- * #childComponentsDeclaredByBuilderEntryPoints() builder methods}.
- */
- final ImmutableSet<ComponentDescriptor> childComponents() {
- return ImmutableSet.<ComponentDescriptor>builder()
- .addAll(childComponentsDeclaredByFactoryMethods().values())
- .addAll(childComponentsDeclaredByBuilderEntryPoints().values())
- .addAll(childComponentsDeclaredByModules())
- .build();
- }
-
- /**
- * All {@linkplain Subcomponent direct child} components that are declared by a {@linkplain
- * Module#subcomponents() module's subcomponents}.
- */
- abstract ImmutableSet<ComponentDescriptor> childComponentsDeclaredByModules();
-
- /**
- * All {@linkplain Subcomponent direct child} components that are declared by a subcomponent
- * factory method.
- */
- abstract ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor>
- childComponentsDeclaredByFactoryMethods();
-
- /** Returns a map of {@link #childComponents()} indexed by {@link #typeElement()}. */
- @Memoized
- ImmutableMap<TypeElement, ComponentDescriptor> childComponentsByElement() {
- return Maps.uniqueIndex(childComponents(), ComponentDescriptor::typeElement);
- }
-
- /** Returns the factory method that declares a child component. */
- final Optional<ComponentMethodDescriptor> getFactoryMethodForChildComponent(
- ComponentDescriptor childComponent) {
- return Optional.ofNullable(
- childComponentsDeclaredByFactoryMethods().inverse().get(childComponent));
- }
-
- /**
- * All {@linkplain Subcomponent direct child} components that are declared by a subcomponent
- * builder method.
- */
- abstract ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor>
- childComponentsDeclaredByBuilderEntryPoints();
-
- private final Supplier<ImmutableMap<TypeElement, ComponentDescriptor>>
- childComponentsByBuilderType =
- Suppliers.memoize(
- () ->
- childComponents().stream()
- .filter(child -> child.creatorDescriptor().isPresent())
- .collect(
- toImmutableMap(
- child -> child.creatorDescriptor().get().typeElement(),
- child -> child)));
-
- /** Returns the child component with the given builder type. */
- final ComponentDescriptor getChildComponentWithBuilderType(TypeElement builderType) {
- return checkNotNull(
- childComponentsByBuilderType.get().get(builderType),
- "no child component found for builder type %s",
- builderType.getQualifiedName());
- }
-
- abstract ImmutableSet<ComponentMethodDescriptor> componentMethods();
-
- /** Returns the first component method associated with this binding request, if one exists. */
- Optional<ComponentMethodDescriptor> firstMatchingComponentMethod(BindingRequest request) {
- return componentMethods().stream()
- .filter(method -> doesComponentMethodMatch(method, request))
- .findFirst();
- }
-
- /** Returns true if the component method matches the binding request. */
- private static boolean doesComponentMethodMatch(
- ComponentMethodDescriptor componentMethod, BindingRequest request) {
- return componentMethod
- .dependencyRequest()
- .map(BindingRequest::bindingRequest)
- .filter(request::equals)
- .isPresent();
- }
-
- /** The entry point methods on the component type. Each has a {@link DependencyRequest}. */
- final ImmutableSet<ComponentMethodDescriptor> entryPointMethods() {
- return componentMethods()
- .stream()
- .filter(method -> method.dependencyRequest().isPresent())
- .collect(toImmutableSet());
- }
-
- // TODO(gak): Consider making this non-optional and revising the
- // interaction between the spec & generation
- /** Returns a descriptor for the creator type for this component type, if the user defined one. */
- abstract Optional<ComponentCreatorDescriptor> creatorDescriptor();
-
- /**
- * Returns {@code true} for components that have a creator, either because the user {@linkplain
- * #creatorDescriptor() specified one} or because it's a top-level component with an implicit
- * builder.
- */
- final boolean hasCreator() {
- return !isSubcomponent() || creatorDescriptor().isPresent();
- }
-
- /**
- * Returns the {@link CancellationPolicy} for this component, or an empty optional if either the
- * component is not a production component or no {@code CancellationPolicy} annotation is present.
- */
- final Optional<CancellationPolicy> cancellationPolicy() {
- return isProduction()
- ? Optional.ofNullable(typeElement().getAnnotation(CancellationPolicy.class))
- : Optional.empty();
- }
-
- @Memoized
- @Override
- public int hashCode() {
- // TODO(b/122962745): Only use typeElement().hashCode()
- return Objects.hash(typeElement(), annotation());
- }
-
- // TODO(ronshapiro): simplify the equality semantics
- @Override
- public abstract boolean equals(Object obj);
-
- /** A component method. */
- @AutoValue
- abstract static class ComponentMethodDescriptor {
- /** The method itself. Note that this may be declared on a supertype of the component. */
- abstract ExecutableElement methodElement();
-
- /**
- * The dependency request for production, provision, and subcomponent creator methods. Absent
- * for subcomponent factory methods.
- */
- abstract Optional<DependencyRequest> dependencyRequest();
-
- /** The subcomponent for subcomponent factory methods and subcomponent creator methods. */
- abstract Optional<ComponentDescriptor> subcomponent();
-
- /**
- * Returns the return type of {@link #methodElement()} as resolved in the {@link
- * ComponentDescriptor#typeElement() component type}. If there are no type variables in the
- * return type, this is the equivalent of {@code methodElement().getReturnType()}.
- */
- TypeMirror resolvedReturnType(DaggerTypes types) {
- checkState(dependencyRequest().isPresent());
-
- TypeMirror returnType = methodElement().getReturnType();
- if (returnType.getKind().isPrimitive() || returnType.getKind().equals(VOID)) {
- return returnType;
- }
- return BindingRequest.bindingRequest(dependencyRequest().get())
- .requestedType(dependencyRequest().get().key().type(), types);
- }
-
- /** A {@link ComponentMethodDescriptor}builder for a method. */
- static Builder builder(ExecutableElement method) {
- return new AutoValue_ComponentDescriptor_ComponentMethodDescriptor.Builder()
- .methodElement(method);
- }
-
- /** A builder of {@link ComponentMethodDescriptor}s. */
- @AutoValue.Builder
- @CanIgnoreReturnValue
- interface Builder {
- /** @see ComponentMethodDescriptor#methodElement() */
- Builder methodElement(ExecutableElement methodElement);
-
- /** @see ComponentMethodDescriptor#dependencyRequest() */
- Builder dependencyRequest(DependencyRequest dependencyRequest);
-
- /** @see ComponentMethodDescriptor#subcomponent() */
- Builder subcomponent(ComponentDescriptor subcomponent);
-
- /** Builds the descriptor. */
- @CheckReturnValue
- ComponentMethodDescriptor build();
- }
- }
-
- /** No-argument methods defined on {@link Object} that are ignored for contribution. */
- private static final ImmutableSet<String> NON_CONTRIBUTING_OBJECT_METHOD_NAMES =
- ImmutableSet.of("toString", "hashCode", "clone", "getClass");
-
- /**
- * Returns {@code true} if a method could be a component entry point but not a members-injection
- * method.
- */
- static boolean isComponentContributionMethod(DaggerElements elements, ExecutableElement method) {
- return method.getParameters().isEmpty()
- && !method.getReturnType().getKind().equals(VOID)
- && !elements.getTypeElement(Object.class).equals(method.getEnclosingElement())
- && !NON_CONTRIBUTING_OBJECT_METHOD_NAMES.contains(method.getSimpleName().toString());
- }
-
- /** Returns {@code true} if a method could be a component production entry point. */
- static boolean isComponentProductionMethod(DaggerElements elements, ExecutableElement method) {
- return isComponentContributionMethod(elements, method) && isFutureType(method.getReturnType());
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentDescriptorFactory.java b/java/dagger/internal/codegen/ComponentDescriptorFactory.java
deleted file mode 100644
index 7d87eac5e..000000000
--- a/java/dagger/internal/codegen/ComponentDescriptorFactory.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreElements.asType;
-import static com.google.auto.common.MoreTypes.asTypeElement;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.ComponentAnnotation.subcomponentAnnotation;
-import static dagger.internal.codegen.ComponentCreatorAnnotation.creatorAnnotationsFor;
-import static dagger.internal.codegen.ComponentDescriptor.isComponentContributionMethod;
-import static dagger.internal.codegen.ConfigurationAnnotations.enclosedAnnotatedTypes;
-import static dagger.internal.codegen.ConfigurationAnnotations.isSubcomponentCreator;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.InjectionAnnotations.getQualifier;
-import static dagger.internal.codegen.Scopes.productionScope;
-import static dagger.internal.codegen.Scopes.scopesOf;
-import static javax.lang.model.type.TypeKind.DECLARED;
-import static javax.lang.model.type.TypeKind.VOID;
-import static javax.lang.model.util.ElementFilter.methodsIn;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableBiMap;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.Scope;
-import java.util.Optional;
-import java.util.function.Function;
-import javax.inject.Inject;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeMirror;
-
-/** A factory for {@link ComponentDescriptor}s. */
-final class ComponentDescriptorFactory {
- private final DaggerElements elements;
- private final DaggerTypes types;
- private final DependencyRequestFactory dependencyRequestFactory;
- private final ModuleDescriptor.Factory moduleDescriptorFactory;
-
- @Inject
- ComponentDescriptorFactory(
- DaggerElements elements,
- DaggerTypes types,
- DependencyRequestFactory dependencyRequestFactory,
- ModuleDescriptor.Factory moduleDescriptorFactory) {
- this.elements = elements;
- this.types = types;
- this.dependencyRequestFactory = dependencyRequestFactory;
- this.moduleDescriptorFactory = moduleDescriptorFactory;
- }
-
- /** Returns a descriptor for a root component type. */
- ComponentDescriptor rootComponentDescriptor(TypeElement typeElement) {
- return create(
- typeElement,
- checkAnnotation(
- typeElement,
- ComponentAnnotation::rootComponentAnnotation,
- "must have a component annotation"));
- }
-
- /** Returns a descriptor for a subcomponent type. */
- ComponentDescriptor subcomponentDescriptor(TypeElement typeElement) {
- return create(
- typeElement,
- checkAnnotation(
- typeElement,
- ComponentAnnotation::subcomponentAnnotation,
- "must have a subcomponent annotation"));
- }
-
- /**
- * Returns a descriptor for a fictional component based on a module type in order to validate its
- * bindings.
- */
- ComponentDescriptor moduleComponentDescriptor(TypeElement typeElement) {
- return create(
- typeElement,
- ComponentAnnotation.fromModuleAnnotation(
- checkAnnotation(
- typeElement, ModuleAnnotation::moduleAnnotation, "must have a module annotation")));
- }
-
- private static <A> A checkAnnotation(
- TypeElement typeElement,
- Function<TypeElement, Optional<A>> annotationFunction,
- String message) {
- return annotationFunction
- .apply(typeElement)
- .orElseThrow(() -> new IllegalArgumentException(typeElement + " " + message));
- }
-
- private ComponentDescriptor create(
- TypeElement typeElement, ComponentAnnotation componentAnnotation) {
- ImmutableSet<ComponentRequirement> componentDependencies =
- componentAnnotation.dependencyTypes().stream()
- .map(ComponentRequirement::forDependency)
- .collect(toImmutableSet());
-
- ImmutableMap.Builder<ExecutableElement, ComponentRequirement> dependenciesByDependencyMethod =
- ImmutableMap.builder();
-
- for (ComponentRequirement componentDependency : componentDependencies) {
- for (ExecutableElement dependencyMethod :
- methodsIn(elements.getAllMembers(componentDependency.typeElement()))) {
- if (isComponentContributionMethod(elements, dependencyMethod)) {
- dependenciesByDependencyMethod.put(dependencyMethod, componentDependency);
- }
- }
- }
-
- // Start with the component's modules. For fictional components built from a module, start with
- // that module.
- ImmutableSet<TypeElement> modules =
- componentAnnotation.isRealComponent()
- ? componentAnnotation.modules()
- : ImmutableSet.of(typeElement);
-
- ImmutableSet<ModuleDescriptor> transitiveModules =
- moduleDescriptorFactory.transitiveModules(modules);
-
- ImmutableSet.Builder<ComponentDescriptor> subcomponentsFromModules = ImmutableSet.builder();
- for (ModuleDescriptor module : transitiveModules) {
- for (SubcomponentDeclaration subcomponentDeclaration : module.subcomponentDeclarations()) {
- TypeElement subcomponent = subcomponentDeclaration.subcomponentType();
- subcomponentsFromModules.add(subcomponentDescriptor(subcomponent));
- }
- }
-
- ImmutableSet.Builder<ComponentMethodDescriptor> componentMethodsBuilder =
- ImmutableSet.builder();
- ImmutableBiMap.Builder<ComponentMethodDescriptor, ComponentDescriptor>
- subcomponentsByFactoryMethod = ImmutableBiMap.builder();
- ImmutableBiMap.Builder<ComponentMethodDescriptor, ComponentDescriptor>
- subcomponentsByBuilderMethod = ImmutableBiMap.builder();
- if (componentAnnotation.isRealComponent()) {
- ImmutableSet<ExecutableElement> unimplementedMethods =
- elements.getUnimplementedMethods(typeElement);
- for (ExecutableElement componentMethod : unimplementedMethods) {
- ComponentMethodDescriptor componentMethodDescriptor =
- getDescriptorForComponentMethod(typeElement, componentAnnotation, componentMethod);
- componentMethodsBuilder.add(componentMethodDescriptor);
- componentMethodDescriptor
- .subcomponent()
- .ifPresent(
- subcomponent -> {
- // If the dependency request is present, that means the method returns the
- // subcomponent factory.
- if (componentMethodDescriptor.dependencyRequest().isPresent()) {
- subcomponentsByBuilderMethod.put(componentMethodDescriptor, subcomponent);
- } else {
- subcomponentsByFactoryMethod.put(componentMethodDescriptor, subcomponent);
- }
- });
- }
- }
-
- // Validation should have ensured that this set will have at most one element.
- ImmutableSet<DeclaredType> enclosedCreators =
- creatorAnnotationsFor(componentAnnotation).stream()
- .flatMap(
- creatorAnnotation ->
- enclosedAnnotatedTypes(typeElement, creatorAnnotation).stream())
- .collect(toImmutableSet());
- Optional<ComponentCreatorDescriptor> creatorDescriptor =
- enclosedCreators.isEmpty()
- ? Optional.empty()
- : Optional.of(
- ComponentCreatorDescriptor.create(
- getOnlyElement(enclosedCreators), elements, types, dependencyRequestFactory));
-
- ImmutableSet<Scope> scopes = scopesOf(typeElement);
- if (componentAnnotation.isProduction()) {
- scopes = ImmutableSet.<Scope>builder().addAll(scopes).add(productionScope(elements)).build();
- }
-
- return new AutoValue_ComponentDescriptor(
- componentAnnotation,
- typeElement,
- componentDependencies,
- transitiveModules,
- dependenciesByDependencyMethod.build(),
- scopes,
- subcomponentsFromModules.build(),
- subcomponentsByFactoryMethod.build(),
- subcomponentsByBuilderMethod.build(),
- componentMethodsBuilder.build(),
- creatorDescriptor);
- }
-
- private ComponentMethodDescriptor getDescriptorForComponentMethod(
- TypeElement componentElement,
- ComponentAnnotation componentAnnotation,
- ExecutableElement componentMethod) {
- ComponentMethodDescriptor.Builder descriptor =
- ComponentMethodDescriptor.builder(componentMethod);
-
- ExecutableType resolvedComponentMethod =
- MoreTypes.asExecutable(
- types.asMemberOf(MoreTypes.asDeclared(componentElement.asType()), componentMethod));
- TypeMirror returnType = resolvedComponentMethod.getReturnType();
- if (returnType.getKind().equals(DECLARED) && !getQualifier(componentMethod).isPresent()) {
- TypeElement returnTypeElement = asTypeElement(returnType);
- if (subcomponentAnnotation(returnTypeElement).isPresent()) {
- // It's a subcomponent factory method. There is no dependency request, and there could be
- // any number of parameters. Just return the descriptor.
- return descriptor.subcomponent(subcomponentDescriptor(returnTypeElement)).build();
- }
- if (isSubcomponentCreator(returnTypeElement)) {
- descriptor.subcomponent(
- subcomponentDescriptor(asType(returnTypeElement.getEnclosingElement())));
- }
- }
-
- switch (componentMethod.getParameters().size()) {
- case 0:
- checkArgument(
- !returnType.getKind().equals(VOID),
- "component method cannot be void: %s",
- componentMethod);
- descriptor.dependencyRequest(
- componentAnnotation.isProduction()
- ? dependencyRequestFactory.forComponentProductionMethod(
- componentMethod, resolvedComponentMethod)
- : dependencyRequestFactory.forComponentProvisionMethod(
- componentMethod, resolvedComponentMethod));
- break;
-
- case 1:
- checkArgument(
- returnType.getKind().equals(VOID)
- || MoreTypes.equivalence()
- .equivalent(returnType, resolvedComponentMethod.getParameterTypes().get(0)),
- "members injection method must return void or parameter type: %s",
- componentMethod);
- descriptor.dependencyRequest(
- dependencyRequestFactory.forComponentMembersInjectionMethod(
- componentMethod, resolvedComponentMethod));
- break;
-
- default:
- throw new IllegalArgumentException(
- "component method has too many parameters: " + componentMethod);
- }
-
- return descriptor.build();
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentDescriptorValidator.java b/java/dagger/internal/codegen/ComponentDescriptorValidator.java
deleted file mode 100644
index 8f85b3acd..000000000
--- a/java/dagger/internal/codegen/ComponentDescriptorValidator.java
+++ /dev/null
@@ -1,484 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreTypes.asDeclared;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Predicates.in;
-import static com.google.common.collect.Collections2.transform;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.ComponentAnnotation.rootComponentAnnotation;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSetMultimap;
-import static dagger.internal.codegen.DiagnosticFormatting.stripCommonTypePrefixes;
-import static dagger.internal.codegen.Formatter.INDENT;
-import static dagger.internal.codegen.Scopes.getReadableSource;
-import static dagger.internal.codegen.Scopes.scopesOf;
-import static dagger.internal.codegen.Scopes.singletonScope;
-import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
-import static java.util.stream.Collectors.joining;
-import static java.util.stream.Collectors.toList;
-import static javax.tools.Diagnostic.Kind.ERROR;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.common.base.Equivalence.Wrapper;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.Multimaps;
-import com.google.common.collect.Sets;
-import dagger.internal.codegen.ComponentRequirement.NullPolicy;
-import dagger.internal.codegen.ErrorMessages.ComponentCreatorMessages;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.Scope;
-import java.util.ArrayDeque;
-import java.util.Collection;
-import java.util.Deque;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Optional;
-import java.util.Set;
-import java.util.StringJoiner;
-import javax.inject.Inject;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeMirror;
-import javax.tools.Diagnostic;
-
-/**
- * Reports errors in the component hierarchy.
- *
- * <ul>
- * <li>Validates scope hierarchy of component dependencies and subcomponents.
- * <li>Reports errors if there are component dependency cycles.
- * <li>Reports errors if any abstract modules have non-abstract instance binding methods.
- * <li>Validates component creator types.
- * </ul>
- */
-// TODO(dpb): Combine with ComponentHierarchyValidator.
-final class ComponentDescriptorValidator {
-
- private final DaggerElements elements;
- private final DaggerTypes types;
- private final CompilerOptions compilerOptions;
- private final MethodSignatureFormatter methodSignatureFormatter;
- private final ComponentHierarchyValidator componentHierarchyValidator;
-
- @Inject
- ComponentDescriptorValidator(
- DaggerElements elements,
- DaggerTypes types,
- CompilerOptions compilerOptions,
- MethodSignatureFormatter methodSignatureFormatter,
- ComponentHierarchyValidator componentHierarchyValidator) {
- this.elements = elements;
- this.types = types;
- this.compilerOptions = compilerOptions;
- this.methodSignatureFormatter = methodSignatureFormatter;
- this.componentHierarchyValidator = componentHierarchyValidator;
- }
-
- ValidationReport<TypeElement> validate(ComponentDescriptor component) {
- ComponentValidation validation = new ComponentValidation(component);
- validation.visitComponent(component);
- validation.report(component).addSubreport(componentHierarchyValidator.validate(component));
- return validation.buildReport();
- }
-
- private final class ComponentValidation {
- final ComponentDescriptor rootComponent;
- final Map<ComponentDescriptor, ValidationReport.Builder<TypeElement>> reports =
- new LinkedHashMap<>();
-
- ComponentValidation(ComponentDescriptor rootComponent) {
- this.rootComponent = checkNotNull(rootComponent);
- }
-
- /** Returns a report that contains all validation messages found during traversal. */
- ValidationReport<TypeElement> buildReport() {
- ValidationReport.Builder<TypeElement> report =
- ValidationReport.about(rootComponent.typeElement());
- reports.values().forEach(subreport -> report.addSubreport(subreport.build()));
- return report.build();
- }
-
- /** Returns the report builder for a (sub)component. */
- private ValidationReport.Builder<TypeElement> report(ComponentDescriptor component) {
- return reentrantComputeIfAbsent(
- reports, component, descriptor -> ValidationReport.about(descriptor.typeElement()));
- }
-
- private void reportComponentItem(
- Diagnostic.Kind kind, ComponentDescriptor component, String message) {
- report(component)
- .addItem(message, kind, component.typeElement(), component.annotation().annotation());
- }
-
- private void reportComponentError(ComponentDescriptor component, String error) {
- reportComponentItem(ERROR, component, error);
- }
-
- void visitComponent(ComponentDescriptor component) {
- validateDependencyScopes(component);
- validateComponentDependencyHierarchy(component);
- validateModules(component);
- validateCreators(component);
- component.childComponents().forEach(this::visitComponent);
- }
-
- /** Validates that component dependencies do not form a cycle. */
- private void validateComponentDependencyHierarchy(ComponentDescriptor component) {
- validateComponentDependencyHierarchy(component, component.typeElement(), new ArrayDeque<>());
- }
-
- /** Recursive method to validate that component dependencies do not form a cycle. */
- private void validateComponentDependencyHierarchy(
- ComponentDescriptor component, TypeElement dependency, Deque<TypeElement> dependencyStack) {
- if (dependencyStack.contains(dependency)) {
- // Current component has already appeared in the component chain.
- StringBuilder message = new StringBuilder();
- message.append(component.typeElement().getQualifiedName());
- message.append(" contains a cycle in its component dependencies:\n");
- dependencyStack.push(dependency);
- appendIndentedComponentsList(message, dependencyStack);
- dependencyStack.pop();
- reportComponentItem(
- compilerOptions.scopeCycleValidationType().diagnosticKind().get(),
- component,
- message.toString());
- } else {
- rootComponentAnnotation(dependency)
- .ifPresent(
- componentAnnotation -> {
- dependencyStack.push(dependency);
-
- for (TypeElement nextDependency : componentAnnotation.dependencies()) {
- validateComponentDependencyHierarchy(
- component, nextDependency, dependencyStack);
- }
-
- dependencyStack.pop();
- });
- }
- }
-
- /**
- * Validates that among the dependencies are at most one scoped dependency, that there are no
- * cycles within the scoping chain, and that singleton components have no scoped dependencies.
- */
- private void validateDependencyScopes(ComponentDescriptor component) {
- ImmutableSet<Scope> scopes = component.scopes();
- ImmutableSet<TypeElement> scopedDependencies =
- scopedTypesIn(
- component
- .dependencies()
- .stream()
- .map(ComponentRequirement::typeElement)
- .collect(toImmutableSet()));
- if (!scopes.isEmpty()) {
- Scope singletonScope = singletonScope(elements);
- // Dagger 1.x scope compatibility requires this be suppress-able.
- if (compilerOptions.scopeCycleValidationType().diagnosticKind().isPresent()
- && scopes.contains(singletonScope)) {
- // Singleton is a special-case representing the longest lifetime, and therefore
- // @Singleton components may not depend on scoped components
- if (!scopedDependencies.isEmpty()) {
- StringBuilder message =
- new StringBuilder(
- "This @Singleton component cannot depend on scoped components:\n");
- appendIndentedComponentsList(message, scopedDependencies);
- reportComponentItem(
- compilerOptions.scopeCycleValidationType().diagnosticKind().get(),
- component,
- message.toString());
- }
- } else if (scopedDependencies.size() > 1) {
- // Scoped components may depend on at most one scoped component.
- StringBuilder message = new StringBuilder();
- for (Scope scope : scopes) {
- message.append(getReadableSource(scope)).append(' ');
- }
- message
- .append(component.typeElement().getQualifiedName())
- .append(" depends on more than one scoped component:\n");
- appendIndentedComponentsList(message, scopedDependencies);
- reportComponentError(component, message.toString());
- } else {
- // Dagger 1.x scope compatibility requires this be suppress-able.
- if (!compilerOptions.scopeCycleValidationType().equals(ValidationType.NONE)) {
- validateDependencyScopeHierarchy(
- component, component.typeElement(), new ArrayDeque<>(), new ArrayDeque<>());
- }
- }
- } else {
- // Scopeless components may not depend on scoped components.
- if (!scopedDependencies.isEmpty()) {
- StringBuilder message =
- new StringBuilder(component.typeElement().getQualifiedName())
- .append(" (unscoped) cannot depend on scoped components:\n");
- appendIndentedComponentsList(message, scopedDependencies);
- reportComponentError(component, message.toString());
- }
- }
- }
-
- private void validateModules(ComponentDescriptor component) {
- for (ModuleDescriptor module : component.modules()) {
- if (module.moduleElement().getModifiers().contains(Modifier.ABSTRACT)) {
- for (ContributionBinding binding : module.bindings()) {
- if (binding.requiresModuleInstance()) {
- report(component).addError(abstractModuleHasInstanceBindingMethodsError(module));
- break;
- }
- }
- }
- }
- }
-
- private String abstractModuleHasInstanceBindingMethodsError(ModuleDescriptor module) {
- String methodAnnotations;
- switch (module.kind()) {
- case MODULE:
- methodAnnotations = "@Provides";
- break;
- case PRODUCER_MODULE:
- methodAnnotations = "@Provides or @Produces";
- break;
- default:
- throw new AssertionError(module.kind());
- }
- return String.format(
- "%s is abstract and has instance %s methods. Consider making the methods static or "
- + "including a non-abstract subclass of the module instead.",
- module.moduleElement(), methodAnnotations);
- }
-
- private void validateCreators(ComponentDescriptor component) {
- if (!component.creatorDescriptor().isPresent()) {
- // If no builder, nothing to validate.
- return;
- }
-
- ComponentCreatorDescriptor creator = component.creatorDescriptor().get();
- ComponentCreatorMessages messages = ErrorMessages.creatorMessagesFor(creator.annotation());
-
- // Requirements for modules and dependencies that the creator can set
- Set<ComponentRequirement> creatorModuleAndDependencyRequirements =
- creator.moduleAndDependencyRequirements();
- // Modules and dependencies the component requires
- Set<ComponentRequirement> componentModuleAndDependencyRequirements =
- component.dependenciesAndConcreteModules();
-
- // Requirements that the creator can set that don't match any requirements that the component
- // actually has.
- Set<ComponentRequirement> inapplicableRequirementsOnCreator =
- Sets.difference(
- creatorModuleAndDependencyRequirements, componentModuleAndDependencyRequirements);
-
- DeclaredType container = asDeclared(creator.typeElement().asType());
- if (!inapplicableRequirementsOnCreator.isEmpty()) {
- Collection<Element> excessElements =
- Multimaps.filterKeys(
- creator.unvalidatedRequirementElements(), in(inapplicableRequirementsOnCreator))
- .values();
- String formatted =
- excessElements.stream()
- .map(element -> formatElement(element, container))
- .collect(joining(", ", "[", "]"));
- report(component)
- .addError(String.format(messages.extraSetters(), formatted), creator.typeElement());
- }
-
- // Component requirements that the creator must be able to set
- Set<ComponentRequirement> mustBePassed =
- Sets.filter(
- componentModuleAndDependencyRequirements,
- input -> input.nullPolicy(elements, types).equals(NullPolicy.THROW));
- // Component requirements that the creator must be able to set, but can't
- Set<ComponentRequirement> missingRequirements =
- Sets.difference(mustBePassed, creatorModuleAndDependencyRequirements);
-
- if (!missingRequirements.isEmpty()) {
- report(component)
- .addError(
- String.format(
- messages.missingSetters(),
- missingRequirements.stream().map(ComponentRequirement::type).collect(toList())),
- creator.typeElement());
- }
-
- // Validate that declared creator requirements (modules, dependencies) have unique types.
- ImmutableSetMultimap<Wrapper<TypeMirror>, Element> declaredRequirementsByType =
- Multimaps.filterKeys(
- creator.unvalidatedRequirementElements(),
- creatorModuleAndDependencyRequirements::contains)
- .entries().stream()
- .collect(
- toImmutableSetMultimap(entry -> entry.getKey().wrappedType(), Entry::getValue));
- declaredRequirementsByType
- .asMap()
- .forEach(
- (typeWrapper, elementsForType) -> {
- if (elementsForType.size() > 1) {
- TypeMirror type = typeWrapper.get();
- // TODO(cgdecker): Attach this error message to the factory method rather than
- // the component type if the elements are factory method parameters AND the
- // factory method is defined by the factory type itself and not by a supertype.
- report(component)
- .addError(
- String.format(
- messages.multipleSettersForModuleOrDependencyType(),
- type,
- transform(
- elementsForType, element -> formatElement(element, container))),
- creator.typeElement());
- }
- });
-
- // TODO(cgdecker): Duplicate binding validation should handle the case of multiple elements
- // that set the same bound-instance Key, but validating that here would make it fail faster
- // for subcomponents.
- }
-
- private String formatElement(Element element, DeclaredType container) {
- // TODO(cgdecker): Extract some or all of this to another class?
- // But note that it does different formatting for parameters than
- // DaggerElements.elementToString(Element).
- switch (element.getKind()) {
- case METHOD:
- return methodSignatureFormatter.format(
- MoreElements.asExecutable(element), Optional.of(container));
- case PARAMETER:
- return formatParameter(MoreElements.asVariable(element), container);
- default:
- // This method shouldn't be called with any other type of element.
- throw new AssertionError();
- }
- }
-
- private String formatParameter(VariableElement parameter, DeclaredType container) {
- // TODO(cgdecker): Possibly leave the type (and annotations?) off of the parameters here and
- // just use their names, since the type will be redundant in the context of the error message.
- StringJoiner joiner = new StringJoiner(" ");
- parameter.getAnnotationMirrors().stream().map(Object::toString).forEach(joiner::add);
- TypeMirror parameterType = resolveParameterType(parameter, container);
- return joiner
- .add(stripCommonTypePrefixes(parameterType.toString()))
- .add(parameter.getSimpleName())
- .toString();
- }
-
- private TypeMirror resolveParameterType(VariableElement parameter, DeclaredType container) {
- ExecutableElement method =
- MoreElements.asExecutable(parameter.getEnclosingElement());
- int parameterIndex = method.getParameters().indexOf(parameter);
-
- ExecutableType methodType = MoreTypes.asExecutable(types.asMemberOf(container, method));
- return methodType.getParameterTypes().get(parameterIndex);
- }
-
- /**
- * Validates that scopes do not participate in a scoping cycle - that is to say, scoped
- * components are in a hierarchical relationship terminating with Singleton.
- *
- * <p>As a side-effect, this means scoped components cannot have a dependency cycle between
- * themselves, since a component's presence within its own dependency path implies a cyclical
- * relationship between scopes. However, cycles in component dependencies are explicitly checked
- * in {@link #validateComponentDependencyHierarchy(ComponentDescriptor)}.
- */
- private void validateDependencyScopeHierarchy(
- ComponentDescriptor component,
- TypeElement dependency,
- Deque<ImmutableSet<Scope>> scopeStack,
- Deque<TypeElement> scopedDependencyStack) {
- ImmutableSet<Scope> scopes = scopesOf(dependency);
- if (stackOverlaps(scopeStack, scopes)) {
- scopedDependencyStack.push(dependency);
- // Current scope has already appeared in the component chain.
- StringBuilder message = new StringBuilder();
- message.append(component.typeElement().getQualifiedName());
- message.append(" depends on scoped components in a non-hierarchical scope ordering:\n");
- appendIndentedComponentsList(message, scopedDependencyStack);
- if (compilerOptions.scopeCycleValidationType().diagnosticKind().isPresent()) {
- reportComponentItem(
- compilerOptions.scopeCycleValidationType().diagnosticKind().get(),
- component,
- message.toString());
- }
- scopedDependencyStack.pop();
- } else {
- // TODO(beder): transitively check scopes of production components too.
- rootComponentAnnotation(dependency)
- .filter(componentAnnotation -> !componentAnnotation.isProduction())
- .ifPresent(
- componentAnnotation -> {
- ImmutableSet<TypeElement> scopedDependencies =
- scopedTypesIn(componentAnnotation.dependencies());
- if (scopedDependencies.size() == 1) {
- // empty can be ignored (base-case), and > 1 is a separately-reported error.
- scopeStack.push(scopes);
- scopedDependencyStack.push(dependency);
- validateDependencyScopeHierarchy(
- component,
- getOnlyElement(scopedDependencies),
- scopeStack,
- scopedDependencyStack);
- scopedDependencyStack.pop();
- scopeStack.pop();
- }
- }); // else: we skip component dependencies which are not components
- }
- }
-
- private <T> boolean stackOverlaps(Deque<ImmutableSet<T>> stack, ImmutableSet<T> set) {
- for (ImmutableSet<T> entry : stack) {
- if (!Sets.intersection(entry, set).isEmpty()) {
- return true;
- }
- }
- return false;
- }
-
- /** Appends and formats a list of indented component types (with their scope annotations). */
- private void appendIndentedComponentsList(StringBuilder message, Iterable<TypeElement> types) {
- for (TypeElement scopedComponent : types) {
- message.append(INDENT);
- for (Scope scope : scopesOf(scopedComponent)) {
- message.append(getReadableSource(scope)).append(' ');
- }
- message
- .append(stripCommonTypePrefixes(scopedComponent.getQualifiedName().toString()))
- .append('\n');
- }
- }
-
- /**
- * Returns a set of type elements containing only those found in the input set that have a
- * scoping annotation.
- */
- private ImmutableSet<TypeElement> scopedTypesIn(Collection<TypeElement> types) {
- return types.stream().filter(type -> !scopesOf(type).isEmpty()).collect(toImmutableSet());
- }
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentGenerator.java b/java/dagger/internal/codegen/ComponentGenerator.java
deleted file mode 100644
index 330ec2d03..000000000
--- a/java/dagger/internal/codegen/ComponentGenerator.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Verify.verify;
-import static dagger.internal.codegen.SourceFiles.classFileName;
-
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.TypeSpec;
-import dagger.Component;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import java.util.Optional;
-import javax.annotation.processing.Filer;
-import javax.inject.Inject;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-
-/**
- * Generates the implementation of the abstract types annotated with {@link Component}.
- */
-final class ComponentGenerator extends SourceFileGenerator<BindingGraph> {
- private final ComponentImplementationFactory componentImplementationFactory;
-
- @Inject
- ComponentGenerator(
- Filer filer,
- DaggerElements elements,
- SourceVersion sourceVersion,
- ComponentImplementationFactory componentImplementationFactory) {
- super(filer, elements, sourceVersion);
- this.componentImplementationFactory = componentImplementationFactory;
- }
-
- @Override
- ClassName nameGeneratedType(BindingGraph input) {
- return componentName(input.componentTypeElement());
- }
-
- static ClassName componentName(TypeElement componentDefinitionType) {
- ClassName componentName = ClassName.get(componentDefinitionType);
- return ClassName.get(componentName.packageName(), "Dagger" + classFileName(componentName));
- }
-
- @Override
- Element originatingElement(BindingGraph input) {
- return input.componentTypeElement();
- }
-
- @Override
- Optional<TypeSpec.Builder> write(ClassName componentName, BindingGraph bindingGraph) {
- ComponentImplementation componentImplementation =
- componentImplementationFactory.createComponentImplementation(bindingGraph);
- verify(componentImplementation.name().equals(componentName));
- return Optional.of(componentImplementation.generate());
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentHierarchyValidator.java b/java/dagger/internal/codegen/ComponentHierarchyValidator.java
deleted file mode 100644
index d1e53330f..000000000
--- a/java/dagger/internal/codegen/ComponentHierarchyValidator.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Functions.constant;
-import static com.google.common.base.Predicates.and;
-import static com.google.common.base.Predicates.in;
-import static com.google.common.base.Predicates.not;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.Scopes.getReadableSource;
-import static dagger.internal.codegen.Scopes.uniqueScopeOf;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.base.Joiner;
-import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multimaps;
-import com.google.common.collect.SetMultimap;
-import com.google.common.collect.Sets;
-import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.model.Scope;
-import java.util.Collection;
-import java.util.Formatter;
-import java.util.Map;
-import javax.inject.Inject;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-
-/** Validates the relationships between parent components and subcomponents. */
-final class ComponentHierarchyValidator {
- private static final Joiner COMMA_SEPARATED_JOINER = Joiner.on(", ");
- private final CompilerOptions compilerOptions;
-
- @Inject
- ComponentHierarchyValidator(CompilerOptions compilerOptions) {
- this.compilerOptions = compilerOptions;
- }
-
- ValidationReport<TypeElement> validate(ComponentDescriptor componentDescriptor) {
- ValidationReport.Builder<TypeElement> report =
- ValidationReport.about(componentDescriptor.typeElement());
- validateSubcomponentMethods(
- report,
- componentDescriptor,
- Maps.toMap(componentDescriptor.moduleTypes(), constant(componentDescriptor.typeElement())));
- validateRepeatedScopedDeclarations(report, componentDescriptor, LinkedHashMultimap.create());
-
- if (compilerOptions.scopeCycleValidationType().diagnosticKind().isPresent()) {
- validateScopeHierarchy(
- report, componentDescriptor, LinkedHashMultimap.<ComponentDescriptor, Scope>create());
- }
- validateProductionModuleUniqueness(report, componentDescriptor, LinkedHashMultimap.create());
- return report.build();
- }
-
- private void validateSubcomponentMethods(
- ValidationReport.Builder<?> report,
- ComponentDescriptor componentDescriptor,
- ImmutableMap<TypeElement, TypeElement> existingModuleToOwners) {
- componentDescriptor
- .childComponentsDeclaredByFactoryMethods()
- .forEach(
- (method, childComponent) -> {
- if (childComponent.hasCreator()) {
- report.addError(
- "Components may not have factory methods for subcomponents that define a "
- + "builder.",
- method.methodElement());
- } else {
- validateFactoryMethodParameters(report, method, existingModuleToOwners);
- }
-
- validateSubcomponentMethods(
- report,
- childComponent,
- new ImmutableMap.Builder<TypeElement, TypeElement>()
- .putAll(existingModuleToOwners)
- .putAll(
- Maps.toMap(
- Sets.difference(
- childComponent.moduleTypes(), existingModuleToOwners.keySet()),
- constant(childComponent.typeElement())))
- .build());
- });
- }
-
- private void validateFactoryMethodParameters(
- ValidationReport.Builder<?> report,
- ComponentMethodDescriptor subcomponentMethodDescriptor,
- ImmutableMap<TypeElement, TypeElement> existingModuleToOwners) {
- for (VariableElement factoryMethodParameter :
- subcomponentMethodDescriptor.methodElement().getParameters()) {
- TypeElement moduleType = MoreTypes.asTypeElement(factoryMethodParameter.asType());
- TypeElement originatingComponent = existingModuleToOwners.get(moduleType);
- if (originatingComponent != null) {
- /* Factory method tries to pass a module that is already present in the parent.
- * This is an error. */
- report.addError(
- String.format(
- "%s is present in %s. A subcomponent cannot use an instance of a "
- + "module that differs from its parent.",
- moduleType.getSimpleName(), originatingComponent.getQualifiedName()),
- factoryMethodParameter);
- }
- }
- }
-
- /**
- * Checks that components do not have any scopes that are also applied on any of their ancestors.
- */
- private void validateScopeHierarchy(
- ValidationReport.Builder<TypeElement> report,
- ComponentDescriptor subject,
- SetMultimap<ComponentDescriptor, Scope> scopesByComponent) {
- scopesByComponent.putAll(subject, subject.scopes());
-
- for (ComponentDescriptor childComponent : subject.childComponents()) {
- validateScopeHierarchy(report, childComponent, scopesByComponent);
- }
-
- scopesByComponent.removeAll(subject);
-
- Predicate<Scope> subjectScopes =
- subject.isProduction()
- // TODO(beder): validate that @ProductionScope is only applied on production components
- ? and(in(subject.scopes()), not(Scope::isProductionScope))
- : in(subject.scopes());
- SetMultimap<ComponentDescriptor, Scope> overlappingScopes =
- Multimaps.filterValues(scopesByComponent, subjectScopes);
- if (!overlappingScopes.isEmpty()) {
- StringBuilder error =
- new StringBuilder()
- .append(subject.typeElement().getQualifiedName())
- .append(" has conflicting scopes:");
- for (Map.Entry<ComponentDescriptor, Scope> entry : overlappingScopes.entries()) {
- Scope scope = entry.getValue();
- error
- .append("\n ")
- .append(entry.getKey().typeElement().getQualifiedName())
- .append(" also has ")
- .append(getReadableSource(scope));
- }
- report.addItem(
- error.toString(),
- compilerOptions.scopeCycleValidationType().diagnosticKind().get(),
- subject.typeElement());
- }
- }
-
- private void validateProductionModuleUniqueness(
- ValidationReport.Builder<TypeElement> report,
- ComponentDescriptor componentDescriptor,
- SetMultimap<ComponentDescriptor, ModuleDescriptor> producerModulesByComponent) {
- ImmutableSet<ModuleDescriptor> producerModules =
- componentDescriptor.modules().stream()
- .filter(module -> module.kind().equals(ModuleKind.PRODUCER_MODULE))
- .collect(toImmutableSet());
-
- producerModulesByComponent.putAll(componentDescriptor, producerModules);
- for (ComponentDescriptor childComponent : componentDescriptor.childComponents()) {
- validateProductionModuleUniqueness(report, childComponent, producerModulesByComponent);
- }
- producerModulesByComponent.removeAll(componentDescriptor);
-
- SetMultimap<ComponentDescriptor, ModuleDescriptor> repeatedModules =
- Multimaps.filterValues(producerModulesByComponent, producerModules::contains);
- if (repeatedModules.isEmpty()) {
- return;
- }
-
- StringBuilder error = new StringBuilder();
- Formatter formatter = new Formatter(error);
-
- formatter.format("%s repeats @ProducerModules:", componentDescriptor.typeElement());
-
- for (Map.Entry<ComponentDescriptor, Collection<ModuleDescriptor>> entry :
- repeatedModules.asMap().entrySet()) {
- formatter.format("\n %s also installs: ", entry.getKey().typeElement());
- COMMA_SEPARATED_JOINER
- .appendTo(error, Iterables.transform(entry.getValue(), m -> m.moduleElement()));
- }
-
- report.addError(error.toString());
- }
-
- private void validateRepeatedScopedDeclarations(
- ValidationReport.Builder<TypeElement> report,
- ComponentDescriptor component,
- // TODO(ronshapiro): optimize ModuleDescriptor.hashCode()/equals. Otherwise this could be
- // quite costly
- SetMultimap<ComponentDescriptor, ModuleDescriptor> modulesWithScopes) {
- ImmutableSet<ModuleDescriptor> modules =
- component.modules().stream().filter(this::hasScopedDeclarations).collect(toImmutableSet());
- modulesWithScopes.putAll(component, modules);
- for (ComponentDescriptor childComponent : component.childComponents()) {
- validateRepeatedScopedDeclarations(report, childComponent, modulesWithScopes);
- }
- modulesWithScopes.removeAll(component);
-
- SetMultimap<ComponentDescriptor, ModuleDescriptor> repeatedModules =
- Multimaps.filterValues(modulesWithScopes, modules::contains);
- if (repeatedModules.isEmpty()) {
- return;
- }
-
- report.addError(
- repeatedModulesWithScopeError(component, ImmutableSetMultimap.copyOf(repeatedModules)));
- }
-
- private boolean hasScopedDeclarations(ModuleDescriptor module) {
- return !moduleScopes(module).isEmpty();
- }
-
- private String repeatedModulesWithScopeError(
- ComponentDescriptor component,
- ImmutableSetMultimap<ComponentDescriptor, ModuleDescriptor> repeatedModules) {
- StringBuilder error =
- new StringBuilder()
- .append(component.typeElement().getQualifiedName())
- .append(" repeats modules with scoped bindings or declarations:");
-
- repeatedModules
- .asMap()
- .forEach(
- (conflictingComponent, conflictingModules) -> {
- error
- .append("\n - ")
- .append(conflictingComponent.typeElement().getQualifiedName())
- .append(" also includes:");
- for (ModuleDescriptor conflictingModule : conflictingModules) {
- error
- .append("\n - ")
- .append(conflictingModule.moduleElement().getQualifiedName())
- .append(" with scopes: ")
- .append(COMMA_SEPARATED_JOINER.join(moduleScopes(conflictingModule)));
- }
- });
- return error.toString();
- }
-
- private ImmutableSet<Scope> moduleScopes(ModuleDescriptor module) {
- return FluentIterable.concat(module.allBindingDeclarations())
- .transform(declaration -> uniqueScopeOf(declaration.bindingElement().get()))
- .filter(scope -> scope.isPresent() && !scope.get().isReusable())
- .transform(scope -> scope.get())
- .toSet();
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentHjarProcessingStep.java b/java/dagger/internal/codegen/ComponentHjarProcessingStep.java
index 47857ca69..260f65ad9 100644
--- a/java/dagger/internal/codegen/ComponentHjarProcessingStep.java
+++ b/java/dagger/internal/codegen/ComponentHjarProcessingStep.java
@@ -16,47 +16,27 @@
package dagger.internal.codegen;
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.common.base.CaseFormat.LOWER_CAMEL;
-import static com.google.common.base.CaseFormat.UPPER_CAMEL;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.squareup.javapoet.MethodSpec.constructorBuilder;
-import static dagger.internal.codegen.ComponentAnnotation.rootComponentAnnotations;
-import static dagger.internal.codegen.ComponentCreatorKind.BUILDER;
-import static dagger.internal.codegen.ComponentGenerator.componentName;
-import static dagger.internal.codegen.javapoet.TypeSpecs.addSupertype;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PUBLIC;
-import static javax.lang.model.element.Modifier.STATIC;
+import static com.google.auto.common.MoreElements.asType;
+import static com.google.common.collect.Sets.union;
+import static dagger.internal.codegen.base.ComponentAnnotation.rootComponentAnnotations;
+import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.rootComponentCreatorAnnotations;
+import static java.util.Collections.disjoint;
import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.common.base.Ascii;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.TypeName;
-import com.squareup.javapoet.TypeSpec;
-import dagger.BindsInstance;
-import dagger.internal.codegen.ComponentValidator.ComponentValidationReport;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.producers.internal.CancellationListener;
+import dagger.internal.codegen.base.SourceFileGenerator;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.ComponentDescriptor;
+import dagger.internal.codegen.binding.ComponentDescriptorFactory;
+import dagger.internal.codegen.validation.ComponentCreatorValidator;
+import dagger.internal.codegen.validation.ComponentValidator;
+import dagger.internal.codegen.validation.TypeCheckingProcessingStep;
+import dagger.internal.codegen.validation.ValidationReport;
import java.lang.annotation.Annotation;
-import java.util.Optional;
import java.util.Set;
-import java.util.stream.Stream;
-import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.inject.Inject;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
/**
* A processing step that emits the API of a generated component, without any actual implementation.
@@ -72,228 +52,57 @@ import javax.lang.model.type.DeclaredType;
* normal step. Method bodies are omitted as Turbine ignores them entirely.
*/
final class ComponentHjarProcessingStep extends TypeCheckingProcessingStep<TypeElement> {
- private final SourceVersion sourceVersion;
- private final DaggerElements elements;
- private final DaggerTypes types;
- private final Filer filer;
private final Messager messager;
private final ComponentValidator componentValidator;
+ private final ComponentCreatorValidator creatorValidator;
private final ComponentDescriptorFactory componentDescriptorFactory;
+ private final SourceFileGenerator<ComponentDescriptor> componentGenerator;
@Inject
ComponentHjarProcessingStep(
- SourceVersion sourceVersion,
- DaggerElements elements,
- DaggerTypes types,
- Filer filer,
Messager messager,
ComponentValidator componentValidator,
- ComponentDescriptorFactory componentDescriptorFactory) {
+ ComponentCreatorValidator creatorValidator,
+ ComponentDescriptorFactory componentDescriptorFactory,
+ SourceFileGenerator<ComponentDescriptor> componentGenerator) {
super(MoreElements::asType);
- this.sourceVersion = sourceVersion;
- this.elements = elements;
- this.types = types;
- this.filer = filer;
this.messager = messager;
this.componentValidator = componentValidator;
+ this.creatorValidator = creatorValidator;
this.componentDescriptorFactory = componentDescriptorFactory;
+ this.componentGenerator = componentGenerator;
}
@Override
public Set<Class<? extends Annotation>> annotations() {
- return rootComponentAnnotations();
+ return union(rootComponentAnnotations(), rootComponentCreatorAnnotations());
}
+ // TODO(ronshapiro): Validation might not even be necessary. We should measure it and figure out
+ // if it's worth seeing if removing it will still work. We could potentially add a new catch
+ // clause for any exception that's not TypeNotPresentException and ignore the component entirely
+ // in that case.
@Override
protected void process(
- TypeElement componentTypeElement, ImmutableSet<Class<? extends Annotation>> annotations) {
- // TODO(ronshapiro): component validation might not be necessary. We should measure it and
- // figure out if it's worth seeing if removing it will still work. We could potentially add a
- // new catch clause for any exception that's not TypeNotPresentException and ignore the
- // component entirely in that case.
- ComponentValidationReport validationReport =
- componentValidator.validate(componentTypeElement, ImmutableSet.of(), ImmutableSet.of());
- validationReport.report().printMessagesTo(messager);
- if (validationReport.report().isClean()) {
- new EmptyComponentGenerator(filer, elements, sourceVersion)
- .generate(
- componentDescriptorFactory.rootComponentDescriptor(componentTypeElement), messager);
+ TypeElement element, ImmutableSet<Class<? extends Annotation>> annotations) {
+ if (!disjoint(annotations, rootComponentAnnotations())) {
+ processRootComponent(element);
}
- }
-
- private final class EmptyComponentGenerator extends SourceFileGenerator<ComponentDescriptor> {
- EmptyComponentGenerator(Filer filer, DaggerElements elements, SourceVersion sourceVersion) {
- super(filer, elements, sourceVersion);
- }
-
- @Override
- ClassName nameGeneratedType(ComponentDescriptor input) {
- return componentName(input.typeElement());
- }
-
- @Override
- Element originatingElement(ComponentDescriptor input) {
- return input.typeElement();
- }
-
- @Override
- Optional<TypeSpec.Builder> write(
- ClassName generatedTypeName, ComponentDescriptor componentDescriptor) {
- TypeSpec.Builder generatedComponent =
- TypeSpec.classBuilder(generatedTypeName)
- .addModifiers(FINAL)
- .addMethod(privateConstructor());
- if (componentDescriptor.typeElement().getModifiers().contains(PUBLIC)) {
- generatedComponent.addModifiers(PUBLIC);
- }
-
- TypeElement componentElement = componentDescriptor.typeElement();
- addSupertype(generatedComponent, componentElement);
-
- TypeName builderMethodReturnType;
- ComponentCreatorKind creatorKind;
- boolean noArgFactoryMethod;
- if (componentDescriptor.creatorDescriptor().isPresent()) {
- ComponentCreatorDescriptor creatorDescriptor =
- componentDescriptor.creatorDescriptor().get();
- builderMethodReturnType = ClassName.get(creatorDescriptor.typeElement());
- creatorKind = creatorDescriptor.kind();
- noArgFactoryMethod = creatorDescriptor.factoryParameters().isEmpty();
- } else {
- TypeSpec.Builder builder =
- TypeSpec.classBuilder("Builder")
- .addModifiers(STATIC, FINAL)
- .addMethod(privateConstructor());
- if (componentDescriptor.typeElement().getModifiers().contains(PUBLIC)) {
- builder.addModifiers(PUBLIC);
- }
-
- ClassName builderClassName = generatedTypeName.nestedClass("Builder");
- builderMethodReturnType = builderClassName;
- creatorKind = BUILDER;
- noArgFactoryMethod = true;
- componentRequirements(componentDescriptor)
- .map(requirement -> builderSetterMethod(requirement.typeElement(), builderClassName))
- .forEach(builder::addMethod);
- builder.addMethod(builderBuildMethod(componentDescriptor));
- generatedComponent.addType(builder.build());
- }
-
- generatedComponent.addMethod(staticCreatorMethod(builderMethodReturnType, creatorKind));
-
- if (noArgFactoryMethod
- && !hasBindsInstanceMethods(componentDescriptor)
- && componentRequirements(componentDescriptor)
- .noneMatch(requirement -> requirement.requiresAPassedInstance(elements, types))) {
- generatedComponent.addMethod(createMethod(componentDescriptor));
- }
-
- DeclaredType componentType = MoreTypes.asDeclared(componentElement.asType());
- // TODO(ronshapiro): unify with ComponentImplementationBuilder
- Set<MethodSignature> methodSignatures =
- Sets.newHashSetWithExpectedSize(componentDescriptor.componentMethods().size());
- componentDescriptor
- .componentMethods()
- .stream()
- .filter(
- method -> {
- return methodSignatures.add(
- MethodSignature.forComponentMethod(method, componentType, types));
- })
- .forEach(
- method ->
- generatedComponent.addMethod(
- emptyComponentMethod(componentElement, method.methodElement())));
-
- if (componentDescriptor.isProduction()) {
- generatedComponent
- .addSuperinterface(ClassName.get(CancellationListener.class))
- .addMethod(onProducerFutureCancelledMethod());
- }
-
- return Optional.of(generatedComponent);
+ if (!disjoint(annotations, rootComponentCreatorAnnotations())) {
+ processRootCreator(element);
}
}
- private MethodSpec emptyComponentMethod(TypeElement typeElement, ExecutableElement baseMethod) {
- return MethodSpec.overriding(baseMethod, MoreTypes.asDeclared(typeElement.asType()), types)
- .build();
- }
-
- private MethodSpec privateConstructor() {
- return constructorBuilder().addModifiers(PRIVATE).build();
- }
-
- /**
- * Returns the {@link ComponentRequirement}s for a component that does not have a {@link
- * ComponentDescriptor#creatorDescriptor()}.
- */
- private Stream<ComponentRequirement> componentRequirements(ComponentDescriptor component) {
- checkArgument(!component.isSubcomponent());
- return Stream.concat(
- component.dependencies().stream(),
- component.modules().stream()
- .filter(module -> !module.moduleElement().getModifiers().contains(ABSTRACT))
- .map(module -> ComponentRequirement.forModule(module.moduleElement().asType())));
- }
-
- private boolean hasBindsInstanceMethods(ComponentDescriptor componentDescriptor) {
- return componentDescriptor.creatorDescriptor().isPresent()
- && elements
- .getUnimplementedMethods(componentDescriptor.creatorDescriptor().get().typeElement())
- .stream()
- .anyMatch(method -> isBindsInstance(method));
- }
-
- private static boolean isBindsInstance(ExecutableElement method) {
- if (isAnnotationPresent(method, BindsInstance.class)) {
- return true;
- }
-
- if (method.getParameters().size() == 1) {
- return isAnnotationPresent(method.getParameters().get(0), BindsInstance.class);
+ private void processRootComponent(TypeElement element) {
+ ValidationReport<TypeElement> validationReport = componentValidator.validate(element);
+ validationReport.printMessagesTo(messager);
+ if (validationReport.isClean()) {
+ componentGenerator.generate(
+ componentDescriptorFactory.rootComponentDescriptor(element), messager);
}
-
- return false;
- }
-
- private MethodSpec builderSetterMethod(
- TypeElement componentRequirement, ClassName builderClass) {
- String simpleName =
- UPPER_CAMEL.to(LOWER_CAMEL, componentRequirement.getSimpleName().toString());
- return MethodSpec.methodBuilder(simpleName)
- .addModifiers(PUBLIC)
- .addParameter(ClassName.get(componentRequirement), simpleName)
- .returns(builderClass)
- .build();
- }
-
- private MethodSpec builderBuildMethod(ComponentDescriptor component) {
- return MethodSpec.methodBuilder("build")
- .addModifiers(PUBLIC)
- .returns(ClassName.get(component.typeElement()))
- .build();
- }
-
- private MethodSpec staticCreatorMethod(
- TypeName creatorMethodReturnType, ComponentCreatorKind creatorKind) {
- return MethodSpec.methodBuilder(Ascii.toLowerCase(creatorKind.typeName()))
- .addModifiers(PUBLIC, STATIC)
- .returns(creatorMethodReturnType)
- .build();
- }
-
- private MethodSpec createMethod(ComponentDescriptor componentDescriptor) {
- return MethodSpec.methodBuilder("create")
- .addModifiers(PUBLIC, STATIC)
- .returns(ClassName.get(componentDescriptor.typeElement()))
- .build();
}
- private MethodSpec onProducerFutureCancelledMethod() {
- return MethodSpec.methodBuilder("onProducerFutureCancelled")
- .addModifiers(PUBLIC)
- .addParameter(TypeName.BOOLEAN, "mayInterruptIfRunning")
- .build();
+ private void processRootCreator(TypeElement creator) {
+ creatorValidator.validate(asType(creator)).printMessagesTo(messager);
}
}
diff --git a/java/dagger/internal/codegen/ComponentImplementation.java b/java/dagger/internal/codegen/ComponentImplementation.java
deleted file mode 100644
index 340da1443..000000000
--- a/java/dagger/internal/codegen/ComponentImplementation.java
+++ /dev/null
@@ -1,929 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.CaseFormat.LOWER_CAMEL;
-import static com.google.common.base.CaseFormat.UPPER_CAMEL;
-import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static com.squareup.javapoet.TypeSpec.classBuilder;
-import static dagger.internal.codegen.ComponentCreatorKind.BUILDER;
-import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
-import static dagger.internal.codegen.serialization.ProtoSerialization.toAnnotationValue;
-import static java.util.stream.Collectors.toList;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PUBLIC;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Supplier;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.Maps;
-import com.google.common.collect.MultimapBuilder;
-import com.google.common.collect.SetMultimap;
-import com.google.common.collect.Sets;
-import com.squareup.javapoet.AnnotationSpec;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.FieldSpec;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.TypeSpec;
-import dagger.internal.ConfigureInitializationParameters;
-import dagger.internal.ModifiableBinding;
-import dagger.internal.ModifiableModule;
-import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
-import dagger.internal.codegen.javapoet.TypeSpecs;
-import dagger.model.DependencyRequest;
-import dagger.model.Key;
-import dagger.model.RequestKind;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.NestingKind;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.TypeMirror;
-
-/** The implementation of a component type. */
-final class ComponentImplementation {
- /** A type of field that this component can contain. */
- enum FieldSpecKind {
-
- /** A field required by the component, e.g. module instances. */
- COMPONENT_REQUIREMENT_FIELD,
-
- /**
- * A field for the lock and cached value for {@linkplain PrivateMethodBindingExpression
- * private-method scoped bindings}.
- */
- PRIVATE_METHOD_SCOPED_FIELD,
-
- /** A framework field for type T, e.g. {@code Provider<T>}. */
- FRAMEWORK_FIELD,
-
- /** A static field that always returns an absent {@code Optional} value for the binding. */
- ABSENT_OPTIONAL_FIELD
- }
-
- /** A type of method that this component can contain. */
- // TODO(user, dpb): Change the oder to constructor, initialize, component, then private
- // (including MIM and AOM—why treat those separately?).
- enum MethodSpecKind {
- /** The component constructor. */
- CONSTRUCTOR,
-
- /**
- * In ahead-of-time subcomponents, this method coordinates the invocation of {@link
- * #INITIALIZE_METHOD initialization methods} instead of constructors.
- */
- // TODO(b/117833324): try to merge this with other initialize() methods so it looks more natural
- CONFIGURE_INITIALIZATION_METHOD,
-
- /** A builder method for the component. (Only used by the root component.) */
- BUILDER_METHOD,
-
- /** A private method that wraps dependency expressions. */
- PRIVATE_METHOD,
-
- /** An initialization method that initializes component requirements and framework types. */
- INITIALIZE_METHOD,
-
- /** An implementation of a component interface method. */
- COMPONENT_METHOD,
-
- /** A private method that encapsulates members injection logic for a binding. */
- MEMBERS_INJECTION_METHOD,
-
- /** A static method that always returns an absent {@code Optional} value for the binding. */
- ABSENT_OPTIONAL_METHOD,
-
- /**
- * A method that encapsulates a modifiable binding. A binding is modifiable if it can change
- * across implementations of a subcomponent. This is only relevant for ahead-of-time
- * subcomponents.
- */
- MODIFIABLE_BINDING_METHOD,
-
- /**
- * The {@link dagger.producers.internal.CancellationListener#onProducerFutureCancelled(boolean)}
- * method for a production component.
- */
- CANCELLATION_LISTENER_METHOD,
- ;
- }
-
- /** A type of nested class that this component can contain. */
- enum TypeSpecKind {
- /** A factory class for a present optional binding. */
- PRESENT_FACTORY,
-
- /** A class for the component creator (only used by the root component.) */
- COMPONENT_CREATOR,
-
- /** A provider class for a component provision. */
- COMPONENT_PROVISION_FACTORY,
-
- /** A class for the subcomponent or subcomponent builder. */
- SUBCOMPONENT
- }
-
- /**
- * The method spec for a {@code configureInitialization} method plus details on the component
- * requirements that its parameters are associated with.
- */
- @AutoValue
- abstract static class ConfigureInitializationMethod {
- /** Creates a new {@link ConfigureInitializationMethod}. */
- static ConfigureInitializationMethod create(
- MethodSpec spec, ImmutableSet<ComponentRequirement> parameters) {
- return new AutoValue_ComponentImplementation_ConfigureInitializationMethod(spec, parameters);
- }
-
- /** The spec for the method. */
- abstract MethodSpec spec();
-
- /**
- * The component requirements associated with the method's parameters, in the same order as the
- * parameters.
- */
- abstract ImmutableSet<ComponentRequirement> parameters();
- }
-
- private final CompilerOptions compilerOptions;
- private final ComponentDescriptor componentDescriptor;
- private final Optional<BindingGraph> graph;
- private final ClassName name;
- private final NestingKind nestingKind;
- private final boolean isAbstract;
- private final Optional<ComponentImplementation> superclassImplementation;
- private Optional<ComponentCreatorImplementation> creatorImplementation;
- private final Map<TypeElement, ComponentImplementation> childImplementations = new HashMap<>();
- private final TypeSpec.Builder component;
- private final Optional<SubcomponentNames> subcomponentNames;
- private final UniqueNameSet componentFieldNames = new UniqueNameSet();
- private final UniqueNameSet componentMethodNames = new UniqueNameSet();
- private final List<CodeBlock> initializations = new ArrayList<>();
- private final Set<ComponentRequirement> componentRequirementParameters = new HashSet<>();
- private final List<CodeBlock> componentRequirementInitializations = new ArrayList<>();
- private final Map<ComponentRequirement, String> componentRequirementParameterNames =
- new HashMap<>();
- private final Set<Key> cancellableProducerKeys = new LinkedHashSet<>();
- private final ListMultimap<FieldSpecKind, FieldSpec> fieldSpecsMap =
- MultimapBuilder.enumKeys(FieldSpecKind.class).arrayListValues().build();
- private final ListMultimap<MethodSpecKind, MethodSpec> methodSpecsMap =
- MultimapBuilder.enumKeys(MethodSpecKind.class).arrayListValues().build();
- private final ListMultimap<TypeSpecKind, TypeSpec> typeSpecsMap =
- MultimapBuilder.enumKeys(TypeSpecKind.class).arrayListValues().build();
- private final List<Supplier<TypeSpec>> switchingProviderSupplier = new ArrayList<>();
- private final ModifiableBindingMethods modifiableBindingMethods = new ModifiableBindingMethods();
- private final SetMultimap<BindingRequest, Key> multibindingContributionsMade =
- LinkedHashMultimap.create();
- private Optional<ConfigureInitializationMethod> configureInitializationMethod = Optional.empty();
- private final Map<ComponentRequirement, String> modifiableModuleMethods = new LinkedHashMap<>();
-
- private ComponentImplementation(
- ComponentDescriptor componentDescriptor,
- Optional<BindingGraph> graph,
- ClassName name,
- NestingKind nestingKind,
- Optional<ComponentImplementation> superclassImplementation,
- Optional<SubcomponentNames> subcomponentNames,
- CompilerOptions compilerOptions,
- ImmutableSet<Modifier> modifiers) {
- checkName(name, nestingKind);
- this.compilerOptions = compilerOptions;
- this.componentDescriptor = componentDescriptor;
- this.graph = graph;
- this.name = name;
- this.nestingKind = nestingKind;
- this.isAbstract = modifiers.contains(ABSTRACT);
- this.superclassImplementation = superclassImplementation;
- this.component = classBuilder(name);
- modifiers.forEach(component::addModifiers);
- this.subcomponentNames = subcomponentNames;
- }
-
- /** Returns a component implementation for a top-level component. */
- static ComponentImplementation topLevelComponentImplementation(
- BindingGraph graph,
- ClassName name,
- SubcomponentNames subcomponentNames,
- CompilerOptions compilerOptions) {
- return new ComponentImplementation(
- graph.componentDescriptor(),
- Optional.of(graph),
- name,
- NestingKind.TOP_LEVEL,
- Optional.empty(), // superclass implementation
- Optional.of(subcomponentNames),
- compilerOptions,
- topLevelComponentImplementationModifiers(graph));
- }
-
- private static ImmutableSet<Modifier> topLevelComponentImplementationModifiers(
- BindingGraph graph) {
- ImmutableSet.Builder<Modifier> modifiers = ImmutableSet.builder();
- if (graph.componentTypeElement().getModifiers().contains(PUBLIC)
- || graph.componentDescriptor().isSubcomponent()) {
- // TODO(ronshapiro): perhaps all generated components should be non-public?
- modifiers.add(PUBLIC);
- }
- return modifiers.add(graph.componentDescriptor().isSubcomponent() ? ABSTRACT : FINAL).build();
- }
-
- /** Returns a component implementation that is a child of the current implementation. */
- ComponentImplementation childComponentImplementation(
- BindingGraph graph,
- Optional<ComponentImplementation> superclassImplementation,
- Modifier... modifiers) {
- return new ComponentImplementation(
- graph.componentDescriptor(),
- Optional.of(graph),
- getSubcomponentName(graph.componentDescriptor()),
- NestingKind.MEMBER,
- superclassImplementation,
- subcomponentNames,
- compilerOptions,
- ImmutableSet.copyOf(modifiers));
- }
-
- /**
- * Returns a component implementation that models a previously compiled class. This {@link
- * ComponentImplementation} is not used for code generation itself; it is used to determine what
- * methods need to be implemented in a subclass implementation.
- */
- static ComponentImplementation forDeserializedComponent(
- ComponentDescriptor componentDescriptor,
- ClassName name,
- NestingKind nestingKind,
- Optional<ComponentImplementation> superclassImplementation,
- CompilerOptions compilerOptions) {
- return new ComponentImplementation(
- componentDescriptor,
- Optional.empty(),
- name,
- nestingKind,
- superclassImplementation,
- Optional.empty(),
- compilerOptions,
- ImmutableSet.of(PUBLIC, ABSTRACT));
- }
-
- // TODO(dpb): Just determine the nesting kind from the name.
- private static void checkName(ClassName name, NestingKind nestingKind) {
- switch (nestingKind) {
- case TOP_LEVEL:
- checkArgument(
- name.enclosingClassName() == null, "must be a top-level class name: %s", name);
- break;
-
- case MEMBER:
- checkNotNull(name.enclosingClassName(), "must not be a top-level class name: %s", name);
- break;
-
- default:
- throw new IllegalArgumentException(
- "nestingKind must be TOP_LEVEL or MEMBER: " + nestingKind);
- }
- }
-
- /**
- * Returns {@code true} if this component implementation represents a component that has already
- * been compiled. If this returns true, the implementation will have no {@link #graph
- * BindingGraph}.
- */
- boolean isDeserializedImplementation() {
- return !graph.isPresent();
- }
-
- // TODO(ronshapiro): see if we can remove this method and instead inject it in the objects that
- // need it.
- /** Returns the binding graph for the component being generated. */
- BindingGraph graph() {
- checkState(!isDeserializedImplementation(),
- "A BindingGraph is not available for deserialized component implementations.");
- return graph.get();
- }
-
- /** Returns the descriptor for the component being generated. */
- ComponentDescriptor componentDescriptor() {
- return componentDescriptor;
- }
-
- /** Returns the name of the component. */
- ClassName name() {
- return name;
- }
-
- /** Returns whether or not the implementation is nested within another class. */
- boolean isNested() {
- return nestingKind.isNested();
- }
-
- /** Returns whether or not the implementation is abstract. */
- boolean isAbstract() {
- return isAbstract;
- }
-
- /** Returns the superclass implementation. */
- Optional<ComponentImplementation> superclassImplementation() {
- return superclassImplementation;
- }
-
- /**
- * Returns the base implementation of this component in ahead-of-time subcomponents mode. If this
- * is the base implementation, this returns {@link Optional#empty()}.
- */
- Optional<ComponentImplementation> baseImplementation() {
- return superclassImplementation.isPresent()
- ? Optional.of(Optionals.rootmostValue(this, c -> c.superclassImplementation))
- : Optional.empty();
- }
-
- /**
- * Returns the {@link #configureInitializationMethod()} of the nearest supertype that defines one,
- * if any.
- *
- * <p>Only returns a present value in {@link CompilerOptions#aheadOfTimeSubcomponents()}.
- */
- Optional<ConfigureInitializationMethod> superConfigureInitializationMethod() {
- for (Optional<ComponentImplementation> currentSuper = superclassImplementation;
- currentSuper.isPresent();
- currentSuper = currentSuper.get().superclassImplementation) {
- if (currentSuper.get().configureInitializationMethod.isPresent()) {
- return currentSuper.get().configureInitializationMethod;
- }
- }
- return Optional.empty();
- }
-
- /**
- * The requirements for creating an instance of this component implementation type.
- *
- * <p>If this component implementation is concrete, these requirements will be in the order that
- * the implementation's constructor takes them as parameters.
- */
- ImmutableSet<ComponentRequirement> requirements() {
- // If the base implementation's creator is being generated in ahead-of-time-subcomponents
- // mode, this uses the ComponentDescriptor's requirements() since Dagger doesn't know what
- // modules may end being unused or owned by an ancestor component. Otherwise, we use the
- // necessary component requirements.
- // TODO(ronshapiro): can we remove the second condition here? Or, is it never going to be
- // called, so we should enforce that invariant?
- return isAbstract() && !superclassImplementation().isPresent()
- ? componentDescriptor().requirements()
- : graph().componentRequirements();
- }
-
- /**
- * Returns the {@link MethodSpecKind#CONFIGURE_INITIALIZATION_METHOD} of this implementation if
- * there is one.
- *
- * <p>Only returns a present value in {@link CompilerOptions#aheadOfTimeSubcomponents()}.
- */
- Optional<ConfigureInitializationMethod> configureInitializationMethod() {
- return configureInitializationMethod;
- }
-
- /**
- * Set's this component implementation's {@code configureInitialization()} method and {@linkplain
- * #addMethod(MethodSpecKind, MethodSpec) adds the method}.
- */
- void setConfigureInitializationMethod(ConfigureInitializationMethod method) {
- configureInitializationMethod = Optional.of(method);
- addMethod(
- MethodSpecKind.CONFIGURE_INITIALIZATION_METHOD,
- addConfigureInitializationMetadata(method));
- }
-
- private MethodSpec addConfigureInitializationMetadata(ConfigureInitializationMethod method) {
- if (!shouldEmitModifiableMetadataAnnotations()) {
- return method.spec();
- }
- AnnotationSpec.Builder annotation =
- AnnotationSpec.builder(ConfigureInitializationParameters.class);
- for (ComponentRequirement parameter : method.parameters()) {
- annotation.addMember("value", toAnnotationValue(parameter.toProto()));
- }
-
- return method.spec().toBuilder().addAnnotation(annotation.build()).build();
- }
-
- void setCreatorImplementation(Optional<ComponentCreatorImplementation> creatorImplementation) {
- checkState(
- this.creatorImplementation == null, "setCreatorImplementation has already been called");
- this.creatorImplementation = creatorImplementation;
- }
-
- Optional<ComponentCreatorImplementation> creatorImplementation() {
- checkState(creatorImplementation != null, "setCreatorImplementation has not been called yet");
- return creatorImplementation;
- }
-
- /**
- * Returns the {@link ComponentCreatorImplementation} defined in the base implementation for this
- * component, if one exists.
- */
- Optional<ComponentCreatorImplementation> baseCreatorImplementation() {
- return baseImplementation().flatMap(baseImpl -> baseImpl.creatorImplementation());
- }
-
- /**
- * Returns the kind of this component's creator.
- *
- * @throws IllegalStateException if the component has no creator
- */
- private ComponentCreatorKind creatorKind() {
- checkState(componentDescriptor().hasCreator());
- return componentDescriptor()
- .creatorDescriptor()
- .map(ComponentCreatorDescriptor::kind)
- .orElse(BUILDER);
- }
-
- /**
- * Returns the name of the creator class for this component. It will be a sibling of this
- * generated class unless this is a top-level component, in which case it will be nested.
- */
- ClassName getCreatorName() {
- return isNested()
- ? name.peerClass(subcomponentNames().getCreatorName(componentDescriptor()))
- : name.nestedClass(creatorKind().typeName());
- }
-
- /** Returns the name of the nested implementation class for a child component. */
- ClassName getSubcomponentName(ComponentDescriptor childDescriptor) {
- checkArgument(
- componentDescriptor().childComponents().contains(childDescriptor),
- "%s is not a child component of %s",
- childDescriptor.typeElement(),
- componentDescriptor().typeElement());
- return name.nestedClass(subcomponentNames().get(childDescriptor) + "Impl");
- }
-
- /**
- * Returns the simple name of the creator implementation class for the given subcomponent creator
- * {@link Key}.
- */
- String getSubcomponentCreatorSimpleName(Key key) {
- return subcomponentNames().getCreatorName(key);
- }
-
- private SubcomponentNames subcomponentNames() {
- checkState(
- subcomponentNames.isPresent(),
- "SubcomponentNames is not available for deserialized component implementations.");
- return subcomponentNames.get();
- }
-
- /** Returns the child implementation. */
- Optional<ComponentImplementation> childImplementation(ComponentDescriptor child) {
- return Optional.ofNullable(childImplementations.get(child.typeElement()));
- }
-
- /** Returns {@code true} if {@code type} is accessible from the generated component. */
- boolean isTypeAccessible(TypeMirror type) {
- return isTypeAccessibleFrom(type, name.packageName());
- }
-
- /** Adds the given super type to the component. */
- void addSupertype(TypeElement supertype) {
- TypeSpecs.addSupertype(component, supertype);
- }
-
- /** Adds the given super class to the subcomponent. */
- void addSuperclass(ClassName className) {
- checkState(
- superclassImplementation.isPresent(),
- "Setting the superclass for component [%s] when there is no superclass implementation.",
- name);
- component.superclass(className);
- }
-
- // TODO(dpb): Consider taking FieldSpec, and returning identical FieldSpec with unique name?
- /** Adds the given field to the component. */
- void addField(FieldSpecKind fieldKind, FieldSpec fieldSpec) {
- fieldSpecsMap.put(fieldKind, fieldSpec);
- }
-
- /** Adds the given fields to the component. */
- void addFields(FieldSpecKind fieldKind, Iterable<FieldSpec> fieldSpecs) {
- fieldSpecsMap.putAll(fieldKind, fieldSpecs);
- }
-
- // TODO(dpb): Consider taking MethodSpec, and returning identical MethodSpec with unique name?
- /** Adds the given method to the component. */
- void addMethod(MethodSpecKind methodKind, MethodSpec methodSpec) {
- methodSpecsMap.put(methodKind, methodSpec);
- }
-
- /** Adds the given annotation to the component. */
- void addAnnotation(AnnotationSpec annotation) {
- component.addAnnotation(annotation);
- }
-
- /**
- * Adds the given method to the component. In this case, the method represents an encapsulation of
- * a modifiable binding between implementations of a subcomponent. This is only relevant for
- * ahead-of-time subcomponents.
- */
- void addModifiableBindingMethod(
- ModifiableBindingType type,
- BindingRequest request,
- TypeMirror returnType,
- MethodSpec methodSpec,
- boolean finalized) {
- addModifiableMethod(
- MethodSpecKind.MODIFIABLE_BINDING_METHOD, type, request, returnType, methodSpec, finalized);
- }
-
- /**
- * Adds a component method that is modifiable to the component. In this case, the method
- * represents an encapsulation of a modifiable binding between implementations of a subcomponent.
- * This is only relevant for ahead-of-time subcomponents.
- */
- void addModifiableComponentMethod(
- ModifiableBindingType type,
- BindingRequest request,
- TypeMirror returnType,
- MethodSpec methodSpec,
- boolean finalized) {
- addModifiableMethod(
- MethodSpecKind.COMPONENT_METHOD, type, request, returnType, methodSpec, finalized);
- }
-
- private void addModifiableMethod(
- MethodSpecKind methodKind,
- ModifiableBindingType type,
- BindingRequest request,
- TypeMirror returnType,
- MethodSpec methodSpec,
- boolean finalized) {
- modifiableBindingMethods.addModifiableMethod(
- type, request, returnType, methodSpec, finalized);
- methodSpecsMap.put(methodKind, withModifiableBindingMetadata(methodSpec, type, request));
- }
-
- /** Adds the implementation for the given {@link ModifiableBindingMethod} to the component. */
- void addImplementedModifiableBindingMethod(ModifiableBindingMethod method) {
- modifiableBindingMethods.addReimplementedMethod(method);
- methodSpecsMap.put(
- MethodSpecKind.MODIFIABLE_BINDING_METHOD,
- withModifiableBindingMetadata(method.methodSpec(), method.type(), method.request()));
- }
-
- private MethodSpec withModifiableBindingMetadata(
- MethodSpec method, ModifiableBindingType type, BindingRequest request) {
- if (!shouldEmitModifiableMetadataAnnotations()) {
- return method;
- }
- AnnotationSpec.Builder metadata =
- AnnotationSpec.builder(ModifiableBinding.class)
- .addMember("modifiableBindingType", "$S", type.name())
- .addMember("bindingRequest", toAnnotationValue(request.toProto()));
- for (Key multibindingContribution : multibindingContributionsMade.get(request)) {
- metadata.addMember(
- "multibindingContributions",
- toAnnotationValue(KeyFactory.toProto(multibindingContribution)));
- }
- return method.toBuilder().addAnnotation(metadata.build()).build();
- }
-
- /** Add's a modifiable module method to this implementation. */
- void addModifiableModuleMethod(ComponentRequirement module, MethodSpec method) {
- registerModifiableModuleMethod(module, method.name);
- methodSpecsMap.put(
- MethodSpecKind.MODIFIABLE_BINDING_METHOD, withModifiableModuleMetadata(module, method));
- }
-
- /** Registers a modifiable module method with {@code name} for {@code module}. */
- void registerModifiableModuleMethod(ComponentRequirement module, String name) {
- checkArgument(module.kind().isModule());
- checkState(modifiableModuleMethods.put(module, name) == null);
- }
-
- private MethodSpec withModifiableModuleMetadata(ComponentRequirement module, MethodSpec method) {
- if (!shouldEmitModifiableMetadataAnnotations()) {
- return method;
- }
- return method
- .toBuilder()
- .addAnnotation(
- AnnotationSpec.builder(ModifiableModule.class)
- .addMember("value", toAnnotationValue(module.toProto()))
- .build())
- .build();
- }
-
- /**
- * Returns {@code true} if the generated component should include metadata annotations with
- * information to deserialize this {@link ComponentImplementation} in future compilations.
- */
- boolean shouldEmitModifiableMetadataAnnotations() {
- return isAbstract && compilerOptions.emitModifiableMetadataAnnotations();
- }
-
- /** Adds the given type to the component. */
- void addType(TypeSpecKind typeKind, TypeSpec typeSpec) {
- typeSpecsMap.put(typeKind, typeSpec);
- }
-
- /** Adds the type generated from the given child implementation. */
- void addChild(ComponentDescriptor child, ComponentImplementation childImplementation) {
- childImplementations.put(child.typeElement(), childImplementation);
- addType(TypeSpecKind.SUBCOMPONENT, childImplementation.generate().build());
- }
-
- /** Adds a {@link Supplier} for the SwitchingProvider for the component. */
- void addSwitchingProvider(Supplier<TypeSpec> typeSpecSupplier) {
- switchingProviderSupplier.add(typeSpecSupplier);
- }
-
- /** Adds the given code block to the initialize methods of the component. */
- void addInitialization(CodeBlock codeBlock) {
- initializations.add(codeBlock);
- }
-
- /**
- * Adds the given component requirement as one that should have a parameter in the component's
- * initialization methods.
- */
- void addComponentRequirementParameter(ComponentRequirement requirement) {
- componentRequirementParameters.add(requirement);
- }
-
- /**
- * The set of component requirements that have parameters in the component's initialization
- * methods.
- */
- ImmutableSet<ComponentRequirement> getComponentRequirementParameters() {
- return ImmutableSet.copyOf(componentRequirementParameters);
- }
-
- /** Adds the given code block that initializes a {@link ComponentRequirement}. */
- void addComponentRequirementInitialization(CodeBlock codeBlock) {
- componentRequirementInitializations.add(codeBlock);
- }
-
- /**
- * Marks the given key of a producer as one that should have a cancellation statement in the
- * cancellation listener method of the component.
- */
- void addCancellableProducerKey(Key key) {
- cancellableProducerKeys.add(key);
- }
-
- /** Returns a new, unique field name for the component based on the given name. */
- String getUniqueFieldName(String name) {
- return componentFieldNames.getUniqueName(name);
- }
-
- /** Returns a new, unique method name for the component based on the given name. */
- String getUniqueMethodName(String name) {
- return componentMethodNames.getUniqueName(name);
- }
-
- /** Returns a new, unique method name for a getter method for the given request. */
- String getUniqueMethodName(BindingRequest request) {
- return uniqueMethodName(request, KeyVariableNamer.name(request.key()));
- }
-
- private String uniqueMethodName(BindingRequest request, String bindingName) {
- String baseMethodName =
- "get"
- + LOWER_CAMEL.to(UPPER_CAMEL, bindingName)
- + (request.isRequestKind(RequestKind.INSTANCE)
- ? ""
- : UPPER_UNDERSCORE.to(UPPER_CAMEL, request.kindName()));
- return getUniqueMethodName(baseMethodName);
- }
-
- /** Gets the parameter name to use for the given requirement for this component. */
- String getParameterName(ComponentRequirement requirement) {
- return getParameterName(requirement, requirement.variableName());
- }
-
- /**
- * Gets the parameter name to use for the given requirement for this component, starting with the
- * given base name if no parameter name has already been selected for the requirement.
- */
- String getParameterName(ComponentRequirement requirement, String baseName) {
- return componentRequirementParameterNames.computeIfAbsent(
- requirement, r -> getUniqueFieldName(baseName));
- }
-
- /** Claims a new method name for the component. Does nothing if method name already exists. */
- void claimMethodName(CharSequence name) {
- componentMethodNames.claim(name);
- }
-
- /** Returns the list of {@link CodeBlock}s that need to go in the initialize method. */
- ImmutableList<CodeBlock> getInitializations() {
- return ImmutableList.copyOf(initializations);
- }
-
- /**
- * Returns a list of {@link CodeBlock}s for initializing {@link ComponentRequirement}s.
- *
- * <p>These initializations are kept separate from {@link #getInitializations()} because they must
- * be executed before the initializations of any framework instance initializations in a
- * superclass implementation that may depend on the instances. We cannot use the same strategy
- * that we use for framework instances (i.e. wrap in a {@link dagger.internal.DelegateFactory} or
- * {@link dagger.producers.internal.DelegateProducer} since the types of these initialized fields
- * have no interface type that we can write a proxy for.
- */
- ImmutableList<CodeBlock> getComponentRequirementInitializations() {
- return ImmutableList.copyOf(componentRequirementInitializations);
- }
-
- /**
- * Returns whether or not this component has any {@linkplain #getInitializations() initilizations}
- * or {@linkplain #getComponentRequirementInitializations() component requirement
- * initializations}.
- */
- boolean hasInitializations() {
- return !initializations.isEmpty() || !componentRequirementInitializations.isEmpty();
- }
-
- /**
- * Returns the list of producer {@link Key}s that need cancellation statements in the cancellation
- * listener method.
- */
- ImmutableList<Key> getCancellableProducerKeys() {
- Optional<ComponentImplementation> currentSuperImplementation = superclassImplementation;
- Set<Key> cancelledKeysFromSuperclass = new HashSet<>();
- while (currentSuperImplementation.isPresent()) {
- cancelledKeysFromSuperclass.addAll(currentSuperImplementation.get().cancellableProducerKeys);
- currentSuperImplementation = currentSuperImplementation.get().superclassImplementation;
- }
- return Sets.difference(cancellableProducerKeys, cancelledKeysFromSuperclass)
- .immutableCopy()
- .asList();
- }
-
- /**
- * Returns the {@link ModifiableBindingMethod}s for this subcomponent implementation and its
- * superclasses.
- */
- ImmutableMap<BindingRequest, ModifiableBindingMethod> getModifiableBindingMethods() {
- Map<BindingRequest, ModifiableBindingMethod> modifiableBindingMethodsBuilder =
- new LinkedHashMap<>();
- if (superclassImplementation.isPresent()) {
- modifiableBindingMethodsBuilder.putAll(
- Maps.filterValues(
- superclassImplementation.get().getModifiableBindingMethods(),
- // filters the modifiable methods of a superclass that are finalized in this component
- method -> !modifiableBindingMethods.finalized(method)));
- }
- // replace superclass modifiable binding methods with any that are defined in this component
- // implementation
- modifiableBindingMethodsBuilder.putAll(modifiableBindingMethods.getNonFinalizedMethods());
- return ImmutableMap.copyOf(modifiableBindingMethodsBuilder);
- }
-
- /**
- * Returns the names of every modifiable method of this implementation and any superclass
- * implementations.
- */
- ImmutableSet<String> getAllModifiableMethodNames() {
- ImmutableSet.Builder<String> names = ImmutableSet.builder();
- modifiableBindingMethods.allMethods().forEach(method -> names.add(method.methodSpec().name));
- names.addAll(modifiableModuleMethods.values());
- superclassImplementation.ifPresent(
- superclass -> names.addAll(superclass.getAllModifiableMethodNames()));
- return names.build();
- }
-
- /**
- * Returns the {@link ModifiableBindingMethod} for this subcomponent for the given binding, if it
- * exists.
- */
- Optional<ModifiableBindingMethod> getModifiableBindingMethod(BindingRequest request) {
- Optional<ModifiableBindingMethod> method = modifiableBindingMethods.getMethod(request);
- if (!method.isPresent() && superclassImplementation.isPresent()) {
- return superclassImplementation.get().getModifiableBindingMethod(request);
- }
- return method;
- }
-
- /**
- * Returns the {@link ModifiableBindingMethod} of a supertype for this method's {@code request},
- * if one exists.
- */
- Optional<ModifiableBindingMethod> supertypeModifiableBindingMethod(BindingRequest request) {
- return superclassImplementation()
- .flatMap(superImplementation -> superImplementation.getModifiableBindingMethod(request));
- }
-
- /**
- * Returns the names of modifiable module methods for this implementation and all inherited
- * implementations, keyed by the corresponding module's {@link ComponentRequirement}.
- */
- ImmutableMap<ComponentRequirement, String> getAllModifiableModuleMethods() {
- ImmutableMap.Builder<ComponentRequirement, String> methods = ImmutableMap.builder();
- methods.putAll(modifiableModuleMethods);
- superclassImplementation.ifPresent(
- superclass -> methods.putAll(superclass.getAllModifiableModuleMethods()));
- return methods.build();
- }
-
- /**
- * Returns the name of the modifiable module method for {@code module} that is inherited in this
- * implementation, or empty if none has been defined.
- */
- Optional<String> supertypeModifiableModuleMethodName(ComponentRequirement module) {
- checkArgument(module.kind().isModule());
- if (!superclassImplementation.isPresent()) {
- return Optional.empty();
- }
- String methodName = superclassImplementation.get().modifiableModuleMethods.get(module);
- if (methodName == null) {
- return superclassImplementation.get().supertypeModifiableModuleMethodName(module);
- }
- return Optional.of(methodName);
- }
-
- /** Generates the component and returns the resulting {@link TypeSpec.Builder}. */
- TypeSpec.Builder generate() {
- fieldSpecsMap.asMap().values().forEach(component::addFields);
- methodSpecsMap.asMap().values().forEach(component::addMethods);
- typeSpecsMap.asMap().values().forEach(component::addTypes);
- switchingProviderSupplier.stream().map(Supplier::get).forEach(component::addType);
- return component;
- }
-
- /**
- * Registers a {@ProvisionBinding} representing a multibinding as having been implemented in this
- * component. Multibindings are modifiable across subcomponent implementations and this allows us
- * to know whether a contribution has been made by a superclass implementation. This is only
- * relevant for ahead-of-time subcomponents.
- */
- void registerImplementedMultibinding(
- ContributionBinding multibinding, BindingRequest bindingRequest) {
- checkArgument(multibinding.isSyntheticMultibinding());
- // We register a multibinding as implemented each time we request the multibinding expression,
- // so only modify the set of contributions once.
- if (!multibindingContributionsMade.containsKey(bindingRequest)) {
- registerImplementedMultibindingKeys(
- bindingRequest,
- multibinding.dependencies().stream().map(DependencyRequest::key).collect(toList()));
- }
- }
-
- /**
- * Registers the multibinding contributions represented by {@code keys} as having been implemented
- * in this component. Multibindings are modifiable across subcomponent implementations and this
- * allows us to know whether a contribution has been made by a superclass implementation. This is
- * only relevant for ahead-of-time subcomponents.
- */
- void registerImplementedMultibindingKeys(BindingRequest bindingRequest, Iterable<Key> keys) {
- multibindingContributionsMade.putAll(bindingRequest, keys);
- }
-
- /**
- * Returns the set of multibinding contributions associated with all superclass implementations of
- * a multibinding.
- */
- ImmutableSet<Key> superclassContributionsMade(BindingRequest bindingRequest) {
- return superclassImplementation
- .map(s -> s.getAllMultibindingContributions(bindingRequest))
- .orElse(ImmutableSet.of());
- }
-
- /**
- * Returns the set of multibinding contributions associated with all implementations of a
- * multibinding.
- */
- private ImmutableSet<Key> getAllMultibindingContributions(BindingRequest bindingRequest) {
- return ImmutableSet.copyOf(
- Sets.union(
- multibindingContributionsMade.get(bindingRequest),
- superclassContributionsMade(bindingRequest)));
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentImplementationBuilder.java b/java/dagger/internal/codegen/ComponentImplementationBuilder.java
deleted file mode 100644
index 7579ac9ef..000000000
--- a/java/dagger/internal/codegen/ComponentImplementationBuilder.java
+++ /dev/null
@@ -1,826 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
-import static com.google.auto.common.MoreTypes.asDeclared;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.base.Predicates.in;
-import static com.squareup.javapoet.MethodSpec.constructorBuilder;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static dagger.internal.codegen.BindingRequest.bindingRequest;
-import static dagger.internal.codegen.ComponentCreatorKind.BUILDER;
-import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.BUILDER_METHOD;
-import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.CANCELLATION_LISTENER_METHOD;
-import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.COMPONENT_METHOD;
-import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.CONSTRUCTOR;
-import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.INITIALIZE_METHOD;
-import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.MODIFIABLE_BINDING_METHOD;
-import static dagger.internal.codegen.ComponentImplementation.TypeSpecKind.COMPONENT_CREATOR;
-import static dagger.internal.codegen.ComponentImplementation.TypeSpecKind.SUBCOMPONENT;
-import static dagger.internal.codegen.DaggerStreams.toImmutableList;
-import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
-import static dagger.internal.codegen.javapoet.CodeBlocks.parameterNames;
-import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
-import static dagger.producers.CancellationPolicy.Propagation.PROPAGATE;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PROTECTED;
-import static javax.lang.model.element.Modifier.PUBLIC;
-import static javax.lang.model.element.Modifier.STATIC;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multimaps;
-import com.google.common.collect.Sets;
-import com.squareup.javapoet.AnnotationSpec;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.ParameterSpec;
-import com.squareup.javapoet.TypeName;
-import com.squareup.javapoet.TypeSpec;
-import dagger.internal.ComponentDefinitionType;
-import dagger.internal.Preconditions;
-import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.internal.codegen.ComponentImplementation.ConfigureInitializationMethod;
-import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
-import dagger.internal.codegen.javapoet.AnnotationSpecs;
-import dagger.internal.codegen.javapoet.CodeBlocks;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.Key;
-import dagger.producers.internal.CancellationListener;
-import dagger.producers.internal.Producers;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.function.Function;
-import javax.inject.Inject;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.type.DeclaredType;
-
-/** A builder of {@link ComponentImplementation}s. */
-abstract class ComponentImplementationBuilder {
- private static final String MAY_INTERRUPT_IF_RUNNING = "mayInterruptIfRunning";
-
- /**
- * How many statements per {@code initialize()} or {@code onProducerFutureCancelled()} method
- * before they get partitioned.
- */
- private static final int STATEMENTS_PER_METHOD = 100;
-
- private static final String CANCELLATION_LISTENER_METHOD_NAME = "onProducerFutureCancelled";
-
- // TODO(ronshapiro): replace this with composition instead of inheritance so we don't have
- // non-final fields
- @Inject BindingGraph graph;
- @Inject ComponentBindingExpressions bindingExpressions;
- @Inject ComponentRequirementExpressions componentRequirementExpressions;
- @Inject ComponentImplementation componentImplementation;
- @Inject ComponentCreatorImplementationFactory componentCreatorImplementationFactory;
- @Inject DaggerTypes types;
- @Inject DaggerElements elements;
- @Inject CompilerOptions compilerOptions;
- @Inject ComponentImplementationFactory componentImplementationFactory;
- @Inject TopLevelImplementationComponent topLevelImplementationComponent;
- private boolean done;
-
- /**
- * Returns a {@link ComponentImplementation} for this component. This is only intended to be
- * called once (and will throw on successive invocations). If the component must be regenerated,
- * use a new instance.
- */
- final ComponentImplementation build() {
- checkState(
- !done,
- "ComponentImplementationBuilder has already built the ComponentImplementation for [%s].",
- componentImplementation.name());
- setSupertype();
- componentImplementation.setCreatorImplementation(
- componentCreatorImplementationFactory.create(
- componentImplementation, Optional.of(componentImplementation.graph())));
- componentImplementation
- .creatorImplementation()
- .map(ComponentCreatorImplementation::spec)
- .ifPresent(this::addCreatorClass);
-
- getLocalAndInheritedMethods(graph.componentTypeElement(), types, elements)
- .forEach(method -> componentImplementation.claimMethodName(method.getSimpleName()));
- componentImplementation
- .superclassImplementation()
- .ifPresent(
- superclassImplementation -> {
- superclassImplementation
- .getAllModifiableMethodNames()
- .forEach(componentImplementation::claimMethodName);
- });
-
- addFactoryMethods();
- addInterfaceMethods();
- addChildComponents();
- implementModifiableModuleMethods();
-
- addConstructorAndInitializationMethods();
-
- if (graph.componentDescriptor().isProduction()) {
- addCancellationListenerImplementation();
- }
-
- if (componentImplementation.isAbstract()
- && !componentImplementation.baseImplementation().isPresent()) {
- componentImplementation.addAnnotation(compilerOptions.toGenerationOptionsAnnotation());
- }
-
- if (componentImplementation.shouldEmitModifiableMetadataAnnotations()) {
- componentImplementation.addAnnotation(
- AnnotationSpec.builder(ComponentDefinitionType.class)
- .addMember("value", "$T.class", graph.componentTypeElement())
- .build());
- }
-
- done = true;
- return componentImplementation;
- }
-
- /** Set the supertype for this generated class. */
- private void setSupertype() {
- if (componentImplementation.superclassImplementation().isPresent()) {
- componentImplementation.addSuperclass(
- componentImplementation.superclassImplementation().get().name());
- } else {
- componentImplementation.addSupertype(graph.componentTypeElement());
- }
- }
-
- /**
- * Adds {@code creator} as a nested creator class. Root components and subcomponents will nest
- * this in different classes.
- */
- protected abstract void addCreatorClass(TypeSpec creator);
-
- /** Adds component factory methods. */
- protected abstract void addFactoryMethods();
-
- protected void addInterfaceMethods() {
- // Each component method may have been declared by several supertypes. We want to implement
- // only one method for each distinct signature.
- ImmutableListMultimap<MethodSignature, ComponentMethodDescriptor> componentMethodsBySignature =
- Multimaps.index(graph.componentDescriptor().entryPointMethods(), this::getMethodSignature);
- for (List<ComponentMethodDescriptor> methodsWithSameSignature :
- Multimaps.asMap(componentMethodsBySignature).values()) {
- ComponentMethodDescriptor anyOneMethod = methodsWithSameSignature.stream().findAny().get();
- MethodSpec methodSpec = bindingExpressions.getComponentMethod(anyOneMethod);
-
- if (compilerOptions.aheadOfTimeSubcomponents()) {
- addPossiblyModifiableInterfaceMethod(anyOneMethod, methodSpec);
- } else {
- componentImplementation.addMethod(COMPONENT_METHOD, methodSpec);
- }
- }
- }
-
- /**
- * Adds a component interface method in ahead-of-time subcomponents mode. If the binding that
- * implements the method is modifiable, registers the method.
- */
- private void addPossiblyModifiableInterfaceMethod(
- ComponentMethodDescriptor methodDescriptor, MethodSpec implementedComponentMethod) {
- if (methodDescriptor.dependencyRequest().isPresent()
- && componentImplementation
- .getModifiableBindingMethod(bindingRequest(methodDescriptor.dependencyRequest().get()))
- .isPresent()) {
- // If there are multiple component methods that are modifiable and for the same binding
- // request, implement all but one in the base implementation to delegate to the one that
- // will remain (and be registered) modifiable
- checkState(componentImplementation.isAbstract() && !componentImplementation.isNested());
- componentImplementation.addMethod(
- COMPONENT_METHOD, implementedComponentMethod.toBuilder().addModifiers(FINAL).build());
- } else {
- // TODO(b/117833324): Can this class be the one to interface with ComponentImplementation
- // instead of having it go through ModifiableBindingExpressions?
- bindingExpressions
- .modifiableBindingExpressions()
- .addPossiblyModifiableComponentMethod(methodDescriptor, implementedComponentMethod);
- }
- }
-
- private void addCancellationListenerImplementation() {
- componentImplementation.addSupertype(elements.getTypeElement(CancellationListener.class));
- componentImplementation.claimMethodName(CANCELLATION_LISTENER_METHOD_NAME);
-
- ImmutableList<ParameterSpec> parameters =
- ImmutableList.of(ParameterSpec.builder(boolean.class, MAY_INTERRUPT_IF_RUNNING).build());
-
- MethodSpec.Builder methodBuilder =
- methodBuilder(CANCELLATION_LISTENER_METHOD_NAME)
- .addModifiers(PUBLIC)
- .addAnnotation(Override.class)
- .addParameters(parameters);
- if (componentImplementation.superclassImplementation().isPresent()) {
- methodBuilder.addStatement(
- "super.$L($L)", CANCELLATION_LISTENER_METHOD_NAME, MAY_INTERRUPT_IF_RUNNING);
- }
-
- ImmutableList<CodeBlock> cancellationStatements = cancellationStatements();
-
- if (cancellationStatements.size() < STATEMENTS_PER_METHOD) {
- methodBuilder.addCode(CodeBlocks.concat(cancellationStatements)).build();
- } else {
- ImmutableList<MethodSpec> cancelProducersMethods =
- createPartitionedMethods(
- "cancelProducers",
- parameters,
- cancellationStatements,
- methodName -> methodBuilder(methodName).addModifiers(PRIVATE));
- for (MethodSpec cancelProducersMethod : cancelProducersMethods) {
- methodBuilder.addStatement("$N($L)", cancelProducersMethod, MAY_INTERRUPT_IF_RUNNING);
- componentImplementation.addMethod(CANCELLATION_LISTENER_METHOD, cancelProducersMethod);
- }
- }
-
- Optional<CodeBlock> cancelParentStatement = cancelParentStatement();
- cancelParentStatement.ifPresent(methodBuilder::addCode);
-
- if (cancellationStatements.isEmpty()
- && !cancelParentStatement.isPresent()
- && componentImplementation.superclassImplementation().isPresent()) {
- // Partial child implementations that have no new cancellations don't need to override
- // the method just to call super().
- return;
- }
-
- componentImplementation.addMethod(CANCELLATION_LISTENER_METHOD, methodBuilder.build());
- }
-
- private ImmutableList<CodeBlock> cancellationStatements() {
- // Reversing should order cancellations starting from entry points and going down to leaves
- // rather than the other way around. This shouldn't really matter but seems *slightly*
- // preferable because:
- // When a future that another future depends on is cancelled, that cancellation will propagate
- // up the future graph toward the entry point. Cancelling in reverse order should ensure that
- // everything that depends on a particular node has already been cancelled when that node is
- // cancelled, so there's no need to propagate. Otherwise, when we cancel a leaf node, it might
- // propagate through most of the graph, making most of the cancel calls that follow in the
- // onProducerFutureCancelled method do nothing.
- ImmutableList<Key> cancellationKeys =
- componentImplementation.getCancellableProducerKeys().reverse();
-
- ImmutableList.Builder<CodeBlock> cancellationStatements = ImmutableList.builder();
- for (Key cancellationKey : cancellationKeys) {
- cancellationStatements.add(
- CodeBlock.of(
- "$T.cancel($L, $N);",
- Producers.class,
- bindingExpressions
- .getDependencyExpression(
- bindingRequest(cancellationKey, FrameworkType.PRODUCER_NODE),
- componentImplementation.name())
- .codeBlock(),
- MAY_INTERRUPT_IF_RUNNING));
- }
- return cancellationStatements.build();
- }
-
- protected Optional<CodeBlock> cancelParentStatement() {
- // Returns empty by default. Overridden in subclass(es) to add a statement if and only if the
- // component being generated is a concrete subcomponent implementation with a parent that
- // allows cancellation to propagate to it from subcomponents.
- return Optional.empty();
- }
-
- /**
- * For final components, reimplements all modifiable module methods that may have been modified.
- */
- private void implementModifiableModuleMethods() {
- if (componentImplementation.isAbstract()) {
- return;
- }
- componentImplementation
- .getAllModifiableModuleMethods()
- .forEach(this::implementModifiableModuleMethod);
- }
-
- private void implementModifiableModuleMethod(ComponentRequirement module, String methodName) {
- // TODO(b/117833324): only reimplement methods for modules that were abstract or were repeated
- // by an ancestor component.
- componentImplementation.addMethod(
- MODIFIABLE_BINDING_METHOD,
- methodBuilder(methodName)
- .addAnnotation(Override.class)
- .addModifiers(PROTECTED)
- .returns(TypeName.get(module.type()))
- .addStatement(
- componentRequirementExpressions
- .getExpression(module)
- .getModifiableModuleMethodExpression(componentImplementation.name()))
- .build());
- }
-
- private MethodSignature getMethodSignature(ComponentMethodDescriptor method) {
- return MethodSignature.forComponentMethod(
- method, MoreTypes.asDeclared(graph.componentTypeElement().asType()), types);
- }
-
- private void addChildComponents() {
- for (BindingGraph subgraph : graph.subgraphs()) {
- // TODO(b/117833324): Can an abstract inner subcomponent implementation be elided if it's
- // totally empty?
- componentImplementation.addChild(
- subgraph.componentDescriptor(), buildChildImplementation(subgraph));
- }
- }
-
- private ComponentImplementation buildChildImplementation(BindingGraph childGraph) {
- ComponentImplementation childImplementation =
- compilerOptions.aheadOfTimeSubcomponents()
- ? abstractInnerSubcomponent(childGraph)
- : concreteSubcomponent(childGraph);
- return topLevelImplementationComponent
- .currentImplementationSubcomponentBuilder()
- .componentImplementation(childImplementation)
- .bindingGraph(childGraph)
- .parentBuilder(Optional.of(this))
- .parentBindingExpressions(Optional.of(bindingExpressions))
- .parentRequirementExpressions(Optional.of(componentRequirementExpressions))
- .build()
- .subcomponentBuilder()
- .build();
- }
-
- /** Creates an inner abstract subcomponent implementation. */
- private ComponentImplementation abstractInnerSubcomponent(BindingGraph childGraph) {
- return componentImplementation.childComponentImplementation(
- childGraph,
- Optional.of(
- componentImplementationFactory.findChildSuperclassImplementation(
- childGraph.componentDescriptor(), componentImplementation)),
- PROTECTED,
- componentImplementation.isAbstract() ? ABSTRACT : FINAL);
- }
-
- /** Creates a concrete inner subcomponent implementation. */
- private ComponentImplementation concreteSubcomponent(BindingGraph childGraph) {
- return componentImplementation.childComponentImplementation(
- childGraph,
- Optional.empty(), // superclassImplementation
- PRIVATE,
- FINAL);
- }
-
- /** Creates and adds the constructor and methods needed for initializing the component. */
- private void addConstructorAndInitializationMethods() {
- MethodSpec.Builder constructor = componentConstructorBuilder();
- if (!componentImplementation.isAbstract()) {
- implementInitializationMethod(constructor, initializationParameters());
- } else if (componentImplementation.hasInitializations()) {
- addConfigureInitializationMethod();
- }
- componentImplementation.addMethod(CONSTRUCTOR, constructor.build());
- }
-
- /** Returns a builder for the component's constructor. */
- private MethodSpec.Builder componentConstructorBuilder() {
- return constructorBuilder()
- .addModifiers(componentImplementation.isAbstract() ? PROTECTED : PRIVATE);
- }
-
- /** Adds parameters and code to the given {@code initializationMethod}. */
- private void implementInitializationMethod(
- MethodSpec.Builder initializationMethod,
- ImmutableMap<ComponentRequirement, ParameterSpec> initializationParameters) {
- initializationMethod.addParameters(initializationParameters.values());
- initializationMethod.addCode(
- CodeBlocks.concat(componentImplementation.getComponentRequirementInitializations()));
- componentImplementation
- .superConfigureInitializationMethod()
- .ifPresent(
- superConfigureInitializationMethod ->
- addSuperConfigureInitializationCall(
- initializationMethod,
- initializationParameters,
- superConfigureInitializationMethod));
- addInitializeMethods(initializationMethod, initializationParameters.values().asList());
- }
-
- /** Creates and adds a {@code configureInitializatoin} method to the component. */
- private void addConfigureInitializationMethod() {
- MethodSpec.Builder method = configureInitializationMethodBuilder();
- ImmutableMap<ComponentRequirement, ParameterSpec> parameters = initializationParameters();
- implementInitializationMethod(method, parameters);
- componentImplementation.setConfigureInitializationMethod(
- ConfigureInitializationMethod.create(method.build(), parameters.keySet()));
- }
-
- /** Returns a builder for the component's {@code configureInitialization} method. */
- private MethodSpec.Builder configureInitializationMethodBuilder() {
- String methodName = componentImplementation.getUniqueMethodName("configureInitialization");
- MethodSpec.Builder configureInitialization = methodBuilder(methodName).addModifiers(PROTECTED);
- if (overridesSuperclassConfigureInitialization(configureInitialization.build())) {
- configureInitialization.addAnnotation(Override.class);
- }
- return configureInitialization;
- }
-
- /**
- * Returns whether or not the given method overrides a configureInitialization method from a
- * superclass.
- */
- private boolean overridesSuperclassConfigureInitialization(MethodSpec method) {
- for (Optional<ComponentImplementation> currentSuperImplementation =
- componentImplementation.superclassImplementation();
- currentSuperImplementation.isPresent();
- currentSuperImplementation = currentSuperImplementation.get().superclassImplementation()) {
- Optional<MethodSpec> superConfigureInitializationMethod =
- currentSuperImplementation.get().configureInitializationMethod().map(m -> m.spec());
- if (superConfigureInitializationMethod
- .filter(superMethod -> haveSameSignature(method, superMethod))
- .isPresent()) {
- return true;
- }
- }
-
- return false;
- }
-
- /** Returns whether or not methods {@code a} and {@code b} have the same signature. */
- private boolean haveSameSignature(MethodSpec a, MethodSpec b) {
- return a.name.equals(b.name) && types(a.parameters).equals(types(b.parameters));
- }
-
- private ImmutableList<TypeName> types(List<ParameterSpec> parameters) {
- return parameters.stream().map(parameter -> parameter.type).collect(toImmutableList());
- }
-
- /**
- * Adds a call to the superclass's {@code configureInitialization} method to the given {@code
- * callingMethod}.
- */
- private void addSuperConfigureInitializationCall(
- MethodSpec.Builder callingMethod,
- ImmutableMap<ComponentRequirement, ParameterSpec> parameters,
- ConfigureInitializationMethod superConfigureInitializationMethod) {
- // This component's constructor may not have all of the parameters that the superclass's
- // configureInitialization method takes, because the superclass configureInitialization method
- // necessarily accepts things that it can't know whether will be needed or not. If they aren't
- // needed (as is the case when the constructor doesn't have a parameter for the module), just
- // pass null to super.configureInitialization for that parameter; it won't be used.
- CodeBlock args =
- superConfigureInitializationMethod.parameters().stream()
- .map(
- requirement ->
- parameters.containsKey(requirement)
- ? CodeBlock.of("$N", parameters.get(requirement))
- : CodeBlock.of("null"))
- .collect(toParametersCodeBlock());
-
- String qualifier =
- haveSameSignature(callingMethod.build(), superConfigureInitializationMethod.spec())
- ? "super."
- : "";
- callingMethod.addStatement(
- qualifier + "$N($L)", superConfigureInitializationMethod.spec(), args);
- }
-
- /**
- * Adds any necessary {@code initialize} methods to the component and adds calls to them to the
- * given {@code callingMethod}.
- */
- private void addInitializeMethods(
- MethodSpec.Builder callingMethod, ImmutableList<ParameterSpec> parameters) {
- // TODO(cgdecker): It's not the case that each initialize() method has need for all of the
- // given parameters. In some cases, those parameters may have already been assigned to fields
- // which could be referenced instead. In other cases, an initialize method may just not need
- // some of the parameters because the set of initializations in that partition does not
- // include any reference to them. Right now, the Dagger code has no way of getting that
- // information because, among other things, componentImplementation.getImplementations() just
- // returns a bunch of CodeBlocks with no semantic information. Additionally, we may not know
- // yet whether a field will end up needing to be created for a specific requirement, and we
- // don't want to create a field that ends up only being used during initialization.
- CodeBlock args = parameterNames(parameters);
- ImmutableList<MethodSpec> methods =
- createPartitionedMethods(
- "initialize",
- makeFinal(parameters),
- componentImplementation.getInitializations(),
- methodName ->
- methodBuilder(methodName)
- .addModifiers(PRIVATE)
- /* TODO(gak): Strictly speaking, we only need the suppression here if we are
- * also initializing a raw field in this method, but the structure of this
- * code makes it awkward to pass that bit through. This will be cleaned up
- * when we no longer separate fields and initialization as we do now. */
- .addAnnotation(AnnotationSpecs.suppressWarnings(UNCHECKED)));
- for (MethodSpec method : methods) {
- callingMethod.addStatement("$N($L)", method, args);
- componentImplementation.addMethod(INITIALIZE_METHOD, method);
- }
- }
-
- /**
- * Creates one or more methods, all taking the given {@code parameters}, which partition the given
- * list of {@code statements} among themselves such that no method has more than {@code
- * STATEMENTS_PER_METHOD} statements in it and such that the returned methods, if called in order,
- * will execute the {@code statements} in the given order.
- */
- private ImmutableList<MethodSpec> createPartitionedMethods(
- String methodName,
- Iterable<ParameterSpec> parameters,
- List<CodeBlock> statements,
- Function<String, MethodSpec.Builder> methodBuilderCreator) {
- return Lists.partition(statements, STATEMENTS_PER_METHOD).stream()
- .map(
- partition ->
- methodBuilderCreator
- .apply(componentImplementation.getUniqueMethodName(methodName))
- .addParameters(parameters)
- .addCode(CodeBlocks.concat(partition))
- .build())
- .collect(toImmutableList());
- }
-
- /** Returns the given parameters with a final modifier added. */
- private final ImmutableList<ParameterSpec> makeFinal(Collection<ParameterSpec> parameters) {
- return parameters.stream()
- .map(param -> param.toBuilder().addModifiers(FINAL).build())
- .collect(toImmutableList());
- }
-
- /**
- * Returns the parameters for the constructor or {@code configureInitilization} method as a map
- * from the requirement the parameter fulfills to the spec for the parameter.
- */
- private final ImmutableMap<ComponentRequirement, ParameterSpec> initializationParameters() {
- Map<ComponentRequirement, ParameterSpec> parameters;
- if (componentImplementation.componentDescriptor().hasCreator()) {
- parameters =
- Maps.toMap(componentImplementation.requirements(), ComponentRequirement::toParameterSpec);
- } else if (componentImplementation.isAbstract() && componentImplementation.isNested()) {
- // If we're generating an abstract inner subcomponent, then we are not implementing module
- // instance bindings and have no need for factory method parameters.
- parameters = ImmutableMap.of();
- } else if (graph.factoryMethod().isPresent()) {
- parameters = getFactoryMethodParameters(graph);
- } else if (componentImplementation.isAbstract()) {
- // If we're generating an abstract base implementation of a subcomponent it's acceptable to
- // have neither a creator nor factory method.
- parameters = ImmutableMap.of();
- } else {
- throw new AssertionError(
- "Expected either a component creator or factory method but found neither.");
- }
-
- if (componentImplementation.isAbstract()) {
- parameters = Maps.filterKeys(parameters, in(configureInitializationRequirements()));
- }
- return renameParameters(parameters);
- }
-
- /**
- * Returns the set of requirements for the configureInitialization method: the parameters that are
- * needed either for initializing a component requirement field or for calling the superclass's
- * {@code configureInitialization} method.
- */
- private ImmutableSet<ComponentRequirement> configureInitializationRequirements() {
- ImmutableSet<ComponentRequirement> initializationParameters =
- componentImplementation.getComponentRequirementParameters();
- ImmutableSet<ComponentRequirement> superConfigureInitializationRequirements =
- componentImplementation
- .superConfigureInitializationMethod()
- .map(ConfigureInitializationMethod::parameters)
- .orElse(ImmutableSet.of());
- return Sets.union(initializationParameters, superConfigureInitializationRequirements)
- .immutableCopy();
- }
-
- /**
- * Renames the given parameters to guarantee their names do not conflict with fields in the
- * component to ensure that a parameter is never referenced where a reference to a field was
- * intended.
- */
- // TODO(cgdecker): This is a bit kludgy; it would be preferable to either qualify the field
- // references with "this." or "super." when needed to disambiguate between field and parameter,
- // but that would require more context than is currently available when the code referencing a
- // field is generated.
- private ImmutableMap<ComponentRequirement, ParameterSpec> renameParameters(
- Map<ComponentRequirement, ParameterSpec> parameters) {
- return ImmutableMap.copyOf(
- Maps.transformEntries(
- parameters,
- (requirement, parameter) ->
- renameParameter(
- parameter,
- componentImplementation.getParameterName(requirement, parameter.name))));
- }
-
- private ParameterSpec renameParameter(ParameterSpec parameter, String newName) {
- return ParameterSpec.builder(parameter.type, newName)
- .addAnnotations(parameter.annotations)
- .addModifiers(parameter.modifiers)
- .build();
- }
-
- /** Builds a root component implementation. */
- static final class RootComponentImplementationBuilder extends ComponentImplementationBuilder {
- @Inject
- RootComponentImplementationBuilder(ComponentImplementation componentImplementation) {
- checkArgument(!componentImplementation.superclassImplementation().isPresent());
- }
-
- @Override
- protected void addCreatorClass(TypeSpec creator) {
- componentImplementation.addType(COMPONENT_CREATOR, creator);
- }
-
- @Override
- protected void addFactoryMethods() {
- // Top-level components have a static method that returns a builder or factory for the
- // component. If the user defined a @Component.Builder or @Component.Factory, an
- // implementation of their type is returned. Otherwise, an autogenerated Builder type is
- // returned.
- // TODO(cgdecker): Replace this abomination with a small class?
- // Better yet, change things so that an autogenerated builder type has a descriptor of sorts
- // just like a user-defined creator type.
- ComponentCreatorKind creatorKind;
- ClassName creatorType;
- String factoryMethodName;
- boolean noArgFactoryMethod;
- if (creatorDescriptor().isPresent()) {
- ComponentCreatorDescriptor descriptor = creatorDescriptor().get();
- creatorKind = descriptor.kind();
- creatorType = ClassName.get(descriptor.typeElement());
- factoryMethodName = descriptor.factoryMethod().getSimpleName().toString();
- noArgFactoryMethod = descriptor.factoryParameters().isEmpty();
- } else {
- creatorKind = BUILDER;
- creatorType = componentCreatorName();
- factoryMethodName = "build";
- noArgFactoryMethod = true;
- }
-
- MethodSpec creatorFactoryMethod =
- methodBuilder(creatorKind.methodName())
- .addModifiers(PUBLIC, STATIC)
- .returns(creatorType)
- .addStatement("return new $T()", componentCreatorName())
- .build();
- componentImplementation.addMethod(BUILDER_METHOD, creatorFactoryMethod);
- if (noArgFactoryMethod && canInstantiateAllRequirements()) {
- componentImplementation.addMethod(
- BUILDER_METHOD,
- methodBuilder("create")
- .returns(ClassName.get(super.graph.componentTypeElement()))
- .addModifiers(PUBLIC, STATIC)
- .addStatement(
- "return new $L().$L()", creatorKind.typeName(), factoryMethodName)
- .build());
- }
- }
-
- private Optional<ComponentCreatorDescriptor> creatorDescriptor() {
- return graph.componentDescriptor().creatorDescriptor();
- }
-
- /** {@code true} if all of the graph's required dependencies can be automatically constructed */
- private boolean canInstantiateAllRequirements() {
- return !Iterables.any(
- graph.componentRequirements(),
- dependency -> dependency.requiresAPassedInstance(elements, types));
- }
-
- private ClassName componentCreatorName() {
- return componentImplementation.creatorImplementation().get().name();
- }
- }
-
- /**
- * Builds a subcomponent implementation. If generating ahead-of-time subcomponents, this may be an
- * abstract base class implementation, an abstract inner implementation, or a concrete
- * implementation that extends an abstract base implementation. Otherwise it represents a private,
- * inner, concrete, final implementation of a subcomponent which extends a user defined type.
- */
- static final class SubcomponentImplementationBuilder extends ComponentImplementationBuilder {
- final Optional<ComponentImplementationBuilder> parent;
-
- @Inject
- SubcomponentImplementationBuilder(
- @ParentComponent Optional<ComponentImplementationBuilder> parent) {
- this.parent = parent;
- }
-
- @Override
- protected void addCreatorClass(TypeSpec creator) {
- if (parent.isPresent()) {
- // In an inner implementation of a subcomponent the creator is a peer class.
- parent.get().componentImplementation.addType(SUBCOMPONENT, creator);
- } else {
- componentImplementation.addType(SUBCOMPONENT, creator);
- }
- }
-
- @Override
- protected void addFactoryMethods() {
- // Only construct instances of subcomponents that have concrete implementations.
- if (!componentImplementation.isAbstract()) {
- // Use the parent's factory method to create this subcomponent if the
- // subcomponent was not added via {@link dagger.Module#subcomponents()}.
- graph.factoryMethod().ifPresent(this::createSubcomponentFactoryMethod);
- }
- }
-
- private void createSubcomponentFactoryMethod(ExecutableElement factoryMethod) {
- checkState(parent.isPresent());
-
- Collection<ParameterSpec> params = getFactoryMethodParameters(graph).values();
- MethodSpec.Builder method = MethodSpec.overriding(factoryMethod, parentType(), types);
- params.forEach(
- param -> method.addStatement("$T.checkNotNull($N)", Preconditions.class, param));
- method.addStatement(
- "return new $T($L)", componentImplementation.name(), parameterNames(params));
-
- parent.get().componentImplementation.addMethod(COMPONENT_METHOD, method.build());
- }
-
- private DeclaredType parentType() {
- return asDeclared(parent.get().graph.componentTypeElement().asType());
- }
-
- @Override
- protected void addInterfaceMethods() {
- if (componentImplementation.superclassImplementation().isPresent()) {
- // Since we're overriding a subcomponent implementation we add to its implementation given
- // an expanded binding graph.
-
- ComponentImplementation superclassImplementation =
- componentImplementation.superclassImplementation().get();
- for (ModifiableBindingMethod superclassModifiableBindingMethod :
- superclassImplementation.getModifiableBindingMethods().values()) {
- bindingExpressions
- .modifiableBindingExpressions()
- .possiblyReimplementedMethod(superclassModifiableBindingMethod)
- .ifPresent(componentImplementation::addImplementedModifiableBindingMethod);
- }
- } else {
- super.addInterfaceMethods();
- }
- }
-
- @Override
- protected Optional<CodeBlock> cancelParentStatement() {
- if (!shouldPropagateCancellationToParent()) {
- return Optional.empty();
- }
- return Optional.of(
- CodeBlock.builder()
- .addStatement(
- "$T.this.$N($N)",
- parent.get().componentImplementation.name(),
- CANCELLATION_LISTENER_METHOD_NAME,
- MAY_INTERRUPT_IF_RUNNING)
- .build());
- }
-
- private boolean shouldPropagateCancellationToParent() {
- return parent.isPresent()
- && parent
- .get()
- .componentImplementation
- .componentDescriptor()
- .cancellationPolicy()
- .map(policy -> policy.fromSubcomponents().equals(PROPAGATE))
- .orElse(false);
- }
- }
-
- /**
- * Returns the map of {@link ComponentRequirement}s to {@link ParameterSpec}s for the
- * given graph's factory method.
- */
- private static Map<ComponentRequirement, ParameterSpec> getFactoryMethodParameters(
- BindingGraph graph) {
- return Maps.transformValues(graph.factoryMethodParameters(), ParameterSpec::get);
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentImplementationFactory.java b/java/dagger/internal/codegen/ComponentImplementationFactory.java
deleted file mode 100644
index 9578ece0e..000000000
--- a/java/dagger/internal/codegen/ComponentImplementationFactory.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkState;
-import static dagger.internal.codegen.ComponentGenerator.componentName;
-import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
-import static javax.tools.Diagnostic.Kind.WARNING;
-
-import com.squareup.javapoet.ClassName;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.serialization.ProtoSerialization.InconsistentSerializedProtoException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import javax.annotation.processing.Messager;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-import javax.lang.model.element.TypeElement;
-
-/** Factory for {@link ComponentImplementation}s. */
-@Singleton
-final class ComponentImplementationFactory implements ClearableCache {
- private final Map<TypeElement, ComponentImplementation> topLevelComponentCache = new HashMap<>();
- private final KeyFactory keyFactory;
- private final CompilerOptions compilerOptions;
- private final BindingGraphFactory bindingGraphFactory;
- private final TopLevelImplementationComponent.Builder topLevelImplementationComponentBuilder;
- private final DeserializedComponentImplementationBuilder
- deserializedComponentImplementationBuilder;
- private final DaggerElements elements;
- private final Messager messager;
-
- @Inject
- ComponentImplementationFactory(
- KeyFactory keyFactory,
- CompilerOptions compilerOptions,
- BindingGraphFactory bindingGraphFactory,
- TopLevelImplementationComponent.Builder topLevelImplementationComponentBuilder,
- DeserializedComponentImplementationBuilder deserializedComponentImplementationBuilder,
- DaggerElements elements,
- Messager messager) {
- this.keyFactory = keyFactory;
- this.compilerOptions = compilerOptions;
- this.bindingGraphFactory = bindingGraphFactory;
- this.topLevelImplementationComponentBuilder = topLevelImplementationComponentBuilder;
- this.deserializedComponentImplementationBuilder = deserializedComponentImplementationBuilder;
- this.elements = elements;
- this.messager = messager;
- }
-
- /**
- * Returns a top-level (non-nested) component implementation for a binding graph.
- *
- * @throws IllegalStateException if the binding graph is for a subcomponent and
- * ahead-of-time-subcomponents mode is not enabled
- */
- ComponentImplementation createComponentImplementation(BindingGraph bindingGraph) {
- return reentrantComputeIfAbsent(
- topLevelComponentCache,
- bindingGraph.componentTypeElement(),
- component -> createComponentImplementationUncached(bindingGraph));
- }
-
- private ComponentImplementation createComponentImplementationUncached(BindingGraph bindingGraph) {
- ComponentImplementation componentImplementation =
- ComponentImplementation.topLevelComponentImplementation(
- bindingGraph,
- componentName(bindingGraph.componentTypeElement()),
- new SubcomponentNames(bindingGraph, keyFactory),
- compilerOptions);
-
- // TODO(dpb): explore using optional bindings for the "parent" bindings
- CurrentImplementationSubcomponent currentImplementationSubcomponent =
- topLevelImplementationComponentBuilder
- .topLevelComponent(componentImplementation)
- .build()
- .currentImplementationSubcomponentBuilder()
- .componentImplementation(componentImplementation)
- .bindingGraph(bindingGraph)
- .parentBuilder(Optional.empty())
- .parentBindingExpressions(Optional.empty())
- .parentRequirementExpressions(Optional.empty())
- .build();
-
- if (componentImplementation.isAbstract()) {
- checkState(
- compilerOptions.aheadOfTimeSubcomponents(),
- "Calling 'componentImplementation()' on %s when not generating ahead-of-time "
- + "subcomponents.",
- bindingGraph.componentTypeElement());
- return currentImplementationSubcomponent.subcomponentBuilder().build();
- } else {
- return currentImplementationSubcomponent.rootComponentBuilder().build();
- }
- }
-
- /** Returns the superclass of the child nested within a superclass of the parent component. */
- ComponentImplementation findChildSuperclassImplementation(
- ComponentDescriptor child, ComponentImplementation parentImplementation) {
- // If the current component has superclass implementations, a superclass may contain a
- // reference to the child. Traverse this component's superimplementation hierarchy looking for
- // the child's implementation. The child superclass implementation may not be present in the
- // direct superclass implementations if the subcomponent builder was previously a pruned
- // binding.
- for (Optional<ComponentImplementation> parent = parentImplementation.superclassImplementation();
- parent.isPresent();
- parent = parent.get().superclassImplementation()) {
- Optional<ComponentImplementation> superclass = parent.get().childImplementation(child);
- if (superclass.isPresent()) {
- return superclass.get();
- }
- }
-
- if (compilerOptions.emitModifiableMetadataAnnotations()) {
- ClassName childSuperclassName = componentName(child.typeElement());
- TypeElement generatedChildSuperclassImplementation =
- elements.getTypeElement(childSuperclassName);
- if (generatedChildSuperclassImplementation != null) {
- try {
- return deserializedComponentImplementationBuilder.create(
- child, generatedChildSuperclassImplementation);
- } catch (InconsistentSerializedProtoException e) {
- messager.printMessage(
- WARNING,
- String.format(
- "%s was compiled with a different version of Dagger than the version in this "
- + "compilation. To ensure the validity of Dagger's generated code, compile "
- + "all Dagger code with the same version.",
- child.typeElement().getQualifiedName()));
- }
- } else if (compilerOptions.forceUseSerializedComponentImplementations()) {
- throw new TypeNotPresentException(childSuperclassName.toString(), null);
- }
- }
-
- // Otherwise, the superclass implementation is top-level, so we must recreate the
- // implementation object for the base implementation of the child by truncating the binding
- // graph at the child.
- BindingGraph truncatedBindingGraph = bindingGraphFactory.create(child, false);
- return createComponentImplementation(truncatedBindingGraph);
- }
-
- @Override
- public void clearCache() {
- topLevelComponentCache.clear();
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentInstanceBindingExpression.java b/java/dagger/internal/codegen/ComponentInstanceBindingExpression.java
deleted file mode 100644
index e98d595d3..000000000
--- a/java/dagger/internal/codegen/ComponentInstanceBindingExpression.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.codegen.javapoet.Expression;
-
-/** A binding expression for the instance of the component itself, i.e. {@code this}. */
-final class ComponentInstanceBindingExpression extends SimpleInvocationBindingExpression {
- private final ClassName componentName;
- private final ContributionBinding binding;
-
- ComponentInstanceBindingExpression(ResolvedBindings resolvedBindings, ClassName componentName) {
- super(resolvedBindings);
- this.componentName = componentName;
- this.binding = resolvedBindings.contributionBinding();
- }
-
- @Override
- Expression getDependencyExpression(ClassName requestingClass) {
- return Expression.create(
- binding.key().type(),
- componentName.equals(requestingClass)
- ? CodeBlock.of("this")
- : CodeBlock.of("$T.this", componentName));
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentKind.java b/java/dagger/internal/codegen/ComponentKind.java
deleted file mode 100644
index 8e5b9bac6..000000000
--- a/java/dagger/internal/codegen/ComponentKind.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.common.collect.Sets.immutableEnumSet;
-import static dagger.internal.codegen.DaggerStreams.stream;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.DaggerStreams.valuesOf;
-import static java.util.EnumSet.allOf;
-
-import com.google.common.collect.ImmutableSet;
-import dagger.Component;
-import dagger.Module;
-import dagger.Subcomponent;
-import dagger.producers.ProducerModule;
-import dagger.producers.ProductionComponent;
-import dagger.producers.ProductionSubcomponent;
-import java.lang.annotation.Annotation;
-import java.util.Optional;
-import javax.lang.model.element.TypeElement;
-
-/** Enumeration of the different kinds of components. */
-enum ComponentKind {
- /** {@code @Component} */
- COMPONENT(Component.class, true, false),
-
- /** {@code @Subcomponent} */
- SUBCOMPONENT(Subcomponent.class, false, false),
-
- /** {@code @ProductionComponent} */
- PRODUCTION_COMPONENT(ProductionComponent.class, true, true),
-
- /** {@code @ProductionSubcomponent} */
- PRODUCTION_SUBCOMPONENT(ProductionSubcomponent.class, false, true),
-
- /**
- * Kind for a descriptor that was generated from a {@link Module} instead of a component type in
- * order to validate the module's bindings.
- */
- MODULE(Module.class, true, false),
-
- /**
- * Kind for a descriptor was generated from a {@link ProducerModule} instead of a component type
- * in order to validate the module's bindings.
- */
- PRODUCER_MODULE(ProducerModule.class, true, true),
- ;
-
- private static final ImmutableSet<ComponentKind> ROOT_COMPONENT_KINDS =
- valuesOf(ComponentKind.class)
- .filter(kind -> !kind.isForModuleValidation())
- .filter(kind -> kind.isRoot())
- .collect(toImmutableSet());
-
- private static final ImmutableSet<ComponentKind> SUBCOMPONENT_KINDS =
- valuesOf(ComponentKind.class)
- .filter(kind -> !kind.isForModuleValidation())
- .filter(kind -> !kind.isRoot())
- .collect(toImmutableSet());
-
- /** Returns the set of kinds for root components. */
- static ImmutableSet<ComponentKind> rootComponentKinds() {
- return ROOT_COMPONENT_KINDS;
- }
-
- /** Returns the set of kinds for subcomponents. */
- static ImmutableSet<ComponentKind> subcomponentKinds() {
- return SUBCOMPONENT_KINDS;
- }
-
- /** Returns the annotations for components of the given kinds. */
- static ImmutableSet<Class<? extends Annotation>> annotationsFor(Iterable<ComponentKind> kinds) {
- return stream(kinds).map(ComponentKind::annotation).collect(toImmutableSet());
- }
-
- /** Returns the set of component kinds the given {@code element} has annotations for. */
- static ImmutableSet<ComponentKind> getComponentKinds(TypeElement element) {
- return valuesOf(ComponentKind.class)
- .filter(kind -> isAnnotationPresent(element, kind.annotation()))
- .collect(toImmutableSet());
- }
-
- /**
- * Returns the kind of an annotated element if it is annotated with one of the {@linkplain
- * #annotation() annotations}.
- *
- * @throws IllegalArgumentException if the element is annotated with more than one of the
- * annotations
- */
- static Optional<ComponentKind> forAnnotatedElement(TypeElement element) {
- ImmutableSet<ComponentKind> kinds = getComponentKinds(element);
- if (kinds.size() > 1) {
- throw new IllegalArgumentException(
- element + " cannot be annotated with more than one of " + annotationsFor(kinds));
- }
- return kinds.stream().findAny();
- }
-
- private final Class<? extends Annotation> annotation;
- private final boolean isRoot;
- private final boolean production;
-
- ComponentKind(
- Class<? extends Annotation> annotation,
- boolean isRoot,
- boolean production) {
- this.annotation = annotation;
- this.isRoot = isRoot;
- this.production = production;
- }
-
- /** Returns the annotation that marks a component of this kind. */
- Class<? extends Annotation> annotation() {
- return annotation;
- }
-
- /** Returns the kinds of modules that can be used with a component of this kind. */
- ImmutableSet<ModuleKind> legalModuleKinds() {
- return isProducer()
- ? immutableEnumSet(allOf(ModuleKind.class))
- : immutableEnumSet(ModuleKind.MODULE);
- }
-
- /** Returns the kinds of subcomponents a component of this kind can have. */
- ImmutableSet<ComponentKind> legalSubcomponentKinds() {
- return isProducer()
- ? immutableEnumSet(PRODUCTION_SUBCOMPONENT)
- : immutableEnumSet(SUBCOMPONENT, PRODUCTION_SUBCOMPONENT);
- }
-
- /**
- * Returns {@code true} if the descriptor is for a root component (not a subcomponent) or is for
- * {@linkplain #isForModuleValidation() module-validation}.
- */
- boolean isRoot() {
- return isRoot;
- }
-
- /** Returns true if this is a production component. */
- boolean isProducer() {
- return production;
- }
-
- /** Returns {@code true} if the descriptor is for a module in order to validate its bindings. */
- boolean isForModuleValidation() {
- switch (this) {
- case MODULE:
- case PRODUCER_MODULE:
- return true;
- default:
- // fall through
- }
- return false;
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentMethodBindingExpression.java b/java/dagger/internal/codegen/ComponentMethodBindingExpression.java
deleted file mode 100644
index 7e7bbd839..000000000
--- a/java/dagger/internal/codegen/ComponentMethodBindingExpression.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.internal.codegen.javapoet.Expression;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * A binding expression that implements and uses a component method.
- *
- * <p>Dependents of this binding expression will just call the component method.
- */
-final class ComponentMethodBindingExpression extends MethodBindingExpression {
- private final ComponentImplementation componentImplementation;
- private final ComponentMethodDescriptor componentMethod;
-
- ComponentMethodBindingExpression(
- BindingRequest request,
- ResolvedBindings resolvedBindings,
- MethodImplementationStrategy methodImplementationStrategy,
- BindingExpression wrappedBindingExpression,
- ComponentImplementation componentImplementation,
- ComponentMethodDescriptor componentMethod,
- DaggerTypes types) {
- super(
- request,
- resolvedBindings,
- methodImplementationStrategy,
- wrappedBindingExpression,
- componentImplementation,
- types);
- this.componentImplementation = checkNotNull(componentImplementation);
- this.componentMethod = checkNotNull(componentMethod);
- }
-
- @Override
- protected CodeBlock getComponentMethodImplementation(
- ComponentMethodDescriptor componentMethod, ComponentImplementation component) {
- // There could be several methods on the component for the same request key and kind.
- // Only one should use the BindingMethodImplementation; the others can delegate that one. So
- // use methodImplementation.body() only if componentMethod equals the method for this instance.
-
- // Separately, the method might be defined on a supertype that is also a supertype of some
- // parent component. In that case, the same ComponentMethodDescriptor will be used to add a CMBE
- // for the parent and the child. Only the parent's should use the BindingMethodImplementation;
- // the child's can delegate to the parent. So use methodImplementation.body() only if
- // componentName equals the component for this instance.
- return componentMethod.equals(this.componentMethod) && component.equals(componentImplementation)
- ? methodBodyForComponentMethod(componentMethod)
- : super.getComponentMethodImplementation(componentMethod, component);
- }
-
- @Override
- Expression getDependencyExpression(ClassName requestingClass) {
- // If a component method returns a primitive, update the expression's type which might be boxed.
- Expression expression = super.getDependencyExpression(requestingClass);
- TypeMirror methodReturnType = componentMethod.methodElement().getReturnType();
- return methodReturnType.getKind().isPrimitive()
- ? Expression.create(methodReturnType, expression.codeBlock())
- : expression;
- }
-
- @Override
- protected void addMethod() {}
-
- @Override
- protected String methodName() {
- return componentMethod.methodElement().getSimpleName().toString();
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentNodeImpl.java b/java/dagger/internal/codegen/ComponentNodeImpl.java
deleted file mode 100644
index 3a31ec7d8..000000000
--- a/java/dagger/internal/codegen/ComponentNodeImpl.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.collect.ImmutableSet;
-import dagger.model.BindingGraph.ComponentNode;
-import dagger.model.ComponentPath;
-import dagger.model.DependencyRequest;
-import dagger.model.Scope;
-
-/** An implementation of {@link ComponentNode} that also exposes the {@link ComponentDescriptor}. */
-@AutoValue
-abstract class ComponentNodeImpl implements ComponentNode {
- static ComponentNode create(
- ComponentPath componentPath, ComponentDescriptor componentDescriptor) {
- return new AutoValue_ComponentNodeImpl(componentPath, componentDescriptor);
- }
-
- @Override
- public final boolean isSubcomponent() {
- return componentDescriptor().isSubcomponent();
- }
-
- @Override
- public boolean isRealComponent() {
- return componentDescriptor().isRealComponent();
- }
-
- @Override
- public final ImmutableSet<DependencyRequest> entryPoints() {
- return componentDescriptor().entryPointMethods().stream()
- .map(method -> method.dependencyRequest().get())
- .collect(toImmutableSet());
- }
-
- @Override
- public ImmutableSet<Scope> scopes() {
- return componentDescriptor().scopes();
- }
-
- abstract ComponentDescriptor componentDescriptor();
-
- @Override
- public final String toString() {
- return componentPath().toString();
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentProcessingStep.java b/java/dagger/internal/codegen/ComponentProcessingStep.java
index 645c94f63..3125ce7f4 100644
--- a/java/dagger/internal/codegen/ComponentProcessingStep.java
+++ b/java/dagger/internal/codegen/ComponentProcessingStep.java
@@ -16,27 +16,30 @@
package dagger.internal.codegen;
+import static com.google.auto.common.MoreElements.asType;
import static com.google.common.collect.Sets.union;
-import static dagger.internal.codegen.ComponentAnnotation.allComponentAnnotations;
-import static dagger.internal.codegen.ComponentAnnotation.rootComponentAnnotations;
-import static dagger.internal.codegen.ComponentAnnotation.subcomponentAnnotations;
-import static dagger.internal.codegen.ComponentCreatorAnnotation.allCreatorAnnotations;
-import static dagger.internal.codegen.ComponentCreatorAnnotation.rootComponentCreatorAnnotations;
-import static dagger.internal.codegen.ComponentCreatorAnnotation.subcomponentCreatorAnnotations;
-import static dagger.internal.codegen.ValidationType.NONE;
+import static dagger.internal.codegen.base.ComponentAnnotation.allComponentAnnotations;
+import static dagger.internal.codegen.base.ComponentAnnotation.rootComponentAnnotations;
+import static dagger.internal.codegen.base.ComponentAnnotation.subcomponentAnnotations;
+import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.allCreatorAnnotations;
import static java.util.Collections.disjoint;
import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
import com.google.auto.common.MoreElements;
-import com.google.common.base.Predicates;
-import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimaps;
-import com.google.common.collect.SetMultimap;
-import dagger.internal.codegen.ComponentValidator.ComponentValidationReport;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import dagger.internal.codegen.base.SourceFileGenerator;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.BindingGraphFactory;
+import dagger.internal.codegen.binding.ComponentDescriptor;
+import dagger.internal.codegen.binding.ComponentDescriptorFactory;
+import dagger.internal.codegen.validation.BindingGraphValidator;
+import dagger.internal.codegen.validation.ComponentCreatorValidator;
+import dagger.internal.codegen.validation.ComponentDescriptorValidator;
+import dagger.internal.codegen.validation.ComponentValidator;
+import dagger.internal.codegen.validation.TypeCheckingProcessingStep;
+import dagger.internal.codegen.validation.ValidationReport;
import java.lang.annotation.Annotation;
-import java.util.HashMap;
-import java.util.Map;
import java.util.Set;
import javax.annotation.processing.Messager;
import javax.inject.Inject;
@@ -55,14 +58,7 @@ final class ComponentProcessingStep extends TypeCheckingProcessingStep<TypeEleme
private final ComponentDescriptorFactory componentDescriptorFactory;
private final BindingGraphFactory bindingGraphFactory;
private final SourceFileGenerator<BindingGraph> componentGenerator;
- private final BindingGraphConverter bindingGraphConverter;
private final BindingGraphValidator bindingGraphValidator;
- private final CompilerOptions compilerOptions;
- private ImmutableSet<Element> subcomponentElements;
- private ImmutableSet<Element> subcomponentCreatorElements;
- private ImmutableMap<Element, ValidationReport<TypeElement>> creatorReportsByComponent;
- private ImmutableMap<Element, ValidationReport<TypeElement>> creatorReportsBySubcomponent;
- private ImmutableMap<Element, ValidationReport<TypeElement>> reportsBySubcomponent;
@Inject
ComponentProcessingStep(
@@ -73,9 +69,7 @@ final class ComponentProcessingStep extends TypeCheckingProcessingStep<TypeEleme
ComponentDescriptorFactory componentDescriptorFactory,
BindingGraphFactory bindingGraphFactory,
SourceFileGenerator<BindingGraph> componentGenerator,
- BindingGraphConverter bindingGraphConverter,
- BindingGraphValidator bindingGraphValidator,
- CompilerOptions compilerOptions) {
+ BindingGraphValidator bindingGraphValidator) {
super(MoreElements::asType);
this.messager = messager;
this.componentValidator = componentValidator;
@@ -84,9 +78,7 @@ final class ComponentProcessingStep extends TypeCheckingProcessingStep<TypeEleme
this.componentDescriptorFactory = componentDescriptorFactory;
this.bindingGraphFactory = bindingGraphFactory;
this.componentGenerator = componentGenerator;
- this.bindingGraphConverter = bindingGraphConverter;
this.bindingGraphValidator = bindingGraphValidator;
- this.compilerOptions = compilerOptions;
}
@Override
@@ -95,27 +87,6 @@ final class ComponentProcessingStep extends TypeCheckingProcessingStep<TypeEleme
}
@Override
- public ImmutableSet<Element> process(
- SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
- subcomponentElements =
- getElementsFromAnnotations(elementsByAnnotation, subcomponentAnnotations());
- subcomponentCreatorElements =
- getElementsFromAnnotations(elementsByAnnotation, subcomponentCreatorAnnotations());
-
- ImmutableSet.Builder<Element> rejectedElements = ImmutableSet.builder();
-
- creatorReportsByComponent =
- processCreators(
- getElementsFromAnnotations(elementsByAnnotation, rootComponentCreatorAnnotations()),
- rejectedElements);
- creatorReportsBySubcomponent = processCreators(subcomponentCreatorElements, rejectedElements);
- reportsBySubcomponent =
- processSubcomponents(subcomponentElements, subcomponentCreatorElements, rejectedElements);
-
- return rejectedElements.addAll(super.process(elementsByAnnotation)).build();
- }
-
- @Override
protected void process(
TypeElement element, ImmutableSet<Class<? extends Annotation>> annotations) {
if (!disjoint(annotations, rootComponentAnnotations())) {
@@ -124,10 +95,13 @@ final class ComponentProcessingStep extends TypeCheckingProcessingStep<TypeEleme
if (!disjoint(annotations, subcomponentAnnotations())) {
processSubcomponent(element);
}
+ if (!disjoint(annotations, allCreatorAnnotations())) {
+ processCreator(element);
+ }
}
private void processRootComponent(TypeElement component) {
- if (!isRootComponentValid(component)) {
+ if (!isComponentValid(component)) {
return;
}
ComponentDescriptor componentDescriptor =
@@ -135,112 +109,47 @@ final class ComponentProcessingStep extends TypeCheckingProcessingStep<TypeEleme
if (!isValid(componentDescriptor)) {
return;
}
- if (!isFullBindingGraphValid(componentDescriptor)) {
+ if (!validateFullBindingGraph(componentDescriptor)) {
return;
}
BindingGraph bindingGraph = bindingGraphFactory.create(componentDescriptor, false);
- if (isValid(bindingGraph)) {
+ if (bindingGraphValidator.isValid(bindingGraph.topLevelBindingGraph())) {
generateComponent(bindingGraph);
}
}
private void processSubcomponent(TypeElement subcomponent) {
- if (!compilerOptions.aheadOfTimeSubcomponents()
- && compilerOptions.fullBindingGraphValidationType(subcomponent).equals(NONE)) {
- return;
- }
- if (!isSubcomponentValid(subcomponent)) {
+ if (!isComponentValid(subcomponent)) {
return;
}
ComponentDescriptor subcomponentDescriptor =
componentDescriptorFactory.subcomponentDescriptor(subcomponent);
// TODO(dpb): ComponentDescriptorValidator for subcomponents, as we do for root components.
- if (!isFullBindingGraphValid(subcomponentDescriptor)) {
- return;
- }
- if (compilerOptions.aheadOfTimeSubcomponents()) {
- BindingGraph bindingGraph = bindingGraphFactory.create(subcomponentDescriptor, false);
- if (isValid(bindingGraph)) {
- generateComponent(bindingGraph);
- }
- }
+ validateFullBindingGraph(subcomponentDescriptor);
}
private void generateComponent(BindingGraph bindingGraph) {
componentGenerator.generate(bindingGraph, messager);
}
- static ImmutableSet<Element> getElementsFromAnnotations(
- final SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation,
- Set<Class<? extends Annotation>> annotations) {
- return ImmutableSet.copyOf(
- Multimaps.filterKeys(elementsByAnnotation, Predicates.in(annotations)).values());
- }
-
- private ImmutableMap<Element, ValidationReport<TypeElement>> processCreators(
- Set<? extends Element> builderElements, ImmutableSet.Builder<Element> rejectedElements) {
- // Can't use an ImmutableMap.Builder here because a component may have (invalidly) more than one
- // builder type, and that would make ImmutableMap.Builder throw.
- Map<Element, ValidationReport<TypeElement>> reports = new HashMap<>();
- for (Element element : builderElements) {
- try {
- ValidationReport<TypeElement> report =
- creatorValidator.validate(MoreElements.asType(element));
- report.printMessagesTo(messager);
- reports.put(element.getEnclosingElement(), report);
- } catch (TypeNotPresentException e) {
- rejectedElements.add(element);
- }
- }
- return ImmutableMap.copyOf(reports);
- }
-
- private ImmutableMap<Element, ValidationReport<TypeElement>> processSubcomponents(
- Set<? extends Element> subcomponentElements,
- Set<? extends Element> subcomponentBuilderElements,
- ImmutableSet.Builder<Element> rejectedElements) {
- ImmutableMap.Builder<Element, ValidationReport<TypeElement>> reports = ImmutableMap.builder();
- for (Element element : subcomponentElements) {
- try {
- ComponentValidationReport report =
- componentValidator.validate(
- MoreElements.asType(element), subcomponentElements, subcomponentBuilderElements);
- report.report().printMessagesTo(messager);
- reports.put(element, report.report());
- } catch (TypeNotPresentException e) {
- rejectedElements.add(element);
- }
- }
- return reports.build();
+ private void processCreator(Element creator) {
+ creatorValidator.validate(MoreElements.asType(creator)).printMessagesTo(messager);
}
- private boolean isRootComponentValid(TypeElement rootComponent) {
- ComponentValidationReport validationReport =
- componentValidator.validate(
- rootComponent, subcomponentElements, subcomponentCreatorElements);
- validationReport.report().printMessagesTo(messager);
- return isClean(validationReport);
- }
-
- // TODO(dpb): Clean up generics so this can take TypeElement.
- private boolean isSubcomponentValid(Element subcomponentElement) {
- ValidationReport<?> subcomponentCreatorReport =
- creatorReportsBySubcomponent.get(subcomponentElement);
- if (subcomponentCreatorReport != null && !subcomponentCreatorReport.isClean()) {
- return false;
- }
- ValidationReport<?> subcomponentReport = reportsBySubcomponent.get(subcomponentElement);
- return subcomponentReport == null || subcomponentReport.isClean();
+ private boolean isComponentValid(Element component) {
+ ValidationReport<?> report = componentValidator.validate(asType(component));
+ report.printMessagesTo(messager);
+ return report.isClean();
}
- private boolean isFullBindingGraphValid(ComponentDescriptor componentDescriptor) {
- if (compilerOptions
- .fullBindingGraphValidationType(componentDescriptor.typeElement())
- .equals(NONE)) {
+ @CanIgnoreReturnValue
+ private boolean validateFullBindingGraph(ComponentDescriptor componentDescriptor) {
+ TypeElement component = componentDescriptor.typeElement();
+ if (!bindingGraphValidator.shouldDoFullBindingGraphValidation(component)) {
return true;
}
BindingGraph fullBindingGraph = bindingGraphFactory.create(componentDescriptor, true);
- return isValid(fullBindingGraph);
+ return bindingGraphValidator.isValid(fullBindingGraph.topLevelBindingGraph());
}
private boolean isValid(ComponentDescriptor componentDescriptor) {
@@ -249,30 +158,4 @@ final class ComponentProcessingStep extends TypeCheckingProcessingStep<TypeEleme
componentDescriptorReport.printMessagesTo(messager);
return componentDescriptorReport.isClean();
}
-
- private boolean isValid(BindingGraph bindingGraph) {
- return bindingGraphValidator.isValid(bindingGraphConverter.convert(bindingGraph));
- }
-
- /**
- * Returns true if the component's report is clean, its builder report is clean, and all
- * referenced subcomponent reports and subcomponent builder reports are clean.
- */
- private boolean isClean(ComponentValidationReport report) {
- Element component = report.report().subject();
- ValidationReport<?> componentReport = report.report();
- if (!componentReport.isClean()) {
- return false;
- }
- ValidationReport<?> builderReport = creatorReportsByComponent.get(component);
- if (builderReport != null && !builderReport.isClean()) {
- return false;
- }
- for (Element element : report.referencedSubcomponents()) {
- if (!isSubcomponentValid(element)) {
- return false;
- }
- }
- return true;
- }
}
diff --git a/java/dagger/internal/codegen/ComponentProcessor.java b/java/dagger/internal/codegen/ComponentProcessor.java
index 541a4ab10..e392252a2 100644
--- a/java/dagger/internal/codegen/ComponentProcessor.java
+++ b/java/dagger/internal/codegen/ComponentProcessor.java
@@ -16,25 +16,42 @@
package dagger.internal.codegen;
-import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.DYNAMIC;
+import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING;
import com.google.auto.common.BasicAnnotationProcessor;
import com.google.auto.service.AutoService;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.google.common.collect.Sets;
import com.google.errorprone.annotations.CheckReturnValue;
import dagger.BindsInstance;
import dagger.Component;
import dagger.Module;
import dagger.Provides;
import dagger.internal.codegen.SpiModule.TestingPlugins;
+import dagger.internal.codegen.base.ClearableCache;
+import dagger.internal.codegen.base.SourceFileGenerationException;
+import dagger.internal.codegen.base.SourceFileGenerator;
+import dagger.internal.codegen.binding.InjectBindingRegistry;
+import dagger.internal.codegen.binding.MembersInjectionBinding;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.bindinggraphvalidation.BindingGraphValidationModule;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions;
+import dagger.internal.codegen.componentgenerator.ComponentGeneratorModule;
+import dagger.internal.codegen.validation.BindingGraphPlugins;
+import dagger.internal.codegen.validation.BindingMethodProcessingStep;
+import dagger.internal.codegen.validation.BindingMethodValidatorsModule;
+import dagger.internal.codegen.validation.BindsInstanceProcessingStep;
+import dagger.internal.codegen.validation.InjectBindingRegistryModule;
+import dagger.internal.codegen.validation.MonitoringModuleProcessingStep;
+import dagger.internal.codegen.validation.MultibindingAnnotationsProcessingStep;
import dagger.spi.BindingGraphPlugin;
import java.util.Arrays;
import java.util.Optional;
import java.util.Set;
+import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.inject.Inject;
@@ -48,7 +65,7 @@ import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
*
* <p>TODO(gak): give this some better documentation
*/
-@IncrementalAnnotationProcessor(DYNAMIC)
+@IncrementalAnnotationProcessor(ISOLATING)
@AutoService(Processor.class)
public class ComponentProcessor extends BasicAnnotationProcessor {
private final Optional<ImmutableSet<BindingGraphPlugin>> testingPlugins;
@@ -58,8 +75,6 @@ public class ComponentProcessor extends BasicAnnotationProcessor {
@Inject SourceFileGenerator<MembersInjectionBinding> membersInjectorGenerator;
@Inject ImmutableList<ProcessingStep> processingSteps;
@Inject BindingGraphPlugins bindingGraphPlugins;
- @Inject CompilerOptions compilerOptions;
- @Inject DaggerStatisticsCollector statisticsCollector;
@Inject Set<ClearableCache> clearableCaches;
public ComponentProcessor() {
@@ -95,28 +110,19 @@ public class ComponentProcessor extends BasicAnnotationProcessor {
@Override
public Set<String> getSupportedOptions() {
- ImmutableSet.Builder<String> options = ImmutableSet.builder();
- options.addAll(ProcessingEnvironmentCompilerOptions.supportedOptions());
- options.addAll(bindingGraphPlugins.allSupportedOptions());
- if (compilerOptions.useGradleIncrementalProcessing()) {
- options.add("org.gradle.annotation.processing.isolating");
- }
- return options.build();
+ return Sets.union(
+ ProcessingEnvironmentCompilerOptions.supportedOptions(),
+ bindingGraphPlugins.allSupportedOptions())
+ .immutableCopy();
}
@Override
protected Iterable<? extends ProcessingStep> initSteps() {
- ProcessorComponent.builder()
- .processingEnvironmentModule(new ProcessingEnvironmentModule(processingEnv))
- .testingPlugins(testingPlugins)
- .build()
- .inject(this);
+ ProcessorComponent.factory().create(processingEnv, testingPlugins).inject(this);
- statisticsCollector.processingStarted();
bindingGraphPlugins.initializePlugins();
- return Iterables.transform(
- processingSteps,
- step -> new DaggerStatisticsCollectingProcessingStep(step, statisticsCollector));
+
+ return processingSteps;
}
@Singleton
@@ -124,32 +130,27 @@ public class ComponentProcessor extends BasicAnnotationProcessor {
modules = {
BindingGraphValidationModule.class,
BindingMethodValidatorsModule.class,
+ ComponentGeneratorModule.class,
InjectBindingRegistryModule.class,
ProcessingEnvironmentModule.class,
ProcessingRoundCacheModule.class,
ProcessingStepsModule.class,
SourceFileGeneratorsModule.class,
- SpiModule.class,
- SystemComponentsModule.class,
- TopLevelImplementationComponent.InstallationModule.class,
+ SpiModule.class
})
interface ProcessorComponent {
void inject(ComponentProcessor processor);
- static Builder builder() {
- return DaggerComponentProcessor_ProcessorComponent.builder();
+ static Factory factory() {
+ return DaggerComponentProcessor_ProcessorComponent.factory();
}
- @CanIgnoreReturnValue
- @Component.Builder
- interface Builder {
- Builder processingEnvironmentModule(ProcessingEnvironmentModule module);
-
- @BindsInstance
- Builder testingPlugins(
- @TestingPlugins Optional<ImmutableSet<BindingGraphPlugin>> testingPlugins);
-
- @CheckReturnValue ProcessorComponent build();
+ @Component.Factory
+ interface Factory {
+ @CheckReturnValue
+ ProcessorComponent create(
+ @BindsInstance ProcessingEnvironment processingEnv,
+ @BindsInstance @TestingPlugins Optional<ImmutableSet<BindingGraphPlugin>> testingPlugins);
}
}
@@ -159,6 +160,9 @@ public class ComponentProcessor extends BasicAnnotationProcessor {
static ImmutableList<ProcessingStep> processingSteps(
MapKeyProcessingStep mapKeyProcessingStep,
InjectProcessingStep injectProcessingStep,
+ AssistedInjectProcessingStep assistedInjectProcessingStep,
+ AssistedFactoryProcessingStep assistedFactoryProcessingStep,
+ AssistedProcessingStep assistedProcessingStep,
MonitoringModuleProcessingStep monitoringModuleProcessingStep,
MultibindingAnnotationsProcessingStep multibindingAnnotationsProcessingStep,
BindsInstanceProcessingStep bindsInstanceProcessingStep,
@@ -170,15 +174,14 @@ public class ComponentProcessor extends BasicAnnotationProcessor {
return ImmutableList.of(
mapKeyProcessingStep,
injectProcessingStep,
+ assistedInjectProcessingStep,
+ assistedFactoryProcessingStep,
+ assistedProcessingStep,
monitoringModuleProcessingStep,
multibindingAnnotationsProcessingStep,
bindsInstanceProcessingStep,
moduleProcessingStep,
compilerOptions.headerCompilation()
- // Ahead Of Time subcomponents use the regular hjar filtering in
- // HjarSourceFileGenerator since they must retain protected implementation methods
- // between subcomponents
- && !compilerOptions.aheadOfTimeSubcomponents()
? componentHjarProcessingStep
: componentProcessingStep,
bindingMethodProcessingStep);
@@ -187,10 +190,7 @@ public class ComponentProcessor extends BasicAnnotationProcessor {
@Override
protected void postRound(RoundEnvironment roundEnv) {
- statisticsCollector.roundFinished();
- if (roundEnv.processingOver()) {
- statisticsCollector.processingStopped();
- } else {
+ if (!roundEnv.processingOver()) {
try {
injectBindingRegistry.generateSourcesForRequiredBindings(
factoryGenerator, membersInjectorGenerator);
diff --git a/java/dagger/internal/codegen/ComponentProvisionBindingExpression.java b/java/dagger/internal/codegen/ComponentProvisionBindingExpression.java
deleted file mode 100644
index b8c60496e..000000000
--- a/java/dagger/internal/codegen/ComponentProvisionBindingExpression.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.Preconditions;
-import dagger.internal.codegen.javapoet.Expression;
-
-/** A binding expression for component provision methods. */
-final class ComponentProvisionBindingExpression extends SimpleInvocationBindingExpression {
- private final ProvisionBinding binding;
- private final BindingGraph bindingGraph;
- private final ComponentRequirementExpressions componentRequirementExpressions;
- private final CompilerOptions compilerOptions;
-
- ComponentProvisionBindingExpression(
- ResolvedBindings resolvedBindings,
- BindingGraph bindingGraph,
- ComponentRequirementExpressions componentRequirementExpressions,
- CompilerOptions compilerOptions) {
- super(resolvedBindings);
- this.binding = (ProvisionBinding) resolvedBindings.contributionBinding();
- this.bindingGraph = checkNotNull(bindingGraph);
- this.componentRequirementExpressions = checkNotNull(componentRequirementExpressions);
- this.compilerOptions = checkNotNull(compilerOptions);
- }
-
- @Override
- Expression getDependencyExpression(ClassName requestingClass) {
- CodeBlock invocation =
- CodeBlock.of(
- "$L.$L()",
- componentRequirementExpressions.getExpression(componentRequirement(), requestingClass),
- binding.bindingElement().get().getSimpleName());
- return Expression.create(
- binding.contributedPrimitiveType().orElse(binding.key().type()),
- maybeCheckForNull(binding, compilerOptions, invocation));
- }
-
- private ComponentRequirement componentRequirement() {
- return bindingGraph
- .componentDescriptor()
- .getDependencyThatDefinesMethod(binding.bindingElement().get());
- }
-
- static CodeBlock maybeCheckForNull(
- ProvisionBinding binding, CompilerOptions compilerOptions, CodeBlock invocation) {
- return binding.shouldCheckForNull(compilerOptions)
- ? CodeBlock.of(
- "$T.checkNotNull($L, $S)",
- Preconditions.class,
- invocation,
- "Cannot return null from a non-@Nullable component method")
- : invocation;
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentRequirement.java b/java/dagger/internal/codegen/ComponentRequirement.java
deleted file mode 100644
index 3bed5da57..000000000
--- a/java/dagger/internal/codegen/ComponentRequirement.java
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static dagger.internal.codegen.SourceFiles.simpleVariableName;
-import static dagger.internal.codegen.Util.componentCanMakeNewInstances;
-import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.STATIC;
-
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Equivalence;
-import com.google.common.collect.ImmutableSet;
-import com.squareup.javapoet.ParameterSpec;
-import com.squareup.javapoet.TypeName;
-import dagger.Binds;
-import dagger.BindsOptionalOf;
-import dagger.Provides;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.internal.codegen.serialization.ComponentRequirementProto;
-import dagger.internal.codegen.serialization.ComponentRequirementProto.BoundInstanceRequirement;
-import dagger.model.BindingKind;
-import dagger.model.Key;
-import dagger.multibindings.Multibinds;
-import dagger.producers.Produces;
-import java.util.Optional;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.TypeMirror;
-
-/** A type that a component needs an instance of. */
-@AutoValue
-abstract class ComponentRequirement {
- enum Kind {
- /** A type listed in the component's {@code dependencies} attribute. */
- DEPENDENCY,
-
- /** A type listed in the component or subcomponent's {@code modules} attribute. */
- MODULE,
-
- /**
- * An object that is passed to a builder's {@link dagger.BindsInstance @BindsInstance} method.
- */
- BOUND_INSTANCE,
- ;
-
- boolean isBoundInstance() {
- return equals(BOUND_INSTANCE);
- }
-
- boolean isModule() {
- return equals(MODULE);
- }
- }
-
- /** The kind of requirement. */
- abstract Kind kind();
-
- /** Returns true if this is a {@link Kind#BOUND_INSTANCE} requirement. */
- // TODO(ronshapiro): consider removing this and inlining the usages
- final boolean isBoundInstance() {
- return kind().isBoundInstance();
- }
-
- /**
- * The type of the instance the component must have, wrapped so that requirements can be used as
- * value types.
- */
- abstract Equivalence.Wrapper<TypeMirror> wrappedType();
-
- /** The type of the instance the component must have. */
- TypeMirror type() {
- return wrappedType().get();
- }
-
- /** The element associated with the type of this requirement. */
- TypeElement typeElement() {
- return MoreTypes.asTypeElement(type());
- }
-
- /** The action a component builder should take if it {@code null} is passed. */
- enum NullPolicy {
- /** Make a new instance. */
- NEW,
- /** Throw an exception. */
- THROW,
- /** Allow use of null values. */
- ALLOW,
- }
-
- /**
- * An override for the requirement's null policy. If set, this is used as the null policy instead
- * of the default behavior in {@link #nullPolicy}.
- *
- * <p>Some implementations' null policy can be determined upon construction (e.g., for binding
- * instances), but others' require Elements and Types, which must wait until {@link #nullPolicy}
- * is called.
- */
- abstract Optional<NullPolicy> overrideNullPolicy();
-
- /** The requirement's null policy. */
- NullPolicy nullPolicy(DaggerElements elements, DaggerTypes types) {
- if (overrideNullPolicy().isPresent()) {
- return overrideNullPolicy().get();
- }
- switch (kind()) {
- case MODULE:
- return componentCanMakeNewInstances(typeElement())
- ? NullPolicy.NEW
- : requiresAPassedInstance(elements, types) ? NullPolicy.THROW : NullPolicy.ALLOW;
- case DEPENDENCY:
- case BOUND_INSTANCE:
- return NullPolicy.THROW;
- }
- throw new AssertionError();
- }
-
- /**
- * Returns true if the passed {@link ComponentRequirement} requires a passed instance in order to
- * be used within a component.
- */
- boolean requiresAPassedInstance(DaggerElements elements, DaggerTypes types) {
- if (!kind().isModule()) {
- // Bound instances and dependencies always require the user to provide an instance.
- return true;
- }
- return requiresModuleInstance(elements, types) && !componentCanMakeNewInstances(typeElement());
- }
-
- /**
- * Returns {@code true} if an instance is needed for this (module) requirement.
- *
- * <p>An instance is only needed if there is a binding method on the module that is neither {@code
- * abstract} nor {@code static}; if all bindings are one of those, then there should be no
- * possible dependency on instance state in the module's bindings.
- */
- private boolean requiresModuleInstance(DaggerElements elements, DaggerTypes types) {
- ImmutableSet<ExecutableElement> methods =
- getLocalAndInheritedMethods(typeElement(), types, elements);
- return methods.stream()
- .filter(this::isBindingMethod)
- .map(ExecutableElement::getModifiers)
- .anyMatch(modifiers -> !modifiers.contains(ABSTRACT) && !modifiers.contains(STATIC));
- }
-
- private boolean isBindingMethod(ExecutableElement method) {
- // TODO(cgdecker): At the very least, we should have utility methods to consolidate this stuff
- // in one place; listing individual annotations all over the place is brittle.
- return isAnyAnnotationPresent(
- method,
- Provides.class,
- Produces.class,
- // TODO(ronshapiro): it would be cool to have internal meta-annotations that could describe
- // these, like @AbstractBindingMethod
- Binds.class,
- Multibinds.class,
- BindsOptionalOf.class);
- }
-
- /** The key for this requirement, if one is available. */
- abstract Optional<Key> key();
-
- /** Returns the name for this requirement that could be used as a variable. */
- abstract String variableName();
-
- /** Returns a parameter spec for this requirement. */
- ParameterSpec toParameterSpec() {
- return ParameterSpec.builder(TypeName.get(type()), variableName()).build();
- }
-
- /** Creates a proto representation of this requirement. */
- ComponentRequirementProto toProto() {
- switch (kind()) {
- case DEPENDENCY:
- return ComponentRequirementProto.newBuilder()
- .setDependency(TypeProtoConverter.toProto(type()))
- .build();
- case MODULE:
- return ComponentRequirementProto.newBuilder()
- .setModule(TypeProtoConverter.toProto(type()))
- .build();
- case BOUND_INSTANCE:
- return ComponentRequirementProto.newBuilder()
- .setBoundInstance(
- BoundInstanceRequirement.newBuilder()
- .setKey(KeyFactory.toProto(key().get()))
- .setNullable(overrideNullPolicy().equals(Optional.of(NullPolicy.ALLOW)))
- .setVariableName(variableName()))
- .build();
- }
- throw new AssertionError(this);
- }
-
- static ComponentRequirement forDependency(TypeMirror type) {
- return new AutoValue_ComponentRequirement(
- Kind.DEPENDENCY,
- MoreTypes.equivalence().wrap(checkNotNull(type)),
- Optional.empty(),
- Optional.empty(),
- simpleVariableName(MoreTypes.asTypeElement(type)));
- }
-
- static ComponentRequirement forModule(TypeMirror type) {
- return new AutoValue_ComponentRequirement(
- Kind.MODULE,
- MoreTypes.equivalence().wrap(checkNotNull(type)),
- Optional.empty(),
- Optional.empty(),
- simpleVariableName(MoreTypes.asTypeElement(type)));
- }
-
- static ComponentRequirement forBoundInstance(Key key, boolean nullable, String variableName) {
- return new AutoValue_ComponentRequirement(
- Kind.BOUND_INSTANCE,
- MoreTypes.equivalence().wrap(key.type()),
- nullable ? Optional.of(NullPolicy.ALLOW) : Optional.empty(),
- Optional.of(key),
- variableName);
- }
-
- static ComponentRequirement forBoundInstance(ContributionBinding binding) {
- checkArgument(binding.kind().equals(BindingKind.BOUND_INSTANCE));
- return forBoundInstance(
- binding.key(),
- binding.nullableType().isPresent(),
- binding.bindingElement().get().getSimpleName().toString());
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentRequirementBindingExpression.java b/java/dagger/internal/codegen/ComponentRequirementBindingExpression.java
deleted file mode 100644
index d6aa053be..000000000
--- a/java/dagger/internal/codegen/ComponentRequirementBindingExpression.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import com.squareup.javapoet.ClassName;
-import dagger.internal.codegen.javapoet.Expression;
-
-/**
- * A binding expression for instances bound with {@link dagger.BindsInstance} and instances of
- * {@linkplain dagger.Component#dependencies() component} and {@linkplain
- * dagger.producers.ProductionComponent#dependencies() production component dependencies}.
- */
-final class ComponentRequirementBindingExpression extends SimpleInvocationBindingExpression {
- private final ComponentRequirement componentRequirement;
- private final ComponentRequirementExpressions componentRequirementExpressions;
-
- ComponentRequirementBindingExpression(
- ResolvedBindings resolvedBindings,
- ComponentRequirement componentRequirement,
- ComponentRequirementExpressions componentRequirementExpressions) {
- super(resolvedBindings);
- this.componentRequirement = componentRequirement;
- this.componentRequirementExpressions = componentRequirementExpressions;
- }
-
- @Override
- Expression getDependencyExpression(ClassName requestingClass) {
- return Expression.create(
- componentRequirement.type(),
- componentRequirementExpressions.getExpression(componentRequirement, requestingClass));
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentRequirementExpression.java b/java/dagger/internal/codegen/ComponentRequirementExpression.java
deleted file mode 100644
index b25c01b67..000000000
--- a/java/dagger/internal/codegen/ComponentRequirementExpression.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-
-/**
- * A factory for expressions of {@link ComponentRequirement}s in the generated component. This is
- * <em>not</em> a {@link BindingExpression}, since {@link ComponentRequirement}s do not have a
- * {@link dagger.model.Key}. See {@link ComponentRequirementBindingExpression} for binding
- * expressions that are themselves a component requirement.
- */
-interface ComponentRequirementExpression {
- /**
- * Returns an expression for the {@link ComponentRequirement} to be used when implementing a
- * component method. This may add a field or method to the component in order to reference the
- * component requirement outside of the {@code initialize()} methods.
- */
- CodeBlock getExpression(ClassName requestingClass);
-
- /**
- * Returns an expression for the {@link ComponentRequirement} to be used only within {@code
- * initialize()} methods, where the constructor parameters are available.
- *
- * <p>When accessing this expression from a subcomponent, this may cause a field to be initialized
- * or a method to be added in the component that owns this {@link ComponentRequirement}.
- */
- default CodeBlock getExpressionDuringInitialization(ClassName requestingClass) {
- return getExpression(requestingClass);
- }
-
- /**
- * Returns the expression for the {@link ComponentRequirement} to be used when reimplementing a
- * modifiable module method.
- */
- default CodeBlock getModifiableModuleMethodExpression(ClassName requestingClass) {
- return CodeBlock.of("return $L", getExpression(requestingClass));
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentRequirementExpressions.java b/java/dagger/internal/codegen/ComponentRequirementExpressions.java
deleted file mode 100644
index 4ab58c926..000000000
--- a/java/dagger/internal/codegen/ComponentRequirementExpressions.java
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.CaseFormat.LOWER_CAMEL;
-import static com.google.common.base.CaseFormat.UPPER_CAMEL;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Suppliers.memoize;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static dagger.internal.codegen.ComponentImplementation.FieldSpecKind.COMPONENT_REQUIREMENT_FIELD;
-import static dagger.internal.codegen.ModuleProxies.newModuleInstance;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PROTECTED;
-
-import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.FieldSpec;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.TypeName;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import javax.inject.Inject;
-import javax.lang.model.element.TypeElement;
-
-/**
- * A central repository of expressions used to access any {@link ComponentRequirement} available to
- * a component.
- */
-@PerComponentImplementation
-final class ComponentRequirementExpressions {
-
- // TODO(dpb,ronshapiro): refactor this and ComponentBindingExpressions into a
- // HierarchicalComponentMap<K, V>, or perhaps this use a flattened ImmutableMap, built from its
- // parents? If so, maybe make ComponentRequirementExpression.Factory create it.
-
- private final Optional<ComponentRequirementExpressions> parent;
- private final Map<ComponentRequirement, ComponentRequirementExpression>
- componentRequirementExpressions = new HashMap<>();
- private final BindingGraph graph;
- private final ComponentImplementation componentImplementation;
- private final CompilerOptions compilerOptions;
- private final DaggerElements elements;
-
- // TODO(ronshapiro): give ComponentImplementation a graph() method
- @Inject
- ComponentRequirementExpressions(
- @ParentComponent Optional<ComponentRequirementExpressions> parent,
- BindingGraph graph,
- ComponentImplementation componentImplementation,
- CompilerOptions compilerOptions,
- DaggerElements elements) {
- this.parent = parent;
- this.graph = graph;
- this.componentImplementation = componentImplementation;
- this.compilerOptions = compilerOptions;
- this.elements = elements;
- }
-
- /**
- * Returns an expression for the {@code componentRequirement} to be used when implementing a
- * component method. This may add a field or method to the component in order to reference the
- * component requirement outside of the {@code initialize()} methods.
- */
- CodeBlock getExpression(ComponentRequirement componentRequirement, ClassName requestingClass) {
- return getExpression(componentRequirement).getExpression(requestingClass);
- }
-
- /**
- * Returns an expression for the {@code componentRequirement} to be used only within {@code
- * initialize()} methods, where the component constructor parameters are available.
- *
- * <p>When accessing this expression from a subcomponent, this may cause a field to be initialized
- * or a method to be added in the component that owns this {@link ComponentRequirement}.
- */
- CodeBlock getExpressionDuringInitialization(
- ComponentRequirement componentRequirement, ClassName requestingClass) {
- return getExpression(componentRequirement).getExpressionDuringInitialization(requestingClass);
- }
-
- ComponentRequirementExpression getExpression(ComponentRequirement componentRequirement) {
- if (graph.componentRequirements().contains(componentRequirement)) {
- return componentRequirementExpressions.computeIfAbsent(
- componentRequirement, this::createMethodOrField);
- }
- if (parent.isPresent()) {
- return parent.get().getExpression(componentRequirement);
- }
-
- if (componentRequirement.kind().isModule() && compilerOptions.aheadOfTimeSubcomponents()) {
- return new PrunedModifiableModule(componentRequirement);
- }
-
- throw new IllegalStateException(
- "no component requirement expression found for " + componentRequirement);
- }
-
- /**
- * If {@code requirement} is a module that may be owned by a future ancestor component, returns a
- * modifiable module method. Otherwise, returns a field for {@code requirement}.
- */
- private ComponentRequirementExpression createMethodOrField(ComponentRequirement requirement) {
- if (componentImplementation.isAbstract() && requirement.kind().isModule()) {
- return new ModifiableModule(requirement);
- }
- return createField(requirement);
- }
-
- /** Returns a field for a {@link ComponentRequirement}. */
- private ComponentRequirementExpression createField(ComponentRequirement requirement) {
- if (componentImplementation.componentDescriptor().hasCreator()) {
- return new ComponentParameterField(requirement, componentImplementation, Optional.empty());
- } else if (graph.factoryMethod().isPresent()
- && graph.factoryMethodParameters().containsKey(requirement)) {
- String parameterName =
- graph.factoryMethodParameters().get(requirement).getSimpleName().toString();
- return new ComponentParameterField(
- requirement, componentImplementation, Optional.of(parameterName));
- } else if (requirement.kind().isModule()) {
- return new InstantiableModuleField(requirement, componentImplementation);
- } else {
- throw new AssertionError(
- String.format("Can't create %s in %s", requirement, componentImplementation.name()));
- }
- }
-
- private abstract static class AbstractField implements ComponentRequirementExpression {
- final ComponentRequirement componentRequirement;
- final ComponentImplementation componentImplementation;
- final String fieldName;
- private final Supplier<MemberSelect> field = memoize(this::addField);
-
- private AbstractField(
- ComponentRequirement componentRequirement,
- ComponentImplementation componentImplementation) {
- this.componentRequirement = checkNotNull(componentRequirement);
- this.componentImplementation = checkNotNull(componentImplementation);
- // Note: The field name is being claimed eagerly here even though we don't know at this point
- // whether or not the requirement will even need a field. This is done because:
- // A) ComponentParameterField wants to ensure that it doesn't give the parameter the same name
- // as any field in the component, which requires that it claim a "field name" for itself
- // when naming the parameter.
- // B) The parameter name may be needed before the field name is.
- // C) We want to prefer giving the best name to the field rather than the parameter given its
- // wider scope.
- this.fieldName =
- componentImplementation.getUniqueFieldName(componentRequirement.variableName());
- }
-
- @Override
- public CodeBlock getExpression(ClassName requestingClass) {
- return field.get().getExpressionFor(requestingClass);
- }
-
- private MemberSelect addField() {
- FieldSpec field = createField();
- componentImplementation.addField(COMPONENT_REQUIREMENT_FIELD, field);
- componentImplementation.addComponentRequirementInitialization(fieldInitialization(field));
- return MemberSelect.localField(componentImplementation.name(), fieldName);
- }
-
- private FieldSpec createField() {
- FieldSpec.Builder field =
- FieldSpec.builder(TypeName.get(componentRequirement.type()), fieldName, PRIVATE);
- if (!componentImplementation.isAbstract()) {
- field.addModifiers(FINAL);
- }
- return field.build();
- }
-
- /** Returns the {@link CodeBlock} that initializes the component field during construction. */
- abstract CodeBlock fieldInitialization(FieldSpec componentField);
- }
-
- /**
- * A {@link ComponentRequirementExpression} for {@link ComponentRequirement}s that can be
- * instantiated by the component (i.e. a static class with a no-arg constructor).
- */
- private final class InstantiableModuleField extends AbstractField {
- private final TypeElement moduleElement;
-
- private InstantiableModuleField(
- ComponentRequirement module, ComponentImplementation componentImplementation) {
- super(module, componentImplementation);
- checkArgument(module.kind().isModule());
- this.moduleElement = module.typeElement();
- }
-
- @Override
- CodeBlock fieldInitialization(FieldSpec componentField) {
- return CodeBlock.of(
- "this.$N = $L;",
- componentField,
- newModuleInstance(moduleElement, componentImplementation.name(), elements));
- }
- }
-
- /**
- * A {@link ComponentRequirementExpression} for {@link ComponentRequirement}s that are passed in
- * as parameters to the component's constructor.
- */
- private static final class ComponentParameterField extends AbstractField {
- private final String parameterName;
-
- private ComponentParameterField(
- ComponentRequirement componentRequirement,
- ComponentImplementation componentImplementation,
- Optional<String> name) {
- super(componentRequirement, componentImplementation);
- componentImplementation.addComponentRequirementParameter(componentRequirement);
- // Get the name that the component implementation will use for its parameter for the
- // requirement. If the given name is different than the name of the field created for the
- // requirement (as may be the case when the parameter name is derived from a user-written
- // factory method parameter), just use that as the base name for the parameter. Otherwise,
- // append "Param" to the end of the name to differentiate.
- // In either case, componentImplementation.getParameterName() will ensure that the final name
- // that is used is not the same name as any field in the component even if there's something
- // weird where the component actually has fields named, say, "foo" and "fooParam".
- String baseName = name.filter(n -> !n.equals(fieldName)).orElse(fieldName + "Param");
- this.parameterName = componentImplementation.getParameterName(componentRequirement, baseName);
- }
-
- @Override
- public CodeBlock getExpressionDuringInitialization(ClassName requestingClass) {
- if (componentImplementation.name().equals(requestingClass)) {
- return CodeBlock.of("$L", parameterName);
- } else {
- // requesting this component requirement during initialization of a child component requires
- // it to be accessed from a field and not the parameter (since it is no longer available)
- return getExpression(requestingClass);
- }
- }
-
- @Override
- CodeBlock fieldInitialization(FieldSpec componentField) {
- // Don't checkNotNull here because the parameter may be nullable; if it isn't, the caller
- // should handle checking that before passing the parameter.
- return CodeBlock.of("this.$N = $L;", componentField, parameterName);
- }
- }
-
- private final class ModifiableModule implements ComponentRequirementExpression {
- private final ComponentRequirement module;
- private final Supplier<MemberSelect> method = Suppliers.memoize(this::methodSelect);
-
- private ModifiableModule(ComponentRequirement module) {
- checkArgument(module.kind().isModule());
- this.module = module;
- }
-
- @Override
- public CodeBlock getExpression(ClassName requestingClass) {
- return method.get().getExpressionFor(requestingClass);
- }
-
- private MemberSelect methodSelect() {
- String methodName =
- componentImplementation
- .supertypeModifiableModuleMethodName(module)
- .orElseGet(this::createMethod);
- return MemberSelect.localMethod(componentImplementation.name(), methodName);
- }
-
- private String createMethod() {
- String methodName =
- UPPER_CAMEL.to(
- LOWER_CAMEL,
- componentImplementation.getUniqueMethodName(
- module.typeElement().getSimpleName().toString()));
- MethodSpec.Builder methodBuilder =
- methodBuilder(methodName)
- .addModifiers(PROTECTED)
- .returns(TypeName.get(module.type()));
- // TODO(b/117833324): if the module is instantiable, we could provide an implementation here
- // too. Then, if no ancestor ever repeats the module, there's nothing to do in subclasses.
- if (graph.componentDescriptor().creatorDescriptor().isPresent()) {
- methodBuilder.addStatement(
- "return $L",
- createField(module).getExpression(componentImplementation.name()));
- } else {
- methodBuilder.addModifiers(ABSTRACT);
- }
- componentImplementation.addModifiableModuleMethod(module, methodBuilder.build());
- return methodName;
- }
- }
-
- private static final class PrunedModifiableModule implements ComponentRequirementExpression {
- private final ComponentRequirement module;
-
- private PrunedModifiableModule(ComponentRequirement module) {
- checkArgument(module.kind().isModule());
- this.module = module;
- }
-
- @Override
- public CodeBlock getExpression(ClassName requestingClass) {
- throw new UnsupportedOperationException(module + " is pruned - it cannot be requested");
- }
-
- @Override
- public CodeBlock getModifiableModuleMethodExpression(ClassName requestingClass) {
- return CodeBlock.builder()
- .add(
- "// $L has been pruned from the final resolved binding graph. The result of this "
- + "method should never be used, but it may be called in an initialize() method "
- + "when creating a framework instance of a now-pruned binding. Those framework "
- + "instances should never be used.\n",
- module.typeElement())
- .add("return null")
- .build();
- }
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentTreeTraverser.java b/java/dagger/internal/codegen/ComponentTreeTraverser.java
deleted file mode 100644
index cc1efd29c..000000000
--- a/java/dagger/internal/codegen/ComponentTreeTraverser.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.base.Verify.verify;
-import static dagger.internal.codegen.DaggerStreams.toImmutableList;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterators;
-import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.model.ComponentPath;
-import dagger.model.DependencyRequest;
-import java.util.ArrayDeque;
-import java.util.Deque;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-
-/**
- * An object that traverses the entire component hierarchy, starting from the root component.
- *
- * <p>Subclasses can override {@link #visitComponent(BindingGraph)} to perform custom logic at each
- * component in the tree, and {@link #visitSubcomponentFactoryMethod(BindingGraph, BindingGraph,
- * ExecutableElement)} to perform custom logic at each subcomponent factory method.
- */
-public class ComponentTreeTraverser {
-
- /** The path from the root graph to the currently visited graph. */
- private final Deque<BindingGraph> bindingGraphPath = new ArrayDeque<>();
-
- /** The {@link ComponentPath} for each component in {@link #bindingGraphPath}. */
- private final Deque<ComponentPath> componentPaths = new ArrayDeque<>();
-
- /** Constructs a traverser for a root (component, not subcomponent) binding graph. */
- public ComponentTreeTraverser(BindingGraph rootGraph) {
- bindingGraphPath.add(rootGraph);
- componentPaths.add(ComponentPath.create(ImmutableList.of(rootGraph.componentTypeElement())));
- }
-
- /**
- * Calls {@link #visitComponent(BindingGraph)} for the root component.
- *
- * @throws IllegalStateException if a traversal is in progress
- */
- public final void traverseComponents() {
- checkState(bindingGraphPath.size() == 1);
- checkState(componentPaths.size() == 1);
- visitComponent(bindingGraphPath.getFirst());
- }
-
- /**
- * Called once for each component in a component hierarchy.
- *
- * <p>Subclasses can override this method to perform whatever logic is required per component.
- * They should call the {@code super} implementation if they want to continue the traversal in the
- * standard order.
- *
- * <p>This implementation does the following:
- *
- * <ol>
- * <li>If this component is installed in its parent by a subcomponent factory method, calls
- * {@link #visitSubcomponentFactoryMethod(BindingGraph, BindingGraph, ExecutableElement)}.
- * <li>For each entry point in the component, calls {@link #visitEntryPoint(DependencyRequest,
- * BindingGraph)}.
- * <li>For each child component, calls {@link #visitComponent(BindingGraph)}, updating the
- * traversal state.
- * </ol>
- *
- * @param graph the currently visited graph
- */
- protected void visitComponent(BindingGraph graph) {
- if (bindingGraphPath.size() > 1) {
- BindingGraph parent = Iterators.get(bindingGraphPath.descendingIterator(), 1);
- parent
- .componentDescriptor()
- .getFactoryMethodForChildComponent(graph.componentDescriptor())
- .ifPresent(
- childFactoryMethod ->
- visitSubcomponentFactoryMethod(
- graph, parent, childFactoryMethod.methodElement()));
- }
-
- for (ComponentMethodDescriptor entryPointMethod :
- graph.componentDescriptor().entryPointMethods()) {
- visitEntryPoint(entryPointMethod.dependencyRequest().get(), graph);
- }
-
- for (BindingGraph child : graph.subgraphs()) {
- bindingGraphPath.addLast(child);
- ComponentPath childPath =
- ComponentPath.create(
- bindingGraphPath.stream()
- .map(BindingGraph::componentTypeElement)
- .collect(toImmutableList()));
- componentPaths.addLast(childPath);
- try {
- visitComponent(child);
- } finally {
- verify(bindingGraphPath.removeLast().equals(child));
- verify(componentPaths.removeLast().equals(childPath));
- }
- }
- }
-
- /**
- * Called if this component was installed in its parent by a subcomponent factory method.
- *
- * <p>This implementation does nothing.
- *
- * @param graph the currently visited graph
- * @param parent the parent graph
- * @param factoryMethod the factory method in the parent component that declares that the current
- * component is a child
- */
- protected void visitSubcomponentFactoryMethod(
- BindingGraph graph, BindingGraph parent, ExecutableElement factoryMethod) {}
-
- /**
- * Called once for each entry point in a component.
- *
- * <p>Subclasses can override this method to perform whatever logic is required per entry point.
- * They should call the {@code super} implementation if they want to continue the traversal in the
- * standard order.
- *
- * <p>This implementation does nothing.
- *
- * @param graph the graph for the component that contains the entry point
- */
- protected void visitEntryPoint(DependencyRequest entryPoint, BindingGraph graph) {}
-
- /**
- * Returns an immutable snapshot of the path from the root component to the currently visited
- * component.
- */
- protected final ComponentPath componentPath() {
- return componentPaths.getLast();
- }
-
- /**
- * Returns the subpath from the root component to the matching {@code ancestor} of the current
- * component.
- */
- protected final ComponentPath pathFromRootToAncestor(TypeElement ancestor) {
- for (ComponentPath componentPath : componentPaths) {
- if (componentPath.currentComponent().equals(ancestor)) {
- return componentPath;
- }
- }
- throw new IllegalArgumentException(
- String.format("%s is not in the current path: %s", ancestor.getQualifiedName(), this));
- }
-
- /**
- * Returns the BindingGraph for {@code ancestor}, where {@code ancestor} is in the component path
- * of the current traversal.
- */
- protected final BindingGraph graphForAncestor(TypeElement ancestor) {
- for (BindingGraph graph : bindingGraphPath) {
- if (graph.componentTypeElement().equals(ancestor)) {
- return graph;
- }
- }
- throw new IllegalArgumentException(
- String.format("%s is not in the current path: %s", ancestor.getQualifiedName(), this));
- }
-}
diff --git a/java/dagger/internal/codegen/ComponentValidator.java b/java/dagger/internal/codegen/ComponentValidator.java
deleted file mode 100644
index 773991516..000000000
--- a/java/dagger/internal/codegen/ComponentValidator.java
+++ /dev/null
@@ -1,522 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreElements.asType;
-import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.auto.common.MoreTypes.asDeclared;
-import static com.google.auto.common.MoreTypes.asExecutable;
-import static com.google.common.base.Verify.verify;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static com.google.common.collect.Multimaps.asMap;
-import static com.google.common.collect.Sets.intersection;
-import static dagger.internal.codegen.ComponentAnnotation.anyComponentAnnotation;
-import static dagger.internal.codegen.ComponentAnnotation.componentAnnotation;
-import static dagger.internal.codegen.ComponentCreatorAnnotation.creatorAnnotationsFor;
-import static dagger.internal.codegen.ComponentCreatorAnnotation.productionCreatorAnnotations;
-import static dagger.internal.codegen.ComponentCreatorAnnotation.subcomponentCreatorAnnotations;
-import static dagger.internal.codegen.ComponentKind.annotationsFor;
-import static dagger.internal.codegen.ConfigurationAnnotations.enclosedAnnotatedTypes;
-import static dagger.internal.codegen.ConfigurationAnnotations.getTransitiveModules;
-import static dagger.internal.codegen.DaggerStreams.toImmutableList;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.ErrorMessages.ComponentCreatorMessages.builderMethodRequiresNoArgs;
-import static dagger.internal.codegen.ErrorMessages.ComponentCreatorMessages.moreThanOneRefToSubcomponent;
-import static dagger.internal.codegen.ModuleAnnotation.moduleAnnotation;
-import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
-import static dagger.internal.codegen.langmodel.DaggerElements.getAnyAnnotation;
-import static java.util.Comparator.comparing;
-import static javax.lang.model.element.ElementKind.CLASS;
-import static javax.lang.model.element.ElementKind.INTERFACE;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.type.TypeKind.VOID;
-import static javax.lang.model.util.ElementFilter.methodsIn;
-
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.Maps;
-import com.google.common.collect.SetMultimap;
-import com.google.common.collect.Sets;
-import dagger.Component;
-import dagger.Reusable;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.DependencyRequest;
-import dagger.model.Key;
-import dagger.producers.CancellationPolicy;
-import dagger.producers.ProductionComponent;
-import java.lang.annotation.Annotation;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import javax.inject.Inject;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.type.TypeVisitor;
-import javax.lang.model.util.SimpleTypeVisitor6;
-import javax.lang.model.util.SimpleTypeVisitor8;
-
-/**
- * Performs superficial validation of the contract of the {@link Component} and {@link
- * ProductionComponent} annotations.
- */
-final class ComponentValidator {
- private final DaggerElements elements;
- private final DaggerTypes types;
- private final ModuleValidator moduleValidator;
- private final ComponentCreatorValidator creatorValidator;
- private final DependencyRequestValidator dependencyRequestValidator;
- private final MembersInjectionValidator membersInjectionValidator;
- private final MethodSignatureFormatter methodSignatureFormatter;
- private final DependencyRequestFactory dependencyRequestFactory;
-
- @Inject
- ComponentValidator(
- DaggerElements elements,
- DaggerTypes types,
- ModuleValidator moduleValidator,
- ComponentCreatorValidator creatorValidator,
- DependencyRequestValidator dependencyRequestValidator,
- MembersInjectionValidator membersInjectionValidator,
- MethodSignatureFormatter methodSignatureFormatter,
- DependencyRequestFactory dependencyRequestFactory) {
- this.elements = elements;
- this.types = types;
- this.moduleValidator = moduleValidator;
- this.creatorValidator = creatorValidator;
- this.dependencyRequestValidator = dependencyRequestValidator;
- this.membersInjectionValidator = membersInjectionValidator;
- this.methodSignatureFormatter = methodSignatureFormatter;
- this.dependencyRequestFactory = dependencyRequestFactory;
- }
-
- @AutoValue
- abstract static class ComponentValidationReport {
- abstract ImmutableSet<Element> referencedSubcomponents();
-
- abstract ValidationReport<TypeElement> report();
- }
-
- /**
- * Validates the given component subject. Also validates any referenced subcomponents that aren't
- * already included in the {@code validatedSubcomponents} set.
- */
- public ComponentValidationReport validate(
- TypeElement subject,
- Set<? extends Element> validatedSubcomponents,
- Set<? extends Element> validatedSubcomponentCreators) {
- ValidationReport.Builder<TypeElement> report = ValidationReport.about(subject);
-
- ImmutableSet<ComponentKind> componentKinds = ComponentKind.getComponentKinds(subject);
- ImmutableSet<Element> allSubcomponents;
- if (componentKinds.size() > 1) {
- String error =
- "Components may not be annotated with more than one component annotation: found "
- + annotationsFor(componentKinds);
- report.addError(error, subject);
- allSubcomponents = ImmutableSet.of();
- } else {
- ComponentKind componentKind = getOnlyElement(componentKinds);
- ComponentAnnotation componentAnnotation = anyComponentAnnotation(subject).get();
- allSubcomponents =
- validate(
- subject,
- componentAnnotation,
- componentKind,
- validatedSubcomponents,
- validatedSubcomponentCreators,
- report);
- }
-
- return new AutoValue_ComponentValidator_ComponentValidationReport(
- allSubcomponents, report.build());
- }
-
- private ImmutableSet<Element> validate(
- TypeElement subject,
- ComponentAnnotation componentAnnotation,
- ComponentKind componentKind,
- Set<? extends Element> validatedSubcomponents,
- Set<? extends Element> validatedSubcomponentCreators,
- ValidationReport.Builder<TypeElement> report) {
- if (isAnnotationPresent(subject, CancellationPolicy.class) && !componentKind.isProducer()) {
- report.addError(
- "@CancellationPolicy may only be applied to production components and subcomponents",
- subject);
- }
-
- if (!subject.getKind().equals(INTERFACE)
- && !(subject.getKind().equals(CLASS) && subject.getModifiers().contains(ABSTRACT))) {
- report.addError(
- String.format(
- "@%s may only be applied to an interface or abstract class",
- componentKind.annotation().getSimpleName()),
- subject);
- }
-
- ImmutableList<DeclaredType> creators =
- creatorAnnotationsFor(componentAnnotation).stream()
- .flatMap(annotation -> enclosedAnnotatedTypes(subject, annotation).stream())
- .collect(toImmutableList());
- if (creators.size() > 1) {
- report.addError(
- String.format(ErrorMessages.componentMessagesFor(componentKind).moreThanOne(), creators),
- subject);
- }
-
- Optional<AnnotationMirror> reusableAnnotation = getAnnotationMirror(subject, Reusable.class);
- if (reusableAnnotation.isPresent()) {
- report.addError(
- "@Reusable cannot be applied to components or subcomponents",
- subject,
- reusableAnnotation.get());
- }
-
- DeclaredType subjectType = MoreTypes.asDeclared(subject.asType());
-
- SetMultimap<Element, ExecutableElement> referencedSubcomponents = LinkedHashMultimap.create();
- getLocalAndInheritedMethods(subject, types, elements).stream()
- .filter(method -> method.getModifiers().contains(ABSTRACT))
- .forEachOrdered(
- method -> {
- ExecutableType resolvedMethod = asExecutable(types.asMemberOf(subjectType, method));
- List<? extends TypeMirror> parameterTypes = resolvedMethod.getParameterTypes();
- List<? extends VariableElement> parameters = method.getParameters();
- TypeMirror returnType = resolvedMethod.getReturnType();
-
- if (!resolvedMethod.getTypeVariables().isEmpty()) {
- report.addError("Component methods cannot have type variables", method);
- }
-
- // abstract methods are ones we have to implement, so they each need to be validated
- // first, check the return type. if it's a subcomponent, validate that method as such.
- Optional<AnnotationMirror> subcomponentAnnotation =
- checkForAnnotations(
- returnType,
- componentKind.legalSubcomponentKinds().stream()
- .map(ComponentKind::annotation)
- .collect(toImmutableSet()));
- Optional<AnnotationMirror> subcomponentCreatorAnnotation =
- checkForAnnotations(
- returnType,
- componentAnnotation.isProduction()
- ? intersection(
- subcomponentCreatorAnnotations(), productionCreatorAnnotations())
- : subcomponentCreatorAnnotations());
- if (subcomponentAnnotation.isPresent()) {
- referencedSubcomponents.put(MoreTypes.asElement(returnType), method);
- validateSubcomponentMethod(
- report,
- ComponentKind.forAnnotatedElement(MoreTypes.asTypeElement(returnType)).get(),
- method,
- parameters,
- parameterTypes,
- returnType,
- subcomponentAnnotation);
- } else if (subcomponentCreatorAnnotation.isPresent()) {
- referencedSubcomponents.put(
- MoreTypes.asElement(returnType).getEnclosingElement(), method);
- validateSubcomponentCreatorMethod(
- report, method, parameters, returnType, validatedSubcomponentCreators);
- } else {
- // if it's not a subcomponent...
- switch (parameters.size()) {
- case 0:
- // no parameters means that it is a provision method
- dependencyRequestValidator.validateDependencyRequest(
- report, method, returnType);
- break;
- case 1:
- // one parameter means that it's a members injection method
- TypeMirror parameterType = Iterables.getOnlyElement(parameterTypes);
- report.addSubreport(
- membersInjectionValidator.validateMembersInjectionMethod(
- method, parameterType));
- if (!(returnType.getKind().equals(VOID)
- || types.isSameType(returnType, parameterType))) {
- report.addError(
- "Members injection methods may only return the injected type or void.",
- method);
- }
- break;
- default:
- // this isn't any method that we know how to implement...
- report.addError(
- "This method isn't a valid provision method, members injection method or "
- + "subcomponent factory method. Dagger cannot implement this method",
- method);
- break;
- }
- }
- });
-
- checkConflictingEntryPoints(report);
-
- Maps.filterValues(referencedSubcomponents.asMap(), methods -> methods.size() > 1)
- .forEach(
- (subcomponent, methods) ->
- report.addError(
- String.format(moreThanOneRefToSubcomponent(), subcomponent, methods), subject));
-
- validateComponentDependencies(report, componentAnnotation.dependencyTypes());
- report.addSubreport(
- moduleValidator.validateReferencedModules(
- subject,
- componentAnnotation.annotation(),
- componentKind.legalModuleKinds(),
- new HashSet<>()));
-
- // Make sure we validate any subcomponents we're referencing, unless we know we validated
- // them already in this pass.
- // TODO(sameb): If subcomponents refer to each other and both aren't in
- // 'validatedSubcomponents' (e.g, both aren't compiled in this pass),
- // then this can loop forever.
- ImmutableSet.Builder<Element> allSubcomponents =
- ImmutableSet.<Element>builder().addAll(referencedSubcomponents.keySet());
- for (Element subcomponent :
- Sets.difference(referencedSubcomponents.keySet(), validatedSubcomponents)) {
- ComponentValidationReport subreport =
- validate(asType(subcomponent), validatedSubcomponents, validatedSubcomponentCreators);
- report.addItems(subreport.report().items());
- allSubcomponents.addAll(subreport.referencedSubcomponents());
- }
- return allSubcomponents.build();
- }
-
- private void checkConflictingEntryPoints(ValidationReport.Builder<TypeElement> report) {
- DeclaredType componentType = asDeclared(report.getSubject().asType());
-
- // Collect entry point methods that are not overridden by others. If the "same" method is
- // inherited from more than one supertype, each will be in the multimap.
- SetMultimap<String, ExecutableElement> entryPointMethods = HashMultimap.create();
-
- methodsIn(elements.getAllMembers(report.getSubject()))
- .stream()
- .filter(
- method -> isEntryPoint(method, asExecutable(types.asMemberOf(componentType, method))))
- .forEach(
- method ->
- addMethodUnlessOverridden(
- method, entryPointMethods.get(method.getSimpleName().toString())));
-
- for (Set<ExecutableElement> methods : asMap(entryPointMethods).values()) {
- if (distinctKeys(methods, report.getSubject()).size() > 1) {
- reportConflictingEntryPoints(methods, report);
- }
- }
- }
-
- private boolean isEntryPoint(ExecutableElement method, ExecutableType methodType) {
- return method.getModifiers().contains(ABSTRACT)
- && method.getParameters().isEmpty()
- && !methodType.getReturnType().getKind().equals(VOID)
- && methodType.getTypeVariables().isEmpty();
- }
-
- private ImmutableSet<Key> distinctKeys(Set<ExecutableElement> methods, TypeElement component) {
- return methods
- .stream()
- .map(method -> dependencyRequest(method, component))
- .map(DependencyRequest::key)
- .collect(toImmutableSet());
- }
-
- private DependencyRequest dependencyRequest(ExecutableElement method, TypeElement component) {
- ExecutableType methodType =
- asExecutable(types.asMemberOf(asDeclared(component.asType()), method));
- return ComponentKind.forAnnotatedElement(component).get().isProducer()
- ? dependencyRequestFactory.forComponentProductionMethod(method, methodType)
- : dependencyRequestFactory.forComponentProvisionMethod(method, methodType);
- }
-
- private void addMethodUnlessOverridden(ExecutableElement method, Set<ExecutableElement> methods) {
- if (methods.stream().noneMatch(existingMethod -> overridesAsDeclared(existingMethod, method))) {
- methods.removeIf(existingMethod -> overridesAsDeclared(method, existingMethod));
- methods.add(method);
- }
- }
-
- /**
- * Returns {@code true} if {@code overrider} overrides {@code overridden} considered from within
- * the type that declares {@code overrider}.
- */
- // TODO(dpb): Does this break for ECJ?
- private boolean overridesAsDeclared(ExecutableElement overridder, ExecutableElement overridden) {
- return elements.overrides(overridder, overridden, asType(overridder.getEnclosingElement()));
- }
-
- private void reportConflictingEntryPoints(
- Collection<ExecutableElement> methods, ValidationReport.Builder<TypeElement> report) {
- verify(
- methods.stream().map(ExecutableElement::getEnclosingElement).distinct().count()
- == methods.size(),
- "expected each method to be declared on a different type: %s",
- methods);
- StringBuilder message = new StringBuilder("conflicting entry point declarations:");
- methodSignatureFormatter
- .typedFormatter(asDeclared(report.getSubject().asType()))
- .formatIndentedList(
- message,
- ImmutableList.sortedCopyOf(
- comparing(
- method -> asType(method.getEnclosingElement()).getQualifiedName().toString()),
- methods),
- 1);
- report.addError(message.toString());
- }
-
- private void validateSubcomponentMethod(
- final ValidationReport.Builder<TypeElement> report,
- final ComponentKind subcomponentKind,
- ExecutableElement method,
- List<? extends VariableElement> parameters,
- List<? extends TypeMirror> parameterTypes,
- TypeMirror returnType,
- Optional<AnnotationMirror> subcomponentAnnotation) {
- ImmutableSet<TypeElement> moduleTypes =
- componentAnnotation(subcomponentAnnotation.get()).modules();
-
- // TODO(gak): This logic maybe/probably shouldn't live here as it requires us to traverse
- // subcomponents and their modules separately from how it is done in ComponentDescriptor and
- // ModuleDescriptor
- @SuppressWarnings("deprecation")
- ImmutableSet<TypeElement> transitiveModules =
- getTransitiveModules(types, elements, moduleTypes);
-
- Set<TypeElement> variableTypes = Sets.newHashSet();
-
- for (int i = 0; i < parameterTypes.size(); i++) {
- VariableElement parameter = parameters.get(i);
- TypeMirror parameterType = parameterTypes.get(i);
- Optional<TypeElement> moduleType =
- parameterType.accept(
- new SimpleTypeVisitor6<Optional<TypeElement>, Void>() {
- @Override
- protected Optional<TypeElement> defaultAction(TypeMirror e, Void p) {
- return Optional.empty();
- }
-
- @Override
- public Optional<TypeElement> visitDeclared(DeclaredType t, Void p) {
- for (ModuleKind moduleKind : subcomponentKind.legalModuleKinds()) {
- if (isAnnotationPresent(t.asElement(), moduleKind.annotation())) {
- return Optional.of(MoreTypes.asTypeElement(t));
- }
- }
- return Optional.empty();
- }
- },
- null);
- if (moduleType.isPresent()) {
- if (variableTypes.contains(moduleType.get())) {
- report.addError(
- String.format(
- "A module may only occur once an an argument in a Subcomponent factory "
- + "method, but %s was already passed.",
- moduleType.get().getQualifiedName()),
- parameter);
- }
- if (!transitiveModules.contains(moduleType.get())) {
- report.addError(
- String.format(
- "%s is present as an argument to the %s factory method, but is not one of the"
- + " modules used to implement the subcomponent.",
- moduleType.get().getQualifiedName(),
- MoreTypes.asTypeElement(returnType).getQualifiedName()),
- method);
- }
- variableTypes.add(moduleType.get());
- } else {
- report.addError(
- String.format(
- "Subcomponent factory methods may only accept modules, but %s is not.",
- parameterType),
- parameter);
- }
- }
- }
-
- private void validateSubcomponentCreatorMethod(
- ValidationReport.Builder<TypeElement> report,
- ExecutableElement method,
- List<? extends VariableElement> parameters,
- TypeMirror returnType,
- Set<? extends Element> validatedSubcomponentCreators) {
- if (!parameters.isEmpty()) {
- report.addError(builderMethodRequiresNoArgs(), method);
- }
-
- // If we haven't already validated the subcomponent creator itself, validate it now.
- TypeElement creatorElement = MoreTypes.asTypeElement(returnType);
- if (!validatedSubcomponentCreators.contains(creatorElement)) {
- // TODO(sameb): The creator validator right now assumes the element is being compiled
- // in this pass, which isn't true here. We should change error messages to spit out
- // this method as the subject and add the original subject to the message output.
- report.addItems(creatorValidator.validate(creatorElement).items());
- }
- }
-
- private static <T extends Element> void validateComponentDependencies(
- ValidationReport.Builder<T> report, Iterable<TypeMirror> types) {
- for (TypeMirror type : types) {
- type.accept(CHECK_DEPENDENCY_TYPES, report);
- }
- }
-
- private static final TypeVisitor<Void, ValidationReport.Builder<?>> CHECK_DEPENDENCY_TYPES =
- new SimpleTypeVisitor8<Void, ValidationReport.Builder<?>>() {
- @Override
- protected Void defaultAction(TypeMirror type, ValidationReport.Builder<?> report) {
- report.addError(type + " is not a valid component dependency type");
- return null;
- }
-
- @Override
- public Void visitDeclared(DeclaredType type, ValidationReport.Builder<?> report) {
- if (moduleAnnotation(MoreTypes.asTypeElement(type)).isPresent()) {
- report.addError(type + " is a module, which cannot be a component dependency");
- }
- return null;
- }
- };
-
- private static Optional<AnnotationMirror> checkForAnnotations(
- TypeMirror type, final Set<? extends Class<? extends Annotation>> annotations) {
- return type.accept(
- new SimpleTypeVisitor6<Optional<AnnotationMirror>, Void>(Optional.empty()) {
- @Override
- public Optional<AnnotationMirror> visitDeclared(DeclaredType t, Void p) {
- return getAnyAnnotation(t.asElement(), annotations);
- }
- },
- null);
- }
-}
diff --git a/java/dagger/internal/codegen/ConfigurationAnnotations.java b/java/dagger/internal/codegen/ConfigurationAnnotations.java
deleted file mode 100644
index a9bba2977..000000000
--- a/java/dagger/internal/codegen/ConfigurationAnnotations.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.collect.Iterables.consumingIterable;
-import static dagger.internal.codegen.ComponentAnnotation.subcomponentAnnotation;
-import static dagger.internal.codegen.ComponentCreatorAnnotation.subcomponentCreatorAnnotations;
-import static dagger.internal.codegen.ModuleAnnotation.moduleAnnotation;
-import static dagger.internal.codegen.MoreAnnotationMirrors.getTypeListValue;
-import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
-import static javax.lang.model.util.ElementFilter.typesIn;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-import dagger.Component;
-import dagger.Module;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import java.lang.annotation.Annotation;
-import java.util.ArrayDeque;
-import java.util.List;
-import java.util.Optional;
-import java.util.Queue;
-import java.util.Set;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * Utility methods related to dagger configuration annotations (e.g.: {@link Component}
- * and {@link Module}).
- */
-final class ConfigurationAnnotations {
-
- static Optional<TypeElement> getSubcomponentCreator(TypeElement subcomponent) {
- checkArgument(subcomponentAnnotation(subcomponent).isPresent());
- for (TypeElement nestedType : typesIn(subcomponent.getEnclosedElements())) {
- if (isSubcomponentCreator(nestedType)) {
- return Optional.of(nestedType);
- }
- }
- return Optional.empty();
- }
-
- static boolean isSubcomponentCreator(Element element) {
- return isAnyAnnotationPresent(element, subcomponentCreatorAnnotations());
- }
-
- // Dagger 1 support.
- static ImmutableList<TypeMirror> getModuleInjects(AnnotationMirror moduleAnnotation) {
- checkNotNull(moduleAnnotation);
- return getTypeListValue(moduleAnnotation, "injects");
- }
-
- /** Returns the first type that specifies this' nullability, or empty if none. */
- static Optional<DeclaredType> getNullableType(Element element) {
- List<? extends AnnotationMirror> mirrors = element.getAnnotationMirrors();
- for (AnnotationMirror mirror : mirrors) {
- if (mirror.getAnnotationType().asElement().getSimpleName().contentEquals("Nullable")) {
- return Optional.of(mirror.getAnnotationType());
- }
- }
- return Optional.empty();
- }
-
- /**
- * Returns the full set of modules transitively {@linkplain Module#includes included} from the
- * given seed modules. If a module is malformed and a type listed in {@link Module#includes} is
- * not annotated with {@link Module}, it is ignored.
- *
- * @deprecated Use {@link ComponentDescriptor#modules()}.
- */
- @Deprecated
- static ImmutableSet<TypeElement> getTransitiveModules(
- DaggerTypes types, DaggerElements elements, Iterable<TypeElement> seedModules) {
- TypeMirror objectType = elements.getTypeElement(Object.class).asType();
- Queue<TypeElement> moduleQueue = new ArrayDeque<>();
- Iterables.addAll(moduleQueue, seedModules);
- Set<TypeElement> moduleElements = Sets.newLinkedHashSet();
- for (TypeElement moduleElement : consumingIterable(moduleQueue)) {
- moduleAnnotation(moduleElement)
- .ifPresent(
- moduleAnnotation -> {
- ImmutableSet.Builder<TypeElement> moduleDependenciesBuilder =
- ImmutableSet.builder();
- moduleDependenciesBuilder.addAll(moduleAnnotation.includes());
- // We don't recur on the parent class because we don't want the parent class as a
- // root that the component depends on, and also because we want the dependencies
- // rooted against this element, not the parent.
- addIncludesFromSuperclasses(
- types, moduleElement, moduleDependenciesBuilder, objectType);
- ImmutableSet<TypeElement> moduleDependencies = moduleDependenciesBuilder.build();
- moduleElements.add(moduleElement);
- for (TypeElement dependencyType : moduleDependencies) {
- if (!moduleElements.contains(dependencyType)) {
- moduleQueue.add(dependencyType);
- }
- }
- });
- }
- return ImmutableSet.copyOf(moduleElements);
- }
-
- /** Returns the enclosed types annotated with the given annotation. */
- static ImmutableList<DeclaredType> enclosedAnnotatedTypes(
- TypeElement typeElement, Class<? extends Annotation> annotation) {
- final ImmutableList.Builder<DeclaredType> builders = ImmutableList.builder();
- for (TypeElement element : typesIn(typeElement.getEnclosedElements())) {
- if (MoreElements.isAnnotationPresent(element, annotation)) {
- builders.add(MoreTypes.asDeclared(element.asType()));
- }
- }
- return builders.build();
- }
-
- /** Traverses includes from superclasses and adds them into the builder. */
- private static void addIncludesFromSuperclasses(
- DaggerTypes types,
- TypeElement element,
- ImmutableSet.Builder<TypeElement> builder,
- TypeMirror objectType) {
- // Also add the superclass to the queue, in case any @Module definitions were on that.
- TypeMirror superclass = element.getSuperclass();
- while (!types.isSameType(objectType, superclass)
- && superclass.getKind().equals(TypeKind.DECLARED)) {
- element = MoreElements.asType(types.asElement(superclass));
- moduleAnnotation(element)
- .ifPresent(moduleAnnotation -> builder.addAll(moduleAnnotation.includes()));
- superclass = element.getSuperclass();
- }
- }
-
- private ConfigurationAnnotations() {}
-}
diff --git a/java/dagger/internal/codegen/ContributionBinding.java b/java/dagger/internal/codegen/ContributionBinding.java
deleted file mode 100644
index 0958bc8c0..000000000
--- a/java/dagger/internal/codegen/ContributionBinding.java
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkState;
-import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.CLASS_CONSTRUCTOR;
-import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.DELEGATE;
-import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.SINGLETON_INSTANCE;
-import static dagger.internal.codegen.MapKeys.unwrapValue;
-import static dagger.internal.codegen.MoreAnnotationMirrors.unwrapOptionalEquivalence;
-import static java.util.Arrays.asList;
-
-import com.google.auto.common.MoreElements;
-import com.google.common.base.Equivalence;
-import com.google.errorprone.annotations.CanIgnoreReturnValue;
-import com.google.errorprone.annotations.CheckReturnValue;
-import dagger.internal.codegen.ContributionType.HasContributionType;
-import dagger.model.BindingKind;
-import dagger.model.DependencyRequest;
-import dagger.model.Key;
-import java.util.Optional;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * An abstract class for a value object representing the mechanism by which a {@link Key} can be
- * contributed to a dependency graph.
- */
-abstract class ContributionBinding extends Binding implements HasContributionType {
-
- /** Returns the type that specifies this' nullability, absent if not nullable. */
- abstract Optional<DeclaredType> nullableType();
-
- abstract Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedMapKeyAnnotation();
-
- final Optional<AnnotationMirror> mapKeyAnnotation() {
- return unwrapOptionalEquivalence(wrappedMapKeyAnnotation());
- }
-
- /**
- * If this is a map contribution, returns the key of its map entry.
- *
- * @throws IllegalStateException if {@link #mapKeyAnnotation()} returns an empty value.
- */
- final Object mapKey() {
- checkState(mapKeyAnnotation().isPresent());
- AnnotationMirror mapKeyAnnotation = mapKeyAnnotation().get();
- return unwrapValue(mapKeyAnnotation).map(AnnotationValue::getValue).orElse(mapKeyAnnotation);
- }
-
- /** If {@link #bindingElement()} is a method that returns a primitive type, returns that type. */
- final Optional<TypeMirror> contributedPrimitiveType() {
- return bindingElement()
- .filter(bindingElement -> bindingElement instanceof ExecutableElement)
- .map(bindingElement -> MoreElements.asExecutable(bindingElement).getReturnType())
- .filter(type -> type.getKind().isPrimitive());
- }
-
- @Override
- public final boolean isNullable() {
- return nullableType().isPresent();
- }
-
- /**
- * The strategy for getting an instance of a factory for a {@link ContributionBinding}.
- */
- enum FactoryCreationStrategy {
- /** The factory class is a single instance. */
- SINGLETON_INSTANCE,
- /** The factory must be created by calling the constructor. */
- CLASS_CONSTRUCTOR,
- /** The factory is simply delegated to another. */
- DELEGATE,
- }
-
- /**
- * Returns the {@link FactoryCreationStrategy} appropriate for a binding.
- *
- * <p>Delegate bindings use the {@link FactoryCreationStrategy#DELEGATE} strategy.
- *
- * <p>Bindings without dependencies that don't require a module instance use the {@link
- * FactoryCreationStrategy#SINGLETON_INSTANCE} strategy.
- *
- * <p>All other bindings use the {@link FactoryCreationStrategy#CLASS_CONSTRUCTOR} strategy.
- */
- final FactoryCreationStrategy factoryCreationStrategy() {
- switch (kind()) {
- case DELEGATE:
- return DELEGATE;
- case PROVISION:
- return dependencies().isEmpty() && !requiresModuleInstance()
- ? SINGLETON_INSTANCE
- : CLASS_CONSTRUCTOR;
- case INJECTION:
- case MULTIBOUND_SET:
- case MULTIBOUND_MAP:
- return dependencies().isEmpty() ? SINGLETON_INSTANCE : CLASS_CONSTRUCTOR;
- default:
- return CLASS_CONSTRUCTOR;
- }
- }
-
- /**
- * The {@link TypeMirror type} for the {@code Factory<T>} or {@code Producer<T>} which is created
- * for this binding. Uses the binding's key, V in the case of {@code Map<K, FrameworkClass<V>>>},
- * and E {@code Set<E>} for {@link dagger.multibindings.IntoSet @IntoSet} methods.
- */
- final TypeMirror contributedType() {
- switch (contributionType()) {
- case MAP:
- return MapType.from(key()).unwrappedFrameworkValueType();
- case SET:
- return SetType.from(key()).elementType();
- case SET_VALUES:
- case UNIQUE:
- return key().type();
- }
- throw new AssertionError();
- }
-
- final boolean isSyntheticMultibinding() {
- switch (kind()) {
- case MULTIBOUND_SET:
- case MULTIBOUND_MAP:
- return true;
- default:
- return false;
- }
- }
-
- /** Whether the bound type has a generated implementation. */
- final boolean requiresGeneratedInstance() {
- switch (kind()) {
- case COMPONENT:
- case SUBCOMPONENT_CREATOR:
- return true;
- default:
- return false;
- }
- }
-
- /**
- * Returns {@link BindingKind#MULTIBOUND_SET} or {@link
- * BindingKind#MULTIBOUND_MAP} if the key is a set or map.
- *
- * @throws IllegalArgumentException if {@code key} is neither a set nor a map
- */
- static BindingKind bindingKindForMultibindingKey(Key key) {
- if (SetType.isSet(key)) {
- return BindingKind.MULTIBOUND_SET;
- } else if (MapType.isMap(key)) {
- return BindingKind.MULTIBOUND_MAP;
- } else {
- throw new IllegalArgumentException(String.format("key is not for a set or map: %s", key));
- }
- }
-
- /**
- * Base builder for {@link com.google.auto.value.AutoValue @AutoValue} subclasses of {@link
- * ContributionBinding}.
- */
- @CanIgnoreReturnValue
- abstract static class Builder<C extends ContributionBinding, B extends Builder<C, B>> {
- abstract B dependencies(Iterable<DependencyRequest> dependencies);
-
- B dependencies(DependencyRequest... dependencies) {
- return dependencies(asList(dependencies));
- }
-
- abstract B unresolved(C unresolved);
-
- abstract B contributionType(ContributionType contributionType);
-
- abstract B bindingElement(Element bindingElement);
-
- abstract B contributingModule(TypeElement contributingModule);
-
- abstract B key(Key key);
-
- abstract B nullableType(Optional<DeclaredType> nullableType);
-
- abstract B wrappedMapKeyAnnotation(
- Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedMapKeyAnnotation);
-
- abstract B kind(BindingKind kind);
-
- @CheckReturnValue
- abstract C build();
- }
-}
diff --git a/java/dagger/internal/codegen/ContributionType.java b/java/dagger/internal/codegen/ContributionType.java
deleted file mode 100644
index 66b528985..000000000
--- a/java/dagger/internal/codegen/ContributionType.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-
-import dagger.Provides;
-import dagger.multibindings.ElementsIntoSet;
-import dagger.multibindings.IntoMap;
-import dagger.multibindings.IntoSet;
-import javax.lang.model.element.Element;
-
-/** Whether a binding or declaration is for a unique contribution or a map or set multibinding. */
-enum ContributionType {
- /** Represents map bindings. */
- MAP,
- /** Represents set bindings. */
- SET,
- /** Represents set values bindings. */
- SET_VALUES,
- /** Represents a valid non-collection binding. */
- UNIQUE,
- ;
-
- /** An object that is associated with a {@link ContributionType}. */
- interface HasContributionType {
-
- /** The contribution type of this object. */
- ContributionType contributionType();
- }
-
- /** {@code true} if this is for a multibinding. */
- boolean isMultibinding() {
- return !this.equals(UNIQUE);
- }
-
- /**
- * The contribution type from a binding element's annotations. Presumes a well-formed binding
- * element (at most one of @IntoSet, @IntoMap, @ElementsIntoSet and @Provides.type). {@link
- * BindingMethodValidator} and {@link BindsInstanceProcessingStep} validate correctness on their
- * own.
- */
- static ContributionType fromBindingElement(Element element) {
- if (isAnnotationPresent(element, IntoMap.class)) {
- return ContributionType.MAP;
- } else if (isAnnotationPresent(element, IntoSet.class)) {
- return ContributionType.SET;
- } else if (isAnnotationPresent(element, ElementsIntoSet.class)) {
- return ContributionType.SET_VALUES;
- }
- return ContributionType.UNIQUE;
- }
-}
diff --git a/java/dagger/internal/codegen/CurrentImplementationSubcomponent.java b/java/dagger/internal/codegen/CurrentImplementationSubcomponent.java
deleted file mode 100644
index c78d60d87..000000000
--- a/java/dagger/internal/codegen/CurrentImplementationSubcomponent.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import dagger.BindsInstance;
-import dagger.Subcomponent;
-import dagger.internal.codegen.ComponentImplementationBuilder.RootComponentImplementationBuilder;
-import dagger.internal.codegen.ComponentImplementationBuilder.SubcomponentImplementationBuilder;
-import java.util.Optional;
-
-/**
- * A subcomponent that injects all objects that are responsible for creating a single {@link
- * ComponentImplementation} instance. Each child {@link ComponentImplementation} will have its own
- * instance of {@link CurrentImplementationSubcomponent}.
- */
-@Subcomponent(modules = GenerationOptionsModule.class)
-@PerComponentImplementation
-interface CurrentImplementationSubcomponent {
- RootComponentImplementationBuilder rootComponentBuilder();
-
- SubcomponentImplementationBuilder subcomponentBuilder();
-
- @Subcomponent.Builder
- interface Builder {
- @BindsInstance
- Builder componentImplementation(ComponentImplementation componentImplementation);
-
- @BindsInstance
- Builder bindingGraph(BindingGraph bindingGraph);
-
- @BindsInstance
- Builder parentBuilder(@ParentComponent Optional<ComponentImplementationBuilder> parentBuilder);
-
- @BindsInstance
- Builder parentBindingExpressions(
- @ParentComponent Optional<ComponentBindingExpressions> parentBindingExpressions);
-
- @BindsInstance
- Builder parentRequirementExpressions(
- @ParentComponent Optional<ComponentRequirementExpressions> parentRequirementExpressions);
-
- CurrentImplementationSubcomponent build();
- }
-}
diff --git a/java/dagger/internal/codegen/DaggerGraphs.java b/java/dagger/internal/codegen/DaggerGraphs.java
deleted file mode 100644
index dda6c118c..000000000
--- a/java/dagger/internal/codegen/DaggerGraphs.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.collect.Sets.difference;
-import static com.google.common.graph.Graphs.reachableNodes;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.graph.Graph;
-import com.google.common.graph.SuccessorsFunction;
-import java.util.ArrayDeque;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Queue;
-import java.util.Set;
-
-/** Utility methods for {@link com.google.common.graph} types. */
-public final class DaggerGraphs {
- /**
- * Returns a shortest path from {@code nodeU} to {@code nodeV} in {@code graph} as a list of the
- * nodes visited in sequence, including both {@code nodeU} and {@code nodeV}. (Note that there may
- * be many possible shortest paths.)
- *
- * <p>If {@code nodeV} is not {@link
- * com.google.common.graph.Graphs#reachableNodes(com.google.common.graph.Graph, Object) reachable}
- * from {@code nodeU}, the list returned is empty.
- *
- * @throws IllegalArgumentException if {@code nodeU} or {@code nodeV} is not present in {@code
- * graph}
- */
- public static <N> ImmutableList<N> shortestPath(SuccessorsFunction<N> graph, N nodeU, N nodeV) {
- if (nodeU.equals(nodeV)) {
- return ImmutableList.of(nodeU);
- }
- Set<N> successors = ImmutableSet.copyOf(graph.successors(nodeU));
- if (successors.contains(nodeV)) {
- return ImmutableList.of(nodeU, nodeV);
- }
-
- Map<N, N> visitedNodeToPathPredecessor = new HashMap<>(); // encodes shortest path tree
- for (N node : successors) {
- visitedNodeToPathPredecessor.put(node, nodeU);
- }
- Queue<N> currentNodes = new ArrayDeque<N>(successors);
- Queue<N> nextNodes = new ArrayDeque<N>();
-
- // Perform a breadth-first traversal starting with the successors of nodeU.
- while (!currentNodes.isEmpty()) {
- while (!currentNodes.isEmpty()) {
- N currentNode = currentNodes.remove();
- for (N nextNode : graph.successors(currentNode)) {
- if (visitedNodeToPathPredecessor.containsKey(nextNode)) {
- continue; // we already have a shortest path to nextNode
- }
- visitedNodeToPathPredecessor.put(nextNode, currentNode);
- if (nextNode.equals(nodeV)) {
- ImmutableList.Builder<N> builder = ImmutableList.builder();
- N node = nodeV;
- builder.add(node);
- while (!node.equals(nodeU)) {
- node = visitedNodeToPathPredecessor.get(node);
- builder.add(node);
- }
- return builder.build().reverse();
- }
- nextNodes.add(nextNode);
- }
- }
- Queue<N> emptyQueue = currentNodes;
- currentNodes = nextNodes;
- nextNodes = emptyQueue; // reusing empty queue faster than allocating new one
- }
-
- return ImmutableList.of();
- }
-
- /** Returns the nodes in a graph that are not reachable from a node. */
- public static <N> ImmutableSet<N> unreachableNodes(Graph<N> graph, N node) {
- return ImmutableSet.copyOf(difference(graph.nodes(), reachableNodes(graph, node)));
- }
-
- private DaggerGraphs() {}
-}
diff --git a/java/dagger/internal/codegen/DaggerKythePlugin.java b/java/dagger/internal/codegen/DaggerKythePlugin.java
deleted file mode 100644
index 5a685ef26..000000000
--- a/java/dagger/internal/codegen/DaggerKythePlugin.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// This must be in the dagger.internal.codegen package since Dagger doesn't expose its APIs publicly
-// https://github.com/google/dagger/issues/773 could present an opportunity to put this somewhere in
-// the regular kythe/java tree.
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.BindingRequest.bindingRequest;
-import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
-
-import com.google.auto.service.AutoService;
-import com.google.common.collect.Iterables;
-import com.google.devtools.kythe.analyzers.base.EntrySet;
-import com.google.devtools.kythe.analyzers.base.FactEmitter;
-import com.google.devtools.kythe.analyzers.base.KytheEntrySets;
-import com.google.devtools.kythe.analyzers.java.Plugin;
-import com.google.devtools.kythe.proto.Storage.VName;
-import com.sun.tools.javac.code.Symbol;
-import com.sun.tools.javac.tree.JCTree.JCClassDecl;
-import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
-import com.sun.tools.javac.util.Context;
-import dagger.BindsInstance;
-import dagger.Component;
-import dagger.model.DependencyRequest;
-import dagger.model.Key;
-import dagger.producers.ProductionComponent;
-import java.util.Optional;
-import java.util.logging.Logger;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-import javax.lang.model.element.Element;
-
-/**
- * A plugin which emits nodes and edges for <a href="https://github.com/google/dagger">Dagger</a>
- * specific code.
- */
-@AutoService(Plugin.class)
-public class DaggerKythePlugin extends Plugin.Scanner<Void, Void> {
- // TODO(ronshapiro): use flogger
- private static final Logger logger = Logger.getLogger(DaggerKythePlugin.class.getCanonicalName());
- private FactEmitter emitter;
- @Inject ComponentDescriptorFactory componentDescriptorFactory;
- @Inject BindingGraphFactory bindingGraphFactory;
-
- @Override
- public Void visitClassDef(JCClassDecl tree, Void p) {
- if (tree.sym != null
- && isAnyAnnotationPresent(tree.sym, Component.class, ProductionComponent.class)) {
- addNodesForGraph(
- bindingGraphFactory.create(
- componentDescriptorFactory.rootComponentDescriptor(tree.sym), false));
- }
- return super.visitClassDef(tree, p);
- }
-
- private void addNodesForGraph(BindingGraph graph) {
- addDependencyEdges(graph);
- addModuleEdges(graph);
- addChildComponentEdges(graph);
-
- graph.subgraphs().forEach(this::addNodesForGraph);
- }
-
- private void addDependencyEdges(BindingGraph graph) {
- for (ResolvedBindings resolvedBinding : graph.resolvedBindings()) {
- for (Binding binding : resolvedBinding.bindings()) {
- for (DependencyRequest dependency : binding.explicitDependencies()) {
- addEdgesForDependencyRequest(dependency, dependency.key(), graph);
- }
- }
- }
-
- for (ComponentDescriptor.ComponentMethodDescriptor componentMethod :
- graph.componentDescriptor().componentMethods()) {
- componentMethod
- .dependencyRequest()
- .ifPresent(request -> addEdgesForDependencyRequest(request, request.key(), graph));
- }
- }
-
- /**
- * Add {@code /inject/satisfiedby} edges from {@code dependency}'s {@link
- * DependencyRequest#requestElement()} to any {@link BindingDeclaration#bindingElement() binding
- * elements} that satisfy the request.
- *
- * <p>This collapses requests for synthetic bindings so that a request for a multibound key
- * points to all of the contributions for the multibound object. It does so by recursively calling
- * this method, with each dependency's key as the {@code targetKey}.
- */
- private void addEdgesForDependencyRequest(
- DependencyRequest dependency, Key targetKey, BindingGraph graph) {
- if (!dependency.requestElement().isPresent()) {
- return;
- }
- BindingRequest request = bindingRequest(targetKey, dependency.kind());
- ResolvedBindings resolvedBindings = graph.resolvedBindings(request);
- for (Binding binding : resolvedBindings.bindings()) {
- if (binding.bindingElement().isPresent()) {
- addDependencyEdge(dependency, binding);
- } else {
- for (DependencyRequest subsequentDependency : binding.explicitDependencies()) {
- addEdgesForDependencyRequest(dependency, subsequentDependency.key(), graph);
- }
- }
- }
- for (BindingDeclaration bindingDeclaration :
- Iterables.concat(
- resolvedBindings.multibindingDeclarations(),
- resolvedBindings.optionalBindingDeclarations())) {
- addDependencyEdge(dependency, bindingDeclaration);
- }
- }
-
- private void addDependencyEdge(
- DependencyRequest dependency, BindingDeclaration bindingDeclaration) {
- Element requestElement = dependency.requestElement().get();
- Element bindingElement = bindingDeclaration.bindingElement().get();
- Optional<VName> requestElementNode = jvmNode(requestElement, "request element");
- Optional<VName> bindingElementNode = jvmNode(bindingElement, "binding element");
- emitEdge(requestElementNode, "/inject/satisfiedby", bindingElementNode);
- // TODO(ronshapiro): emit facts about the component that satisfies the edge
- }
-
- private void addModuleEdges(BindingGraph graph) {
- Optional<VName> componentNode = jvmNode(graph.componentTypeElement(), "component");
- for (ModuleDescriptor module : graph.componentDescriptor().modules()) {
- Optional<VName> moduleNode = jvmNode(module.moduleElement(), "module");
- emitEdge(componentNode, "/inject/installsmodule", moduleNode);
- }
- }
-
- private void addChildComponentEdges(BindingGraph graph) {
- Optional<VName> componentNode = jvmNode(graph.componentTypeElement(), "component");
- for (BindingGraph subgraph : graph.subgraphs()) {
- Optional<VName> subcomponentNode =
- jvmNode(subgraph.componentTypeElement(), "child component");
- emitEdge(componentNode, "/inject/childcomponent", subcomponentNode);
- }
- }
-
- private Optional<VName> jvmNode(Element element, String name) {
- Optional<VName> jvmNode = kytheGraph.getJvmNode((Symbol) element).map(KytheNode::getVName);
- if (!jvmNode.isPresent()) {
- logger.warning(String.format("Missing JVM node for %s: %s", name, element));
- }
- return jvmNode;
- }
-
- private void emitEdge(Optional<VName> source, String edgeName, Optional<VName> target) {
- source.ifPresent(
- s -> target.ifPresent(t -> new EntrySet.Builder(s, edgeName, t).build().emit(emitter)));
- }
-
- @Override
- public void run(
- JCCompilationUnit compilationUnit, KytheEntrySets entrySets, KytheGraph kytheGraph) {
- if (bindingGraphFactory == null) {
- emitter = entrySets.getEmitter();
- DaggerDaggerKythePlugin_PluginComponent.builder()
- .context(kytheGraph.getJavaContext())
- .build()
- .inject(this);
- }
- super.run(compilationUnit, entrySets, kytheGraph);
- }
-
- @Singleton
- @Component(modules = JavacPluginModule.class)
- interface PluginComponent {
- void inject(DaggerKythePlugin plugin);
-
- @Component.Builder
- interface Builder {
- @BindsInstance
- Builder context(Context context);
-
- PluginComponent build();
- }
- }
-}
diff --git a/java/dagger/internal/codegen/DaggerStatistics.java b/java/dagger/internal/codegen/DaggerStatistics.java
deleted file mode 100644
index 790ec7654..000000000
--- a/java/dagger/internal/codegen/DaggerStatistics.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
-import com.google.auto.value.AutoValue;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.errorprone.annotations.CanIgnoreReturnValue;
-import java.time.Duration;
-
-/** Statistics collected over the course of Dagger annotation processing. */
-@AutoValue
-abstract class DaggerStatistics {
- /** Returns a new {@link Builder}. */
- static Builder builder() {
- return new AutoValue_DaggerStatistics.Builder();
- }
-
- /** Returns a new {@link RoundStatistics} builder. */
- static RoundStatistics.Builder roundBuilder() {
- return new AutoValue_DaggerStatistics_RoundStatistics.Builder();
- }
-
- /** Total time spent in Dagger annotation processing. */
- abstract Duration totalProcessingTime();
-
- /** List of statistics for processing rounds that the Dagger processor handled. */
- abstract ImmutableList<RoundStatistics> rounds();
-
- /** Builder for {@link DaggerStatistics}. */
- @AutoValue.Builder
- abstract static class Builder {
- /** Sets the given duration for the total time spent in Dagger processing. */
- @CanIgnoreReturnValue
- abstract Builder setTotalProcessingTime(Duration totalProcessingTime);
-
- /** Returns a builder for adding processing round statistics. */
- abstract ImmutableList.Builder<RoundStatistics> roundsBuilder();
-
- /** Adds the given {@code round} statistics. */
- @CanIgnoreReturnValue
- final Builder addRound(RoundStatistics round) {
- roundsBuilder().add(round);
- return this;
- }
-
- /** Creates a new {@link DaggerStatistics} instance. */
- abstract DaggerStatistics build();
- }
-
- /** Statistics for each processing step in a single processing round. */
- @AutoValue
- abstract static class RoundStatistics {
- /** Map of processing step class to duration of that step for this round. */
- abstract ImmutableMap<Class<? extends ProcessingStep>, Duration> stepDurations();
-
- /** Builder for {@link RoundStatistics}. */
- @AutoValue.Builder
- abstract static class Builder {
- /** Returns a builder for adding durations for each processing step for the round. */
- abstract ImmutableMap.Builder<Class<? extends ProcessingStep>, Duration>
- stepDurationsBuilder();
-
- /** Adds the given {@code duration} for the given {@code step}. */
- @CanIgnoreReturnValue
- final Builder addStepDuration(ProcessingStep step, Duration duration) {
- stepDurationsBuilder().put(step.getClass(), duration);
- return this;
- }
-
- /** Creates a new {@link RoundStatistics} instance. */
- abstract RoundStatistics build();
- }
- }
-}
diff --git a/java/dagger/internal/codegen/DaggerStatisticsCollectingProcessingStep.java b/java/dagger/internal/codegen/DaggerStatisticsCollectingProcessingStep.java
deleted file mode 100644
index 51f6fc3b0..000000000
--- a/java/dagger/internal/codegen/DaggerStatisticsCollectingProcessingStep.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2019 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
-import com.google.common.collect.SetMultimap;
-import java.lang.annotation.Annotation;
-import java.util.Set;
-import javax.lang.model.element.Element;
-
-/**
- * {@link ProcessingStep} that delegates to another {@code ProcessingStep} and collects timing
- * statistics for each processing round for that step.
- */
-final class DaggerStatisticsCollectingProcessingStep implements ProcessingStep {
-
- private final ProcessingStep delegate;
- private final DaggerStatisticsCollector statisticsCollector;
-
- DaggerStatisticsCollectingProcessingStep(
- ProcessingStep delegate, DaggerStatisticsCollector statisticsCollector) {
- this.delegate = checkNotNull(delegate);
- this.statisticsCollector = checkNotNull(statisticsCollector);
- }
-
- @Override
- public Set<? extends Class<? extends Annotation>> annotations() {
- return delegate.annotations();
- }
-
- @Override
- public Set<? extends Element> process(
- SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
- statisticsCollector.stepStarted(delegate);
- try {
- return delegate.process(elementsByAnnotation);
- } finally {
- statisticsCollector.stepFinished(delegate);
- }
- }
-}
diff --git a/java/dagger/internal/codegen/DaggerStatisticsCollector.java b/java/dagger/internal/codegen/DaggerStatisticsCollector.java
deleted file mode 100644
index e14fbb759..000000000
--- a/java/dagger/internal/codegen/DaggerStatisticsCollector.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkState;
-import static java.util.concurrent.TimeUnit.NANOSECONDS;
-
-import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
-import com.google.common.base.Stopwatch;
-import com.google.common.base.Ticker;
-import java.time.Duration;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/** Collects {@link DaggerStatistics} over the course of Dagger annotation processing. */
-@Singleton // for state sharing
-final class DaggerStatisticsCollector {
-
- private final Ticker ticker;
- private final Stopwatch totalRuntimeStopwatch;
- private final Map<ProcessingStep, Stopwatch> stepStopwatches = new HashMap<>();
-
- private final DaggerStatistics.Builder statisticsBuilder = DaggerStatistics.builder();
- private DaggerStatistics.RoundStatistics.Builder roundBuilder = DaggerStatistics.roundBuilder();
-
- private final Optional<DaggerStatisticsRecorder> statisticsRecorder;
-
- @Inject
- DaggerStatisticsCollector(Ticker ticker, Optional<DaggerStatisticsRecorder> statisticsRecorder) {
- this.ticker = ticker;
- this.totalRuntimeStopwatch = Stopwatch.createUnstarted(ticker);
- this.statisticsRecorder = statisticsRecorder;
- }
-
- /** Called when Dagger annotation processing starts. */
- void processingStarted() {
- checkState(!totalRuntimeStopwatch.isRunning());
- totalRuntimeStopwatch.start();
- }
-
- /** Called when the given {@code step} starts processing for a round. */
- void stepStarted(ProcessingStep step) {
- Stopwatch stopwatch =
- stepStopwatches.computeIfAbsent(step, unused -> Stopwatch.createUnstarted(ticker));
- stopwatch.start();
- }
-
- /** Called when the given {@code step} finishes processing for a round. */
- void stepFinished(ProcessingStep step) {
- Stopwatch stopwatch = stepStopwatches.get(step);
- roundBuilder.addStepDuration(step, elapsedTime(stopwatch));
- stopwatch.reset();
- }
-
- /** Called when Dagger finishes a processing round. */
- void roundFinished() {
- statisticsBuilder.addRound(roundBuilder.build());
- roundBuilder = DaggerStatistics.roundBuilder();
- }
-
- /** Called when Dagger annotation processing completes. */
- void processingStopped() {
- checkState(totalRuntimeStopwatch.isRunning());
- totalRuntimeStopwatch.stop();
- statisticsBuilder.setTotalProcessingTime(elapsedTime(totalRuntimeStopwatch));
-
- statisticsRecorder.ifPresent(
- recorder -> recorder.recordStatistics(statisticsBuilder.build()));
- }
-
- @SuppressWarnings("StopwatchNanosToDuration") // intentional
- private Duration elapsedTime(Stopwatch stopwatch) {
- // Using the java 7 method here as opposed to the Duration-returning version to avoid issues
- // when other annotation processors rely on java 7's guava
- return Duration.ofNanos(stopwatch.elapsed(NANOSECONDS));
- }
-}
diff --git a/java/dagger/internal/codegen/DaggerStatisticsRecorder.java b/java/dagger/internal/codegen/DaggerStatisticsRecorder.java
deleted file mode 100644
index 66f41d110..000000000
--- a/java/dagger/internal/codegen/DaggerStatisticsRecorder.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-/** Records collected {@link DaggerStatistics}. */
-interface DaggerStatisticsRecorder {
- /** Records the given {@code statistics}. */
- void recordStatistics(DaggerStatistics statistics);
-}
diff --git a/java/dagger/internal/codegen/DaggerStreams.java b/java/dagger/internal/codegen/DaggerStreams.java
deleted file mode 100644
index 50d17a6de..000000000
--- a/java/dagger/internal/codegen/DaggerStreams.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2013 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static java.util.stream.Collectors.collectingAndThen;
-import static java.util.stream.Collectors.toList;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.Maps;
-import java.util.Collection;
-import java.util.EnumSet;
-import java.util.Map;
-import java.util.Optional;
-import java.util.function.Function;
-import java.util.stream.Collector;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
-
-/** Utilities for streams. */
-public final class DaggerStreams {
-
- /**
- * Returns a {@link Collector} that accumulates the input elements into a new {@link
- * ImmutableList}, in encounter order.
- */
- // TODO(b/68008628): Use ImmutableList.toImmutableList().
- public static <T> Collector<T, ?, ImmutableList<T>> toImmutableList() {
- return collectingAndThen(toList(), ImmutableList::copyOf);
- }
-
- /**
- * Returns a {@link Collector} that accumulates the input elements into a new {@link
- * ImmutableSet}, in encounter order.
- */
- // TODO(b/68008628): Use ImmutableSet.toImmutableSet().
- public static <T> Collector<T, ?, ImmutableSet<T>> toImmutableSet() {
- return collectingAndThen(toList(), ImmutableSet::copyOf);
- }
-
- /**
- * Returns a {@link Collector} that accumulates elements into an {@code ImmutableMap} whose keys
- * and values are the result of applying the provided mapping functions to the input elements.
- * Entries appear in the result {@code ImmutableMap} in encounter order.
- */
- // TODO(b/68008628): Use ImmutableMap.toImmutableMap().
- public static <T, K, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableMap(
- Function<? super T, K> keyMapper, Function<? super T, V> valueMapper) {
- return Collectors.mapping(
- value -> Maps.immutableEntry(keyMapper.apply(value), valueMapper.apply(value)),
- Collector.of(
- ImmutableMap::builder,
- (ImmutableMap.Builder<K, V> builder, Map.Entry<K, V> entry) -> builder.put(entry),
- (left, right) -> left.putAll(right.build()),
- ImmutableMap.Builder::build));
- }
-
- /**
- * Returns a {@link Collector} that accumulates elements into an {@code ImmutableSetMultimap}
- * whose keys and values are the result of applying the provided mapping functions to the input
- * elements. Entries appear in the result {@code ImmutableSetMultimap} in encounter order.
- */
- // TODO(b/68008628): Use ImmutableSetMultimap.toImmutableSetMultimap().
- public static <T, K, V> Collector<T, ?, ImmutableSetMultimap<K, V>> toImmutableSetMultimap(
- Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends V> valueMapper) {
- return Collectors.mapping(
- value -> Maps.immutableEntry(keyMapper.apply(value), valueMapper.apply(value)),
- Collector.of(
- ImmutableSetMultimap::builder,
- (ImmutableSetMultimap.Builder<K, V> builder, Map.Entry<K, V> entry) ->
- builder.put(entry),
- (left, right) -> left.putAll(right.build()),
- ImmutableSetMultimap.Builder::build));
- }
-
- /**
- * Returns a function from {@link Object} to {@code Stream<T>}, which returns a stream containing
- * its input if its input is an instance of {@code T}.
- *
- * <p>Use as an argument to {@link Stream#flatMap(Function)}:
- *
- * <pre>{@code Stream<Bar>} barStream = fooStream.flatMap(instancesOf(Bar.class));</pre>
- */
- public static <T> Function<Object, Stream<T>> instancesOf(Class<T> to) {
- return f -> to.isInstance(f) ? Stream.of(to.cast(f)) : Stream.empty();
- }
-
- /** Returns a stream of all values of the given {@code enumType}. */
- public static <E extends Enum<E>> Stream<E> valuesOf(Class<E> enumType) {
- return EnumSet.allOf(enumType).stream();
- }
-
- /**
- * A function that you can use to extract the present values from a stream of {@link Optional}s.
- *
- * <pre>{@code
- * Set<Foo> foos =
- * optionalFoos()
- * .flatMap(DaggerStreams.presentValues())
- * .collect(toSet());
- * }</pre>
- */
- public static <T> Function<Optional<T>, Stream<T>> presentValues() {
- return optional -> optional.map(Stream::of).orElse(Stream.empty());
- }
-
- /**
- * Returns a sequential {@link Stream} of the contents of {@code iterable}, delegating to {@link
- * Collection#stream} if possible.
- */
- public static <T> Stream<T> stream(Iterable<T> iterable) {
- return (iterable instanceof Collection)
- ? ((Collection<T>) iterable).stream()
- : StreamSupport.stream(iterable.spliterator(), false);
- }
-
- private DaggerStreams() {}
-}
diff --git a/java/dagger/internal/codegen/DeferredModifiableBindingExpression.java b/java/dagger/internal/codegen/DeferredModifiableBindingExpression.java
deleted file mode 100644
index c41cf2c22..000000000
--- a/java/dagger/internal/codegen/DeferredModifiableBindingExpression.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import java.util.Optional;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * A {@link ModifiableAbstractMethodBindingExpression} for a binding that exists but is not ready to
- * be expressed in this compilation unit and should be deferred until a future compilation.
- * Generates a method that will be implemented in the future compilation.
- *
- * <p>A deferred modifiable binding expression is used when:
- *
- * <ul>
- * <li>The generated code for a binding requires an instance of a type that is generated in the
- * root component compilation unit.
- * <li>A {@linkplain ModifiableBindingType#BINDS_METHOD_WITH_MISSING_DEPENDENCY {@code @Binds}
- * method's dependency is missing} in a subcomponent.
- * </ul>
- */
-final class DeferredModifiableBindingExpression extends ModifiableAbstractMethodBindingExpression {
- private final ComponentImplementation componentImplementation;
- private final ContributionBinding binding;
- private final BindingRequest request;
-
- DeferredModifiableBindingExpression(
- ComponentImplementation componentImplementation,
- ModifiableBindingType modifiableBindingType,
- ContributionBinding binding,
- BindingRequest request,
- Optional<ModifiableBindingMethod> matchingModifiableBindingMethod,
- Optional<ComponentMethodDescriptor> matchingComponentMethod,
- DaggerTypes types) {
- super(
- componentImplementation,
- modifiableBindingType,
- request,
- matchingModifiableBindingMethod,
- matchingComponentMethod,
- types);
- this.componentImplementation = checkNotNull(componentImplementation);
- this.binding = checkNotNull(binding);
- this.request = checkNotNull(request);
- }
-
- @Override
- String chooseMethodName() {
- return componentImplementation.getUniqueMethodName(request);
- }
-
- @Override
- protected TypeMirror contributedType() {
- return binding.contributedType();
- }
-}
diff --git a/java/dagger/internal/codegen/DelegateBindingExpression.java b/java/dagger/internal/codegen/DelegateBindingExpression.java
deleted file mode 100644
index 8cdf6d16c..000000000
--- a/java/dagger/internal/codegen/DelegateBindingExpression.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.BindingRequest.bindingRequest;
-import static dagger.internal.codegen.RequestKinds.requestType;
-import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
-import static dagger.model.BindingKind.DELEGATE;
-
-import com.squareup.javapoet.ClassName;
-import dagger.internal.codegen.javapoet.Expression;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.RequestKind;
-import javax.lang.model.type.TypeMirror;
-
-/** A {@link BindingExpression} for {@code @Binds} methods. */
-final class DelegateBindingExpression extends BindingExpression {
- private final ContributionBinding binding;
- private final RequestKind requestKind;
- private final ComponentBindingExpressions componentBindingExpressions;
- private final DaggerTypes types;
- private final BindsTypeChecker bindsTypeChecker;
-
- DelegateBindingExpression(
- ResolvedBindings resolvedBindings,
- RequestKind requestKind,
- ComponentBindingExpressions componentBindingExpressions,
- DaggerTypes types,
- DaggerElements elements) {
- this.binding = checkNotNull(resolvedBindings.contributionBinding());
- this.requestKind = checkNotNull(requestKind);
- this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
- this.types = checkNotNull(types);
- this.bindsTypeChecker = new BindsTypeChecker(types, elements);
- }
-
- /**
- * Returns {@code true} if the {@code @Binds} binding's scope is stronger than the scope of the
- * binding it depends on.
- */
- static boolean isBindsScopeStrongerThanDependencyScope(
- ResolvedBindings resolvedBindings, BindingGraph graph) {
- ContributionBinding bindsBinding = resolvedBindings.contributionBinding();
- checkArgument(bindsBinding.kind().equals(DELEGATE));
- Binding dependencyBinding =
- graph
- .contributionBindings()
- .get(getOnlyElement(bindsBinding.dependencies()).key())
- .binding();
- ScopeKind bindsScope = ScopeKind.get(bindsBinding, graph);
- ScopeKind dependencyScope = ScopeKind.get(dependencyBinding, graph);
- return bindsScope.isStrongerScopeThan(dependencyScope);
- }
-
- @Override
- Expression getDependencyExpression(ClassName requestingClass) {
- Expression delegateExpression =
- componentBindingExpressions.getDependencyExpression(
- bindingRequest(getOnlyElement(binding.dependencies()).key(), requestKind),
- requestingClass);
-
- TypeMirror contributedType = binding.contributedType();
- switch (requestKind) {
- case INSTANCE:
- return instanceRequiresCast(delegateExpression, requestingClass)
- ? delegateExpression.castTo(contributedType)
- : delegateExpression;
- default:
- return castToRawTypeIfNecessary(
- delegateExpression, requestType(requestKind, contributedType, types));
- }
- }
-
- private boolean instanceRequiresCast(Expression delegateExpression, ClassName requestingClass) {
- // delegateExpression.type() could be Object if expression is satisfied with a raw
- // Provider's get() method.
- return !bindsTypeChecker.isAssignable(
- delegateExpression.type(), binding.contributedType(), binding.contributionType())
- && isTypeAccessibleFrom(binding.contributedType(), requestingClass.packageName());
- }
-
- /**
- * If {@code delegateExpression} can be assigned to {@code desiredType} safely, then {@code
- * delegateExpression} is returned unchanged. If the {@code delegateExpression} is already a raw
- * type, returns {@code delegateExpression} as well, as casting would have no effect. Otherwise,
- * returns a {@link Expression#castTo(TypeMirror) casted} version of {@code delegateExpression}
- * to the raw type of {@code desiredType}.
- */
- // TODO(ronshapiro): this probably can be generalized for usage in InjectionMethods
- private Expression castToRawTypeIfNecessary(
- Expression delegateExpression, TypeMirror desiredType) {
- if (types.isAssignable(delegateExpression.type(), desiredType)) {
- return delegateExpression;
- }
- return delegateExpression.castTo(types.erasure(desiredType));
- }
-
- private enum ScopeKind {
- UNSCOPED,
- SINGLE_CHECK,
- DOUBLE_CHECK,
- ;
-
- static ScopeKind get(Binding binding, BindingGraph graph) {
- return binding
- .scope()
- .map(scope -> scope.isReusable() ? SINGLE_CHECK : DOUBLE_CHECK)
- .orElse(UNSCOPED);
- }
-
- boolean isStrongerScopeThan(ScopeKind other) {
- return this.ordinal() > other.ordinal();
- }
- }
-}
diff --git a/java/dagger/internal/codegen/DelegateDeclaration.java b/java/dagger/internal/codegen/DelegateDeclaration.java
deleted file mode 100644
index 67991deb6..000000000
--- a/java/dagger/internal/codegen/DelegateDeclaration.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static dagger.internal.codegen.MapKeys.getMapKey;
-import static dagger.internal.codegen.MoreAnnotationMirrors.wrapOptionalInEquivalence;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.auto.value.extension.memoized.Memoized;
-import com.google.common.base.Equivalence;
-import com.google.common.collect.Iterables;
-import dagger.Binds;
-import dagger.internal.codegen.ContributionType.HasContributionType;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.DependencyRequest;
-import java.util.Optional;
-import javax.inject.Inject;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.ExecutableType;
-
-/**
- * The declaration for a delegate binding established by a {@link Binds} method.
- */
-@AutoValue
-abstract class DelegateDeclaration extends BindingDeclaration implements HasContributionType {
- abstract DependencyRequest delegateRequest();
-
- abstract Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedMapKey();
-
- @Memoized
- @Override
- public abstract int hashCode();
-
- @Override
- public abstract boolean equals(Object obj);
-
- static final class Factory {
- private final DaggerTypes types;
- private final KeyFactory keyFactory;
- private final DependencyRequestFactory dependencyRequestFactory;
-
- @Inject
- Factory(
- DaggerTypes types,
- KeyFactory keyFactory,
- DependencyRequestFactory dependencyRequestFactory) {
- this.types = types;
- this.keyFactory = keyFactory;
- this.dependencyRequestFactory = dependencyRequestFactory;
- }
-
- DelegateDeclaration create(
- ExecutableElement bindsMethod, TypeElement contributingModule) {
- checkArgument(MoreElements.isAnnotationPresent(bindsMethod, Binds.class));
- ExecutableType resolvedMethod =
- MoreTypes.asExecutable(
- types.asMemberOf(MoreTypes.asDeclared(contributingModule.asType()), bindsMethod));
- DependencyRequest delegateRequest =
- dependencyRequestFactory.forRequiredResolvedVariable(
- Iterables.getOnlyElement(bindsMethod.getParameters()),
- Iterables.getOnlyElement(resolvedMethod.getParameterTypes()));
- return new AutoValue_DelegateDeclaration(
- ContributionType.fromBindingElement(bindsMethod),
- keyFactory.forBindsMethod(bindsMethod, contributingModule),
- Optional.<Element>of(bindsMethod),
- Optional.of(contributingModule),
- delegateRequest,
- wrapOptionalInEquivalence(getMapKey(bindsMethod)));
- }
- }
-}
diff --git a/java/dagger/internal/codegen/DelegatingFrameworkInstanceCreationExpression.java b/java/dagger/internal/codegen/DelegatingFrameworkInstanceCreationExpression.java
deleted file mode 100644
index 7524cdf06..000000000
--- a/java/dagger/internal/codegen/DelegatingFrameworkInstanceCreationExpression.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.BindingRequest.bindingRequest;
-
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
-import dagger.internal.codegen.javapoet.CodeBlocks;
-import dagger.model.DependencyRequest;
-
-/** A framework instance creation expression for a {@link dagger.Binds @Binds} binding. */
-final class DelegatingFrameworkInstanceCreationExpression
- implements FrameworkInstanceCreationExpression {
-
- private final ContributionBinding binding;
- private final ComponentImplementation componentImplementation;
- private final ComponentBindingExpressions componentBindingExpressions;
-
- DelegatingFrameworkInstanceCreationExpression(
- ContributionBinding binding,
- ComponentImplementation componentImplementation,
- ComponentBindingExpressions componentBindingExpressions) {
- this.binding = checkNotNull(binding);
- this.componentImplementation = checkNotNull(componentImplementation);
- this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
- }
-
- @Override
- public CodeBlock creationExpression() {
- DependencyRequest dependency = getOnlyElement(binding.dependencies());
- return CodeBlocks.cast(
- componentBindingExpressions
- .getDependencyExpression(
- bindingRequest(dependency.key(), binding.frameworkType()),
- componentImplementation.name())
- .codeBlock(),
- binding.frameworkType().frameworkClass());
- }
-}
diff --git a/java/dagger/internal/codegen/DependencyCycleValidator.java b/java/dagger/internal/codegen/DependencyCycleValidator.java
deleted file mode 100644
index d5d4b629e..000000000
--- a/java/dagger/internal/codegen/DependencyCycleValidator.java
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.Iterables.getLast;
-import static com.google.common.collect.Iterables.limit;
-import static com.google.common.collect.Iterables.skip;
-import static com.google.common.collect.Sets.newHashSetWithExpectedSize;
-import static dagger.internal.codegen.DaggerGraphs.shortestPath;
-import static dagger.internal.codegen.DaggerStreams.instancesOf;
-import static dagger.internal.codegen.DaggerStreams.toImmutableList;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.RequestKinds.extractKeyType;
-import static dagger.internal.codegen.RequestKinds.getRequestKind;
-import static javax.tools.Diagnostic.Kind.ERROR;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.graph.EndpointPair;
-import com.google.common.graph.ImmutableNetwork;
-import com.google.common.graph.MutableNetwork;
-import com.google.common.graph.NetworkBuilder;
-import dagger.model.BindingGraph;
-import dagger.model.BindingGraph.ComponentNode;
-import dagger.model.BindingGraph.DependencyEdge;
-import dagger.model.BindingGraph.Node;
-import dagger.model.BindingKind;
-import dagger.model.DependencyRequest;
-import dagger.model.RequestKind;
-import dagger.spi.BindingGraphPlugin;
-import dagger.spi.DiagnosticReporter;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Stream;
-import javax.inject.Inject;
-import javax.inject.Provider;
-import javax.lang.model.type.TypeMirror;
-
-/** Reports errors for dependency cycles. */
-final class DependencyCycleValidator implements BindingGraphPlugin {
-
- private final DependencyRequestFormatter dependencyRequestFormatter;
-
- @Inject
- DependencyCycleValidator(DependencyRequestFormatter dependencyRequestFormatter) {
- this.dependencyRequestFormatter = dependencyRequestFormatter;
- }
-
- @Override
- public String pluginName() {
- return "Dagger/DependencyCycle";
- }
-
- @Override
- public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
- ImmutableNetwork<Node, DependencyEdge> dependencyGraph =
- nonCycleBreakingDependencyGraph(bindingGraph);
- // Check each endpoint pair only once, no matter how many parallel edges connect them.
- Set<EndpointPair<Node>> dependencyEndpointPairs = dependencyGraph.asGraph().edges();
- Set<EndpointPair<Node>> visited = newHashSetWithExpectedSize(dependencyEndpointPairs.size());
- for (EndpointPair<Node> endpointPair : dependencyEndpointPairs) {
- cycleContainingEndpointPair(endpointPair, dependencyGraph, visited)
- .ifPresent(cycle -> reportCycle(cycle, bindingGraph, diagnosticReporter));
- }
- }
-
- private Optional<Cycle<Node>> cycleContainingEndpointPair(
- EndpointPair<Node> endpoints,
- ImmutableNetwork<Node, DependencyEdge> dependencyGraph,
- Set<EndpointPair<Node>> visited) {
- if (!visited.add(endpoints)) {
- // don't recheck endpoints we already know are part of a cycle
- return Optional.empty();
- }
-
- // If there's a path from the target back to the source, there's a cycle.
- ImmutableList<Node> cycleNodes =
- shortestPath(dependencyGraph, endpoints.target(), endpoints.source());
- if (cycleNodes.isEmpty()) {
- return Optional.empty();
- }
-
- Cycle<Node> cycle = Cycle.fromPath(cycleNodes);
- visited.addAll(cycle.endpointPairs()); // no need to check any edge in this cycle again
- return Optional.of(cycle);
- }
-
- /**
- * Reports a dependency cycle at the dependency into the cycle that is closest to an entry point.
- *
- * <p>For cycles found in reachable binding graphs, looks for the shortest path from the component
- * that contains the cycle (all bindings in a cycle must be in the same component; see below) to
- * some binding in the cycle. Then looks for the last dependency in that path that is not in the
- * cycle; that is the dependency that will be reported, so that the dependency trace will end just
- * before the cycle.
- *
- * <p>For cycles found during full binding graph validation, just reports the component that
- * contains the cycle.
- *
- * <p>Proof (by counterexample) that all bindings in a cycle must be in the same component: Assume
- * one binding in the cycle is in a parent component. Bindings cannot depend on bindings in child
- * components, so that binding cannot depend on the next binding in the cycle.
- */
- private void reportCycle(
- Cycle<Node> cycle, BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
- if (bindingGraph.isFullBindingGraph()) {
- diagnosticReporter.reportComponent(
- ERROR,
- bindingGraph.componentNode(cycle.nodes().asList().get(0).componentPath()).get(),
- errorMessage(cycle, bindingGraph));
- return;
- }
-
- ImmutableList<Node> path = shortestPathToCycleFromAnEntryPoint(cycle, bindingGraph);
- Node cycleStartNode = path.get(path.size() - 1);
- Node previousNode = path.get(path.size() - 2);
- DependencyEdge dependencyToReport =
- chooseDependencyEdgeConnecting(previousNode, cycleStartNode, bindingGraph);
- diagnosticReporter.reportDependency(
- ERROR, dependencyToReport, errorMessage(cycle.shift(cycleStartNode), bindingGraph));
- }
-
- private ImmutableList<Node> shortestPathToCycleFromAnEntryPoint(
- Cycle<Node> cycle, BindingGraph bindingGraph) {
- Node someCycleNode = cycle.nodes().asList().get(0);
- ComponentNode componentContainingCycle =
- bindingGraph.componentNode(someCycleNode.componentPath()).get();
- ImmutableList<Node> pathToCycle =
- shortestPath(bindingGraph.network(), componentContainingCycle, someCycleNode);
- return subpathToCycle(pathToCycle, cycle);
- }
-
- /**
- * Returns the subpath from the head of {@code path} to the first node in {@code path} that's in
- * the cycle.
- */
- private ImmutableList<Node> subpathToCycle(ImmutableList<Node> path, Cycle<Node> cycle) {
- ImmutableList.Builder<Node> subpath = ImmutableList.builder();
- for (Node node : path) {
- subpath.add(node);
- if (cycle.nodes().contains(node)) {
- return subpath.build();
- }
- }
- throw new IllegalArgumentException(
- "path " + path + " doesn't contain any nodes in cycle " + cycle);
- }
-
- private String errorMessage(Cycle<Node> cycle, BindingGraph graph) {
- StringBuilder message = new StringBuilder("Found a dependency cycle:");
- ImmutableList<DependencyRequest> cycleRequests =
- cycle.endpointPairs().stream()
- // TODO(dpb): Would be nice to take the dependency graph here.
- .map(endpointPair -> nonCycleBreakingEdge(endpointPair, graph))
- .map(DependencyEdge::dependencyRequest)
- .collect(toImmutableList())
- .reverse();
- dependencyRequestFormatter.formatIndentedList(message, cycleRequests, 0);
- return message.toString();
- }
-
- /**
- * Returns one of the edges between two nodes that doesn't {@linkplain
- * #breaksCycle(DependencyEdge, BindingGraph) break} a cycle.
- */
- private DependencyEdge nonCycleBreakingEdge(EndpointPair<Node> endpointPair, BindingGraph graph) {
- return graph.network().edgesConnecting(endpointPair.source(), endpointPair.target()).stream()
- .flatMap(instancesOf(DependencyEdge.class))
- .filter(edge -> !breaksCycle(edge, graph))
- .findFirst()
- .get();
- }
-
- private boolean breaksCycle(DependencyEdge edge, BindingGraph graph) {
- if (edge.dependencyRequest().key().multibindingContributionIdentifier().isPresent()) {
- return false;
- }
- if (breaksCycle(edge.dependencyRequest().key().type(), edge.dependencyRequest().kind())) {
- return true;
- }
- Node target = graph.network().incidentNodes(edge).target();
- if (target instanceof dagger.model.Binding
- && ((dagger.model.Binding) target).kind().equals(BindingKind.OPTIONAL)) {
- /* For @BindsOptionalOf bindings, unwrap the type inside the Optional. If the unwrapped type
- * breaks the cycle, so does the optional binding. */
- TypeMirror optionalValueType = OptionalType.from(edge.dependencyRequest().key()).valueType();
- RequestKind requestKind = getRequestKind(optionalValueType);
- return breaksCycle(extractKeyType(requestKind, optionalValueType), requestKind);
- }
- return false;
- }
-
- private boolean breaksCycle(TypeMirror requestedType, RequestKind requestKind) {
- switch (requestKind) {
- case PROVIDER:
- case LAZY:
- case PROVIDER_OF_LAZY:
- return true;
-
- case INSTANCE:
- if (MapType.isMap(requestedType)) {
- MapType mapType = MapType.from(requestedType);
- return !mapType.isRawType() && mapType.valuesAreTypeOf(Provider.class);
- }
- // fall through
-
- default:
- return false;
- }
- }
-
- private DependencyEdge chooseDependencyEdgeConnecting(
- Node source, Node target, BindingGraph bindingGraph) {
- return bindingGraph.network().edgesConnecting(source, target).stream()
- .flatMap(instancesOf(DependencyEdge.class))
- .findFirst()
- .get();
- }
-
- /** Returns the subgraph containing only {@link DependencyEdge}s that would not break a cycle. */
- // TODO(dpb): Return a network containing only Binding nodes.
- private ImmutableNetwork<Node, DependencyEdge> nonCycleBreakingDependencyGraph(
- BindingGraph bindingGraph) {
- MutableNetwork<Node, DependencyEdge> dependencyNetwork =
- NetworkBuilder.from(bindingGraph.network())
- .expectedNodeCount(bindingGraph.network().nodes().size())
- .expectedEdgeCount(bindingGraph.dependencyEdges().size())
- .build();
- bindingGraph.dependencyEdges().stream()
- .filter(edge -> !breaksCycle(edge, bindingGraph))
- .forEach(
- edge -> {
- EndpointPair<Node> endpoints = bindingGraph.network().incidentNodes(edge);
- dependencyNetwork.addEdge(endpoints.source(), endpoints.target(), edge);
- });
- return ImmutableNetwork.copyOf(dependencyNetwork);
- }
-
- /**
- * An ordered set of endpoint pairs representing the edges in the cycle. The target of each pair
- * is the source of the next pair. The target of the last pair is the source of the first pair.
- */
- @AutoValue
- abstract static class Cycle<N> {
- /**
- * The ordered set of endpoint pairs representing the edges in the cycle. The target of each
- * pair is the source of the next pair. The target of the last pair is the source of the first
- * pair.
- */
- abstract ImmutableSet<EndpointPair<N>> endpointPairs();
-
- /** Returns the nodes that participate in the cycle. */
- ImmutableSet<N> nodes() {
- return endpointPairs().stream()
- .flatMap(pair -> Stream.of(pair.source(), pair.target()))
- .collect(toImmutableSet());
- }
-
- /** Returns the number of edges in the cycle. */
- int size() {
- return endpointPairs().size();
- }
-
- /**
- * Shifts this cycle so that it starts with a specific node.
- *
- * @return a cycle equivalent to this one but whose first pair starts with {@code startNode}
- */
- Cycle<N> shift(N startNode) {
- int startIndex = Iterables.indexOf(endpointPairs(), pair -> pair.source().equals(startNode));
- checkArgument(
- startIndex >= 0, "startNode (%s) is not part of this cycle: %s", startNode, this);
- if (startIndex == 0) {
- return this;
- }
- ImmutableSet.Builder<EndpointPair<N>> shifted = ImmutableSet.builder();
- shifted.addAll(skip(endpointPairs(), startIndex));
- shifted.addAll(limit(endpointPairs(), size() - startIndex));
- return new AutoValue_DependencyCycleValidator_Cycle<>(shifted.build());
- }
-
- @Override
- public final String toString() {
- return endpointPairs().toString();
- }
-
- /**
- * Creates a {@link Cycle} from a nonempty list of nodes, assuming there is an edge between each
- * pair of nodes as well as an edge from the last node to the first.
- */
- static <N> Cycle<N> fromPath(List<N> nodes) {
- checkArgument(!nodes.isEmpty());
- ImmutableSet.Builder<EndpointPair<N>> cycle = ImmutableSet.builder();
- cycle.add(EndpointPair.ordered(getLast(nodes), nodes.get(0)));
- for (int i = 0; i < nodes.size() - 1; i++) {
- cycle.add(EndpointPair.ordered(nodes.get(i), nodes.get(i + 1)));
- }
- return new AutoValue_DependencyCycleValidator_Cycle<>(cycle.build());
- }
- }
-}
diff --git a/java/dagger/internal/codegen/DependencyEdgeImpl.java b/java/dagger/internal/codegen/DependencyEdgeImpl.java
deleted file mode 100644
index 64b08457a..000000000
--- a/java/dagger/internal/codegen/DependencyEdgeImpl.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import dagger.model.BindingGraph.DependencyEdge;
-import dagger.model.DependencyRequest;
-
-/** An implementation of {@link DependencyEdge}. */
-final class DependencyEdgeImpl implements DependencyEdge {
-
- private final DependencyRequest dependencyRequest;
- private final boolean entryPoint;
-
- DependencyEdgeImpl(DependencyRequest dependencyRequest, boolean entryPoint) {
- this.dependencyRequest = dependencyRequest;
- this.entryPoint = entryPoint;
- }
-
- @Override
- public DependencyRequest dependencyRequest() {
- return dependencyRequest;
- }
-
- @Override
- public boolean isEntryPoint() {
- return entryPoint;
- }
-
- @Override
- public String toString() {
- String string =
- dependencyRequest
- .requestElement()
- .map(ElementFormatter::elementToString)
- .orElseGet(
- () ->
- "synthetic request for "
- + dependencyRequest.kind().format(dependencyRequest.key()));
- return entryPoint ? string + " (entry point)" : string;
- }
-}
diff --git a/java/dagger/internal/codegen/DependencyMethodProducerCreationExpression.java b/java/dagger/internal/codegen/DependencyMethodProducerCreationExpression.java
deleted file mode 100644
index b42f9c424..000000000
--- a/java/dagger/internal/codegen/DependencyMethodProducerCreationExpression.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static com.squareup.javapoet.TypeSpec.anonymousClassBuilder;
-import static dagger.internal.codegen.javapoet.TypeNames.dependencyMethodProducerOf;
-import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PUBLIC;
-
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.FieldSpec;
-import com.squareup.javapoet.TypeName;
-import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
-
-/**
- * A {@link dagger.producers.Producer} creation expression for a production method on a production
- * component's {@linkplain dagger.producers.ProductionComponent#dependencies()} dependency} that
- * returns a {@link com.google.common.util.concurrent.ListenableFuture}.
- */
-// TODO(dpb): Resolve with DependencyMethodProviderCreationExpression.
-final class DependencyMethodProducerCreationExpression
- implements FrameworkInstanceCreationExpression {
- private final ContributionBinding binding;
- private final ComponentImplementation componentImplementation;
- private final ComponentRequirementExpressions componentRequirementExpressions;
- private final BindingGraph graph;
-
- DependencyMethodProducerCreationExpression(
- ContributionBinding binding,
- ComponentImplementation componentImplementation,
- ComponentRequirementExpressions componentRequirementExpressions,
- BindingGraph graph) {
- this.binding = checkNotNull(binding);
- this.componentImplementation = checkNotNull(componentImplementation);
- this.componentRequirementExpressions = checkNotNull(componentRequirementExpressions);
- this.graph = checkNotNull(graph);
- }
-
- @Override
- public CodeBlock creationExpression() {
- ComponentRequirement dependency =
- graph.componentDescriptor().getDependencyThatDefinesMethod(binding.bindingElement().get());
- FieldSpec dependencyField =
- FieldSpec.builder(
- ClassName.get(dependency.typeElement()), dependency.variableName(), PRIVATE, FINAL)
- .initializer(
- componentRequirementExpressions.getExpressionDuringInitialization(
- dependency,
- // This isn't a real class name, but we want the requesting class for the
- // expression to *not* be the same class as the component implementation,
- // because it isn't... it's an anonymous inner class.
- // TODO(cgdecker): If we didn't use an anonymous inner class here but instead
- // generated a named nested class as with
- // DependencyMethodProviderCreationExpression, we wouldn't need to deal with
- // this and might be able to avoid potentially creating an extra field in the
- // component?
- componentImplementation.name().nestedClass("Anonymous")))
- .build();
- // TODO(b/70395982): Explore using a private static type instead of an anonymous class.
- TypeName keyType = TypeName.get(binding.key().type());
- return CodeBlock.of(
- "$L",
- anonymousClassBuilder("")
- .superclass(dependencyMethodProducerOf(keyType))
- .addField(dependencyField)
- .addMethod(
- methodBuilder("callDependencyMethod")
- .addAnnotation(Override.class)
- .addModifiers(PUBLIC)
- .returns(listenableFutureOf(keyType))
- .addStatement(
- "return $N.$L()",
- dependencyField,
- binding.bindingElement().get().getSimpleName())
- .build())
- .build());
- }
-}
diff --git a/java/dagger/internal/codegen/DependencyMethodProviderCreationExpression.java b/java/dagger/internal/codegen/DependencyMethodProviderCreationExpression.java
deleted file mode 100644
index 853248121..000000000
--- a/java/dagger/internal/codegen/DependencyMethodProviderCreationExpression.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.squareup.javapoet.MethodSpec.constructorBuilder;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static com.squareup.javapoet.TypeSpec.classBuilder;
-import static dagger.internal.codegen.ComponentImplementation.TypeSpecKind.COMPONENT_PROVISION_FACTORY;
-import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PUBLIC;
-import static javax.lang.model.element.Modifier.STATIC;
-
-import com.google.auto.common.MoreTypes;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.TypeName;
-import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
-import javax.lang.model.element.Element;
-
-/**
- * A {@link javax.inject.Provider} creation expression for a provision method on a component's
- * {@linkplain dagger.Component#dependencies()} dependency}.
- */
-// TODO(dpb): Resolve with DependencyMethodProducerCreationExpression.
-final class DependencyMethodProviderCreationExpression
- implements FrameworkInstanceCreationExpression {
-
- private final ComponentImplementation componentImplementation;
- private final ComponentRequirementExpressions componentRequirementExpressions;
- private final CompilerOptions compilerOptions;
- private final BindingGraph graph;
- private final ContributionBinding binding;
-
- DependencyMethodProviderCreationExpression(
- ContributionBinding binding,
- ComponentImplementation componentImplementation,
- ComponentRequirementExpressions componentRequirementExpressions,
- CompilerOptions compilerOptions,
- BindingGraph graph) {
- this.binding = checkNotNull(binding);
- this.componentImplementation = checkNotNull(componentImplementation);
- this.componentRequirementExpressions = checkNotNull(componentRequirementExpressions);
- this.compilerOptions = checkNotNull(compilerOptions);
- this.graph = checkNotNull(graph);
- }
-
- @Override
- public CodeBlock creationExpression() {
- // TODO(sameb): The Provider.get() throws a very vague NPE. The stack trace doesn't
- // help to figure out what the method or return type is. If we include a string
- // of the return type or method name in the error message, that can defeat obfuscation.
- // We can easily include the raw type (no generics) + annotation type (no values),
- // using .class & String.format -- but that wouldn't be the whole story.
- // What should we do?
- CodeBlock invocation =
- ComponentProvisionBindingExpression.maybeCheckForNull(
- (ProvisionBinding) binding,
- compilerOptions,
- CodeBlock.of(
- "$N.$N()", dependency().variableName(), provisionMethod().getSimpleName()));
- ClassName dependencyClassName = ClassName.get(dependency().typeElement());
- TypeName keyType = TypeName.get(binding.key().type());
- MethodSpec.Builder getMethod =
- methodBuilder("get")
- .addAnnotation(Override.class)
- .addModifiers(PUBLIC)
- .returns(keyType)
- .addStatement("return $L", invocation);
- if (binding.nullableType().isPresent()) {
- getMethod.addAnnotation(ClassName.get(MoreTypes.asTypeElement(binding.nullableType().get())));
- }
- componentImplementation.addType(
- COMPONENT_PROVISION_FACTORY,
- classBuilder(factoryClassName())
- .addSuperinterface(providerOf(keyType))
- .addModifiers(PRIVATE, STATIC)
- .addField(dependencyClassName, dependency().variableName(), PRIVATE, FINAL)
- .addMethod(
- constructorBuilder()
- .addParameter(dependencyClassName, dependency().variableName())
- .addStatement("this.$1L = $1L", dependency().variableName())
- .build())
- .addMethod(getMethod.build())
- .build());
- return CodeBlock.of(
- "new $T($L)",
- factoryClassName(),
- componentRequirementExpressions.getExpressionDuringInitialization(
- dependency(), componentImplementation.name()));
- }
-
- private ClassName factoryClassName() {
- String factoryName =
- ClassName.get(dependency().typeElement()).toString().replace('.', '_')
- + "_"
- + binding.bindingElement().get().getSimpleName();
- return componentImplementation.name().nestedClass(factoryName);
- }
-
- private ComponentRequirement dependency() {
- return graph.componentDescriptor().getDependencyThatDefinesMethod(provisionMethod());
- }
-
- private Element provisionMethod() {
- return binding.bindingElement().get();
- }
-}
diff --git a/java/dagger/internal/codegen/DependencyRequestFactory.java b/java/dagger/internal/codegen/DependencyRequestFactory.java
deleted file mode 100644
index 3ad12e299..000000000
--- a/java/dagger/internal/codegen/DependencyRequestFactory.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreTypes.isTypeOf;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.ConfigurationAnnotations.getNullableType;
-import static dagger.internal.codegen.RequestKinds.extractKeyType;
-import static dagger.internal.codegen.RequestKinds.frameworkClass;
-import static dagger.internal.codegen.RequestKinds.getRequestKind;
-import static dagger.model.RequestKind.FUTURE;
-import static dagger.model.RequestKind.INSTANCE;
-import static dagger.model.RequestKind.MEMBERS_INJECTION;
-import static dagger.model.RequestKind.PRODUCER;
-import static dagger.model.RequestKind.PROVIDER;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.Lazy;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.DependencyRequest;
-import dagger.model.Key;
-import dagger.model.RequestKind;
-import java.util.List;
-import java.util.Optional;
-import javax.inject.Inject;
-import javax.inject.Provider;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * Factory for {@link DependencyRequest}s.
- *
- * <p>Any factory method may throw {@link TypeNotPresentException} if a type is not available, which
- * may mean that the type will be generated in a later round of processing.
- */
-final class DependencyRequestFactory {
- private final KeyFactory keyFactory;
- private final DaggerTypes types;
-
- @Inject
- DependencyRequestFactory(KeyFactory keyFactory, DaggerTypes types) {
- this.keyFactory = keyFactory;
- this.types = types;
- }
-
- ImmutableSet<DependencyRequest> forRequiredResolvedVariables(
- List<? extends VariableElement> variables, List<? extends TypeMirror> resolvedTypes) {
- checkState(resolvedTypes.size() == variables.size());
- ImmutableSet.Builder<DependencyRequest> builder = ImmutableSet.builder();
- for (int i = 0; i < variables.size(); i++) {
- builder.add(forRequiredResolvedVariable(variables.get(i), resolvedTypes.get(i)));
- }
- return builder.build();
- }
-
- /**
- * Creates synthetic dependency requests for each individual multibinding contribution in {@code
- * multibindingContributions}.
- */
- ImmutableSet<DependencyRequest> forMultibindingContributions(
- Key multibindingKey, Iterable<ContributionBinding> multibindingContributions) {
- ImmutableSet.Builder<DependencyRequest> requests = ImmutableSet.builder();
- for (ContributionBinding multibindingContribution : multibindingContributions) {
- requests.add(forMultibindingContribution(multibindingKey, multibindingContribution));
- }
- return requests.build();
- }
-
- /** Creates a synthetic dependency request for one individual {@code multibindingContribution}. */
- private DependencyRequest forMultibindingContribution(
- Key multibindingKey, ContributionBinding multibindingContribution) {
- checkArgument(
- multibindingContribution.key().multibindingContributionIdentifier().isPresent(),
- "multibindingContribution's key must have a multibinding contribution identifier: %s",
- multibindingContribution);
- return DependencyRequest.builder()
- .kind(multibindingContributionRequestKind(multibindingKey, multibindingContribution))
- .key(multibindingContribution.key())
- .build();
- }
-
- // TODO(b/28555349): support PROVIDER_OF_LAZY here too
- private static final ImmutableSet<RequestKind> WRAPPING_MAP_VALUE_FRAMEWORK_TYPES =
- ImmutableSet.of(PROVIDER, PRODUCER);
-
- private RequestKind multibindingContributionRequestKind(
- Key multibindingKey, ContributionBinding multibindingContribution) {
- switch (multibindingContribution.contributionType()) {
- case MAP:
- MapType mapType = MapType.from(multibindingKey);
- for (RequestKind kind : WRAPPING_MAP_VALUE_FRAMEWORK_TYPES) {
- if (mapType.valuesAreTypeOf(frameworkClass(kind))) {
- return kind;
- }
- }
- // fall through
- case SET:
- case SET_VALUES:
- return INSTANCE;
- case UNIQUE:
- throw new IllegalArgumentException(
- "multibindingContribution must be a multibinding: " + multibindingContribution);
- }
- throw new AssertionError(multibindingContribution.toString());
- }
-
- DependencyRequest forRequiredResolvedVariable(
- VariableElement variableElement, TypeMirror resolvedType) {
- checkNotNull(variableElement);
- checkNotNull(resolvedType);
- Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(variableElement);
- return newDependencyRequest(variableElement, resolvedType, qualifier);
- }
-
- DependencyRequest forComponentProvisionMethod(
- ExecutableElement provisionMethod, ExecutableType provisionMethodType) {
- checkNotNull(provisionMethod);
- checkNotNull(provisionMethodType);
- checkArgument(
- provisionMethod.getParameters().isEmpty(),
- "Component provision methods must be empty: %s",
- provisionMethod);
- Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(provisionMethod);
- return newDependencyRequest(provisionMethod, provisionMethodType.getReturnType(), qualifier);
- }
-
- DependencyRequest forComponentProductionMethod(
- ExecutableElement productionMethod, ExecutableType productionMethodType) {
- checkNotNull(productionMethod);
- checkNotNull(productionMethodType);
- checkArgument(
- productionMethod.getParameters().isEmpty(),
- "Component production methods must be empty: %s",
- productionMethod);
- TypeMirror type = productionMethodType.getReturnType();
- Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(productionMethod);
- // Only a component production method can be a request for a ListenableFuture, so we
- // special-case it here.
- if (isTypeOf(ListenableFuture.class, type)) {
- return DependencyRequest.builder()
- .kind(FUTURE)
- .key(keyFactory.forQualifiedType(qualifier, types.unwrapType(type)))
- .requestElement(productionMethod)
- .build();
- } else {
- return newDependencyRequest(productionMethod, type, qualifier);
- }
- }
-
- DependencyRequest forComponentMembersInjectionMethod(
- ExecutableElement membersInjectionMethod, ExecutableType membersInjectionMethodType) {
- checkNotNull(membersInjectionMethod);
- checkNotNull(membersInjectionMethodType);
- Optional<AnnotationMirror> qualifier =
- InjectionAnnotations.getQualifier(membersInjectionMethod);
- checkArgument(!qualifier.isPresent());
- TypeMirror membersInjectedType = getOnlyElement(membersInjectionMethodType.getParameterTypes());
- return DependencyRequest.builder()
- .kind(MEMBERS_INJECTION)
- .key(keyFactory.forMembersInjectedType(membersInjectedType))
- .requestElement(membersInjectionMethod)
- .build();
- }
-
- DependencyRequest forProductionImplementationExecutor() {
- return DependencyRequest.builder()
- .kind(PROVIDER)
- .key(keyFactory.forProductionImplementationExecutor())
- .build();
- }
-
- DependencyRequest forProductionComponentMonitor() {
- return DependencyRequest.builder()
- .kind(PROVIDER)
- .key(keyFactory.forProductionComponentMonitor())
- .build();
- }
-
- /**
- * Returns a synthetic request for the present value of an optional binding generated from a
- * {@link dagger.BindsOptionalOf} declaration.
- */
- DependencyRequest forSyntheticPresentOptionalBinding(Key requestKey, RequestKind kind) {
- Optional<Key> key = keyFactory.unwrapOptional(requestKey);
- checkArgument(key.isPresent(), "not a request for optional: %s", requestKey);
- return DependencyRequest.builder()
- .kind(kind)
- .key(key.get())
- .isNullable(
- allowsNull(getRequestKind(OptionalType.from(requestKey).valueType()), Optional.empty()))
- .build();
- }
-
- private DependencyRequest newDependencyRequest(
- Element requestElement, TypeMirror type, Optional<AnnotationMirror> qualifier) {
- RequestKind requestKind = getRequestKind(type);
- return DependencyRequest.builder()
- .kind(requestKind)
- .key(keyFactory.forQualifiedType(qualifier, extractKeyType(requestKind, type)))
- .requestElement(requestElement)
- .isNullable(allowsNull(requestKind, getNullableType(requestElement)))
- .build();
- }
-
- /**
- * Returns {@code true} if a given request element allows null values. {@link
- * RequestKind#INSTANCE} requests must be annotated with {@code @Nullable} in order to allow null
- * values. All other request kinds implicitly allow null values because they are are wrapped
- * inside {@link Provider}, {@link Lazy}, etc.
- */
- private boolean allowsNull(RequestKind kind, Optional<DeclaredType> nullableType) {
- return nullableType.isPresent() || !kind.equals(INSTANCE);
- }
-}
diff --git a/java/dagger/internal/codegen/DependencyRequestFormatter.java b/java/dagger/internal/codegen/DependencyRequestFormatter.java
deleted file mode 100644
index 25becaf38..000000000
--- a/java/dagger/internal/codegen/DependencyRequestFormatter.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.ElementFormatter.elementToString;
-import static dagger.internal.codegen.RequestKinds.requestType;
-
-import com.google.errorprone.annotations.CanIgnoreReturnValue;
-import dagger.Provides;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.DependencyRequest;
-import dagger.producers.Produces;
-import java.util.Optional;
-import javax.inject.Inject;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementVisitor;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.ElementKindVisitor8;
-
-/**
- * Formats a {@link DependencyRequest} into a {@link String} suitable for an error message listing a
- * chain of dependencies.
- *
- * <dl>
- * <dt>For component provision methods
- * <dd>{@code @Qualifier SomeType is provided at\n ComponentType.method()}
- * <dt>For component injection methods
- * <dd>{@code SomeType is injected at\n ComponentType.method(foo)}
- * <dt>For parameters to {@link Provides @Provides}, {@link Produces @Produces}, or {@link
- * Inject @Inject} methods:
- * <dd>{@code @Qualified ResolvedType is injected at\n EnclosingType.method([…, ]param[, …])}
- * <dt>For parameters to {@link Inject @Inject} constructors:
- * <dd>{@code @Qualified ResolvedType is injected at\n EnclosingType([…, ]param[, …])}
- * <dt>For {@link Inject @Inject} fields:
- * <dd>{@code @Qualified ResolvedType is injected at\n EnclosingType.field}
- * </dl>
- */
-final class DependencyRequestFormatter extends Formatter<DependencyRequest> {
-
- private final DaggerTypes types;
-
- @Inject
- DependencyRequestFormatter(DaggerTypes types) {
- this.types = types;
- }
-
- @Override
- public String format(DependencyRequest request) {
- return request
- .requestElement()
- .map(element -> element.accept(formatVisitor, request))
- .orElse("");
- }
-
- /**
- * Appends a newline and the formatted dependency request unless {@link
- * #format(DependencyRequest)} returns the empty string.
- */
- @CanIgnoreReturnValue
- StringBuilder appendFormatLine(StringBuilder builder, DependencyRequest dependencyRequest) {
- String formatted = format(dependencyRequest);
- if (!formatted.isEmpty()) {
- builder.append('\n').append(formatted);
- }
- return builder;
- }
-
- private final ElementVisitor<String, DependencyRequest> formatVisitor =
- new ElementKindVisitor8<String, DependencyRequest>() {
-
- @Override
- public String visitExecutableAsMethod(ExecutableElement method, DependencyRequest request) {
- return INDENT
- + request.key()
- + " is "
- + componentMethodRequestVerb(request)
- + " at\n"
- + DOUBLE_INDENT
- + elementToString(method);
- }
-
- @Override
- public String visitVariable(VariableElement variable, DependencyRequest request) {
- TypeMirror requestedType = requestType(request.kind(), request.key().type(), types);
- return INDENT
- + formatQualifier(request.key().qualifier())
- + requestedType
- + " is injected at\n"
- + DOUBLE_INDENT
- + elementToString(variable);
- }
-
- @Override
- public String visitType(TypeElement e, DependencyRequest request) {
- return ""; // types by themselves provide no useful information.
- }
-
- @Override
- protected String defaultAction(Element element, DependencyRequest request) {
- throw new IllegalStateException(
- "Invalid request " + element.getKind() + " element " + element);
- }
- };
-
- private String formatQualifier(Optional<AnnotationMirror> maybeQualifier) {
- return maybeQualifier.map(qualifier -> qualifier + " ").orElse("");
- }
-
- /**
- * Returns the verb for a component method dependency request. Returns "produced", "provided", or
- * "injected", depending on the kind of request.
- */
- private String componentMethodRequestVerb(DependencyRequest request) {
- switch (request.kind()) {
- case FUTURE:
- case PRODUCER:
- return "produced";
-
- case INSTANCE:
- case LAZY:
- case PROVIDER:
- case PROVIDER_OF_LAZY:
- return "provided";
-
- case MEMBERS_INJECTION:
- return "injected";
-
- case PRODUCED:
- break;
- }
- throw new AssertionError("illegal request kind for method: " + request);
- }
-}
diff --git a/java/dagger/internal/codegen/DependencyRequestValidator.java b/java/dagger/internal/codegen/DependencyRequestValidator.java
deleted file mode 100644
index 1a9981852..000000000
--- a/java/dagger/internal/codegen/DependencyRequestValidator.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.InjectionAnnotations.getQualifiers;
-import static dagger.internal.codegen.RequestKinds.extractKeyType;
-import static dagger.internal.codegen.RequestKinds.getRequestKind;
-import static javax.lang.model.type.TypeKind.WILDCARD;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableSet;
-import dagger.MembersInjector;
-import javax.inject.Inject;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-
-/** Validation for dependency requests. */
-final class DependencyRequestValidator {
- private final MembersInjectionValidator membersInjectionValidator;
-
- @Inject
- DependencyRequestValidator(MembersInjectionValidator membersInjectionValidator) {
- this.membersInjectionValidator = membersInjectionValidator;
- }
-
- /**
- * Adds an error if the given dependency request has more than one qualifier annotation or is a
- * non-instance request with a wildcard type.
- */
- void validateDependencyRequest(
- ValidationReport.Builder<?> report, Element requestElement, TypeMirror requestType) {
- ImmutableSet<? extends AnnotationMirror> qualifiers = getQualifiers(requestElement);
- if (qualifiers.size() > 1) {
- for (AnnotationMirror qualifier : qualifiers) {
- report.addError(
- "A single dependency request may not use more than one @Qualifier",
- requestElement,
- qualifier);
- }
- }
-
- TypeMirror keyType = extractKeyType(getRequestKind(requestType), requestType);
- if (keyType.getKind().equals(WILDCARD)) {
- // TODO(ronshapiro): Explore creating this message using RequestKinds.
- report.addError(
- "Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, "
- + "or Produced<T> when T is a wildcard type such as "
- + keyType,
- requestElement);
- }
- if (MoreTypes.isType(keyType) && MoreTypes.isTypeOf(MembersInjector.class, keyType)) {
- DeclaredType membersInjectorType = MoreTypes.asDeclared(keyType);
- if (membersInjectorType.getTypeArguments().isEmpty()) {
- report.addError("Cannot inject a raw MembersInjector", requestElement);
- } else {
- report.addSubreport(
- membersInjectionValidator.validateMembersInjectionRequest(
- requestElement, membersInjectorType.getTypeArguments().get(0)));
- }
- }
- }
-
- /**
- * Adds an error if the given dependency request is for a {@link dagger.producers.Producer} or
- * {@link dagger.producers.Produced}.
- *
- * <p>Only call this when processing a provision binding.
- */
- // TODO(dpb): Should we disallow Producer entry points in non-production components?
- void checkNotProducer(ValidationReport.Builder<?> report, VariableElement requestElement) {
- TypeMirror requestType = requestElement.asType();
- if (FrameworkTypes.isProducerType(requestType)) {
- report.addError(
- String.format(
- "%s may only be injected in @Produces methods",
- MoreTypes.asTypeElement(requestType).getSimpleName()),
- requestElement);
- }
- }
-}
diff --git a/java/dagger/internal/codegen/DependencyVariableNamer.java b/java/dagger/internal/codegen/DependencyVariableNamer.java
deleted file mode 100644
index 21f2d32af..000000000
--- a/java/dagger/internal/codegen/DependencyVariableNamer.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.SourceFiles.simpleVariableName;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.base.Ascii;
-import com.google.common.base.CaseFormat;
-import dagger.Lazy;
-import dagger.model.DependencyRequest;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import javax.inject.Provider;
-
-/**
- * Picks a reasonable name for what we think is being provided from the variable name associated
- * with the {@link DependencyRequest}. I.e. strips out words like "lazy" and "provider" if we
- * believe that those refer to {@link Lazy} and {@link Provider} rather than the type being
- * provided.
- */
-//TODO(gak): develop the heuristics to get better names
-final class DependencyVariableNamer {
- private static final Pattern LAZY_PROVIDER_PATTERN = Pattern.compile("lazy(\\w+)Provider");
-
- static String name(DependencyRequest dependency) {
- if (!dependency.requestElement().isPresent()) {
- return simpleVariableName(MoreTypes.asTypeElement(dependency.key().type()));
- }
-
- String variableName = dependency.requestElement().get().getSimpleName().toString();
- if (Ascii.isUpperCase(variableName.charAt(0))) {
- variableName = toLowerCamel(variableName);
- }
- switch (dependency.kind()) {
- case INSTANCE:
- return variableName;
- case LAZY:
- return variableName.startsWith("lazy") && !variableName.equals("lazy")
- ? toLowerCamel(variableName.substring(4))
- : variableName;
- case PROVIDER_OF_LAZY:
- Matcher matcher = LAZY_PROVIDER_PATTERN.matcher(variableName);
- if (matcher.matches()) {
- return toLowerCamel(matcher.group(1));
- }
- // fall through
- case PROVIDER:
- return variableName.endsWith("Provider") && !variableName.equals("Provider")
- ? variableName.substring(0, variableName.length() - 8)
- : variableName;
- case PRODUCED:
- return variableName.startsWith("produced") && !variableName.equals("produced")
- ? toLowerCamel(variableName.substring(8))
- : variableName;
- case PRODUCER:
- return variableName.endsWith("Producer") && !variableName.equals("Producer")
- ? variableName.substring(0, variableName.length() - 8)
- : variableName;
- default:
- throw new AssertionError();
- }
- }
-
- private static String toLowerCamel(String name) {
- return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, name);
- }
-}
diff --git a/java/dagger/internal/codegen/DependsOnProductionExecutorValidator.java b/java/dagger/internal/codegen/DependsOnProductionExecutorValidator.java
deleted file mode 100644
index e611d22fb..000000000
--- a/java/dagger/internal/codegen/DependsOnProductionExecutorValidator.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.DaggerStreams.instancesOf;
-import static javax.tools.Diagnostic.Kind.ERROR;
-
-import dagger.model.BindingGraph;
-import dagger.model.BindingGraph.MaybeBinding;
-import dagger.model.Key;
-import dagger.spi.BindingGraphPlugin;
-import dagger.spi.DiagnosticReporter;
-import javax.inject.Inject;
-
-/**
- * Reports an error on all bindings that depend explicitly on the {@code @Production Executor} key.
- */
-// TODO(dpb,beder): Validate this during @Inject/@Provides/@Produces validation.
-final class DependsOnProductionExecutorValidator implements BindingGraphPlugin {
- private final CompilerOptions compilerOptions;
- private final KeyFactory keyFactory;
-
- @Inject
- DependsOnProductionExecutorValidator(CompilerOptions compilerOptions, KeyFactory keyFactory) {
- this.compilerOptions = compilerOptions;
- this.keyFactory = keyFactory;
- }
-
- @Override
- public String pluginName() {
- return "Dagger/DependsOnProductionExecutor";
- }
-
- @Override
- public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
- if (!compilerOptions.usesProducers()) {
- return;
- }
-
- Key productionImplementationExecutorKey = keyFactory.forProductionImplementationExecutor();
- Key productionExecutorKey = keyFactory.forProductionExecutor();
-
- bindingGraph.network().nodes().stream()
- .flatMap(instancesOf(MaybeBinding.class))
- .filter(node -> node.key().equals(productionExecutorKey))
- .flatMap(productionExecutor -> bindingGraph.requestingBindings(productionExecutor).stream())
- .filter(binding -> !binding.key().equals(productionImplementationExecutorKey))
- .forEach(binding -> reportError(diagnosticReporter, binding));
- }
-
- private void reportError(DiagnosticReporter diagnosticReporter, dagger.model.Binding binding) {
- diagnosticReporter.reportBinding(
- ERROR, binding, "%s may not depend on the production executor", binding.key());
- }
-}
diff --git a/java/dagger/internal/codegen/DerivedFromFrameworkInstanceBindingExpression.java b/java/dagger/internal/codegen/DerivedFromFrameworkInstanceBindingExpression.java
deleted file mode 100644
index 4f2f622ea..000000000
--- a/java/dagger/internal/codegen/DerivedFromFrameworkInstanceBindingExpression.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static dagger.internal.codegen.BindingRequest.bindingRequest;
-
-import com.squareup.javapoet.ClassName;
-import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.internal.codegen.javapoet.Expression;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.Key;
-import dagger.model.RequestKind;
-import javax.lang.model.type.TypeMirror;
-
-/** A binding expression that depends on a framework instance. */
-final class DerivedFromFrameworkInstanceBindingExpression extends BindingExpression {
-
- private final BindingRequest frameworkRequest;
- private final RequestKind requestKind;
- private final FrameworkType frameworkType;
- private final ComponentBindingExpressions componentBindingExpressions;
- private final DaggerTypes types;
-
- DerivedFromFrameworkInstanceBindingExpression(
- Key key,
- FrameworkType frameworkType,
- RequestKind requestKind,
- ComponentBindingExpressions componentBindingExpressions,
- DaggerTypes types) {
- this.frameworkRequest = bindingRequest(key, frameworkType);
- this.requestKind = checkNotNull(requestKind);
- this.frameworkType = checkNotNull(frameworkType);
- this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
- this.types = checkNotNull(types);
- }
-
- @Override
- Expression getDependencyExpression(ClassName requestingClass) {
- return frameworkType.to(
- requestKind,
- componentBindingExpressions.getDependencyExpression(frameworkRequest, requestingClass),
- types);
- }
-
- @Override
- Expression getDependencyExpressionForComponentMethod(
- ComponentMethodDescriptor componentMethod, ComponentImplementation component) {
- Expression frameworkInstance =
- componentBindingExpressions.getDependencyExpressionForComponentMethod(
- frameworkRequest, componentMethod, component);
- Expression forRequestKind = frameworkType.to(requestKind, frameworkInstance, types);
- TypeMirror rawReturnType = types.erasure(componentMethod.resolvedReturnType(types));
- if (!types.isAssignable(forRequestKind.type(), rawReturnType)) {
- checkState(
- component.isAbstract(),
- "FrameworkType.to() should always return an accessible type unless we're in "
- + "ahead-of-time mode, where the framework instance type is erased since it's not "
- + "publicly accessible, but the return type is accessible to the package. "
- + "\n Component: %s, method: %s",
- component.name(),
- componentMethod);
- return forRequestKind.castTo(rawReturnType);
- }
- return forRequestKind;
- }
-}
diff --git a/java/dagger/internal/codegen/DeserializedComponentImplementationBuilder.java b/java/dagger/internal/codegen/DeserializedComponentImplementationBuilder.java
deleted file mode 100644
index 45e99a8d4..000000000
--- a/java/dagger/internal/codegen/DeserializedComponentImplementationBuilder.java
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * Copyright (C) 2019 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
-import static com.google.auto.common.MoreElements.getAnnotationMirror;
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static dagger.internal.codegen.BindingRequest.bindingRequest;
-import static dagger.internal.codegen.DaggerStreams.toImmutableList;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.MoreAnnotationValues.asAnnotationValues;
-import static dagger.internal.codegen.serialization.ProtoSerialization.fromAnnotationValue;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.util.ElementFilter.methodsIn;
-import static javax.lang.model.util.ElementFilter.typesIn;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.TypeName;
-import dagger.internal.ComponentDefinitionType;
-import dagger.internal.ConfigureInitializationParameters;
-import dagger.internal.ModifiableBinding;
-import dagger.internal.ModifiableModule;
-import dagger.internal.codegen.ComponentImplementation.ConfigureInitializationMethod;
-import dagger.internal.codegen.serialization.BindingRequestProto;
-import dagger.internal.codegen.serialization.ComponentRequirementProto;
-import dagger.internal.codegen.serialization.FrameworkTypeWrapper;
-import dagger.internal.codegen.serialization.KeyProto;
-import dagger.model.Key;
-import dagger.model.RequestKind;
-import java.util.Optional;
-import javax.inject.Inject;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * Reconstructs {@link ComponentImplementation}s that have already been compiled. Uses metadata
- * annotations on the generated type and it's methods to reconstitute the equivalent {@link
- * ComponentImplementation} state.
- */
-final class DeserializedComponentImplementationBuilder {
- private final CompilerOptions compilerOptions;
- private final ComponentCreatorImplementationFactory componentCreatorImplementationFactory;
- private final TypeProtoConverter typeProtoConverter;
- private final KeyFactory keyFactory;
-
- @Inject
- DeserializedComponentImplementationBuilder(
- CompilerOptions compilerOptions,
- ComponentCreatorImplementationFactory componentCreatorImplementationFactory,
- TypeProtoConverter typeProtoConverter,
- KeyFactory keyFactory) {
- this.compilerOptions = compilerOptions;
- this.componentCreatorImplementationFactory = componentCreatorImplementationFactory;
- this.typeProtoConverter = typeProtoConverter;
- this.keyFactory = keyFactory;
- }
-
- /** Creates a new {@link ComponentImplementation} from a compiled component. */
- ComponentImplementation create(ComponentDescriptor component, TypeElement generatedComponent) {
- Optional<ComponentImplementation> superclassImplementation =
- deserializedSuperclassImplementation(
- component, MoreTypes.asTypeElement(generatedComponent.getSuperclass()));
-
- ComponentImplementation componentImplementation =
- ComponentImplementation.forDeserializedComponent(
- component,
- ClassName.get(generatedComponent),
- generatedComponent.getNestingKind(),
- superclassImplementation,
- compilerOptions);
-
- componentImplementation.setCreatorImplementation(
- superclassImplementation.isPresent()
- ? Optional.empty()
- : componentCreatorImplementationFactory.create(
- componentImplementation, Optional.empty()));
-
- // TODO(b/117833324): Consider omitting superclass implementations, so that only one instance of
- // ComponentImplementation needs to be created (in most cases, we don't care about nested levels
- // of superclass implementations, except for the base implementation). If that's possible, use
- // getLocalAndInheritedMethods instead of getEnclosedElements() here.
- for (ExecutableElement method : methodsIn(generatedComponent.getEnclosedElements())) {
- getAnnotationMirror(method, ModifiableBinding.class)
- .asSet()
- .forEach(
- annotation ->
- addModifiableBindingMethod(componentImplementation, method, annotation));
-
- getAnnotationMirror(method, ModifiableModule.class)
- .asSet()
- .forEach(
- annotation -> addModifiableModuleMethod(componentImplementation, method, annotation));
-
- getAnnotationMirror(method, ConfigureInitializationParameters.class)
- .asSet()
- .forEach(
- annotation ->
- setConfigureInitializationMethod(componentImplementation, method, annotation));
- }
-
- for (TypeElement nestedType : typesIn(generatedComponent.getEnclosedElements())) {
- addChildImplementation(component, componentImplementation, nestedType);
- }
-
- return componentImplementation;
- }
-
- private Optional<ComponentImplementation> deserializedSuperclassImplementation(
- ComponentDescriptor component, TypeElement superclassElement) {
- return isAnnotationPresent(superclassElement, ComponentDefinitionType.class)
- ? Optional.of(create(component, superclassElement))
- : Optional.empty();
- }
-
- private void addModifiableBindingMethod(
- ComponentImplementation componentImplementation,
- ExecutableElement method,
- AnnotationMirror metadataAnnotation) {
- ModifiableBindingType modifiableBindingType =
- ModifiableBindingType.valueOf(
- getAnnotationValue(metadataAnnotation, "modifiableBindingType").getValue().toString());
-
- BindingRequest request =
- parseBindingRequest(getAnnotationValue(metadataAnnotation, "bindingRequest"));
-
- ImmutableList<Key> multibindingContributions =
- asAnnotationValues(getAnnotationValue(metadataAnnotation, "multibindingContributions"))
- .stream()
- .map(this::parseKey)
- .collect(toImmutableList());
-
- componentImplementation.addModifiableBindingMethod(
- modifiableBindingType,
- request,
- method.getReturnType(),
- methodDeclaration(method),
- method.getModifiers().contains(FINAL));
- componentImplementation.registerImplementedMultibindingKeys(request, multibindingContributions);
- }
-
- private BindingRequest fromProto(BindingRequestProto bindingRequest) {
- Key key = keyFactory.fromProto(bindingRequest.getKey());
- return bindingRequest.getFrameworkType().equals(FrameworkTypeWrapper.FrameworkType.UNKNOWN)
- ? bindingRequest(key, RequestKind.valueOf(bindingRequest.getRequestKind().name()))
- : bindingRequest(key, FrameworkType.valueOf(bindingRequest.getFrameworkType().name()));
- }
-
- /**
- * Returns a {@link MethodSpec} for a {@link
- * dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod}. The method contents
- * are not relevant since this represents a method that has already been compiled.
- *
- * <p>Ideally this could be {@code MethodSpec.overriding(method).build()}, but that doesn't work
- * for {@code final} methods
- */
- private MethodSpec methodDeclaration(ExecutableElement method) {
- return methodBuilder(method.getSimpleName().toString())
- .addModifiers(method.getModifiers())
- .returns(TypeName.get(method.getReturnType()))
- .build();
- }
-
- private void addModifiableModuleMethod(
- ComponentImplementation componentImplementation,
- ExecutableElement method,
- AnnotationMirror metadataAnnotation) {
- ComponentRequirement moduleRequirement =
- parseComponentRequirement(getAnnotationValue(metadataAnnotation, "value"));
- componentImplementation.registerModifiableModuleMethod(
- moduleRequirement, method.getSimpleName().toString());
- }
-
- private void setConfigureInitializationMethod(
- ComponentImplementation componentImplementation,
- ExecutableElement method,
- AnnotationMirror metadataAnnotation) {
- ImmutableSet<ComponentRequirement> parameters =
- asAnnotationValues(getAnnotationValue(metadataAnnotation, "value")).stream()
- .map(this::parseComponentRequirement)
- .collect(toImmutableSet());
-
- componentImplementation.setConfigureInitializationMethod(
- ConfigureInitializationMethod.create(MethodSpec.overriding(method).build(), parameters));
- }
-
- private void addChildImplementation(
- ComponentDescriptor component,
- ComponentImplementation componentImplementation,
- TypeElement nestedType) {
- getAnnotationMirror(nestedType, ComponentDefinitionType.class)
- .transform(annotation -> (TypeMirror) getAnnotationValue(annotation, "value").getValue())
- .transform(MoreTypes::asTypeElement)
- .asSet()
- .forEach(
- componentDefinitionType -> {
- ComponentDescriptor child =
- component.childComponentsByElement().get(componentDefinitionType);
- componentImplementation.addChild(child, create(child, nestedType));
- });
- }
-
- private Key parseKey(AnnotationValue annotationValue) {
- return keyFactory.fromProto(
- fromAnnotationValue(annotationValue, KeyProto.getDefaultInstance()));
- }
-
- private BindingRequest parseBindingRequest(AnnotationValue annotationValue) {
- return fromProto(
- fromAnnotationValue(annotationValue, BindingRequestProto.getDefaultInstance()));
- }
-
- private ComponentRequirement parseComponentRequirement(AnnotationValue annotationValue) {
- return fromProto(
- fromAnnotationValue(annotationValue, ComponentRequirementProto.getDefaultInstance()));
- }
-
- private ComponentRequirement fromProto(ComponentRequirementProto proto) {
- switch (proto.getRequirementCase()) {
- case MODULE:
- return ComponentRequirement.forModule(typeProtoConverter.fromProto(proto.getModule()));
- case DEPENDENCY:
- return ComponentRequirement.forDependency(
- typeProtoConverter.fromProto(proto.getDependency()));
- case BOUND_INSTANCE:
- return ComponentRequirement.forBoundInstance(
- keyFactory.fromProto(proto.getBoundInstance().getKey()),
- proto.getBoundInstance().getNullable(),
- proto.getBoundInstance().getVariableName());
- case REQUIREMENT_NOT_SET:
- // fall through
- }
- throw new AssertionError(proto);
- }
-}
diff --git a/java/dagger/internal/codegen/DiagnosticFormatting.java b/java/dagger/internal/codegen/DiagnosticFormatting.java
deleted file mode 100644
index 8502ecb25..000000000
--- a/java/dagger/internal/codegen/DiagnosticFormatting.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Utility methods for formatting diagnostics to the {@link javax.annotation.processing.Messager}.
- */
-final class DiagnosticFormatting {
-
- /**
- * A regular expression to match a small list of specific packages deemed to be unhelpful to
- * display in fully qualified types in error messages.
- *
- * <p>Note: This should never be applied to messages themselves.
- */
- private static final Pattern COMMON_PACKAGE_PATTERN =
- Pattern.compile(
- "(?:^|[^.a-z_])" // What we want to match on but not capture.
- + "((?:" // Start a group with a non-capturing or part
- + "java[.]lang"
- + "|java[.]util"
- + "|javax[.]inject"
- + "|dagger"
- + "|com[.]google[.]common[.]base"
- + "|com[.]google[.]common[.]collect"
- + ")[.])" // Always end with a literal .
- + "[A-Z]"); // What we want to match on but not capture.
-
- /**
- * A method to strip out common packages and a few rare type prefixes from types' string
- * representation before being used in error messages.
- *
- * <p>This type assumes a String value that is a valid fully qualified (and possibly
- * parameterized) type, and should NOT be used with arbitrary text, especially prose error
- * messages.
- *
- * <p>TODO(cgruber): Tighten these to take type representations (mirrors and elements) to avoid
- * accidental mis-use by running errors through this method.
- */
- static String stripCommonTypePrefixes(String type) {
- // Do regex magic to remove common packages we care to shorten.
- Matcher matcher = COMMON_PACKAGE_PATTERN.matcher(type);
- StringBuilder result = new StringBuilder();
- int index = 0;
- while (matcher.find()) {
- result.append(type.subSequence(index, matcher.start(1)));
- index = matcher.end(1); // Skip the matched pattern content.
- }
- result.append(type.subSequence(index, type.length()));
- return result.toString();
- }
-
- private DiagnosticFormatting() {}
-}
diff --git a/java/dagger/internal/codegen/DiagnosticReporterFactory.java b/java/dagger/internal/codegen/DiagnosticReporterFactory.java
deleted file mode 100644
index f657cee4d..000000000
--- a/java/dagger/internal/codegen/DiagnosticReporterFactory.java
+++ /dev/null
@@ -1,519 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreTypes.asTypeElement;
-import static com.google.common.base.Predicates.equalTo;
-import static com.google.common.base.Verify.verify;
-import static com.google.common.collect.Iterables.filter;
-import static com.google.common.collect.Iterables.getLast;
-import static com.google.common.collect.Iterables.indexOf;
-import static com.google.common.collect.Iterables.transform;
-import static com.google.common.collect.Lists.asList;
-import static dagger.internal.codegen.DaggerGraphs.shortestPath;
-import static dagger.internal.codegen.DaggerStreams.instancesOf;
-import static dagger.internal.codegen.DaggerStreams.presentValues;
-import static dagger.internal.codegen.DaggerStreams.toImmutableList;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.ElementFormatter.elementToString;
-import static dagger.internal.codegen.ValidationType.NONE;
-import static dagger.internal.codegen.langmodel.DaggerElements.DECLARATION_ORDER;
-import static dagger.internal.codegen.langmodel.DaggerElements.closestEnclosingTypeElement;
-import static dagger.internal.codegen.langmodel.DaggerElements.elementEncloses;
-import static java.util.Collections.min;
-import static java.util.Comparator.comparing;
-import static java.util.Comparator.comparingInt;
-import static javax.tools.Diagnostic.Kind.ERROR;
-
-import com.google.auto.common.MoreElements;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-import com.google.common.collect.HashBasedTable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Table;
-import com.google.errorprone.annotations.FormatMethod;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.BindingGraph;
-import dagger.model.BindingGraph.ChildFactoryMethodEdge;
-import dagger.model.BindingGraph.ComponentNode;
-import dagger.model.BindingGraph.DependencyEdge;
-import dagger.model.BindingGraph.Edge;
-import dagger.model.BindingGraph.MaybeBinding;
-import dagger.model.BindingGraph.Node;
-import dagger.model.ComponentPath;
-import dagger.spi.BindingGraphPlugin;
-import dagger.spi.DiagnosticReporter;
-import java.util.Comparator;
-import java.util.Set;
-import java.util.function.Function;
-import javax.annotation.processing.Messager;
-import javax.inject.Inject;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-import javax.tools.Diagnostic;
-import org.checkerframework.checker.nullness.compatqual.NullableDecl;
-
-/** A factory for {@link DiagnosticReporter}s. */
-// TODO(ronshapiro): If multiple plugins print errors on the same node/edge, should we condense the
-// messages and only print the dependency trace once?
-final class DiagnosticReporterFactory {
- private final DaggerTypes types;
- private final Messager messager;
- private final DependencyRequestFormatter dependencyRequestFormatter;
- private final ElementFormatter elementFormatter;
- private final CompilerOptions compilerOptions;
-
- @Inject
- DiagnosticReporterFactory(
- DaggerTypes types,
- Messager messager,
- DependencyRequestFormatter dependencyRequestFormatter,
- ElementFormatter elementFormatter,
- CompilerOptions compilerOptions) {
- this.types = types;
- this.messager = messager;
- this.dependencyRequestFormatter = dependencyRequestFormatter;
- this.elementFormatter = elementFormatter;
- this.compilerOptions = compilerOptions;
- }
-
- /** Creates a reporter for a binding graph and a plugin. */
- DiagnosticReporterImpl reporter(BindingGraph graph, BindingGraphPlugin plugin) {
- return new DiagnosticReporterImpl(graph, plugin.pluginName());
- }
-
- private static <K, V> Function<K, V> memoize(Function<K, V> uncached) {
- // If Android Guava is on the processor path, then c.g.c.b.Function (which LoadingCache
- // implements) does not extend j.u.f.Function.
-
- // First, explicitly convert uncached to c.g.c.b.Function because CacheLoader.from() expects
- // one.
- com.google.common.base.Function<K, V> uncachedAsBaseFunction = uncached::apply;
-
- LoadingCache<K, V> cache =
- CacheBuilder.newBuilder().build(CacheLoader.from(uncachedAsBaseFunction));
-
- // Second, explicitly convert LoadingCache to j.u.f.Function.
- @SuppressWarnings("deprecation") // uncachedAsBaseFunction throws only unchecked exceptions
- Function<K, V> memoized = cache::apply;
-
- return memoized;
- }
-
- /**
- * A {@link DiagnosticReporter} that keeps track of which {@linkplain Diagnostic.Kind kinds} of
- * diagnostics were reported.
- */
- final class DiagnosticReporterImpl implements DiagnosticReporter {
-
- /** A cached function from type to all of its supertypes in breadth-first order. */
- private final Function<TypeElement, Iterable<TypeElement>> supertypes =
- memoize(
- component ->
- transform(types.supertypes(component.asType()), type -> asTypeElement(type)));
-
- /** The shortest path (value) from an entry point (column) to a binding (row). */
- private final Table<MaybeBinding, DependencyEdge, ImmutableList<Node>> shortestPaths =
- HashBasedTable.create();
-
- private final BindingGraph graph;
- private final String plugin;
- private final TypeElement rootComponent;
- private final ImmutableSet.Builder<Diagnostic.Kind> reportedDiagnosticKinds =
- ImmutableSet.builder();
-
- DiagnosticReporterImpl(BindingGraph graph, String plugin) {
- this.graph = graph;
- this.plugin = plugin;
- this.rootComponent = graph.rootComponentNode().componentPath().currentComponent();
- }
-
- /** Returns which {@linkplain Diagnostic.Kind kinds} of diagnostics were reported. */
- ImmutableSet<Diagnostic.Kind> reportedDiagnosticKinds() {
- return reportedDiagnosticKinds.build();
- }
-
- @Override
- public void reportComponent(
- Diagnostic.Kind diagnosticKind, ComponentNode componentNode, String messageFormat) {
- StringBuilder message = new StringBuilder(messageFormat);
- appendComponentPathUnlessAtRoot(message, componentNode);
- // TODO(dpb): Report at the component node component.
- printMessage(diagnosticKind, message, rootComponent);
- }
-
- @Override
- @FormatMethod
- public void reportComponent(
- Diagnostic.Kind diagnosticKind,
- ComponentNode componentNode,
- String messageFormat,
- Object firstArg,
- Object... moreArgs) {
- reportComponent(
- diagnosticKind, componentNode, formatMessage(messageFormat, firstArg, moreArgs));
- }
-
- // TODO(ronshapiro): should this also include the binding element?
- @Override
- public void reportBinding(
- Diagnostic.Kind diagnosticKind, MaybeBinding binding, String message) {
- printMessage(diagnosticKind, message + new DiagnosticInfo(binding), rootComponent);
- }
-
- @Override
- public void reportBinding(
- Diagnostic.Kind diagnosticKind,
- MaybeBinding binding,
- String messageFormat,
- Object firstArg,
- Object... moreArgs) {
- reportBinding(diagnosticKind, binding, formatMessage(messageFormat, firstArg, moreArgs));
- }
-
- @Override
- public void reportDependency(
- Diagnostic.Kind diagnosticKind, DependencyEdge dependencyEdge, String message) {
- printMessage(diagnosticKind, message + new DiagnosticInfo(dependencyEdge), rootComponent);
- }
-
- @Override
- public void reportDependency(
- Diagnostic.Kind diagnosticKind,
- DependencyEdge dependencyEdge,
- String messageFormat,
- Object firstArg,
- Object... moreArgs) {
- reportDependency(
- diagnosticKind, dependencyEdge, formatMessage(messageFormat, firstArg, moreArgs));
- }
-
- @Override
- public void reportSubcomponentFactoryMethod(
- Diagnostic.Kind diagnosticKind,
- ChildFactoryMethodEdge childFactoryMethodEdge,
- String message) {
- printMessage(diagnosticKind, message, childFactoryMethodEdge.factoryMethod());
- }
-
- @Override
- public void reportSubcomponentFactoryMethod(
- Diagnostic.Kind diagnosticKind,
- ChildFactoryMethodEdge childFactoryMethodEdge,
- String messageFormat,
- Object firstArg,
- Object... moreArgs) {
- reportSubcomponentFactoryMethod(
- diagnosticKind, childFactoryMethodEdge, formatMessage(messageFormat, firstArg, moreArgs));
- }
-
- private String formatMessage(String messageFormat, Object firstArg, Object[] moreArgs) {
- return String.format(messageFormat, asList(firstArg, moreArgs).toArray());
- }
-
- private Node source(Edge edge) {
- return graph.network().incidentNodes(edge).source();
- }
-
- void printMessage(
- Diagnostic.Kind diagnosticKind,
- CharSequence message,
- @NullableDecl Element elementToReport) {
- if (graph.isFullBindingGraph()) {
- ValidationType validationType =
- compilerOptions.fullBindingGraphValidationType(rootComponent);
- if (validationType.equals(NONE)) {
- return;
- }
- if (diagnosticKind.equals(ERROR)) {
- diagnosticKind = validationType.diagnosticKind().get();
- }
- }
- reportedDiagnosticKinds.add(diagnosticKind);
- StringBuilder fullMessage = new StringBuilder();
- appendBracketPrefix(fullMessage, plugin);
-
- // TODO(ronshapiro): should we create a HashSet out of elementEncloses() so we don't
- // need to do an O(n) contains() each time?
- if (elementToReport != null && !elementEncloses(rootComponent, elementToReport)) {
- appendBracketPrefix(fullMessage, elementToString(elementToReport));
- elementToReport = rootComponent;
- }
-
- messager.printMessage(diagnosticKind, fullMessage.append(message), elementToReport);
- }
-
- private void appendComponentPathUnlessAtRoot(StringBuilder message, Node node) {
- if (!node.componentPath().equals(graph.rootComponentNode().componentPath())) {
- message.append(String.format(" [%s]", node.componentPath()));
- }
- }
-
- private void appendBracketPrefix(StringBuilder message, String prefix) {
- message.append(String.format("[%s] ", prefix));
- }
-
- /** The diagnostic information associated with an error. */
- private final class DiagnosticInfo {
- final ImmutableList<DependencyEdge> dependencyTrace;
- final ImmutableSet<DependencyEdge> requests;
- final ImmutableSet<DependencyEdge> entryPoints;
-
- DiagnosticInfo(MaybeBinding binding) {
- entryPoints = graph.entryPointEdgesDependingOnBinding(binding);
- requests = requests(binding);
- dependencyTrace = dependencyTrace(binding, entryPoints);
- }
-
- DiagnosticInfo(DependencyEdge dependencyEdge) {
- requests = ImmutableSet.of(dependencyEdge);
- ImmutableList.Builder<DependencyEdge> dependencyTraceBuilder = ImmutableList.builder();
- dependencyTraceBuilder.add(dependencyEdge);
-
- if (dependencyEdge.isEntryPoint()) {
- entryPoints = ImmutableSet.of(dependencyEdge);
- } else {
- // It's not an entry point, so it's part of a binding
- dagger.model.Binding binding = (dagger.model.Binding) source(dependencyEdge);
- entryPoints = graph.entryPointEdgesDependingOnBinding(binding);
- dependencyTraceBuilder.addAll(dependencyTrace(binding, entryPoints));
- }
- dependencyTrace = dependencyTraceBuilder.build();
- }
-
- @Override
- public String toString() {
- StringBuilder message =
- graph.isFullBindingGraph()
- ? new StringBuilder()
- : new StringBuilder(dependencyTrace.size() * 100 /* a guess heuristic */);
-
- // Print the dependency trace unless it's a full binding graph
- if (!graph.isFullBindingGraph()) {
- dependencyTrace.forEach(
- edge ->
- dependencyRequestFormatter.appendFormatLine(message, edge.dependencyRequest()));
- if (!dependencyTrace.isEmpty()) {
- appendComponentPathUnlessAtRoot(message, source(getLast(dependencyTrace)));
- }
- }
-
- // Print any dependency requests that aren't shown as part of the dependency trace.
- ImmutableSet<Element> requestsToPrint =
- requests.stream()
- // if printing entry points, skip entry points and the traced request
- .filter(
- request ->
- graph.isFullBindingGraph()
- || (!request.isEntryPoint() && !isTracedRequest(request)))
- .map(request -> request.dependencyRequest().requestElement())
- .flatMap(presentValues())
- .collect(toImmutableSet());
- if (!requestsToPrint.isEmpty()) {
- message
- .append("\nIt is")
- .append(graph.isFullBindingGraph() ? " " : " also ")
- .append("requested at:");
- elementFormatter.formatIndentedList(message, requestsToPrint, 1);
- }
-
- // Print the remaining entry points, showing which component they're in, unless it's a full
- // binding graph
- if (!graph.isFullBindingGraph() && entryPoints.size() > 1) {
- message.append("\nThe following other entry points also depend on it:");
- entryPointFormatter.formatIndentedList(
- message,
- entryPoints.stream()
- .filter(entryPoint -> !entryPoint.equals(getLast(dependencyTrace)))
- .sorted(
- // 1. List entry points in components closest to the root first.
- // 2. List entry points declared in a component before those in a supertype.
- // 3. List entry points in declaration order in their declaring type.
- rootComponentFirst()
- .thenComparing(nearestComponentSupertypeFirst())
- .thenComparing(requestElementDeclarationOrder()))
- .collect(toImmutableList()),
- 1);
- }
- return message.toString();
- }
-
- private final Formatter<DependencyEdge> entryPointFormatter =
- new Formatter<DependencyEdge>() {
- @Override
- public String format(DependencyEdge object) {
- Element requestElement = object.dependencyRequest().requestElement().get();
- StringBuilder element = new StringBuilder(elementToString(requestElement));
-
- // For entry points declared in subcomponents or supertypes of the root component,
- // append the component path to make clear to the user which component it's in.
- ComponentPath componentPath = source(object).componentPath();
- if (!componentPath.atRoot()
- || !requestElement.getEnclosingElement().equals(rootComponent)) {
- element.append(String.format(" [%s]", componentPath));
- }
- return element.toString();
- }
- };
-
- private boolean isTracedRequest(DependencyEdge request) {
- return !dependencyTrace.isEmpty() && request.equals(dependencyTrace.get(0));
- }
-
- /**
- * Returns the dependency trace from one of the {@code entryPoints} to {@code binding} to
- * {@code message} as a list <i>ending with</i> the entry point.
- */
- // TODO(ronshapiro): Adding a DependencyPath type to dagger.model could be useful, i.e.
- // bindingGraph.shortestPathFromEntryPoint(DependencyEdge, MaybeBindingNode)
- ImmutableList<DependencyEdge> dependencyTrace(
- MaybeBinding binding, ImmutableSet<DependencyEdge> entryPoints) {
- // Module binding graphs may have bindings unreachable from any entry points. If there are
- // no entry points for this DiagnosticInfo, don't try to print a dependency trace.
- if (entryPoints.isEmpty()) {
- return ImmutableList.of();
- }
- // Show the full dependency trace for one entry point.
- DependencyEdge entryPointForTrace =
- min(
- entryPoints,
- // prefer entry points in components closest to the root
- rootComponentFirst()
- // then prefer entry points with a short dependency path to the error
- .thenComparing(shortestDependencyPathFirst(binding))
- // then prefer entry points declared in the component to those declared in a
- // supertype
- .thenComparing(nearestComponentSupertypeFirst())
- // finally prefer entry points declared first in their enclosing type
- .thenComparing(requestElementDeclarationOrder()));
-
- ImmutableList<Node> shortestBindingPath =
- shortestPathFromEntryPoint(entryPointForTrace, binding);
- verify(
- !shortestBindingPath.isEmpty(),
- "no dependency path from %s to %s in %s",
- entryPointForTrace,
- binding,
- graph);
-
- ImmutableList.Builder<DependencyEdge> dependencyTrace = ImmutableList.builder();
- dependencyTrace.add(entryPointForTrace);
- for (int i = 0; i < shortestBindingPath.size() - 1; i++) {
- Set<Edge> dependenciesBetween =
- graph
- .network()
- .edgesConnecting(shortestBindingPath.get(i), shortestBindingPath.get(i + 1));
- // If a binding requests a key more than once, any of them should be fine to get to the
- // shortest path
- dependencyTrace.add((DependencyEdge) Iterables.get(dependenciesBetween, 0));
- }
- return dependencyTrace.build().reverse();
- }
-
- /** Returns all the nonsynthetic dependency requests for a binding. */
- ImmutableSet<DependencyEdge> requests(MaybeBinding binding) {
- return graph.network().inEdges(binding).stream()
- .flatMap(instancesOf(DependencyEdge.class))
- .filter(edge -> edge.dependencyRequest().requestElement().isPresent())
- .sorted(requestEnclosingTypeName().thenComparing(requestElementDeclarationOrder()))
- .collect(toImmutableSet());
- }
-
- /**
- * Returns a comparator that sorts entry points in components whose paths from the root are
- * shorter first.
- */
- Comparator<DependencyEdge> rootComponentFirst() {
- return comparingInt(entryPoint -> source(entryPoint).componentPath().components().size());
- }
-
- /**
- * Returns a comparator that puts entry points whose shortest dependency path to {@code
- * binding} is shortest first.
- */
- Comparator<DependencyEdge> shortestDependencyPathFirst(MaybeBinding binding) {
- return comparing(entryPoint -> shortestPathFromEntryPoint(entryPoint, binding).size());
- }
-
- ImmutableList<Node> shortestPathFromEntryPoint(
- DependencyEdge entryPoint, MaybeBinding binding) {
- return shortestPaths
- .row(binding)
- .computeIfAbsent(
- entryPoint,
- ep ->
- shortestPath(
- node ->
- filter(
- graph.network().successors(node), MaybeBinding.class::isInstance),
- graph.network().incidentNodes(ep).target(),
- binding));
- }
-
- /**
- * Returns a comparator that sorts entry points in by the distance of the type that declares
- * them from the type of the component that contains them.
- *
- * <p>For instance, an entry point declared directly in the component type would sort before
- * one declared in a direct supertype, which would sort before one declared in a supertype of
- * a supertype.
- */
- Comparator<DependencyEdge> nearestComponentSupertypeFirst() {
- return comparingInt(
- entryPoint ->
- indexOf(
- supertypes.apply(componentContainingEntryPoint(entryPoint)),
- equalTo(typeDeclaringEntryPoint(entryPoint))));
- }
-
- TypeElement componentContainingEntryPoint(DependencyEdge entryPoint) {
- return source(entryPoint).componentPath().currentComponent();
- }
-
- TypeElement typeDeclaringEntryPoint(DependencyEdge entryPoint) {
- return MoreElements.asType(
- entryPoint.dependencyRequest().requestElement().get().getEnclosingElement());
- }
-
- /**
- * Returns a comparator that sorts dependency edges lexicographically by the qualified name of
- * the type that contains them. Only appropriate for edges with request elements.
- */
- Comparator<DependencyEdge> requestEnclosingTypeName() {
- return comparing(
- edge ->
- closestEnclosingTypeElement(edge.dependencyRequest().requestElement().get())
- .getQualifiedName()
- .toString());
- }
-
- /**
- * Returns a comparator that sorts edges in the order in which their request elements were
- * declared in their declaring type.
- *
- * <p>Only useful to compare edges whose request elements were declared in the same type.
- */
- Comparator<DependencyEdge> requestElementDeclarationOrder() {
- return comparing(
- edge -> edge.dependencyRequest().requestElement().get(), DECLARATION_ORDER);
- }
- }
- }
-}
diff --git a/java/dagger/internal/codegen/DuplicateBindingsValidator.java b/java/dagger/internal/codegen/DuplicateBindingsValidator.java
deleted file mode 100644
index 3eed45fdb..000000000
--- a/java/dagger/internal/codegen/DuplicateBindingsValidator.java
+++ /dev/null
@@ -1,332 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Verify.verify;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSetMultimap;
-import static dagger.internal.codegen.Formatter.INDENT;
-import static dagger.internal.codegen.Optionals.emptiesLast;
-import static dagger.model.BindingKind.INJECTION;
-import static dagger.model.BindingKind.MEMBERS_INJECTION;
-import static java.util.Comparator.comparing;
-import static javax.tools.Diagnostic.Kind.ERROR;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.collect.ImmutableCollection;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ImmutableMultiset;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Multimaps;
-import com.google.common.collect.Sets;
-import dagger.model.Binding;
-import dagger.model.BindingGraph;
-import dagger.model.BindingKind;
-import dagger.model.ComponentPath;
-import dagger.model.Key;
-import dagger.spi.BindingGraphPlugin;
-import dagger.spi.DiagnosticReporter;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.Optional;
-import java.util.Set;
-import java.util.function.Predicate;
-import javax.inject.Inject;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-import javax.tools.Diagnostic.Kind;
-
-/** Reports errors for conflicting bindings with the same key. */
-final class DuplicateBindingsValidator implements BindingGraphPlugin {
-
- // 1. contributing module or enclosing type
- // 2. binding element's simple name
- // 3. binding element's type
- private static final Comparator<BindingDeclaration> BINDING_DECLARATION_COMPARATOR =
- comparing(
- (BindingDeclaration declaration) ->
- declaration.contributingModule().isPresent()
- ? declaration.contributingModule()
- : declaration.bindingTypeElement(),
- emptiesLast(comparing((TypeElement type) -> type.getQualifiedName().toString())))
- .thenComparing(
- (BindingDeclaration declaration) -> declaration.bindingElement(),
- emptiesLast(
- comparing((Element element) -> element.getSimpleName().toString())
- .thenComparing((Element element) -> element.asType().toString())));
-
- private static final Comparator<Binding> BY_LENGTH_OF_COMPONENT_PATH =
- comparing(binding -> binding.componentPath().components().size());
-
- private final BindingDeclarationFormatter bindingDeclarationFormatter;
- private final CompilerOptions compilerOptions;
-
- @Inject
- DuplicateBindingsValidator(
- BindingDeclarationFormatter bindingDeclarationFormatter, CompilerOptions compilerOptions) {
- this.bindingDeclarationFormatter = bindingDeclarationFormatter;
- this.compilerOptions = compilerOptions;
- }
-
- @Override
- public String pluginName() {
- return "Dagger/DuplicateBindings";
- }
-
- @Override
- public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
- // If two unrelated subcomponents have the same duplicate bindings only because they install the
- // same two modules, then fixing the error in one subcomponent will uncover the second
- // subcomponent to fix.
- // TODO(ronshapiro): Explore ways to address such underreporting without overreporting.
- Set<ImmutableSet<BindingElement>> reportedDuplicateBindingSets = new HashSet<>();
- duplicateBindingSets(bindingGraph)
- .forEach(
- duplicateBindings -> {
- // Only report each set of duplicate bindings once, ignoring the installed component.
- if (reportedDuplicateBindingSets.add(duplicateBindings.keySet())) {
- reportDuplicateBindings(duplicateBindings, bindingGraph, diagnosticReporter);
- }
- });
- }
-
- /**
- * Returns sets of duplicate bindings. Bindings are duplicates if they bind the same key and are
- * visible from the same component. Two bindings that differ only in the component that owns them
- * are not considered to be duplicates, because that means the same binding was "copied" down to a
- * descendant component because it depends on local multibindings or optional bindings. Hence each
- * "set" is represented as a multimap from binding element (ignoring component path) to binding.
- */
- private ImmutableSet<ImmutableSetMultimap<BindingElement, Binding>> duplicateBindingSets(
- BindingGraph bindingGraph) {
- return groupBindingsByKey(bindingGraph).stream()
- .flatMap(bindings -> mutuallyVisibleSubsets(bindings).stream())
- .map(BindingElement::index)
- .filter(duplicates -> duplicates.keySet().size() > 1)
- .collect(toImmutableSet());
- }
-
- private static ImmutableSet<ImmutableSet<Binding>> groupBindingsByKey(BindingGraph bindingGraph) {
- return valueSetsForEachKey(
- bindingGraph.bindings().stream()
- .filter(binding -> !binding.kind().equals(MEMBERS_INJECTION))
- .collect(toImmutableSetMultimap(Binding::key, binding -> binding)));
- }
-
- /**
- * Returns the subsets of the input set that contain bindings that are all visible from the same
- * component. A binding is visible from its component and all its descendants.
- */
- private static ImmutableSet<ImmutableSet<Binding>> mutuallyVisibleSubsets(
- Set<Binding> duplicateBindings) {
- ImmutableListMultimap<ComponentPath, Binding> bindingsByComponentPath =
- Multimaps.index(duplicateBindings, Binding::componentPath);
- ImmutableSetMultimap.Builder<ComponentPath, Binding> mutuallyVisibleBindings =
- ImmutableSetMultimap.builder();
- bindingsByComponentPath
- .asMap()
- .forEach(
- (componentPath, bindings) -> {
- mutuallyVisibleBindings.putAll(componentPath, bindings);
- for (ComponentPath ancestor = componentPath; !ancestor.atRoot(); ) {
- ancestor = ancestor.parent();
- ImmutableList<Binding> bindingsInAncestor = bindingsByComponentPath.get(ancestor);
- mutuallyVisibleBindings.putAll(componentPath, bindingsInAncestor);
- }
- });
- return valueSetsForEachKey(mutuallyVisibleBindings.build());
- }
-
- private void reportDuplicateBindings(
- ImmutableSetMultimap<BindingElement, Binding> duplicateBindings,
- BindingGraph bindingGraph,
- DiagnosticReporter diagnosticReporter) {
- if (explicitBindingConfictsWithInject(duplicateBindings.keySet())) {
- compilerOptions
- .explicitBindingConflictsWithInjectValidationType()
- .diagnosticKind()
- .ifPresent(
- diagnosticKind ->
- reportExplicitBindingConflictsWithInject(
- duplicateBindings, diagnosticReporter, diagnosticKind));
- return;
- }
- ImmutableSet<Binding> bindings = ImmutableSet.copyOf(duplicateBindings.values());
- Binding oneBinding = bindings.asList().get(0);
- diagnosticReporter.reportBinding(
- ERROR,
- oneBinding,
- Iterables.any(bindings, binding -> binding.kind().isMultibinding())
- ? incompatibleBindingsMessage(oneBinding.key(), bindings, bindingGraph)
- : duplicateBindingMessage(oneBinding.key(), bindings, bindingGraph));
- }
-
- /**
- * Returns {@code true} if the bindings contain one {@code @Inject} binding and one that isn't.
- */
- private static boolean explicitBindingConfictsWithInject(
- ImmutableSet<BindingElement> duplicateBindings) {
- ImmutableMultiset<BindingKind> bindingKinds =
- Multimaps.index(duplicateBindings, BindingElement::bindingKind).keys();
- return bindingKinds.count(INJECTION) == 1 && bindingKinds.size() == 2;
- }
-
- private void reportExplicitBindingConflictsWithInject(
- ImmutableSetMultimap<BindingElement, Binding> duplicateBindings,
- DiagnosticReporter diagnosticReporter,
- Kind diagnosticKind) {
- Binding injectBinding =
- rootmostBindingWithKind(k -> k.equals(INJECTION), duplicateBindings.values());
- Binding explicitBinding =
- rootmostBindingWithKind(k -> !k.equals(INJECTION), duplicateBindings.values());
- StringBuilder message =
- new StringBuilder()
- .append(explicitBinding.key())
- .append(" is bound multiple times:")
- .append(formatWithComponentPath(injectBinding))
- .append(formatWithComponentPath(explicitBinding))
- .append(
- "\nThis condition was never validated before, and will soon be an error. "
- + "See https://dagger.dev/conflicting-inject.");
-
- diagnosticReporter.reportBinding(diagnosticKind, explicitBinding, message.toString());
- }
-
- private String formatWithComponentPath(Binding binding) {
- return String.format(
- "\n%s%s [%s]",
- Formatter.INDENT,
- bindingDeclarationFormatter.format(((BindingNode) binding).delegate()),
- binding.componentPath());
- }
-
- private String duplicateBindingMessage(
- Key key, ImmutableSet<Binding> duplicateBindings, BindingGraph graph) {
- StringBuilder message = new StringBuilder().append(key).append(" is bound multiple times:");
- formatDeclarations(message, 1, declarations(graph, duplicateBindings));
- return message.toString();
- }
-
- private String incompatibleBindingsMessage(
- Key key, ImmutableSet<Binding> duplicateBindings, BindingGraph graph) {
- ImmutableSet<dagger.model.Binding> multibindings =
- duplicateBindings.stream()
- .filter(binding -> binding.kind().isMultibinding())
- .collect(toImmutableSet());
- verify(
- multibindings.size() == 1, "expected only one multibinding for %s: %s", key, multibindings);
- StringBuilder message = new StringBuilder();
- java.util.Formatter messageFormatter = new java.util.Formatter(message);
- messageFormatter.format("%s has incompatible bindings or declarations:\n", key);
- message.append(INDENT);
- dagger.model.Binding multibinding = getOnlyElement(multibindings);
- messageFormatter.format("%s bindings and declarations:", multibindingTypeString(multibinding));
- formatDeclarations(message, 2, declarations(graph, multibindings));
-
- Set<dagger.model.Binding> uniqueBindings =
- Sets.filter(duplicateBindings, binding -> !binding.equals(multibinding));
- message.append('\n').append(INDENT).append("Unique bindings and declarations:");
- formatDeclarations(
- message,
- 2,
- Sets.filter(
- declarations(graph, uniqueBindings),
- declaration -> !(declaration instanceof MultibindingDeclaration)));
- return message.toString();
- }
-
- private void formatDeclarations(
- StringBuilder builder,
- int indentLevel,
- Iterable<? extends BindingDeclaration> bindingDeclarations) {
- bindingDeclarationFormatter.formatIndentedList(
- builder, ImmutableList.copyOf(bindingDeclarations), indentLevel);
- }
-
- private ImmutableSet<BindingDeclaration> declarations(
- BindingGraph graph, Set<dagger.model.Binding> bindings) {
- return bindings.stream()
- .flatMap(binding -> declarations(graph, binding).stream())
- .distinct()
- .sorted(BINDING_DECLARATION_COMPARATOR)
- .collect(toImmutableSet());
- }
-
- private ImmutableSet<BindingDeclaration> declarations(
- BindingGraph graph, dagger.model.Binding binding) {
- ImmutableSet.Builder<BindingDeclaration> declarations = ImmutableSet.builder();
- BindingNode bindingNode = (BindingNode) binding;
- bindingNode.associatedDeclarations().forEach(declarations::add);
- if (bindingDeclarationFormatter.canFormat(bindingNode.delegate())) {
- declarations.add(bindingNode.delegate());
- } else {
- graph.requestedBindings(binding).stream()
- .flatMap(requestedBinding -> declarations(graph, requestedBinding).stream())
- .forEach(declarations::add);
- }
- return declarations.build();
- }
-
- private String multibindingTypeString(dagger.model.Binding multibinding) {
- switch (multibinding.kind()) {
- case MULTIBOUND_MAP:
- return "Map";
- case MULTIBOUND_SET:
- return "Set";
- default:
- throw new AssertionError(multibinding);
- }
- }
-
- private static <E> ImmutableSet<ImmutableSet<E>> valueSetsForEachKey(Multimap<?, E> multimap) {
- return multimap.asMap().values().stream().map(ImmutableSet::copyOf).collect(toImmutableSet());
- }
-
- /** Returns the binding of the given kind that is closest to the root component. */
- private static Binding rootmostBindingWithKind(
- Predicate<BindingKind> bindingKindPredicate, ImmutableCollection<Binding> bindings) {
- return bindings.stream()
- .filter(b -> bindingKindPredicate.test(b.kind()))
- .min(BY_LENGTH_OF_COMPONENT_PATH)
- .get();
- }
-
- /** The identifying information about a binding, excluding its {@link Binding#componentPath()}. */
- @AutoValue
- abstract static class BindingElement {
-
- abstract BindingKind bindingKind();
-
- abstract Optional<Element> bindingElement();
-
- abstract Optional<TypeElement> contributingModule();
-
- static ImmutableSetMultimap<BindingElement, Binding> index(Set<Binding> bindings) {
- return bindings.stream().collect(toImmutableSetMultimap(BindingElement::forBinding, b -> b));
- }
-
- private static BindingElement forBinding(Binding binding) {
- return new AutoValue_DuplicateBindingsValidator_BindingElement(
- binding.kind(), binding.bindingElement(), binding.contributingModule());
- }
- }
-}
diff --git a/java/dagger/internal/codegen/ElementFormatter.java b/java/dagger/internal/codegen/ElementFormatter.java
deleted file mode 100644
index 40d7606a8..000000000
--- a/java/dagger/internal/codegen/ElementFormatter.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2013 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreElements.asExecutable;
-import static java.util.stream.Collectors.joining;
-
-import javax.inject.Inject;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementVisitor;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.util.ElementKindVisitor8;
-
-/**
- * Formats elements into a useful string representation.
- *
- * <p>Elements directly enclosed by a type are preceded by the enclosing type's qualified name.
- *
- * <p>Parameters are given with their enclosing executable, with other parameters elided.
- */
-final class ElementFormatter extends Formatter<Element> {
- @Inject
- ElementFormatter() {}
-
- @Override
- public String format(Element element) {
- return elementToString(element);
- }
-
- /**
- * Returns a useful string form for an element.
- *
- * <p>Elements directly enclosed by a type are preceded by the enclosing type's qualified name.
- *
- * <p>Parameters are given with their enclosing executable, with other parameters elided.
- */
- static String elementToString(Element element) {
- return element.accept(ELEMENT_TO_STRING, null);
- }
-
- private static final ElementVisitor<String, Void> ELEMENT_TO_STRING =
- new ElementKindVisitor8<String, Void>() {
- @Override
- public String visitExecutable(ExecutableElement executableElement, Void aVoid) {
- return enclosingTypeAndMemberName(executableElement)
- .append(
- executableElement.getParameters().stream()
- .map(parameter -> parameter.asType().toString())
- .collect(joining(", ", "(", ")")))
- .toString();
- }
-
- @Override
- public String visitVariableAsParameter(VariableElement parameter, Void aVoid) {
- ExecutableElement methodOrConstructor = asExecutable(parameter.getEnclosingElement());
- return enclosingTypeAndMemberName(methodOrConstructor)
- .append('(')
- .append(
- formatArgumentInList(
- methodOrConstructor.getParameters().indexOf(parameter),
- methodOrConstructor.getParameters().size(),
- parameter.getSimpleName()))
- .append(')')
- .toString();
- }
-
- @Override
- public String visitVariableAsField(VariableElement field, Void aVoid) {
- return enclosingTypeAndMemberName(field).toString();
- }
-
- @Override
- public String visitType(TypeElement type, Void aVoid) {
- return type.getQualifiedName().toString();
- }
-
- @Override
- protected String defaultAction(Element element, Void aVoid) {
- throw new UnsupportedOperationException(
- "Can't determine string for " + element.getKind() + " element " + element);
- }
-
- private StringBuilder enclosingTypeAndMemberName(Element element) {
- StringBuilder name = new StringBuilder(element.getEnclosingElement().accept(this, null));
- if (!element.getSimpleName().contentEquals("<init>")) {
- name.append('.').append(element.getSimpleName());
- }
- return name;
- }
- };
-}
diff --git a/java/dagger/internal/codegen/ErrorMessages.java b/java/dagger/internal/codegen/ErrorMessages.java
deleted file mode 100644
index 4195e1a20..000000000
--- a/java/dagger/internal/codegen/ErrorMessages.java
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableMap;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.function.UnaryOperator;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.TypeMirror;
-
-/** The collection of error messages to be reported back to users. */
-final class ErrorMessages {
-
- private static final UnaryOperator<String> PRODUCTION =
- s ->
- s.replace("component", "production component")
- .replace("Component", "ProductionComponent");
-
- private static final UnaryOperator<String> SUBCOMPONENT =
- s -> s.replace("component", "subcomponent").replace("Component", "Subcomponent");
-
- private static final UnaryOperator<String> FACTORY = s -> s.replace("Builder", "Factory");
-
- private static final ImmutableMap<ComponentKind, Function<String, String>>
- COMPONENT_TRANSFORMATIONS =
- ImmutableMap.of(
- ComponentKind.COMPONENT, UnaryOperator.identity(),
- ComponentKind.SUBCOMPONENT, SUBCOMPONENT,
- ComponentKind.PRODUCTION_COMPONENT, PRODUCTION,
- ComponentKind.PRODUCTION_SUBCOMPONENT, PRODUCTION.andThen(SUBCOMPONENT));
-
- static ComponentMessages componentMessagesFor(ComponentKind componentKind) {
- return new ComponentMessages(COMPONENT_TRANSFORMATIONS.get(componentKind));
- }
-
- static ComponentMessages componentMessagesFor(ComponentAnnotation componentAnnotation) {
- return new ComponentMessages(
- transformation(componentAnnotation.isProduction(), componentAnnotation.isSubcomponent()));
- }
-
- static ComponentCreatorMessages creatorMessagesFor(ComponentCreatorAnnotation creatorAnnotation) {
- Function<String, String> transformation =
- transformation(
- creatorAnnotation.isProductionCreatorAnnotation(),
- creatorAnnotation.isSubcomponentCreatorAnnotation());
- switch (creatorAnnotation.creatorKind()) {
- case BUILDER:
- return new BuilderMessages(transformation);
- case FACTORY:
- return new FactoryMessages(transformation);
- }
- throw new AssertionError(creatorAnnotation);
- }
-
- private static Function<String, String> transformation(
- boolean isProduction, boolean isSubcomponent) {
- Function<String, String> transformation = isProduction ? PRODUCTION : UnaryOperator.identity();
- return isSubcomponent ? transformation.andThen(SUBCOMPONENT) : transformation;
- }
-
- private abstract static class Messages {
- private final Function<String, String> transformation;
-
- Messages(Function<String, String> transformation) {
- this.transformation = transformation;
- }
-
- protected final String process(String s) {
- return transformation.apply(s);
- }
- }
-
- /** Errors for components. */
- static final class ComponentMessages extends Messages {
- ComponentMessages(Function<String, String> transformation) {
- super(transformation);
- }
-
- final String moreThanOne() {
- return process("@Component has more than one @Component.Builder or @Component.Factory: %s");
- }
- }
-
- /** Errors for component creators. */
- abstract static class ComponentCreatorMessages extends Messages {
- ComponentCreatorMessages(Function<String, String> transformation) {
- super(transformation);
- }
-
- static String builderMethodRequiresNoArgs() {
- return "Methods returning a @Component.Builder must have no arguments";
- }
-
- static String moreThanOneRefToSubcomponent() {
- return "Only one method can create a given subcomponent. %s is created by: %s";
- }
-
- final String invalidConstructor() {
- return process("@Component.Builder classes must have exactly one constructor,"
- + " and it must not be private or have any parameters");
- }
-
- final String generics() {
- return process("@Component.Builder types must not have any generic types");
- }
-
- final String mustBeInComponent() {
- return process("@Component.Builder types must be nested within a @Component");
- }
-
- final String mustBeClassOrInterface() {
- return process("@Component.Builder types must be abstract classes or interfaces");
- }
-
- final String isPrivate() {
- return process("@Component.Builder types must not be private");
- }
-
- final String mustBeStatic() {
- return process("@Component.Builder types must be static");
- }
-
- final String mustBeAbstract() {
- return process("@Component.Builder types must be abstract");
- }
-
- abstract String missingFactoryMethod();
-
- abstract String multipleSettersForModuleOrDependencyType();
-
- abstract String extraSetters();
-
- abstract String missingSetters();
-
- abstract String twoFactoryMethods();
-
- abstract String inheritedTwoFactoryMethods();
-
- abstract String factoryMethodMustReturnComponentType();
-
- final String inheritedFactoryMethodMustReturnComponentType() {
- return factoryMethodMustReturnComponentType() + ". Inherited method: %s";
- }
-
- abstract String factoryMethodMayNotBeAnnotatedWithBindsInstance();
-
- final String inheritedFactoryMethodMayNotBeAnnotatedWithBindsInstance() {
- return factoryMethodMayNotBeAnnotatedWithBindsInstance() + ". Inherited method: %s";
- }
-
- final String setterMethodsMustTakeOneArg() {
- return process("@Component.Builder methods must not have more than one argument");
- }
-
- final String inheritedSetterMethodsMustTakeOneArg() {
- return setterMethodsMustTakeOneArg() + ". Inherited method: %s";
- }
-
- final String setterMethodsMustReturnVoidOrBuilder() {
- return process("@Component.Builder setter methods must return void, the builder,"
- + " or a supertype of the builder");
- }
-
- final String inheritedSetterMethodsMustReturnVoidOrBuilder() {
- return setterMethodsMustReturnVoidOrBuilder() + ". Inherited method: %s";
- }
-
- final String methodsMayNotHaveTypeParameters() {
- return process("@Component.Builder methods must not have type parameters");
- }
-
- final String inheritedMethodsMayNotHaveTypeParameters() {
- return methodsMayNotHaveTypeParameters() + ". Inherited method: %s";
- }
-
- abstract String nonBindsInstanceParametersMayNotBePrimitives();
-
- final String inheritedNonBindsInstanceParametersMayNotBePrimitives() {
- return nonBindsInstanceParametersMayNotBePrimitives() + ". Inherited method: %s";
- }
-
- final String factoryMethodReturnsSupertypeWithMissingMethods(
- TypeElement component,
- TypeElement componentBuilder,
- TypeMirror returnType,
- ExecutableElement buildMethod,
- Set<ExecutableElement> additionalMethods) {
- return String.format(
- "%1$s.%2$s() returns %3$s, but %4$s declares additional component method(s): %5$s. In "
- + "order to provide type-safe access to these methods, override %2$s() to return "
- + "%4$s",
- componentBuilder.getQualifiedName(),
- buildMethod.getSimpleName(),
- returnType,
- component.getQualifiedName(),
- Joiner.on(", ").join(additionalMethods));
- }
-
- final String bindsInstanceNotAllowedOnBothSetterMethodAndParameter() {
- return process("@Component.Builder setter methods may not have @BindsInstance on both the "
- + "method and its parameter; choose one or the other");
- }
-
- final String inheritedBindsInstanceNotAllowedOnBothSetterMethodAndParameter() {
- return bindsInstanceNotAllowedOnBothSetterMethodAndParameter() + ". Inherited method: %s";
- }
- }
-
- private static final class BuilderMessages extends ComponentCreatorMessages {
- BuilderMessages(Function<String, String> transformation) {
- super(transformation);
- }
-
- @Override
- String missingFactoryMethod() {
- return process(
- "@Component.Builder types must have exactly one no-args method that "
- + " returns the @Component type");
- }
-
- @Override
- String multipleSettersForModuleOrDependencyType() {
- return process(
- "@Component.Builder types must not have more than one setter method per module or "
- + "dependency, but %s is set by %s");
- }
-
- @Override
- String extraSetters() {
- return process(
- "@Component.Builder has setters for modules or components that aren't required: %s");
- }
-
- @Override
- String missingSetters() {
- return process(
- "@Component.Builder is missing setters for required modules or components: %s");
- }
-
- @Override
- String twoFactoryMethods() {
- return process(
- "@Component.Builder types must have exactly one zero-arg method, and that"
- + " method must return the @Component type. Already found: %s");
- }
-
- @Override
- String inheritedTwoFactoryMethods() {
- return process(
- "@Component.Builder types must have exactly one zero-arg method, and that"
- + " method must return the @Component type. Found %s and %s");
- }
-
- @Override
- String factoryMethodMustReturnComponentType() {
- return process(
- "@Component.Builder methods that have no arguments must return the @Component type or a "
- + "supertype of the @Component");
- }
-
- @Override
- String factoryMethodMayNotBeAnnotatedWithBindsInstance() {
- return process(
- "@Component.Builder no-arg build methods may not be annotated with @BindsInstance");
- }
-
- @Override
- String nonBindsInstanceParametersMayNotBePrimitives() {
- return process(
- "@Component.Builder methods that are not annotated with @BindsInstance "
- + "must take either a module or a component dependency, not a primitive");
- }
- }
-
- private static final class FactoryMessages extends ComponentCreatorMessages {
- FactoryMessages(Function<String, String> transformation) {
- super(transformation.andThen(FACTORY));
- }
-
- @Override
- String missingFactoryMethod() {
- return process(
- "@Component.Factory types must have exactly one method that "
- + "returns the @Component type");
- }
-
- @Override
- String multipleSettersForModuleOrDependencyType() {
- return process(
- "@Component.Factory methods must not have more than one parameter per module or "
- + "dependency, but %s is set by %s");
- }
-
- @Override
- String extraSetters() {
- return process(
- "@Component.Factory method has parameters for modules or components that aren't "
- + "required: %s");
- }
-
- @Override
- String missingSetters() {
- return process(
- "@Component.Factory method is missing parameters for required modules or components: %s");
- }
-
- @Override
- String twoFactoryMethods() {
- return process(
- "@Component.Factory types must have exactly one abstract method. Already found: %s");
- }
-
- @Override
- String inheritedTwoFactoryMethods() {
- return twoFactoryMethods();
- }
-
- @Override
- String factoryMethodMustReturnComponentType() {
- return process(
- "@Component.Factory abstract methods must return the @Component type or a "
- + "supertype of the @Component");
- }
-
- @Override
- String factoryMethodMayNotBeAnnotatedWithBindsInstance() {
- return process("@Component.Factory method may not be annotated with @BindsInstance");
- }
-
- @Override
- String nonBindsInstanceParametersMayNotBePrimitives() {
- return process(
- "@Component.Factory method parameters that are not annotated with @BindsInstance "
- + "must be either a module or a component dependency, not a primitive");
- }
- }
-
- private ErrorMessages() {}
-}
diff --git a/java/dagger/internal/codegen/FactoryGenerator.java b/java/dagger/internal/codegen/FactoryGenerator.java
deleted file mode 100644
index d367bc532..000000000
--- a/java/dagger/internal/codegen/FactoryGenerator.java
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.Maps.transformValues;
-import static com.squareup.javapoet.MethodSpec.constructorBuilder;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static com.squareup.javapoet.TypeSpec.classBuilder;
-import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.DELEGATE;
-import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.SINGLETON_INSTANCE;
-import static dagger.internal.codegen.GwtCompatibility.gwtIncompatibleAnnotation;
-import static dagger.internal.codegen.SourceFiles.bindingTypeElementTypeVariableNames;
-import static dagger.internal.codegen.SourceFiles.frameworkFieldUsages;
-import static dagger.internal.codegen.SourceFiles.frameworkTypeUsageStatement;
-import static dagger.internal.codegen.SourceFiles.generateBindingFieldsForDependencies;
-import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
-import static dagger.internal.codegen.SourceFiles.parameterizedGeneratedTypeNameForBinding;
-import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
-import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
-import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings;
-import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
-import static dagger.internal.codegen.javapoet.TypeNames.factoryOf;
-import static dagger.model.BindingKind.PROVISION;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PUBLIC;
-import static javax.lang.model.element.Modifier.STATIC;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.FieldSpec;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.ParameterSpec;
-import com.squareup.javapoet.TypeName;
-import com.squareup.javapoet.TypeSpec;
-import dagger.internal.Factory;
-import dagger.internal.Preconditions;
-import dagger.internal.codegen.InjectionMethods.InjectionSiteMethod;
-import dagger.internal.codegen.InjectionMethods.ProvisionMethod;
-import dagger.internal.codegen.javapoet.CodeBlocks;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.Key;
-import java.util.List;
-import java.util.Optional;
-import javax.annotation.processing.Filer;
-import javax.inject.Inject;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
-
-/**
- * Generates {@link Factory} implementations from {@link ProvisionBinding} instances for
- * {@link Inject} constructors.
- */
-final class FactoryGenerator extends SourceFileGenerator<ProvisionBinding> {
- private final DaggerTypes types;
- private final DaggerElements elements;
- private final CompilerOptions compilerOptions;
-
- @Inject
- FactoryGenerator(
- Filer filer,
- SourceVersion sourceVersion,
- DaggerTypes types,
- DaggerElements elements,
- CompilerOptions compilerOptions) {
- super(filer, elements, sourceVersion);
- this.types = types;
- this.elements = elements;
- this.compilerOptions = compilerOptions;
- }
-
- @Override
- ClassName nameGeneratedType(ProvisionBinding binding) {
- return generatedClassNameForBinding(binding);
- }
-
- @Override
- Element originatingElement(ProvisionBinding binding) {
- // we only create factories for bindings that have a binding element
- return binding.bindingElement().get();
- }
-
- @Override
- Optional<TypeSpec.Builder> write(ClassName generatedTypeName, ProvisionBinding binding) {
- // We don't want to write out resolved bindings -- we want to write out the generic version.
- checkArgument(!binding.unresolved().isPresent());
- checkArgument(binding.bindingElement().isPresent());
-
- return binding.factoryCreationStrategy().equals(DELEGATE)
- ? Optional.empty()
- : Optional.of(factoryBuilder(binding));
- }
-
- private TypeSpec.Builder factoryBuilder(ProvisionBinding binding) {
- TypeSpec.Builder factoryBuilder =
- classBuilder(nameGeneratedType(binding))
- .addModifiers(PUBLIC, FINAL)
- .addSuperinterface(factoryTypeName(binding))
- .addTypeVariables(bindingTypeElementTypeVariableNames(binding));
-
- addConstructorAndFields(binding, factoryBuilder);
- factoryBuilder.addMethod(getMethod(binding));
- addCreateMethod(binding, factoryBuilder);
-
- factoryBuilder.addMethod(
- ProvisionMethod.create(binding, compilerOptions, elements).toMethodSpec());
- gwtIncompatibleAnnotation(binding).ifPresent(factoryBuilder::addAnnotation);
-
- return factoryBuilder;
- }
-
- private void addConstructorAndFields(ProvisionBinding binding, TypeSpec.Builder factoryBuilder) {
- if (binding.factoryCreationStrategy().equals(SINGLETON_INSTANCE)) {
- return;
- }
- // TODO(user): Make the constructor private?
- MethodSpec.Builder constructor = constructorBuilder().addModifiers(PUBLIC);
- constructorParams(binding).forEach(
- param -> {
- constructor.addParameter(param).addStatement("this.$1N = $1N", param);
- factoryBuilder.addField(
- FieldSpec.builder(param.type, param.name, PRIVATE, FINAL).build());
- });
- factoryBuilder.addMethod(constructor.build());
- }
-
- private ImmutableList<ParameterSpec> constructorParams(ProvisionBinding binding) {
- ImmutableList.Builder<ParameterSpec> params = ImmutableList.builder();
- moduleParameter(binding).ifPresent(params::add);
- frameworkFields(binding).values().forEach(field -> params.add(toParameter(field)));
- return params.build();
- }
-
- private Optional<ParameterSpec> moduleParameter(ProvisionBinding binding) {
- if (binding.requiresModuleInstance()) {
- // TODO(user, dpb): Should this use contributingModule()?
- TypeName type = TypeName.get(binding.bindingTypeElement().get().asType());
- return Optional.of(ParameterSpec.builder(type, "module").build());
- }
- return Optional.empty();
- }
-
- private ImmutableMap<Key, FieldSpec> frameworkFields(ProvisionBinding binding) {
- UniqueNameSet uniqueFieldNames = new UniqueNameSet();
- // TODO(user, dpb): Add a test for the case when a Factory parameter is named "module".
- if (binding.requiresModuleInstance()) {
- uniqueFieldNames.claim("module");
- }
- return ImmutableMap.copyOf(
- transformValues(
- generateBindingFieldsForDependencies(binding),
- field ->
- FieldSpec.builder(
- field.type(), uniqueFieldNames.getUniqueName(field.name()), PRIVATE, FINAL)
- .build()));
- }
-
- private void addCreateMethod(ProvisionBinding binding, TypeSpec.Builder factoryBuilder) {
- // If constructing a factory for @Inject or @Provides bindings, we use a static create method
- // so that generated components can avoid having to refer to the generic types
- // of the factory. (Otherwise they may have visibility problems referring to the types.)
- MethodSpec.Builder createMethodBuilder =
- methodBuilder("create")
- .addModifiers(PUBLIC, STATIC)
- .returns(parameterizedGeneratedTypeNameForBinding(binding))
- .addTypeVariables(bindingTypeElementTypeVariableNames(binding));
-
- switch (binding.factoryCreationStrategy()) {
- case SINGLETON_INSTANCE:
- FieldSpec.Builder instanceFieldBuilder =
- FieldSpec.builder(nameGeneratedType(binding), "INSTANCE", PRIVATE, STATIC, FINAL)
- .initializer("new $T()", nameGeneratedType(binding));
-
- if (!bindingTypeElementTypeVariableNames(binding).isEmpty()) {
- // If the factory has type parameters, ignore them in the field declaration & initializer
- instanceFieldBuilder.addAnnotation(suppressWarnings(RAWTYPES));
-
- createMethodBuilder.addAnnotation(suppressWarnings(UNCHECKED));
- }
- createMethodBuilder.addStatement("return INSTANCE");
- factoryBuilder.addField(instanceFieldBuilder.build());
- break;
- case CLASS_CONSTRUCTOR:
- List<ParameterSpec> params = constructorParams(binding);
- createMethodBuilder.addParameters(params);
- createMethodBuilder.addStatement(
- "return new $T($L)",
- parameterizedGeneratedTypeNameForBinding(binding),
- makeParametersCodeBlock(Lists.transform(params, input -> CodeBlock.of("$N", input))));
- break;
- default:
- throw new AssertionError();
- }
- factoryBuilder.addMethod(createMethodBuilder.build());
- }
-
- private MethodSpec getMethod(ProvisionBinding binding) {
- TypeName providedTypeName = providedTypeName(binding);
- MethodSpec.Builder getMethod =
- methodBuilder("get")
- .addAnnotation(Override.class)
- .addModifiers(PUBLIC)
- .returns(providedTypeName);
-
- ImmutableMap<Key, FieldSpec> frameworkFields = frameworkFields(binding);
- CodeBlock parametersCodeBlock =
- makeParametersCodeBlock(
- frameworkFieldUsages(binding.provisionDependencies(), frameworkFields).values());
-
- if (binding.kind().equals(PROVISION)) {
- binding
- .nullableType()
- .ifPresent(nullableType -> CodeBlocks.addAnnotation(getMethod, nullableType));
- getMethod.addStatement(
- "return $L",
- ProvisionMethod.invoke(
- binding,
- request ->
- frameworkTypeUsageStatement(
- CodeBlock.of("$N", frameworkFields.get(request.key())), request.kind()),
- nameGeneratedType(binding),
- binding.requiresModuleInstance()
- ? Optional.of(CodeBlock.of("module"))
- : Optional.empty(),
- compilerOptions,
- elements));
- } else if (!binding.injectionSites().isEmpty()) {
- CodeBlock instance = CodeBlock.of("instance");
- getMethod
- .addStatement("$1T $2L = new $1T($3L)", providedTypeName, instance, parametersCodeBlock)
- .addCode(
- InjectionSiteMethod.invokeAll(
- binding.injectionSites(),
- nameGeneratedType(binding),
- instance,
- binding.key().type(),
- types,
- frameworkFieldUsages(binding.dependencies(), frameworkFields)::get,
- elements))
- .addStatement("return $L", instance);
- } else {
- getMethod.addStatement(
- "return new $T($L)", providedTypeName, parametersCodeBlock);
- }
- return getMethod.build();
- }
-
- private static TypeName providedTypeName(ProvisionBinding binding) {
- return TypeName.get(binding.contributedType());
- }
-
- private static TypeName factoryTypeName(ProvisionBinding binding) {
- return factoryOf(providedTypeName(binding));
- }
-
- private static ParameterSpec toParameter(FieldSpec field) {
- return ParameterSpec.builder(field.type, field.name).build();
- }
-
- /**
- * Returns {@code Preconditions.checkNotNull(providesMethodInvocation)} with a message suitable
- * for {@code @Provides} methods.
- */
- static CodeBlock checkNotNullProvidesMethod(CodeBlock providesMethodInvocation) {
- return CodeBlock.of(
- "$T.checkNotNull($L, $S)",
- Preconditions.class,
- providesMethodInvocation,
- "Cannot return null from a non-@Nullable @Provides method");
- }
-}
diff --git a/java/dagger/internal/codegen/FeatureStatus.java b/java/dagger/internal/codegen/FeatureStatus.java
deleted file mode 100644
index 9ff254e2c..000000000
--- a/java/dagger/internal/codegen/FeatureStatus.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-/** Allows options to control how features in component processing are enabled. */
-enum FeatureStatus {
- ENABLED,
- DISABLED;
-}
diff --git a/java/dagger/internal/codegen/Formatter.java b/java/dagger/internal/codegen/Formatter.java
deleted file mode 100644
index 53d4f9ade..000000000
--- a/java/dagger/internal/codegen/Formatter.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkElementIndex;
-
-import com.google.common.base.Function;
-import com.google.common.collect.Iterables;
-
-/**
- * A formatter which transforms an instance of a particular type into a string
- * representation.
- *
- * @param <T> the type of the object to be transformed.
- */
-abstract class Formatter<T> implements Function<T, String> {
-
- static final String INDENT = " ";
- static final String DOUBLE_INDENT = INDENT + INDENT;
- private static final int LIST_LIMIT = 10;
-
- /**
- * Performs the transformation of an object into a string representation.
- */
- public abstract String format(T object);
-
- /**
- * Performs the transformation of an object into a string representation in conformity with the
- * {@link Function}{@code <T, String>} contract, delegating to {@link #format(Object)}.
- *
- * @deprecated Call {@link #format(Object)} instead. This method exists to make formatters easy to
- * use when functions are required, but shouldn't be called directly.
- */
- @SuppressWarnings("javadoc")
- @Deprecated
- @Override
- public final String apply(T object) {
- return format(object);
- }
-
- /** Formats {@code items}, one per line. Stops after {@value #LIST_LIMIT} items. */
- public void formatIndentedList(
- StringBuilder builder, Iterable<? extends T> items, int indentLevel) {
- for (T item : Iterables.limit(items, LIST_LIMIT)) {
- String formatted = format(item);
- if (formatted.isEmpty()) {
- continue;
- }
- builder.append('\n');
- appendIndent(builder, indentLevel);
- builder.append(formatted);
- }
- int numberOfOtherItems = Iterables.size(items) - LIST_LIMIT;
- if (numberOfOtherItems > 0) {
- builder.append('\n');
- appendIndent(builder, indentLevel);
- builder.append("and ").append(numberOfOtherItems).append(" other");
- }
- if (numberOfOtherItems > 1) {
- builder.append('s');
- }
- }
-
- private void appendIndent(StringBuilder builder, int indentLevel) {
- for (int i = 0; i < indentLevel; i++) {
- builder.append(INDENT);
- }
- }
-
- static String formatArgumentInList(int index, int size, CharSequence name) {
- checkElementIndex(index, size);
- StringBuilder builder = new StringBuilder();
- if (index > 0) {
- builder.append("…, ");
- }
- builder.append(name);
- if (index < size - 1) {
- builder.append(", …");
- }
- return builder.toString();
- }
-}
diff --git a/java/dagger/internal/codegen/ForwardingCompilerOptions.java b/java/dagger/internal/codegen/ForwardingCompilerOptions.java
deleted file mode 100644
index 4a1deda59..000000000
--- a/java/dagger/internal/codegen/ForwardingCompilerOptions.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2019 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import javax.lang.model.element.TypeElement;
-import javax.tools.Diagnostic;
-
-/** A {@link CompilerOptions} object that delegates to another one. */
-class ForwardingCompilerOptions extends CompilerOptions {
-
- private final CompilerOptions delegate;
-
- ForwardingCompilerOptions(CompilerOptions delegate) {
- this.delegate = checkNotNull(delegate);
- }
-
- @Override
- boolean usesProducers() {
- return delegate.usesProducers();
- }
-
- @Override
- boolean fastInit() {
- return delegate.fastInit();
- }
-
- @Override
- boolean formatGeneratedSource() {
- return delegate.formatGeneratedSource();
- }
-
- @Override
- boolean writeProducerNameInToken() {
- return delegate.writeProducerNameInToken();
- }
-
- @Override
- Diagnostic.Kind nullableValidationKind() {
- return delegate.nullableValidationKind();
- }
-
- @Override
- Diagnostic.Kind privateMemberValidationKind() {
- return delegate.privateMemberValidationKind();
- }
-
- @Override
- Diagnostic.Kind staticMemberValidationKind() {
- return delegate.staticMemberValidationKind();
- }
-
- @Override
- boolean ignorePrivateAndStaticInjectionForComponent() {
- return delegate.ignorePrivateAndStaticInjectionForComponent();
- }
-
- @Override
- ValidationType scopeCycleValidationType() {
- return delegate.scopeCycleValidationType();
- }
-
- @Override
- boolean warnIfInjectionFactoryNotGeneratedUpstream() {
- return delegate.warnIfInjectionFactoryNotGeneratedUpstream();
- }
-
- @Override
- boolean headerCompilation() {
- return delegate.headerCompilation();
- }
-
- @Override
- boolean aheadOfTimeSubcomponents() {
- return delegate.aheadOfTimeSubcomponents();
- }
-
- @Override
- boolean forceUseSerializedComponentImplementations() {
- return delegate.forceUseSerializedComponentImplementations();
- }
-
- @Override
- boolean emitModifiableMetadataAnnotations() {
- return delegate.emitModifiableMetadataAnnotations();
- }
-
- @Override
- boolean useGradleIncrementalProcessing() {
- return delegate.useGradleIncrementalProcessing();
- }
-
- @Override
- ValidationType fullBindingGraphValidationType(TypeElement element) {
- return delegate.fullBindingGraphValidationType(element);
- }
-
- @Override
- Diagnostic.Kind moduleHasDifferentScopesDiagnosticKind() {
- return delegate.moduleHasDifferentScopesDiagnosticKind();
- }
-
- @Override
- ValidationType explicitBindingConflictsWithInjectValidationType() {
- return delegate.explicitBindingConflictsWithInjectValidationType();
- }
-}
diff --git a/java/dagger/internal/codegen/FrameworkDependency.java b/java/dagger/internal/codegen/FrameworkDependency.java
deleted file mode 100644
index feea7a0a6..000000000
--- a/java/dagger/internal/codegen/FrameworkDependency.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import com.google.auto.value.AutoValue;
-import dagger.model.Key;
-
-/**
- * The framework class and binding key for a resolved dependency of a binding. If a binding has
- * several dependencies for a key, then only one instance of this class will represent them all.
- *
- * <p>In the following example, the binding {@code provideFoo()} has two dependency requests:
- *
- * <ol>
- * <li>{@code Bar bar}
- * <li>{@code Provider<Bar> barProvider}
- * </ol>
- *
- * But they both can be satisfied with the same instance of {@code Provider<Bar>}. So one instance
- * of {@code FrameworkDependency} will be used for both. Its {@link #key()} will be for {@code Bar},
- * and its {@link #frameworkType()} will be {@link FrameworkType#PROVIDER}.
- *
- * <pre><code>
- * {@literal @Provides} static Foo provideFoo(Bar bar, {@literal Provider<Bar>} barProvider) {
- * return new Foo(…);
- * }
- * </code></pre>
- */
-@AutoValue
-abstract class FrameworkDependency {
-
- /** The fully-resolved key shared by all the dependency requests. */
- abstract Key key();
-
- /** The type of the framework dependency. */
- abstract FrameworkType frameworkType();
-
- /** The framework class to use for this dependency. */
- final Class<?> frameworkClass() {
- return frameworkType().frameworkClass();
- }
-
- /** Returns a new instance with the given key and type. */
- static FrameworkDependency create(Key key, FrameworkType frameworkType) {
- return new AutoValue_FrameworkDependency(key, frameworkType);
- }
-}
diff --git a/java/dagger/internal/codegen/FrameworkField.java b/java/dagger/internal/codegen/FrameworkField.java
deleted file mode 100644
index de2ada047..000000000
--- a/java/dagger/internal/codegen/FrameworkField.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.model.BindingKind.MEMBERS_INJECTOR;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.base.CaseFormat;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.ParameterizedTypeName;
-import com.squareup.javapoet.TypeName;
-import java.util.Optional;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementVisitor;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.ElementKindVisitor8;
-
-/**
- * A value object that represents a field in the generated Component class.
- *
- * <p>Examples:
- * <ul>
- * <li>{@code Provider<String>}
- * <li>{@code Producer<Widget>}
- * <li>{@code Provider<Map<SomeMapKey, MapValue>>}.
- * </ul>
- */
-@AutoValue
-abstract class FrameworkField {
-
- /**
- * Creates a framework field.
- *
- * @param frameworkClassName the name of the framework class (e.g., {@link javax.inject.Provider})
- * @param valueTypeName the name of the type parameter of the framework class (e.g., {@code Foo}
- * for {@code Provider<Foo>}
- * @param fieldName the name of the field
- */
- static FrameworkField create(
- ClassName frameworkClassName, TypeName valueTypeName, String fieldName) {
- String suffix = frameworkClassName.simpleName();
- return new AutoValue_FrameworkField(
- ParameterizedTypeName.get(frameworkClassName, valueTypeName),
- fieldName.endsWith(suffix) ? fieldName : fieldName + suffix);
- }
-
- /**
- * A framework field for a {@link ResolvedBindings}.
- *
- * @param frameworkClass if present, the field will use this framework class instead of the normal
- * one for the bindings
- */
- static FrameworkField forResolvedBindings(
- ResolvedBindings resolvedBindings, Optional<ClassName> frameworkClass) {
- return create(
- frameworkClass.orElse(
- ClassName.get(
- FrameworkType.forBindingType(resolvedBindings.bindingType()).frameworkClass())),
- TypeName.get(fieldValueType(resolvedBindings)),
- frameworkFieldName(resolvedBindings));
- }
-
- private static TypeMirror fieldValueType(ResolvedBindings resolvedBindings) {
- return resolvedBindings.isMultibindingContribution()
- ? resolvedBindings.contributionBinding().contributedType()
- : resolvedBindings.key().type();
- }
-
- private static String frameworkFieldName(ResolvedBindings resolvedBindings) {
- if (!resolvedBindings.contributionBindings().isEmpty()) {
- ContributionBinding binding = resolvedBindings.contributionBinding();
- if (binding.bindingElement().isPresent()) {
- String name = BINDING_ELEMENT_NAME.visit(binding.bindingElement().get(), binding);
- return binding.kind().equals(MEMBERS_INJECTOR)
- ? name + "MembersInjector"
- : name;
- }
- }
- return KeyVariableNamer.name(resolvedBindings.key());
- }
-
- private static final ElementVisitor<String, Binding> BINDING_ELEMENT_NAME =
- new ElementKindVisitor8<String, Binding>() {
-
- @Override
- protected String defaultAction(Element e, Binding p) {
- throw new IllegalArgumentException("Unexpected binding " + p);
- }
-
- @Override
- public String visitExecutableAsConstructor(ExecutableElement e, Binding p) {
- return visit(e.getEnclosingElement(), p);
- }
-
- @Override
- public String visitExecutableAsMethod(ExecutableElement e, Binding p) {
- return e.getSimpleName().toString();
- }
-
- @Override
- public String visitType(TypeElement e, Binding p) {
- return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, e.getSimpleName().toString());
- }
-
- @Override
- public String visitVariableAsParameter(VariableElement e, Binding p) {
- return e.getSimpleName().toString();
- }
- };
-
- abstract ParameterizedTypeName type();
- abstract String name();
-}
diff --git a/java/dagger/internal/codegen/FrameworkFieldInitializer.java b/java/dagger/internal/codegen/FrameworkFieldInitializer.java
deleted file mode 100644
index a3de08353..000000000
--- a/java/dagger/internal/codegen/FrameworkFieldInitializer.java
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static dagger.internal.codegen.ComponentImplementation.FieldSpecKind.FRAMEWORK_FIELD;
-import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
-import static javax.lang.model.element.Modifier.PRIVATE;
-
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.FieldSpec;
-import com.squareup.javapoet.TypeName;
-import dagger.internal.DelegateFactory;
-import dagger.internal.codegen.javapoet.AnnotationSpecs;
-import dagger.internal.codegen.javapoet.TypeNames;
-import dagger.producers.internal.DelegateProducer;
-import java.util.Optional;
-
-/**
- * An object that can initialize a framework-type component field for a binding. An instance should
- * be created for each field.
- */
-class FrameworkFieldInitializer implements FrameworkInstanceSupplier {
-
- /**
- * An object that can determine the expression to use to assign to the component field for a
- * binding.
- */
- interface FrameworkInstanceCreationExpression {
- /** Returns the expression to use to assign to the component field for the binding. */
- CodeBlock creationExpression();
-
- /**
- * Returns the framework class to use for the field, if different from the one implied by the
- * binding. This implementation returns {@link Optional#empty()}.
- */
- default Optional<ClassName> alternativeFrameworkClass() {
- return Optional.empty();
- }
-
- /**
- * Returns {@code true} if instead of using {@link #creationExpression()} to create a framework
- * instance, a case in {@link InnerSwitchingProviders} should be created for this binding.
- */
- // TODO(ronshapiro): perhaps this isn't the right approach. Instead of saying "Use
- // SetFactory.EMPTY because you will only get 1 class for all types of bindings that use
- // SetFactory", maybe we should still use an inner switching provider but the same switching
- // provider index for all cases.
- default boolean useInnerSwitchingProvider() {
- return true;
- }
- }
-
- private final ComponentImplementation componentImplementation;
- private final ResolvedBindings resolvedBindings;
- private final FrameworkInstanceCreationExpression frameworkInstanceCreationExpression;
- private FieldSpec fieldSpec;
- private InitializationState fieldInitializationState = InitializationState.UNINITIALIZED;
-
- FrameworkFieldInitializer(
- ComponentImplementation componentImplementation,
- ResolvedBindings resolvedBindings,
- FrameworkInstanceCreationExpression frameworkInstanceCreationExpression) {
- this.componentImplementation = checkNotNull(componentImplementation);
- this.resolvedBindings = checkNotNull(resolvedBindings);
- this.frameworkInstanceCreationExpression = checkNotNull(frameworkInstanceCreationExpression);
- }
-
- /**
- * Returns the {@link MemberSelect} for the framework field, and adds the field and its
- * initialization code to the component if it's needed and not already added.
- */
- @Override
- public final MemberSelect memberSelect() {
- initializeField();
- return MemberSelect.localField(componentImplementation.name(), checkNotNull(fieldSpec).name);
- }
-
- /** Adds the field and its initialization code to the component. */
- private void initializeField() {
- switch (fieldInitializationState) {
- case UNINITIALIZED:
- // Change our state in case we are recursively invoked via initializeBindingExpression
- fieldInitializationState = InitializationState.INITIALIZING;
- CodeBlock.Builder codeBuilder = CodeBlock.builder();
- CodeBlock fieldInitialization = frameworkInstanceCreationExpression.creationExpression();
- CodeBlock initCode = CodeBlock.of("this.$N = $L;", getOrCreateField(), fieldInitialization);
-
- if (isReplacingSuperclassFrameworkInstance()
- || fieldInitializationState == InitializationState.DELEGATED) {
- codeBuilder.add(
- "$T.setDelegate($N, $L);", delegateType(), fieldSpec, fieldInitialization);
- } else {
- codeBuilder.add(initCode);
- }
- componentImplementation.addInitialization(codeBuilder.build());
-
- fieldInitializationState = InitializationState.INITIALIZED;
- break;
-
- case INITIALIZING:
- // We were recursively invoked, so create a delegate factory instead
- fieldInitializationState = InitializationState.DELEGATED;
- componentImplementation.addInitialization(
- CodeBlock.of("this.$N = new $T<>();", getOrCreateField(), delegateType()));
- break;
-
- case DELEGATED:
- case INITIALIZED:
- break;
- }
- }
-
- /**
- * Adds a field representing the resolved bindings, optionally forcing it to use a particular
- * binding type (instead of the type the resolved bindings would typically use).
- */
- private FieldSpec getOrCreateField() {
- if (fieldSpec != null) {
- return fieldSpec;
- }
- boolean useRawType = !componentImplementation.isTypeAccessible(resolvedBindings.key().type());
- FrameworkField contributionBindingField =
- FrameworkField.forResolvedBindings(
- resolvedBindings, frameworkInstanceCreationExpression.alternativeFrameworkClass());
-
- TypeName fieldType =
- useRawType ? contributionBindingField.type().rawType : contributionBindingField.type();
-
- FieldSpec.Builder contributionField =
- FieldSpec.builder(
- fieldType, componentImplementation.getUniqueFieldName(contributionBindingField.name()));
- contributionField.addModifiers(PRIVATE);
- if (useRawType) {
- contributionField.addAnnotation(AnnotationSpecs.suppressWarnings(RAWTYPES));
- }
-
- if (isReplacingSuperclassFrameworkInstance()) {
- // If a binding is modified in a subclass, the framework instance will be replaced in the
- // subclass implementation. The superclass framework instance initialization will run first,
- // however, and may refer to the modifiable binding method returning this type's modified
- // framework instance before it is initialized, so we use a delegate factory as a placeholder
- // until it has properly been initialized.
- contributionField.initializer("new $T<>()", delegateType());
- }
-
- fieldSpec = contributionField.build();
- componentImplementation.addField(FRAMEWORK_FIELD, fieldSpec);
-
- return fieldSpec;
- }
-
- /**
- * Returns true if this framework field is replacing a superclass's implementation of the
- * framework field.
- */
- private boolean isReplacingSuperclassFrameworkInstance() {
- return componentImplementation
- .superclassImplementation()
- .flatMap(
- superclassImplementation ->
- // TODO(b/117833324): can we constrain this further?
- superclassImplementation.getModifiableBindingMethod(
- BindingRequest.bindingRequest(
- resolvedBindings.key(),
- isProvider() ? FrameworkType.PROVIDER : FrameworkType.PRODUCER_NODE)))
- .isPresent();
- }
-
- private Class<?> delegateType() {
- return isProvider() ? DelegateFactory.class : DelegateProducer.class;
- }
-
- private boolean isProvider() {
- return resolvedBindings.bindingType().equals(BindingType.PROVISION)
- && frameworkInstanceCreationExpression
- .alternativeFrameworkClass()
- .map(TypeNames.PROVIDER::equals)
- .orElse(true);
- }
-
- /** Initialization state for a factory field. */
- private enum InitializationState {
- /** The field is {@code null}. */
- UNINITIALIZED,
-
- /**
- * The field's dependencies are being set up. If the field is needed in this state, use a {@link
- * DelegateFactory}.
- */
- INITIALIZING,
-
- /**
- * The field's dependencies are being set up, but the field can be used because it has already
- * been set to a {@link DelegateFactory}.
- */
- DELEGATED,
-
- /** The field is set to an undelegated factory. */
- INITIALIZED;
- }
-}
diff --git a/java/dagger/internal/codegen/FrameworkInstanceBindingExpression.java b/java/dagger/internal/codegen/FrameworkInstanceBindingExpression.java
deleted file mode 100644
index 6f62d66a7..000000000
--- a/java/dagger/internal/codegen/FrameworkInstanceBindingExpression.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
-
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.FieldSpec;
-import dagger.internal.codegen.javapoet.Expression;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-
-/** A binding expression that uses a {@link FrameworkType} field. */
-abstract class FrameworkInstanceBindingExpression extends BindingExpression {
- private final ResolvedBindings resolvedBindings;
- private final FrameworkInstanceSupplier frameworkInstanceSupplier;
- private final DaggerTypes types;
- private final DaggerElements elements;
-
- FrameworkInstanceBindingExpression(
- ResolvedBindings resolvedBindings,
- FrameworkInstanceSupplier frameworkInstanceSupplier,
- DaggerTypes types,
- DaggerElements elements) {
- this.resolvedBindings = checkNotNull(resolvedBindings);
- this.frameworkInstanceSupplier = checkNotNull(frameworkInstanceSupplier);
- this.types = checkNotNull(types);
- this.elements = checkNotNull(elements);
- }
-
- /**
- * The expression for the framework instance for this binding. The field will be {@link
- * ComponentImplementation#addInitialization(CodeBlock) initialized} and {@link
- * ComponentImplementation#addField(ComponentImplementation.FieldSpecKind, FieldSpec) added} to
- * the component the first time this method is invoked.
- */
- @Override
- Expression getDependencyExpression(ClassName requestingClass) {
- MemberSelect memberSelect = frameworkInstanceSupplier.memberSelect();
- TypeMirror contributedType = resolvedBindings.contributionBinding().contributedType();
- TypeMirror expressionType =
- isTypeAccessibleFrom(contributedType, requestingClass.packageName())
- || isInlinedFactoryCreation(memberSelect)
- ? types.wrapType(contributedType, frameworkType().frameworkClass())
- : rawFrameworkType();
- return Expression.create(expressionType, memberSelect.getExpressionFor(requestingClass));
- }
-
- /** Returns the framework type for the binding. */
- protected abstract FrameworkType frameworkType();
-
- /**
- * Returns {@code true} if a factory is created inline each time it is requested. For example, in
- * the initialization {@code this.fooProvider = Foo_Factory.create(Bar_Factory.create());}, {@code
- * Bar_Factory} is considered to be inline.
- *
- * <p>This is used in {@link #getDependencyExpression(ClassName)} when determining the type of a
- * factory. Normally if the {@link ContributionBinding#contributedType()} is not accessible from
- * the component, the type of the expression will be a raw {@link javax.inject.Provider}. However,
- * if the factory is created inline, even if contributed type is not accessible, javac will still
- * be able to determine the type that is returned from the {@code Foo_Factory.create()} method.
- */
- private static boolean isInlinedFactoryCreation(MemberSelect memberSelect) {
- return memberSelect.staticMember();
- }
-
- private DeclaredType rawFrameworkType() {
- return types.getDeclaredType(elements.getTypeElement(frameworkType().frameworkClass()));
- }
-}
diff --git a/java/dagger/internal/codegen/FrameworkInstanceSupplier.java b/java/dagger/internal/codegen/FrameworkInstanceSupplier.java
deleted file mode 100644
index 4c45630a3..000000000
--- a/java/dagger/internal/codegen/FrameworkInstanceSupplier.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-/** An object that supplies a {@link MemberSelect} for a framework instance. */
-interface FrameworkInstanceSupplier {
- /** Returns a {@link MemberSelect}, with possible side effects on the first call. */
- MemberSelect memberSelect();
-}
diff --git a/java/dagger/internal/codegen/FrameworkType.java b/java/dagger/internal/codegen/FrameworkType.java
deleted file mode 100644
index f4e377972..000000000
--- a/java/dagger/internal/codegen/FrameworkType.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.CaseFormat.UPPER_CAMEL;
-import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
-import static dagger.model.RequestKind.INSTANCE;
-
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.ParameterizedTypeName;
-import com.squareup.javapoet.TypeName;
-import dagger.Lazy;
-import dagger.internal.DoubleCheck;
-import dagger.internal.ProviderOfLazy;
-import dagger.internal.codegen.javapoet.Expression;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.DependencyRequest;
-import dagger.model.RequestKind;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
-import dagger.producers.internal.Producers;
-import java.util.Optional;
-import javax.inject.Provider;
-import javax.lang.model.type.TypeMirror;
-
-/** One of the core types initialized as fields in a generated component. */
-enum FrameworkType {
- /** A {@link Provider}. */
- PROVIDER {
- @Override
- Class<?> frameworkClass() {
- return Provider.class;
- }
-
- @Override
- Optional<RequestKind> requestKind() {
- return Optional.of(RequestKind.PROVIDER);
- }
-
- @Override
- CodeBlock to(RequestKind requestKind, CodeBlock from) {
- switch (requestKind) {
- case INSTANCE:
- return CodeBlock.of("$L.get()", from);
-
- case LAZY:
- return CodeBlock.of("$T.lazy($L)", DoubleCheck.class, from);
-
- case PROVIDER:
- return from;
-
- case PROVIDER_OF_LAZY:
- return CodeBlock.of("$T.create($L)", ProviderOfLazy.class, from);
-
- case PRODUCER:
- return CodeBlock.of("$T.producerFromProvider($L)", Producers.class, from);
-
- case FUTURE:
- return CodeBlock.of("$T.immediateFuture($L)", Futures.class, to(INSTANCE, from));
-
- case PRODUCED:
- return CodeBlock.of("$T.successful($L)", Produced.class, to(INSTANCE, from));
-
- default:
- throw new IllegalArgumentException(
- String.format("Cannot request a %s from a %s", requestKind, this));
- }
- }
-
- @Override
- Expression to(RequestKind requestKind, Expression from, DaggerTypes types) {
- CodeBlock codeBlock = to(requestKind, from.codeBlock());
- switch (requestKind) {
- case INSTANCE:
- return Expression.create(types.unwrapTypeOrObject(from.type()), codeBlock);
-
- case PROVIDER:
- return from;
-
- case PROVIDER_OF_LAZY:
- TypeMirror lazyType = types.rewrapType(from.type(), Lazy.class);
- return Expression.create(types.wrapType(lazyType, Provider.class), codeBlock);
-
- case FUTURE:
- return Expression.create(
- types.rewrapType(from.type(), ListenableFuture.class), codeBlock);
-
- default:
- return Expression.create(
- types.rewrapType(from.type(), RequestKinds.frameworkClass(requestKind)), codeBlock);
- }
- }
- },
-
- /** A {@link Producer}. */
- PRODUCER_NODE {
- @Override
- Class<?> frameworkClass() {
- // TODO(cgdecker): Replace this with new class for representing internal producer nodes.
- // Currently the new class is CancellableProducer, but it may be changed to ProducerNode and
- // made to not implement Producer.
- return Producer.class;
- }
-
- @Override
- Optional<RequestKind> requestKind() {
- return Optional.empty();
- }
-
- @Override
- CodeBlock to(RequestKind requestKind, CodeBlock from) {
- switch (requestKind) {
- case FUTURE:
- return CodeBlock.of("$L.get()", from);
-
- case PRODUCER:
- return from;
-
- default:
- throw new IllegalArgumentException(
- String.format("Cannot request a %s from a %s", requestKind, this));
- }
- }
-
- @Override
- Expression to(RequestKind requestKind, Expression from, DaggerTypes types) {
- switch (requestKind) {
- case FUTURE:
- return Expression.create(
- types.rewrapType(from.type(), ListenableFuture.class),
- to(requestKind, from.codeBlock()));
-
- case PRODUCER:
- return Expression.create(from.type(), to(requestKind, from.codeBlock()));
-
- default:
- throw new IllegalArgumentException(
- String.format("Cannot request a %s from a %s", requestKind, this));
- }
- }
- },
- ;
-
- /** Returns the framework type appropriate for fields for a given binding type. */
- static FrameworkType forBindingType(BindingType bindingType) {
- switch (bindingType) {
- case PROVISION:
- return PROVIDER;
- case PRODUCTION:
- return PRODUCER_NODE;
- case MEMBERS_INJECTION:
- }
- throw new AssertionError(bindingType);
- }
-
- /** Returns the framework type that exactly matches the given request kind, if one exists. */
- static Optional<FrameworkType> forRequestKind(RequestKind requestKind) {
- switch (requestKind) {
- case PROVIDER:
- return Optional.of(FrameworkType.PROVIDER);
- default:
- return Optional.empty();
- }
- }
-
- /** The class of fields of this type. */
- abstract Class<?> frameworkClass();
-
- /** Returns the {@link #frameworkClass()} parameterized with a type. */
- ParameterizedTypeName frameworkClassOf(TypeName valueType) {
- return ParameterizedTypeName.get(ClassName.get(frameworkClass()), valueType);
- }
-
- /** The request kind that an instance of this framework type can satisfy directly, if any. */
- abstract Optional<RequestKind> requestKind();
-
- /**
- * Returns a {@link CodeBlock} that evaluates to a requested object given an expression that
- * evaluates to an instance of this framework type.
- *
- * @param requestKind the kind of {@link DependencyRequest} that the returned expression can
- * satisfy
- * @param from a {@link CodeBlock} that evaluates to an instance of this framework type
- * @throws IllegalArgumentException if a valid expression cannot be generated for {@code
- * requestKind}
- */
- abstract CodeBlock to(RequestKind requestKind, CodeBlock from);
-
- /**
- * Returns an {@link Expression} that evaluates to a requested object given an expression that
- * evaluates to an instance of this framework type.
- *
- * @param requestKind the kind of {@link DependencyRequest} that the returned expression can
- * satisfy
- * @param from an expression that evaluates to an instance of this framework type
- * @throws IllegalArgumentException if a valid expression cannot be generated for {@code
- * requestKind}
- */
- abstract Expression to(RequestKind requestKind, Expression from, DaggerTypes types);
-
- @Override
- public String toString() {
- return UPPER_UNDERSCORE.to(UPPER_CAMEL, super.toString());
- }
-}
diff --git a/java/dagger/internal/codegen/FrameworkTypeMapper.java b/java/dagger/internal/codegen/FrameworkTypeMapper.java
deleted file mode 100644
index 774669253..000000000
--- a/java/dagger/internal/codegen/FrameworkTypeMapper.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.BindingType.PRODUCTION;
-import static java.util.stream.Collectors.toSet;
-
-import dagger.model.DependencyRequest;
-import dagger.model.Key;
-import dagger.model.RequestKind;
-import dagger.producers.Producer;
-import java.util.Set;
-import javax.inject.Provider;
-
-/**
- * A mapper for associating a {@link RequestKind} to a {@link FrameworkType}, dependent on the type
- * of code to be generated (e.g., for {@link Provider} or {@link Producer}).
- */
-enum FrameworkTypeMapper {
- FOR_PROVIDER() {
- @Override
- public FrameworkType getFrameworkType(RequestKind requestKind) {
- switch (requestKind) {
- case INSTANCE:
- case PROVIDER:
- case PROVIDER_OF_LAZY:
- case LAZY:
- return FrameworkType.PROVIDER;
- case PRODUCED:
- case PRODUCER:
- throw new IllegalArgumentException(requestKind.toString());
- default:
- throw new AssertionError(requestKind);
- }
- }
- },
- FOR_PRODUCER() {
- @Override
- public FrameworkType getFrameworkType(RequestKind requestKind) {
- switch (requestKind) {
- case INSTANCE:
- case PRODUCED:
- case PRODUCER:
- return FrameworkType.PRODUCER_NODE;
- case PROVIDER:
- case PROVIDER_OF_LAZY:
- case LAZY:
- return FrameworkType.PROVIDER;
- default:
- throw new AssertionError(requestKind);
- }
- }
- };
-
- static FrameworkTypeMapper forBindingType(BindingType bindingType) {
- return bindingType.equals(PRODUCTION) ? FOR_PRODUCER : FOR_PROVIDER;
- }
-
- abstract FrameworkType getFrameworkType(RequestKind requestKind);
-
- /**
- * Returns the {@link FrameworkType} to use for a collection of requests of the same {@link Key}.
- * This allows factories to only take a single argument for multiple requests of the same key.
- */
- FrameworkType getFrameworkType(Set<DependencyRequest> requests) {
- Set<FrameworkType> frameworkTypes =
- requests.stream().map(request -> getFrameworkType(request.kind())).collect(toSet());
- return frameworkTypes.size() == 1 ? getOnlyElement(frameworkTypes) : FrameworkType.PROVIDER;
- }
-}
diff --git a/java/dagger/internal/codegen/FrameworkTypes.java b/java/dagger/internal/codegen/FrameworkTypes.java
deleted file mode 100644
index 19d2eda2d..000000000
--- a/java/dagger/internal/codegen/FrameworkTypes.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreTypes.isType;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableSet;
-import dagger.Lazy;
-import dagger.MembersInjector;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
-import java.util.Set;
-import javax.inject.Provider;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * A collection of utility methods for dealing with Dagger framework types. A framework type is any
- * type that the framework itself defines.
- */
-final class FrameworkTypes {
- private static final ImmutableSet<Class<?>> PROVISION_TYPES =
- ImmutableSet.of(Provider.class, Lazy.class, MembersInjector.class);
-
- // NOTE(beder): ListenableFuture is not considered a producer framework type because it is not
- // defined by the framework, so we can't treat it specially in ordinary Dagger.
- private static final ImmutableSet<Class<?>> PRODUCTION_TYPES =
- ImmutableSet.of(Produced.class, Producer.class);
-
- /** Returns true if the type represents a producer-related framework type. */
- static boolean isProducerType(TypeMirror type) {
- return isType(type) && typeIsOneOf(PRODUCTION_TYPES, type);
- }
-
- /** Returns true if the type represents a framework type. */
- static boolean isFrameworkType(TypeMirror type) {
- return isType(type)
- && (typeIsOneOf(PROVISION_TYPES, type)
- || typeIsOneOf(PRODUCTION_TYPES, type));
- }
-
- private static boolean typeIsOneOf(Set<Class<?>> classes, TypeMirror type) {
- for (Class<?> clazz : classes) {
- if (MoreTypes.isTypeOf(clazz, type)) {
- return true;
- }
- }
- return false;
- }
-
- private FrameworkTypes() {}
-}
diff --git a/java/dagger/internal/codegen/GenerationCompilerOptions.java b/java/dagger/internal/codegen/GenerationCompilerOptions.java
deleted file mode 100644
index 1b14a7eb7..000000000
--- a/java/dagger/internal/codegen/GenerationCompilerOptions.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import java.lang.annotation.Retention;
-import javax.inject.Qualifier;
-
-/**
- * A {@link Qualifier} for bindings associated with the serialization/deserialization of {@link
- * dagger.internal.GenerationOptions}.
- */
-@Retention(RUNTIME)
-@Qualifier
-@interface GenerationCompilerOptions {}
diff --git a/java/dagger/internal/codegen/GenerationOptionsModule.java b/java/dagger/internal/codegen/GenerationOptionsModule.java
deleted file mode 100644
index aa3c46191..000000000
--- a/java/dagger/internal/codegen/GenerationOptionsModule.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import dagger.Module;
-import dagger.Provides;
-import dagger.internal.GenerationOptions;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import java.util.Optional;
-
-/** Adds bindings for serializing and rereading {@link GenerationOptions}. */
-@Module
-interface GenerationOptionsModule {
- @Provides
- @PerComponentImplementation
- @GenerationCompilerOptions
- static CompilerOptions generationOptions(
- CompilerOptions defaultOptions,
- ComponentImplementation componentImplementation,
- DaggerElements elements) {
- // Avoid looking up types that don't exist. Performance improves for large components.
- if (!defaultOptions.aheadOfTimeSubcomponents()) {
- return defaultOptions;
- }
- // Inspect the base implementation for the @GenerationOptions annotation. Even if
- // componentImplementation is the base implementation, inspect it for the case where we are
- // recomputing the ComponentImplementation from a previous compilation.
- // TODO(b/117833324): consider adding a method that returns baseImplementation.orElse(this).
- // The current state of the world is a little confusing and maybe not intuitive: the base
- // implementation has no base implementation, but it _is_ a base implementation.
- return Optional.of(componentImplementation.baseImplementation().orElse(componentImplementation))
- .map(baseImplementation -> elements.getTypeElement(baseImplementation.name()))
- // If this returns null, the type has not been generated yet and Optional will switch to an
- // empty state. This means that we're currently generating componentImplementation, or that
- // the base implementation is being generated in this round, and thus the options passed to
- // this compilation are applicable
- .map(typeElement -> typeElement.getAnnotation(GenerationOptions.class))
- .map(defaultOptions::withGenerationOptions)
- .orElse(defaultOptions);
- }
-}
diff --git a/java/dagger/internal/codegen/GwtCompatibility.java b/java/dagger/internal/codegen/GwtCompatibility.java
deleted file mode 100644
index 34d8f6d69..000000000
--- a/java/dagger/internal/codegen/GwtCompatibility.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-import com.squareup.javapoet.AnnotationSpec;
-import java.util.Optional;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.Name;
-
-final class GwtCompatibility {
-
- /**
- * Returns a {@code @GwtIncompatible} annotation that is applied to {@code binding}'s {@link
- * Binding#bindingElement()} or any enclosing type.
- */
- static Optional<AnnotationSpec> gwtIncompatibleAnnotation(Binding binding) {
- checkArgument(binding.bindingElement().isPresent());
- Element element = binding.bindingElement().get();
- while (element != null) {
- Optional<AnnotationSpec> gwtIncompatible =
- element
- .getAnnotationMirrors()
- .stream()
- .filter(annotation -> isGwtIncompatible(annotation))
- .map(AnnotationSpec::get)
- .findFirst();
- if (gwtIncompatible.isPresent()) {
- return gwtIncompatible;
- }
- element = element.getEnclosingElement();
- }
- return Optional.empty();
- }
-
- private static boolean isGwtIncompatible(AnnotationMirror annotation) {
- Name simpleName = annotation.getAnnotationType().asElement().getSimpleName();
- return simpleName.contentEquals("GwtIncompatible");
- }
-}
diff --git a/java/dagger/internal/codegen/HjarSourceFileGenerator.java b/java/dagger/internal/codegen/HjarSourceFileGenerator.java
deleted file mode 100644
index 5c363225a..000000000
--- a/java/dagger/internal/codegen/HjarSourceFileGenerator.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.squareup.javapoet.MethodSpec.constructorBuilder;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static com.squareup.javapoet.TypeSpec.classBuilder;
-import static javax.lang.model.element.Modifier.PRIVATE;
-
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.FieldSpec;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.TypeSpec;
-import java.util.Optional;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.Modifier;
-
-/**
- * A source file generator that only writes the relevant code necessary for Bazel to create a
- * correct header (ABI) jar.
- */
-final class HjarSourceFileGenerator<T> extends SourceFileGenerator<T> {
- private final SourceFileGenerator<T> delegate;
-
- private HjarSourceFileGenerator(SourceFileGenerator<T> delegate) {
- super(delegate);
- this.delegate = delegate;
- }
-
- static <T> SourceFileGenerator<T> wrap(SourceFileGenerator<T> delegate) {
- return new HjarSourceFileGenerator<>(delegate);
- }
-
- @Override
- ClassName nameGeneratedType(T input) {
- return delegate.nameGeneratedType(input);
- }
-
- @Override
- Element originatingElement(T input) {
- return delegate.originatingElement(input);
- }
-
- @Override
- Optional<TypeSpec.Builder> write(ClassName generatedTypeName, T input) {
- return delegate
- .write(generatedTypeName, input)
- .map(completeType -> skeletonType(completeType.build()));
- }
-
- private TypeSpec.Builder skeletonType(TypeSpec completeType) {
- TypeSpec.Builder skeleton =
- classBuilder(completeType.name)
- .addSuperinterfaces(completeType.superinterfaces)
- .addTypeVariables(completeType.typeVariables)
- .addModifiers(completeType.modifiers.toArray(new Modifier[0]))
- .addAnnotations(completeType.annotations);
-
- if (!completeType.superclass.equals(ClassName.OBJECT)) {
- skeleton.superclass(completeType.superclass);
- }
-
- completeType.methodSpecs.stream()
- .filter(method -> !method.modifiers.contains(PRIVATE) || method.isConstructor())
- .map(this::skeletonMethod)
- .forEach(skeleton::addMethod);
-
- completeType.fieldSpecs.stream()
- .filter(field -> !field.modifiers.contains(PRIVATE))
- .map(this::skeletonField)
- .forEach(skeleton::addField);
-
- completeType.typeSpecs.stream()
- .map(type -> skeletonType(type).build())
- .forEach(skeleton::addType);
-
- return skeleton;
- }
-
- private MethodSpec skeletonMethod(MethodSpec completeMethod) {
- MethodSpec.Builder skeleton =
- completeMethod.isConstructor()
- ? constructorBuilder()
- : methodBuilder(completeMethod.name).returns(completeMethod.returnType);
-
- if (completeMethod.isConstructor()) {
- // Code in Turbine must (for technical reasons in javac) have a valid super() call for
- // constructors, otherwise javac will bark, and Turbine has no way to avoid this. So we retain
- // constructor method bodies if they do exist
- skeleton.addCode(completeMethod.code);
- }
-
- return skeleton
- .addModifiers(completeMethod.modifiers)
- .addTypeVariables(completeMethod.typeVariables)
- .addParameters(completeMethod.parameters)
- .addExceptions(completeMethod.exceptions)
- .varargs(completeMethod.varargs)
- .addAnnotations(completeMethod.annotations)
- .build();
- }
-
- private FieldSpec skeletonField(FieldSpec completeField) {
- return FieldSpec.builder(
- completeField.type,
- completeField.name,
- completeField.modifiers.toArray(new Modifier[0]))
- .addAnnotations(completeField.annotations)
- .build();
- }
-}
diff --git a/java/dagger/internal/codegen/ImmediateFutureBindingExpression.java b/java/dagger/internal/codegen/ImmediateFutureBindingExpression.java
deleted file mode 100644
index 69b107c2b..000000000
--- a/java/dagger/internal/codegen/ImmediateFutureBindingExpression.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static dagger.internal.codegen.BindingRequest.bindingRequest;
-
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.codegen.javapoet.Expression;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.Key;
-import dagger.model.RequestKind;
-import javax.lang.model.SourceVersion;
-
-final class ImmediateFutureBindingExpression extends BindingExpression {
-
- private final ComponentBindingExpressions componentBindingExpressions;
- private final DaggerTypes types;
- private final SourceVersion sourceVersion;
- private final Key key;
-
- ImmediateFutureBindingExpression(
- ResolvedBindings resolvedBindings,
- ComponentBindingExpressions componentBindingExpressions,
- DaggerTypes types,
- SourceVersion sourceVersion) {
- this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
- this.types = checkNotNull(types);
- this.sourceVersion = checkNotNull(sourceVersion);
- this.key = resolvedBindings.key();
- }
-
- @Override
- Expression getDependencyExpression(ClassName requestingClass) {
- return Expression.create(
- types.wrapType(key.type(), ListenableFuture.class),
- CodeBlock.of("$T.immediateFuture($L)", Futures.class, instanceExpression(requestingClass)));
- }
-
- private CodeBlock instanceExpression(ClassName requestingClass) {
- Expression expression =
- componentBindingExpressions.getDependencyExpression(
- bindingRequest(key, RequestKind.INSTANCE), requestingClass);
- if (sourceVersion.compareTo(SourceVersion.RELEASE_7) <= 0) {
- // Java 7 type inference is not as strong as in Java 8, and therefore some generated code must
- // cast.
- //
- // For example, javac7 cannot detect that Futures.immediateFuture(ImmutableSet.of("T"))
- // can safely be assigned to ListenableFuture<Set<T>>.
- if (!types.isSameType(expression.type(), key.type())) {
- return CodeBlock.of(
- "($T) $L", types.accessibleType(key.type(), requestingClass), expression.codeBlock());
- }
- }
- return expression.codeBlock();
- }
-}
diff --git a/java/dagger/internal/codegen/InaccessibleMapKeyProxyGenerator.java b/java/dagger/internal/codegen/InaccessibleMapKeyProxyGenerator.java
deleted file mode 100644
index 19c4d19f1..000000000
--- a/java/dagger/internal/codegen/InaccessibleMapKeyProxyGenerator.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.squareup.javapoet.MethodSpec.constructorBuilder;
-import static com.squareup.javapoet.TypeSpec.classBuilder;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PUBLIC;
-
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.TypeSpec;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import java.util.Optional;
-import javax.annotation.processing.Filer;
-import javax.inject.Inject;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
-
-/**
- * Generates a class that exposes a non-{@code public} {@link
- * ContributionBinding#mapKeyAnnotation()} @MapKey} annotation.
- */
-final class InaccessibleMapKeyProxyGenerator extends SourceFileGenerator<ContributionBinding> {
- private final DaggerTypes types;
- private final DaggerElements elements;
-
- @Inject
- InaccessibleMapKeyProxyGenerator(
- Filer filer, DaggerTypes types, DaggerElements elements, SourceVersion sourceVersion) {
- super(filer, elements, sourceVersion);
- this.types = types;
- this.elements = elements;
- }
-
- @Override
- ClassName nameGeneratedType(ContributionBinding binding) {
- return MapKeys.mapKeyProxyClassName(binding);
- }
-
- @Override
- Element originatingElement(ContributionBinding binding) {
- // a map key is only ever present on bindings that have a binding element
- return binding.bindingElement().get();
- }
-
- @Override
- Optional<TypeSpec.Builder> write(ClassName generatedName, ContributionBinding binding) {
- return MapKeys.mapKeyFactoryMethod(binding, types, elements)
- .map(
- method ->
- classBuilder(generatedName)
- .addModifiers(PUBLIC, FINAL)
- .addMethod(constructorBuilder().addModifiers(PRIVATE).build())
- .addMethod(method));
- }
-}
diff --git a/java/dagger/internal/codegen/IncompatiblyScopedBindingsValidator.java b/java/dagger/internal/codegen/IncompatiblyScopedBindingsValidator.java
deleted file mode 100644
index d649e46ab..000000000
--- a/java/dagger/internal/codegen/IncompatiblyScopedBindingsValidator.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.Formatter.INDENT;
-import static dagger.internal.codegen.Scopes.getReadableSource;
-import static dagger.internal.codegen.langmodel.DaggerElements.closestEnclosingTypeElement;
-import static dagger.model.BindingKind.INJECTION;
-import static java.util.stream.Collectors.joining;
-import static javax.tools.Diagnostic.Kind.ERROR;
-
-import com.google.auto.common.MoreElements;
-import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.Multimaps;
-import dagger.model.Binding;
-import dagger.model.BindingGraph;
-import dagger.model.BindingGraph.ComponentNode;
-import dagger.spi.BindingGraphPlugin;
-import dagger.spi.DiagnosticReporter;
-import java.util.Optional;
-import java.util.Set;
-import javax.inject.Inject;
-import javax.tools.Diagnostic;
-
-/**
- * Reports an error for any component that uses bindings with scopes that are not assigned to the
- * component.
- */
-final class IncompatiblyScopedBindingsValidator implements BindingGraphPlugin {
-
- private final MethodSignatureFormatter methodSignatureFormatter;
- private final CompilerOptions compilerOptions;
-
- @Inject
- IncompatiblyScopedBindingsValidator(
- MethodSignatureFormatter methodSignatureFormatter, CompilerOptions compilerOptions) {
- this.methodSignatureFormatter = methodSignatureFormatter;
- this.compilerOptions = compilerOptions;
- }
-
- @Override
- public String pluginName() {
- return "Dagger/IncompatiblyScopedBindings";
- }
-
- @Override
- public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
- ImmutableSetMultimap.Builder<ComponentNode, dagger.model.Binding> incompatibleBindings =
- ImmutableSetMultimap.builder();
- for (dagger.model.Binding binding : bindingGraph.bindings()) {
- binding
- .scope()
- .filter(scope -> !scope.isReusable())
- .ifPresent(
- scope -> {
- ComponentNode componentNode =
- bindingGraph.componentNode(binding.componentPath()).get();
- if (!componentNode.scopes().contains(scope)) {
- // @Inject bindings in module or subcomponent binding graphs will appear at the
- // properly scoped ancestor component, so ignore them here.
- if (binding.kind().equals(INJECTION)
- && (bindingGraph.rootComponentNode().isSubcomponent()
- || !bindingGraph.rootComponentNode().isRealComponent())) {
- return;
- }
- incompatibleBindings.put(componentNode, binding);
- }
- });
- }
- Multimaps.asMap(incompatibleBindings.build())
- .forEach((componentNode, bindings) -> report(componentNode, bindings, diagnosticReporter));
- }
-
- private void report(
- ComponentNode componentNode,
- Set<Binding> bindings,
- DiagnosticReporter diagnosticReporter) {
- Diagnostic.Kind diagnosticKind = ERROR;
- StringBuilder message =
- new StringBuilder(componentNode.componentPath().currentComponent().getQualifiedName());
-
- if (!componentNode.isRealComponent()) {
- // If the "component" is really a module, it will have no scopes attached. We want to report
- // if there is more than one scope in that component.
- if (bindings.stream().map(Binding::scope).map(Optional::get).distinct().count() <= 1) {
- return;
- }
- message.append(" contains bindings with different scopes:");
- diagnosticKind = compilerOptions.moduleHasDifferentScopesDiagnosticKind();
- } else if (componentNode.scopes().isEmpty()) {
- message.append(" (unscoped) may not reference scoped bindings:");
- } else {
- message
- .append(" scoped with ")
- .append(
- componentNode.scopes().stream().map(Scopes::getReadableSource).collect(joining(" ")))
- .append(" may not reference bindings with different scopes:");
- }
-
- // TODO(ronshapiro): Should we group by scope?
- for (Binding binding : bindings) {
- message.append('\n').append(INDENT);
-
- // TODO(dpb): Use BindingDeclarationFormatter.
- // But that doesn't print scopes for @Inject-constructed types.
- switch (binding.kind()) {
- case DELEGATE:
- case PROVISION:
- message.append(
- methodSignatureFormatter.format(
- MoreElements.asExecutable(binding.bindingElement().get())));
- break;
-
- case INJECTION:
- message
- .append(getReadableSource(binding.scope().get()))
- .append(" class ")
- .append(
- closestEnclosingTypeElement(binding.bindingElement().get()).getQualifiedName());
- break;
-
- default:
- throw new AssertionError(binding);
- }
- }
- diagnosticReporter.reportComponent(diagnosticKind, componentNode, message.toString());
- }
-}
diff --git a/java/dagger/internal/codegen/InjectBindingRegistry.java b/java/dagger/internal/codegen/InjectBindingRegistry.java
deleted file mode 100644
index 2840d75f4..000000000
--- a/java/dagger/internal/codegen/InjectBindingRegistry.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import com.google.errorprone.annotations.CanIgnoreReturnValue;
-import dagger.Component;
-import dagger.Provides;
-import dagger.model.Key;
-import java.util.Optional;
-import javax.inject.Inject;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-
-/**
- * Maintains the collection of provision bindings from {@link Inject} constructors and members
- * injection bindings from {@link Inject} fields and methods known to the annotation processor. Note
- * that this registry <b>does not</b> handle any explicit bindings (those from {@link Provides}
- * methods, {@link Component} dependencies, etc.).
- */
-interface InjectBindingRegistry {
- /**
- * Returns a {@link ProvisionBinding} for {@code key}. If none has been registered yet, registers
- * one.
- */
- Optional<ProvisionBinding> getOrFindProvisionBinding(Key key);
-
- /**
- * Returns a {@link MembersInjectionBinding} for {@code key}. If none has been registered yet,
- * registers one, along with all necessary members injection bindings for superclasses.
- */
- Optional<MembersInjectionBinding> getOrFindMembersInjectionBinding(Key key);
-
- /**
- * Returns a {@link ProvisionBinding} for a {@link dagger.MembersInjector} of {@code key}. If none
- * has been registered yet, registers one.
- */
- Optional<ProvisionBinding> getOrFindMembersInjectorProvisionBinding(Key key);
-
- @CanIgnoreReturnValue
- Optional<ProvisionBinding> tryRegisterConstructor(ExecutableElement constructorElement);
-
- @CanIgnoreReturnValue
- Optional<MembersInjectionBinding> tryRegisterMembersInjectedType(TypeElement typeElement);
-
- /**
- * This method ensures that sources for all registered {@link Binding bindings} (either explicitly
- * or implicitly via {@link #getOrFindMembersInjectionBinding} or {@link
- * #getOrFindProvisionBinding}) are generated.
- */
- void generateSourcesForRequiredBindings(
- SourceFileGenerator<ProvisionBinding> factoryGenerator,
- SourceFileGenerator<MembersInjectionBinding> membersInjectorGenerator)
- throws SourceFileGenerationException;
-}
diff --git a/java/dagger/internal/codegen/InjectBindingRegistryImpl.java b/java/dagger/internal/codegen/InjectBindingRegistryImpl.java
deleted file mode 100644
index 45dc391e4..000000000
--- a/java/dagger/internal/codegen/InjectBindingRegistryImpl.java
+++ /dev/null
@@ -1,340 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static dagger.internal.codegen.InjectionAnnotations.injectedConstructors;
-import static dagger.internal.codegen.Keys.isValidImplicitProvisionKey;
-import static dagger.internal.codegen.Keys.isValidMembersInjectionKey;
-import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import com.google.errorprone.annotations.CanIgnoreReturnValue;
-import com.squareup.javapoet.ClassName;
-import dagger.Component;
-import dagger.MembersInjector;
-import dagger.Provides;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.Key;
-import java.util.ArrayDeque;
-import java.util.Deque;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import javax.annotation.processing.Messager;
-import javax.inject.Inject;
-import javax.inject.Provider;
-import javax.inject.Singleton;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-import javax.tools.Diagnostic.Kind;
-
-/**
- * Maintains the collection of provision bindings from {@link Inject} constructors and members
- * injection bindings from {@link Inject} fields and methods known to the annotation processor.
- * Note that this registry <b>does not</b> handle any explicit bindings (those from {@link Provides}
- * methods, {@link Component} dependencies, etc.).
- */
-@Singleton
-final class InjectBindingRegistryImpl implements InjectBindingRegistry {
- private final DaggerElements elements;
- private final DaggerTypes types;
- private final Messager messager;
- private final InjectValidator injectValidator;
- private final InjectValidator injectValidatorWhenGeneratingCode;
- private final KeyFactory keyFactory;
- private final BindingFactory bindingFactory;
- private final CompilerOptions compilerOptions;
-
- final class BindingsCollection<B extends Binding> {
- private final Class<?> factoryClass;
- private final Map<Key, B> bindingsByKey = Maps.newLinkedHashMap();
- private final Deque<B> bindingsRequiringGeneration = new ArrayDeque<>();
- private final Set<Key> materializedBindingKeys = Sets.newLinkedHashSet();
-
- BindingsCollection(Class<?> factoryClass) {
- this.factoryClass = factoryClass;
- }
-
- void generateBindings(SourceFileGenerator<B> generator) throws SourceFileGenerationException {
- for (B binding = bindingsRequiringGeneration.poll();
- binding != null;
- binding = bindingsRequiringGeneration.poll()) {
- checkState(!binding.unresolved().isPresent());
- if (injectValidatorWhenGeneratingCode.isValidType(binding.key().type())) {
- generator.generate(binding);
- }
- materializedBindingKeys.add(binding.key());
- }
- // Because Elements instantiated across processing rounds are not guaranteed to be equals() to
- // the logically same element, clear the cache after generating
- bindingsByKey.clear();
- }
-
- /** Returns a previously cached binding. */
- B getBinding(Key key) {
- return bindingsByKey.get(key);
- }
-
- /** Caches the binding and generates it if it needs generation. */
- void tryRegisterBinding(B binding, boolean warnIfNotAlreadyGenerated) {
- tryToCacheBinding(binding);
- tryToGenerateBinding(binding, warnIfNotAlreadyGenerated);
- }
-
- /**
- * Tries to generate a binding, not generating if it already is generated. For resolved
- * bindings, this will try to generate the unresolved version of the binding.
- */
- void tryToGenerateBinding(B binding, boolean warnIfNotAlreadyGenerated) {
- if (shouldGenerateBinding(binding, generatedClassNameForBinding(binding))) {
- bindingsRequiringGeneration.offer(binding);
- if (compilerOptions.warnIfInjectionFactoryNotGeneratedUpstream()
- && warnIfNotAlreadyGenerated) {
- messager.printMessage(
- Kind.NOTE,
- String.format(
- "Generating a %s for %s. "
- + "Prefer to run the dagger processor over that class instead.",
- factoryClass.getSimpleName(),
- types.erasure(binding.key().type()))); // erasure to strip <T> from msgs.
- }
- }
- }
-
- /** Returns true if the binding needs to be generated. */
- private boolean shouldGenerateBinding(B binding, ClassName factoryName) {
- return !binding.unresolved().isPresent()
- && !materializedBindingKeys.contains(binding.key())
- && !bindingsRequiringGeneration.contains(binding)
- && elements.getTypeElement(factoryName) == null;
- }
-
- /** Caches the binding for future lookups by key. */
- private void tryToCacheBinding(B binding) {
- // We only cache resolved bindings or unresolved bindings w/o type arguments.
- // Unresolved bindings w/ type arguments aren't valid for the object graph.
- if (binding.unresolved().isPresent()
- || binding.bindingTypeElement().get().getTypeParameters().isEmpty()) {
- Key key = binding.key();
- Binding previousValue = bindingsByKey.put(key, binding);
- checkState(previousValue == null || binding.equals(previousValue),
- "couldn't register %s. %s was already registered for %s",
- binding, previousValue, key);
- }
- }
- }
-
- private final BindingsCollection<ProvisionBinding> provisionBindings =
- new BindingsCollection<>(Provider.class);
- private final BindingsCollection<MembersInjectionBinding> membersInjectionBindings =
- new BindingsCollection<>(MembersInjector.class);
-
- @Inject
- InjectBindingRegistryImpl(
- DaggerElements elements,
- DaggerTypes types,
- Messager messager,
- InjectValidator injectValidator,
- KeyFactory keyFactory,
- BindingFactory bindingFactory,
- CompilerOptions compilerOptions) {
- this.elements = elements;
- this.types = types;
- this.messager = messager;
- this.injectValidator = injectValidator;
- this.injectValidatorWhenGeneratingCode = injectValidator.whenGeneratingCode();
- this.keyFactory = keyFactory;
- this.bindingFactory = bindingFactory;
- this.compilerOptions = compilerOptions;
- }
-
- // TODO(dpb): make the SourceFileGenerators fields so they don't have to be passed in
- @Override
- public void generateSourcesForRequiredBindings(
- SourceFileGenerator<ProvisionBinding> factoryGenerator,
- SourceFileGenerator<MembersInjectionBinding> membersInjectorGenerator)
- throws SourceFileGenerationException {
- provisionBindings.generateBindings(factoryGenerator);
- membersInjectionBindings.generateBindings(membersInjectorGenerator);
- }
-
- /**
- * Registers the binding for generation and later lookup. If the binding is resolved, we also
- * attempt to register an unresolved version of it.
- */
- private void registerBinding(ProvisionBinding binding, boolean warnIfNotAlreadyGenerated) {
- provisionBindings.tryRegisterBinding(binding, warnIfNotAlreadyGenerated);
- if (binding.unresolved().isPresent()) {
- provisionBindings.tryToGenerateBinding(binding.unresolved().get(), warnIfNotAlreadyGenerated);
- }
- }
-
- /**
- * Registers the binding for generation and later lookup. If the binding is resolved, we also
- * attempt to register an unresolved version of it.
- */
- private void registerBinding(MembersInjectionBinding binding, boolean warnIfNotAlreadyGenerated) {
- /*
- * We generate MembersInjector classes for types with @Inject constructors only if they have any
- * injection sites.
- *
- * We generate MembersInjector classes for types without @Inject constructors only if they have
- * local (non-inherited) injection sites.
- *
- * Warn only when registering bindings post-hoc for those types.
- */
- warnIfNotAlreadyGenerated =
- warnIfNotAlreadyGenerated
- && (!injectedConstructors(binding.membersInjectedType()).isEmpty()
- ? !binding.injectionSites().isEmpty()
- : binding.hasLocalInjectionSites());
- membersInjectionBindings.tryRegisterBinding(binding, warnIfNotAlreadyGenerated);
- if (binding.unresolved().isPresent()) {
- membersInjectionBindings.tryToGenerateBinding(
- binding.unresolved().get(), warnIfNotAlreadyGenerated);
- }
- }
-
- @Override
- public Optional<ProvisionBinding> tryRegisterConstructor(ExecutableElement constructorElement) {
- return tryRegisterConstructor(constructorElement, Optional.empty(), false);
- }
-
- @CanIgnoreReturnValue
- private Optional<ProvisionBinding> tryRegisterConstructor(
- ExecutableElement constructorElement,
- Optional<TypeMirror> resolvedType,
- boolean warnIfNotAlreadyGenerated) {
- TypeElement typeElement = MoreElements.asType(constructorElement.getEnclosingElement());
- DeclaredType type = MoreTypes.asDeclared(typeElement.asType());
- Key key = keyFactory.forInjectConstructorWithResolvedType(type);
- ProvisionBinding cachedBinding = provisionBindings.getBinding(key);
- if (cachedBinding != null) {
- return Optional.of(cachedBinding);
- }
-
- ValidationReport<TypeElement> report = injectValidator.validateConstructor(constructorElement);
- report.printMessagesTo(messager);
- if (report.isClean()) {
- ProvisionBinding binding = bindingFactory.injectionBinding(constructorElement, resolvedType);
- registerBinding(binding, warnIfNotAlreadyGenerated);
- if (!binding.injectionSites().isEmpty()) {
- tryRegisterMembersInjectedType(typeElement, resolvedType, warnIfNotAlreadyGenerated);
- }
- return Optional.of(binding);
- }
- return Optional.empty();
- }
-
- @Override
- public Optional<MembersInjectionBinding> tryRegisterMembersInjectedType(TypeElement typeElement) {
- return tryRegisterMembersInjectedType(typeElement, Optional.empty(), false);
- }
-
- @CanIgnoreReturnValue
- private Optional<MembersInjectionBinding> tryRegisterMembersInjectedType(
- TypeElement typeElement,
- Optional<TypeMirror> resolvedType,
- boolean warnIfNotAlreadyGenerated) {
- DeclaredType type = MoreTypes.asDeclared(typeElement.asType());
- Key key = keyFactory.forInjectConstructorWithResolvedType(type);
- MembersInjectionBinding cachedBinding = membersInjectionBindings.getBinding(key);
- if (cachedBinding != null) {
- return Optional.of(cachedBinding);
- }
-
- ValidationReport<TypeElement> report =
- injectValidator.validateMembersInjectionType(typeElement);
- report.printMessagesTo(messager);
- if (report.isClean()) {
- MembersInjectionBinding binding = bindingFactory.membersInjectionBinding(type, resolvedType);
- registerBinding(binding, warnIfNotAlreadyGenerated);
- for (Optional<DeclaredType> supertype = types.nonObjectSuperclass(type);
- supertype.isPresent();
- supertype = types.nonObjectSuperclass(supertype.get())) {
- getOrFindMembersInjectionBinding(keyFactory.forMembersInjectedType(supertype.get()));
- }
- return Optional.of(binding);
- }
- return Optional.empty();
- }
-
- @CanIgnoreReturnValue
- @Override
- public Optional<ProvisionBinding> getOrFindProvisionBinding(Key key) {
- checkNotNull(key);
- if (!isValidImplicitProvisionKey(key, types)) {
- return Optional.empty();
- }
- ProvisionBinding binding = provisionBindings.getBinding(key);
- if (binding != null) {
- return Optional.of(binding);
- }
-
- // ok, let's see if we can find an @Inject constructor
- TypeElement element = MoreElements.asType(types.asElement(key.type()));
- ImmutableSet<ExecutableElement> injectConstructors = injectedConstructors(element);
- switch (injectConstructors.size()) {
- case 0:
- // No constructor found.
- return Optional.empty();
- case 1:
- return tryRegisterConstructor(
- Iterables.getOnlyElement(injectConstructors), Optional.of(key.type()), true);
- default:
- throw new IllegalStateException("Found multiple @Inject constructors: "
- + injectConstructors);
- }
- }
-
- @CanIgnoreReturnValue
- @Override
- public Optional<MembersInjectionBinding> getOrFindMembersInjectionBinding(Key key) {
- checkNotNull(key);
- // TODO(gak): is checking the kind enough?
- checkArgument(isValidMembersInjectionKey(key));
- MembersInjectionBinding binding = membersInjectionBindings.getBinding(key);
- if (binding != null) {
- return Optional.of(binding);
- }
- Optional<MembersInjectionBinding> newBinding =
- tryRegisterMembersInjectedType(
- MoreTypes.asTypeElement(key.type()), Optional.of(key.type()), true);
- return newBinding;
- }
-
- @Override
- public Optional<ProvisionBinding> getOrFindMembersInjectorProvisionBinding(Key key) {
- if (!isValidMembersInjectionKey(key)) {
- return Optional.empty();
- }
- Key membersInjectionKey = keyFactory.forMembersInjectedType(types.unwrapType(key.type()));
- return getOrFindMembersInjectionBinding(membersInjectionKey)
- .map(binding -> bindingFactory.membersInjectorBinding(key, binding));
- }
-}
diff --git a/java/dagger/internal/codegen/InjectBindingRegistryModule.java b/java/dagger/internal/codegen/InjectBindingRegistryModule.java
deleted file mode 100644
index 45633620d..000000000
--- a/java/dagger/internal/codegen/InjectBindingRegistryModule.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import dagger.Binds;
-import dagger.Module;
-
-@Module
-interface InjectBindingRegistryModule {
- @Binds
- InjectBindingRegistry injectBindingRegistry(InjectBindingRegistryImpl impl);
-}
diff --git a/java/dagger/internal/codegen/InjectBindingValidator.java b/java/dagger/internal/codegen/InjectBindingValidator.java
deleted file mode 100644
index 183d16200..000000000
--- a/java/dagger/internal/codegen/InjectBindingValidator.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.model.BindingKind.INJECTION;
-
-import com.google.auto.common.MoreTypes;
-import dagger.internal.codegen.ValidationReport.Item;
-import dagger.model.BindingGraph;
-import dagger.spi.BindingGraphPlugin;
-import dagger.spi.DiagnosticReporter;
-import javax.inject.Inject;
-import javax.lang.model.element.TypeElement;
-
-/** Validates bindings from {@code @Inject}-annotated constructors. */
-final class InjectBindingValidator implements BindingGraphPlugin {
-
- private final InjectValidator injectValidator;
-
- @Inject
- InjectBindingValidator(InjectValidator injectValidator) {
- this.injectValidator = injectValidator.whenGeneratingCode();
- }
-
- @Override
- public String pluginName() {
- return "Dagger/InjectBinding";
- }
-
- @Override
- public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
- bindingGraph.bindings().stream()
- .filter(binding -> binding.kind().equals(INJECTION)) // TODO(dpb): Move to BindingGraph
- .forEach(binding -> validateInjectionBinding(binding, diagnosticReporter));
- }
-
- private void validateInjectionBinding(
- dagger.model.Binding node, DiagnosticReporter diagnosticReporter) {
- ValidationReport<TypeElement> typeReport =
- injectValidator.validateType(MoreTypes.asTypeElement(node.key().type()));
- for (Item item : typeReport.allItems()) {
- diagnosticReporter.reportBinding(item.kind(), node, item.message());
- }
- }
-}
diff --git a/java/dagger/internal/codegen/InjectProcessingStep.java b/java/dagger/internal/codegen/InjectProcessingStep.java
index be8c975a1..537459235 100644
--- a/java/dagger/internal/codegen/InjectProcessingStep.java
+++ b/java/dagger/internal/codegen/InjectProcessingStep.java
@@ -18,6 +18,10 @@ package dagger.internal.codegen;
import com.google.auto.common.MoreElements;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import dagger.assisted.AssistedInject;
+import dagger.internal.codegen.binding.InjectBindingRegistry;
+import dagger.internal.codegen.validation.TypeCheckingProcessingStep;
import java.lang.annotation.Annotation;
import java.util.Set;
import javax.inject.Inject;
@@ -34,6 +38,7 @@ import javax.lang.model.util.ElementKindVisitor8;
// TODO(gak): add some error handling for bad source files
final class InjectProcessingStep extends TypeCheckingProcessingStep<Element> {
private final ElementVisitor<Void, Void> visitor;
+ private final Set<Element> processedElements = Sets.newLinkedHashSet();
@Inject
InjectProcessingStep(InjectBindingRegistry injectBindingRegistry) {
@@ -65,12 +70,20 @@ final class InjectProcessingStep extends TypeCheckingProcessingStep<Element> {
@Override
public Set<Class<? extends Annotation>> annotations() {
- return ImmutableSet.of(Inject.class);
+ return ImmutableSet.of(Inject.class, AssistedInject.class);
}
@Override
protected void process(
Element injectElement, ImmutableSet<Class<? extends Annotation>> annotations) {
+ // Only process an element once to avoid getting duplicate errors when an element is annotated
+ // with multiple inject annotations.
+ if (processedElements.contains(injectElement)) {
+ return;
+ }
+
injectElement.accept(visitor, null);
+
+ processedElements.add(injectElement);
}
}
diff --git a/java/dagger/internal/codegen/InjectValidator.java b/java/dagger/internal/codegen/InjectValidator.java
deleted file mode 100644
index d3c4ce8d9..000000000
--- a/java/dagger/internal/codegen/InjectValidator.java
+++ /dev/null
@@ -1,332 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static dagger.internal.codegen.InjectionAnnotations.getQualifiers;
-import static dagger.internal.codegen.InjectionAnnotations.injectedConstructors;
-import static dagger.internal.codegen.Scopes.scopesOf;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.STATIC;
-import static javax.lang.model.type.TypeKind.DECLARED;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableSet;
-import dagger.internal.codegen.langmodel.Accessibility;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.Scope;
-import java.util.Optional;
-import java.util.Set;
-import javax.inject.Inject;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.ElementFilter;
-import javax.tools.Diagnostic;
-import javax.tools.Diagnostic.Kind;
-
-/**
- * A {@linkplain ValidationReport validator} for {@link Inject}-annotated elements and the types
- * that contain them.
- */
-final class InjectValidator {
- private final DaggerTypes types;
- private final DaggerElements elements;
- private final CompilerOptions compilerOptions;
- private final DependencyRequestValidator dependencyRequestValidator;
- private final Optional<Diagnostic.Kind> privateAndStaticInjectionDiagnosticKind;
-
- @Inject
- InjectValidator(
- DaggerTypes types,
- DaggerElements elements,
- DependencyRequestValidator dependencyRequestValidator,
- CompilerOptions compilerOptions) {
- this(types, elements, compilerOptions, dependencyRequestValidator, Optional.empty());
- }
-
- private InjectValidator(
- DaggerTypes types,
- DaggerElements elements,
- CompilerOptions compilerOptions,
- DependencyRequestValidator dependencyRequestValidator,
- Optional<Kind> privateAndStaticInjectionDiagnosticKind) {
- this.types = types;
- this.elements = elements;
- this.compilerOptions = compilerOptions;
- this.dependencyRequestValidator = dependencyRequestValidator;
- this.privateAndStaticInjectionDiagnosticKind = privateAndStaticInjectionDiagnosticKind;
- }
-
- /**
- * Returns a new validator that performs the same validation as this one, but is strict about
- * rejecting optionally-specified JSR 330 behavior that Dagger doesn't support (unless {@code
- * -Adagger.ignorePrivateAndStaticInjectionForComponent=enabled} was set in the javac options).
- */
- InjectValidator whenGeneratingCode() {
- return compilerOptions.ignorePrivateAndStaticInjectionForComponent()
- ? this
- : new InjectValidator(
- types,
- elements,
- compilerOptions,
- dependencyRequestValidator,
- Optional.of(Diagnostic.Kind.ERROR));
- }
-
- ValidationReport<TypeElement> validateConstructor(ExecutableElement constructorElement) {
- ValidationReport.Builder<TypeElement> builder =
- ValidationReport.about(MoreElements.asType(constructorElement.getEnclosingElement()));
- if (constructorElement.getModifiers().contains(PRIVATE)) {
- builder.addError(
- "Dagger does not support injection into private constructors", constructorElement);
- }
-
- for (AnnotationMirror qualifier : getQualifiers(constructorElement)) {
- builder.addError(
- "@Qualifier annotations are not allowed on @Inject constructors",
- constructorElement,
- qualifier);
- }
-
- for (Scope scope : scopesOf(constructorElement)) {
- builder.addError(
- "@Scope annotations are not allowed on @Inject constructors; annotate the class instead",
- constructorElement,
- scope.scopeAnnotation());
- }
-
- for (VariableElement parameter : constructorElement.getParameters()) {
- validateDependencyRequest(builder, parameter);
- }
-
- if (throwsCheckedExceptions(constructorElement)) {
- builder.addItem(
- "Dagger does not support checked exceptions on @Inject constructors",
- privateMemberDiagnosticKind(),
- constructorElement);
- }
-
- checkInjectIntoPrivateClass(constructorElement, builder);
-
- TypeElement enclosingElement =
- MoreElements.asType(constructorElement.getEnclosingElement());
-
- Set<Modifier> typeModifiers = enclosingElement.getModifiers();
- if (typeModifiers.contains(ABSTRACT)) {
- builder.addError(
- "@Inject is nonsense on the constructor of an abstract class", constructorElement);
- }
-
- if (enclosingElement.getNestingKind().isNested()
- && !typeModifiers.contains(STATIC)) {
- builder.addError(
- "@Inject constructors are invalid on inner classes. "
- + "Did you mean to make the class static?",
- constructorElement);
- }
-
- // This is computationally expensive, but probably preferable to a giant index
- ImmutableSet<ExecutableElement> injectConstructors = injectedConstructors(enclosingElement);
-
- if (injectConstructors.size() > 1) {
- builder.addError("Types may only contain one @Inject constructor", constructorElement);
- }
-
- ImmutableSet<Scope> scopes = scopesOf(enclosingElement);
- if (scopes.size() > 1) {
- for (Scope scope : scopes) {
- builder.addError(
- "A single binding may not declare more than one @Scope",
- enclosingElement,
- scope.scopeAnnotation());
- }
- }
-
- return builder.build();
- }
-
- private ValidationReport<VariableElement> validateField(VariableElement fieldElement) {
- ValidationReport.Builder<VariableElement> builder = ValidationReport.about(fieldElement);
- Set<Modifier> modifiers = fieldElement.getModifiers();
- if (modifiers.contains(FINAL)) {
- builder.addError("@Inject fields may not be final", fieldElement);
- }
-
- if (modifiers.contains(PRIVATE)) {
- builder.addItem(
- "Dagger does not support injection into private fields",
- privateMemberDiagnosticKind(),
- fieldElement);
- }
-
- if (modifiers.contains(STATIC)) {
- builder.addItem(
- "Dagger does not support injection into static fields",
- staticMemberDiagnosticKind(),
- fieldElement);
- }
-
- validateDependencyRequest(builder, fieldElement);
-
- return builder.build();
- }
-
- private ValidationReport<ExecutableElement> validateMethod(ExecutableElement methodElement) {
- ValidationReport.Builder<ExecutableElement> builder = ValidationReport.about(methodElement);
- Set<Modifier> modifiers = methodElement.getModifiers();
- if (modifiers.contains(ABSTRACT)) {
- builder.addError("Methods with @Inject may not be abstract", methodElement);
- }
-
- if (modifiers.contains(PRIVATE)) {
- builder.addItem(
- "Dagger does not support injection into private methods",
- privateMemberDiagnosticKind(),
- methodElement);
- }
-
- if (modifiers.contains(STATIC)) {
- builder.addItem(
- "Dagger does not support injection into static methods",
- staticMemberDiagnosticKind(),
- methodElement);
- }
-
- if (!methodElement.getTypeParameters().isEmpty()) {
- builder.addError("Methods with @Inject may not declare type parameters", methodElement);
- }
-
- for (VariableElement parameter : methodElement.getParameters()) {
- validateDependencyRequest(builder, parameter);
- }
-
- return builder.build();
- }
-
- private void validateDependencyRequest(
- ValidationReport.Builder<?> builder, VariableElement parameter) {
- dependencyRequestValidator.validateDependencyRequest(builder, parameter, parameter.asType());
- dependencyRequestValidator.checkNotProducer(builder, parameter);
- }
-
- ValidationReport<TypeElement> validateMembersInjectionType(TypeElement typeElement) {
- // TODO(beder): This element might not be currently compiled, so this error message could be
- // left in limbo. Find an appropriate way to display the error message in that case.
- ValidationReport.Builder<TypeElement> builder = ValidationReport.about(typeElement);
- boolean hasInjectedMembers = false;
- for (VariableElement element : ElementFilter.fieldsIn(typeElement.getEnclosedElements())) {
- if (MoreElements.isAnnotationPresent(element, Inject.class)) {
- hasInjectedMembers = true;
- ValidationReport<VariableElement> report = validateField(element);
- if (!report.isClean()) {
- builder.addSubreport(report);
- }
- }
- }
- for (ExecutableElement element : ElementFilter.methodsIn(typeElement.getEnclosedElements())) {
- if (MoreElements.isAnnotationPresent(element, Inject.class)) {
- hasInjectedMembers = true;
- ValidationReport<ExecutableElement> report = validateMethod(element);
- if (!report.isClean()) {
- builder.addSubreport(report);
- }
- }
- }
-
- if (hasInjectedMembers) {
- checkInjectIntoPrivateClass(typeElement, builder);
- }
- TypeMirror superclass = typeElement.getSuperclass();
- if (!superclass.getKind().equals(TypeKind.NONE)) {
- ValidationReport<TypeElement> report = validateType(MoreTypes.asTypeElement(superclass));
- if (!report.isClean()) {
- builder.addSubreport(report);
- }
- }
- return builder.build();
- }
-
- ValidationReport<TypeElement> validateType(TypeElement typeElement) {
- ValidationReport.Builder<TypeElement> builder = ValidationReport.about(typeElement);
- ValidationReport<TypeElement> membersInjectionReport =
- validateMembersInjectionType(typeElement);
- if (!membersInjectionReport.isClean()) {
- builder.addSubreport(membersInjectionReport);
- }
- for (ExecutableElement element :
- ElementFilter.constructorsIn(typeElement.getEnclosedElements())) {
- if (isAnnotationPresent(element, Inject.class)) {
- ValidationReport<TypeElement> report = validateConstructor(element);
- if (!report.isClean()) {
- builder.addSubreport(report);
- }
- }
- }
- return builder.build();
- }
-
- boolean isValidType(TypeMirror type) {
- if (!type.getKind().equals(DECLARED)) {
- return true;
- }
- return validateType(MoreTypes.asTypeElement(type)).isClean();
- }
-
- /** Returns true if the given method element declares a checked exception. */
- private boolean throwsCheckedExceptions(ExecutableElement methodElement) {
- TypeMirror runtimeExceptionType = elements.getTypeElement(RuntimeException.class).asType();
- TypeMirror errorType = elements.getTypeElement(Error.class).asType();
- for (TypeMirror thrownType : methodElement.getThrownTypes()) {
- if (!types.isSubtype(thrownType, runtimeExceptionType)
- && !types.isSubtype(thrownType, errorType)) {
- return true;
- }
- }
- return false;
- }
-
- private void checkInjectIntoPrivateClass(
- Element element, ValidationReport.Builder<TypeElement> builder) {
- if (!Accessibility.isElementAccessibleFromOwnPackage(
- DaggerElements.closestEnclosingTypeElement(element))) {
- builder.addItem(
- "Dagger does not support injection into private classes",
- privateMemberDiagnosticKind(),
- element);
- }
- }
-
- private Diagnostic.Kind privateMemberDiagnosticKind() {
- return privateAndStaticInjectionDiagnosticKind.orElse(
- compilerOptions.privateMemberValidationKind());
- }
-
- private Diagnostic.Kind staticMemberDiagnosticKind() {
- return privateAndStaticInjectionDiagnosticKind.orElse(
- compilerOptions.staticMemberValidationKind());
- }
-}
diff --git a/java/dagger/internal/codegen/InjectionAnnotations.java b/java/dagger/internal/codegen/InjectionAnnotations.java
deleted file mode 100644
index 521ad433f..000000000
--- a/java/dagger/internal/codegen/InjectionAnnotations.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static javax.lang.model.util.ElementFilter.constructorsIn;
-
-import com.google.auto.common.AnnotationMirrors;
-import com.google.auto.common.SuperficialValidation;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableSet;
-import java.util.Optional;
-import javax.inject.Inject;
-import javax.inject.Qualifier;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-
-/**
- * Utilities relating to annotations defined in the {@code javax.inject} package.
- */
-final class InjectionAnnotations {
- static Optional<AnnotationMirror> getQualifier(Element e) {
- if (!SuperficialValidation.validateElement(e)) {
- throw new TypeNotPresentException(e.toString(), null);
- }
- checkNotNull(e);
- ImmutableSet<? extends AnnotationMirror> qualifierAnnotations = getQualifiers(e);
- switch (qualifierAnnotations.size()) {
- case 0:
- return Optional.empty();
- case 1:
- return Optional.<AnnotationMirror>of(qualifierAnnotations.iterator().next());
- default:
- throw new IllegalArgumentException(
- e + " was annotated with more than one @Qualifier annotation");
- }
- }
-
- static ImmutableSet<? extends AnnotationMirror> getQualifiers(Element element) {
- return AnnotationMirrors.getAnnotatedAnnotations(element, Qualifier.class);
- }
-
- /** Returns the constructors in {@code type} that are annotated with {@link Inject}. */
- static ImmutableSet<ExecutableElement> injectedConstructors(TypeElement type) {
- return FluentIterable.from(constructorsIn(type.getEnclosedElements()))
- .filter(constructor -> isAnnotationPresent(constructor, Inject.class))
- .toSet();
- }
-
- private InjectionAnnotations() {}
-}
diff --git a/java/dagger/internal/codegen/InjectionMethod.java b/java/dagger/internal/codegen/InjectionMethod.java
deleted file mode 100644
index 2785b18b0..000000000
--- a/java/dagger/internal/codegen/InjectionMethod.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
-import static dagger.internal.codegen.langmodel.Accessibility.isRawTypePubliclyAccessible;
-import static javax.lang.model.element.Modifier.PUBLIC;
-import static javax.lang.model.element.Modifier.STATIC;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.errorprone.annotations.CanIgnoreReturnValue;
-import com.google.errorprone.annotations.CheckReturnValue;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.ParameterSpec;
-import com.squareup.javapoet.TypeName;
-import com.squareup.javapoet.TypeVariableName;
-import dagger.internal.codegen.javapoet.CodeBlocks;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import java.util.List;
-import java.util.Optional;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.Parameterizable;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * A static method that implements provision and/or injection in one step:
- *
- * <ul>
- * <li>methods that invoke {@code @Inject} constructors and do members injection if necessary
- * <li>methods that call {@code @Provides} module methods
- * <li>methods that perform members injection
- * </ul>
- *
- * <p>Note that although this type uses {@code @AutoValue}, it uses instance equality. It uses
- * {@code @AutoValue} to avoid the boilerplate of writing a correct builder, but is not intended to
- * actually be a value type.
- */
-@AutoValue
-abstract class InjectionMethod {
- abstract String name();
-
- abstract boolean varargs();
-
- abstract ImmutableList<TypeVariableName> typeVariables();
-
- abstract ImmutableMap<ParameterSpec, TypeMirror> parameters();
-
- abstract Optional<TypeMirror> returnType();
-
- abstract Optional<DeclaredType> nullableAnnotation();
-
- abstract ImmutableList<TypeMirror> exceptions();
-
- abstract CodeBlock methodBody();
-
- abstract ClassName enclosingClass();
-
- MethodSpec toMethodSpec() {
- MethodSpec.Builder builder =
- methodBuilder(name())
- .addModifiers(PUBLIC, STATIC)
- .varargs(varargs())
- .addTypeVariables(typeVariables())
- .addParameters(parameters().keySet())
- .addCode(methodBody());
- returnType().map(TypeName::get).ifPresent(builder::returns);
- nullableAnnotation()
- .ifPresent(nullableType -> CodeBlocks.addAnnotation(builder, nullableType));
- exceptions().stream().map(TypeName::get).forEach(builder::addException);
- return builder.build();
- }
-
- CodeBlock invoke(List<CodeBlock> arguments, ClassName requestingClass) {
- checkArgument(arguments.size() == parameters().size());
- CodeBlock.Builder invocation = CodeBlock.builder();
- if (!enclosingClass().equals(requestingClass)) {
- invocation.add("$T.", enclosingClass());
- }
- return invocation.add("$L($L)", name(), makeParametersCodeBlock(arguments)).build();
- }
-
- @Override
- public final int hashCode() {
- return System.identityHashCode(this);
- }
-
- @Override
- public final boolean equals(Object obj) {
- return this == obj;
- }
-
- static Builder builder(DaggerElements elements) {
- Builder builder = new AutoValue_InjectionMethod.Builder();
- builder.elements = elements;
- builder.varargs(false).exceptions(ImmutableList.of()).nullableAnnotation(Optional.empty());
- return builder;
- }
-
- @CanIgnoreReturnValue
- @AutoValue.Builder
- abstract static class Builder {
- private final UniqueNameSet parameterNames = new UniqueNameSet();
- private final CodeBlock.Builder methodBody = CodeBlock.builder();
- private DaggerElements elements;
-
- abstract ImmutableMap.Builder<ParameterSpec, TypeMirror> parametersBuilder();
- abstract ImmutableList.Builder<TypeVariableName> typeVariablesBuilder();
- abstract Builder name(String name);
- abstract Builder varargs(boolean varargs);
- abstract Builder returnType(TypeMirror returnType);
- abstract Builder exceptions(Iterable<? extends TypeMirror> exceptions);
- abstract Builder nullableAnnotation(Optional<DeclaredType> nullableAnnotation);
- abstract Builder methodBody(CodeBlock methodBody);
-
- final CodeBlock.Builder methodBodyBuilder() {
- return methodBody;
- }
-
- abstract Builder enclosingClass(ClassName enclosingClass);
-
- /**
- * Adds a parameter for the given name and type. If another parameter has already been added
- * with the same name, the name is disambiguated.
- */
- ParameterSpec addParameter(String name, TypeMirror type) {
- ParameterSpec parameter =
- ParameterSpec.builder(TypeName.get(type), parameterNames.getUniqueName(name)).build();
- parametersBuilder().put(parameter, type);
- return parameter;
- }
-
- /**
- * Calls {@link #copyParameter(VariableElement)} for each parameter of of {@code method}, and
- * concatenates the results of each call, {@link CodeBlocks#makeParametersCodeBlock(Iterable)
- * separated with commas}.
- */
- CodeBlock copyParameters(ExecutableElement method) {
- ImmutableList.Builder<CodeBlock> argumentsBuilder = ImmutableList.builder();
- for (VariableElement parameter : method.getParameters()) {
- argumentsBuilder.add(copyParameter(parameter));
- }
- varargs(method.isVarArgs());
- return makeParametersCodeBlock(argumentsBuilder.build());
- }
-
- /**
- * Adds {@code parameter} as a parameter of this method, using a publicly accessible version of
- * the parameter's type. Returns a {@link CodeBlock} of the usage of this parameter within the
- * injection method's {@link #methodBody()}.
- */
- CodeBlock copyParameter(VariableElement parameter) {
- TypeMirror elementType = parameter.asType();
- boolean useObject = !isRawTypePubliclyAccessible(elementType);
- TypeMirror publicType = useObject ? objectType() : elementType;
- ParameterSpec parameterSpec = addParameter(parameter.getSimpleName().toString(), publicType);
- return useObject
- ? CodeBlock.of("($T) $N", elementType, parameterSpec)
- : CodeBlock.of("$N", parameterSpec);
- }
-
- private TypeMirror objectType() {
- return elements.getTypeElement(Object.class).asType();
- }
-
- /**
- * Adds each type parameter of {@code parameterizable} as a type parameter of this injection
- * method.
- */
- Builder copyTypeParameters(Parameterizable parameterizable) {
- parameterizable.getTypeParameters().stream()
- .map(TypeVariableName::get)
- .forEach(typeVariablesBuilder()::add);
- return this;
- }
-
- Builder copyThrows(ExecutableElement element) {
- exceptions(element.getThrownTypes());
- return this;
- }
-
- @CheckReturnValue
- final InjectionMethod build() {
- return methodBody(methodBody.build()).buildInternal();
- }
-
- abstract InjectionMethod buildInternal();
- }
-}
diff --git a/java/dagger/internal/codegen/InjectionMethods.java b/java/dagger/internal/codegen/InjectionMethods.java
deleted file mode 100644
index d61aaa597..000000000
--- a/java/dagger/internal/codegen/InjectionMethods.java
+++ /dev/null
@@ -1,544 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.CaseFormat.LOWER_CAMEL;
-import static com.google.common.base.CaseFormat.UPPER_CAMEL;
-import static com.google.common.base.Preconditions.checkArgument;
-import static dagger.internal.codegen.ConfigurationAnnotations.getNullableType;
-import static dagger.internal.codegen.DaggerStreams.toImmutableList;
-import static dagger.internal.codegen.FactoryGenerator.checkNotNullProvidesMethod;
-import static dagger.internal.codegen.RequestKinds.requestTypeName;
-import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
-import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
-import static dagger.internal.codegen.javapoet.CodeBlocks.toConcatenatedCodeBlock;
-import static dagger.internal.codegen.javapoet.TypeNames.rawTypeName;
-import static dagger.internal.codegen.langmodel.Accessibility.isElementAccessibleFrom;
-import static dagger.internal.codegen.langmodel.Accessibility.isRawTypeAccessible;
-import static dagger.internal.codegen.langmodel.Accessibility.isRawTypePubliclyAccessible;
-import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
-import static java.util.stream.Collectors.toList;
-import static javax.lang.model.element.Modifier.STATIC;
-import static javax.lang.model.type.TypeKind.VOID;
-
-import com.google.auto.common.MoreElements;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.ParameterSpec;
-import com.squareup.javapoet.TypeName;
-import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
-import dagger.internal.codegen.javapoet.Expression;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.DependencyRequest;
-import dagger.model.RequestKind;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-import java.util.function.Function;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-
-/** Convenience methods for creating and invoking {@link InjectionMethod}s. */
-final class InjectionMethods {
-
- /**
- * A method that returns an object from a {@code @Provides} method or an {@code @Inject}ed
- * constructor. Its parameters match the dependency requests for constructor and members
- * injection.
- *
- * <p>For {@code @Provides} methods named "foo", the method name is "proxyFoo". If the
- * {@code @Provides} method and its raw parameter types are publicly accessible, no method is
- * necessary and this method returns {@link Optional#empty()}.
- *
- * <p>Example:
- *
- * <pre><code>
- * abstract class FooModule {
- * {@literal @Provides} static Foo provideFoo(Bar bar, Baz baz) { … }
- * }
- *
- * public static proxyProvideFoo(Bar bar, Baz baz) { … }
- * </code></pre>
- *
- * <p>For {@code @Inject}ed constructors, the method name is "newFoo". If the constructor and its
- * raw parameter types are publicly accessible, no method is necessary and this method returns
- * {@code Optional#empty()}.
- *
- * <p>Example:
- *
- * <pre><code>
- * class Foo {
- * {@literal @Inject} Foo(Bar bar) {}
- * }
- *
- * public static Foo newFoo(Bar bar) { … }
- * </code></pre>
- */
- static final class ProvisionMethod {
- /**
- * Names of methods that are already defined in factories and shouldn't be used for the proxy
- * method name.
- */
- private static final ImmutableSet<String> BANNED_PROXY_NAMES = ImmutableSet.of("get", "create");
-
- /**
- * Returns a method that invokes the binding's {@linkplain ProvisionBinding#bindingElement()
- * constructor} and injects the instance's members.
- */
- static InjectionMethod create(
- ProvisionBinding binding, CompilerOptions compilerOptions, DaggerElements elements) {
- ClassName proxyEnclosingClass = generatedClassNameForBinding(binding);
- ExecutableElement element = MoreElements.asExecutable(binding.bindingElement().get());
- switch (element.getKind()) {
- case CONSTRUCTOR:
- return constructorProxy(proxyEnclosingClass, element, elements);
- case METHOD:
- return methodProxy(
- proxyEnclosingClass,
- element,
- methodName(element),
- ReceiverAccessibility.IGNORE,
- CheckNotNullPolicy.get(binding, compilerOptions),
- elements);
- default:
- throw new AssertionError(element);
- }
- }
-
- /**
- * Invokes the injection method for {@code binding}, with the dependencies transformed with the
- * {@code dependencyUsage} function.
- */
- // TODO(ronshapiro): Further extract a ProvisionMethod type that composes an InjectionMethod, so
- // users can write ProvisionMethod.create().invoke()
- static CodeBlock invoke(
- ProvisionBinding binding,
- Function<DependencyRequest, CodeBlock> dependencyUsage,
- ClassName requestingClass,
- Optional<CodeBlock> moduleReference,
- CompilerOptions compilerOptions,
- DaggerElements elements) {
- ImmutableList.Builder<CodeBlock> arguments = ImmutableList.builder();
- moduleReference.ifPresent(arguments::add);
- arguments.addAll(
- injectionMethodArguments(
- binding.provisionDependencies(), dependencyUsage, requestingClass));
- // TODO(ronshapiro): make InjectionMethods @Injectable
- return create(binding, compilerOptions, elements).invoke(arguments.build(), requestingClass);
- }
-
- private static InjectionMethod constructorProxy(
- ClassName proxyEnclosingClass, ExecutableElement constructor, DaggerElements elements) {
- TypeElement enclosingType = MoreElements.asType(constructor.getEnclosingElement());
- InjectionMethod.Builder injectionMethod =
- InjectionMethod.builder(elements)
- .name(methodName(constructor))
- .returnType(enclosingType.asType())
- .enclosingClass(proxyEnclosingClass);
-
- injectionMethod
- .copyTypeParameters(enclosingType)
- .copyThrows(constructor);
-
- CodeBlock arguments = injectionMethod.copyParameters(constructor);
- injectionMethod
- .methodBodyBuilder()
- .addStatement("return new $T($L)", enclosingType, arguments);
- return injectionMethod.build();
- }
-
- /**
- * Returns {@code true} if injecting an instance of {@code binding} from {@code callingPackage}
- * requires the use of an injection method.
- */
- static boolean requiresInjectionMethod(
- ProvisionBinding binding,
- ImmutableList<Expression> arguments,
- CompilerOptions compilerOptions,
- String callingPackage,
- DaggerTypes types) {
- ExecutableElement method = MoreElements.asExecutable(binding.bindingElement().get());
- return !binding.injectionSites().isEmpty()
- || binding.shouldCheckForNull(compilerOptions)
- || !isElementAccessibleFrom(method, callingPackage)
- || !areParametersAssignable(method, arguments, types)
- // This check should be removable once we drop support for -source 7
- || method.getParameters().stream()
- .map(VariableElement::asType)
- .anyMatch(type -> !isRawTypeAccessible(type, callingPackage));
- }
-
- private static boolean areParametersAssignable(
- ExecutableElement element, ImmutableList<Expression> arguments, DaggerTypes types) {
- List<? extends VariableElement> parameters = element.getParameters();
- checkArgument(parameters.size() == arguments.size());
- for (int i = 0; i < parameters.size(); i++) {
- if (!types.isAssignable(arguments.get(i).type(), parameters.get(i).asType())) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Returns the name of the {@code static} method that wraps {@code method}. For methods that are
- * associated with {@code @Inject} constructors, the method will also inject all {@link
- * InjectionSite}s.
- */
- private static String methodName(ExecutableElement method) {
- switch (method.getKind()) {
- case CONSTRUCTOR:
- return "newInstance";
- case METHOD:
- String methodName = method.getSimpleName().toString();
- return BANNED_PROXY_NAMES.contains(methodName)
- ? "proxy" + LOWER_CAMEL.to(UPPER_CAMEL, methodName)
- : methodName;
- default:
- throw new AssertionError(method);
- }
- }
- }
-
- /**
- * A static method that injects one member of an instance of a type. Its first parameter is an
- * instance of the type to be injected. The remaining parameters match the dependency requests for
- * the injection site.
- *
- * <p>Example:
- *
- * <pre><code>
- * class Foo {
- * {@literal @Inject} Bar bar;
- * {@literal @Inject} void setThings(Baz baz, Qux qux) {}
- * }
- *
- * public static injectBar(Foo instance, Bar bar) { … }
- * public static injectSetThings(Foo instance, Baz baz, Qux qux) { … }
- * </code></pre>
- */
- static final class InjectionSiteMethod {
- /**
- * When a type has an inaccessible member from a supertype (e.g. an @Inject field in a parent
- * that's in a different package), a method in the supertype's package must be generated to give
- * the subclass's members injector a way to inject it. Each potentially inaccessible member
- * receives its own method, as the subclass may need to inject them in a different order from
- * the parent class.
- */
- static InjectionMethod create(InjectionSite injectionSite, DaggerElements elements) {
- String methodName = methodName(injectionSite);
- ClassName proxyEnclosingClass = membersInjectorNameForType(
- MoreElements.asType(injectionSite.element().getEnclosingElement()));
- switch (injectionSite.kind()) {
- case METHOD:
- return methodProxy(
- proxyEnclosingClass,
- MoreElements.asExecutable(injectionSite.element()),
- methodName,
- ReceiverAccessibility.CAST_IF_NOT_PUBLIC,
- CheckNotNullPolicy.IGNORE,
- elements);
- case FIELD:
- return fieldProxy(
- proxyEnclosingClass,
- MoreElements.asVariable(injectionSite.element()),
- methodName,
- elements);
- }
- throw new AssertionError(injectionSite);
- }
-
- /**
- * Invokes each of the injection methods for {@code injectionSites}, with the dependencies
- * transformed using the {@code dependencyUsage} function.
- *
- * @param instanceType the type of the {@code instance} parameter
- */
- static CodeBlock invokeAll(
- ImmutableSet<InjectionSite> injectionSites,
- ClassName generatedTypeName,
- CodeBlock instanceCodeBlock,
- TypeMirror instanceType,
- DaggerTypes types,
- Function<DependencyRequest, CodeBlock> dependencyUsage,
- DaggerElements elements) {
- return injectionSites
- .stream()
- .map(
- injectionSite -> {
- TypeMirror injectSiteType =
- types.erasure(injectionSite.element().getEnclosingElement().asType());
-
- // If instance has been declared as Object because it is not accessible from the
- // component, but the injectionSite is in a supertype of instanceType that is
- // publicly accessible, the InjectionSiteMethod will request the actual type and not
- // Object as the first parameter. If so, cast to the supertype which is accessible
- // from within generatedTypeName
- CodeBlock maybeCastedInstance =
- !types.isSubtype(instanceType, injectSiteType)
- && isTypeAccessibleFrom(injectSiteType, generatedTypeName.packageName())
- ? CodeBlock.of("($T) $L", injectSiteType, instanceCodeBlock)
- : instanceCodeBlock;
- return CodeBlock.of(
- "$L;",
- invoke(
- injectionSite,
- generatedTypeName,
- maybeCastedInstance,
- dependencyUsage,
- elements));
- })
- .collect(toConcatenatedCodeBlock());
- }
-
- /**
- * Invokes the injection method for {@code injectionSite}, with the dependencies transformed
- * using the {@code dependencyUsage} function.
- */
- private static CodeBlock invoke(
- InjectionSite injectionSite,
- ClassName generatedTypeName,
- CodeBlock instanceCodeBlock,
- Function<DependencyRequest, CodeBlock> dependencyUsage,
- DaggerElements elements) {
- List<CodeBlock> arguments = new ArrayList<>();
- arguments.add(instanceCodeBlock);
- if (!injectionSite.dependencies().isEmpty()) {
- arguments.addAll(
- injectionSite
- .dependencies()
- .stream()
- .map(dependencyUsage)
- .collect(toList()));
- }
- return create(injectionSite, elements).invoke(arguments, generatedTypeName);
- }
-
- /*
- * TODO(ronshapiro): this isn't perfect, as collisions could still exist. Some examples:
- *
- * - @Inject void members() {} will generate a method that conflicts with the instance
- * method `injectMembers(T)`
- * - Adding the index could conflict with another member:
- * @Inject void a(Object o) {}
- * @Inject void a(String s) {}
- * @Inject void a1(String s) {}
- *
- * Here, Method a(String) will add the suffix "1", which will conflict with the method
- * generated for a1(String)
- * - Members named "members" or "methods" could also conflict with the {@code static} injection
- * method.
- */
- private static String methodName(InjectionSite injectionSite) {
- int index = injectionSite.indexAmongAtInjectMembersWithSameSimpleName();
- String indexString = index == 0 ? "" : String.valueOf(index + 1);
- return "inject"
- + LOWER_CAMEL.to(UPPER_CAMEL, injectionSite.element().getSimpleName().toString())
- + indexString;
- }
- }
-
- /**
- * Returns an argument list suitable for calling an injection method. Down-casts any arguments
- * that are {@code Object} (or {@code Provider<Object>}) at the caller but not the method.
- *
- * @param dependencies the dependencies used by the method
- * @param dependencyUsage function to apply on each of {@code dependencies} before casting
- * @param requestingClass the class calling the injection method
- */
- private static ImmutableList<CodeBlock> injectionMethodArguments(
- ImmutableSet<DependencyRequest> dependencies,
- Function<DependencyRequest, CodeBlock> dependencyUsage,
- ClassName requestingClass) {
- return dependencies.stream()
- .map(dep -> injectionMethodArgument(dep, dependencyUsage.apply(dep), requestingClass))
- .collect(toImmutableList());
- }
-
- private static CodeBlock injectionMethodArgument(
- DependencyRequest dependency, CodeBlock argument, ClassName generatedTypeName) {
- TypeMirror keyType = dependency.key().type();
- CodeBlock.Builder codeBlock = CodeBlock.builder();
- if (!isRawTypeAccessible(keyType, generatedTypeName.packageName())
- && isTypeAccessibleFrom(keyType, generatedTypeName.packageName())) {
- if (!dependency.kind().equals(RequestKind.INSTANCE)) {
- TypeName usageTypeName = accessibleType(dependency);
- codeBlock.add("($T) ($T)", usageTypeName, rawTypeName(usageTypeName));
- } else if (dependency.requestElement().get().asType().getKind().equals(TypeKind.TYPEVAR)) {
- codeBlock.add("($T)", keyType);
- }
- }
- return codeBlock.add(argument).build();
- }
-
- /**
- * Returns the parameter type for {@code dependency}. If the raw type is not accessible, returns
- * {@link Object}.
- */
- private static TypeName accessibleType(DependencyRequest dependency) {
- TypeName typeName = requestTypeName(dependency.kind(), accessibleType(dependency.key().type()));
- return dependency
- .requestElement()
- .map(element -> element.asType().getKind().isPrimitive())
- .orElse(false)
- ? typeName.unbox()
- : typeName;
- }
-
- /**
- * Returns the accessible type for {@code type}. If the raw type is not accessible, returns {@link
- * Object}.
- */
- private static TypeName accessibleType(TypeMirror type) {
- return isRawTypePubliclyAccessible(type) ? TypeName.get(type) : TypeName.OBJECT;
- }
-
- /**
- * Returns the accessible type for {@code type}. If the raw type is not accessible, returns {@link
- * Object}.
- */
- // TODO(ronshapiro): Can we use DaggerTypes.publiclyAccessibleType in place of this method?
- private static TypeMirror accessibleType(TypeMirror type, DaggerElements elements) {
- return isRawTypePubliclyAccessible(type)
- ? type
- : elements.getTypeElement(Object.class).asType();
- }
-
- private enum ReceiverAccessibility {
- CAST_IF_NOT_PUBLIC {
- @Override
- TypeMirror parameterType(TypeMirror type, DaggerElements elements) {
- return accessibleType(type, elements);
- }
-
- @Override
- CodeBlock potentiallyCast(CodeBlock instance, TypeMirror instanceType) {
- return instanceWithPotentialCast(instance, instanceType);
- }
- },
- IGNORE {
- @Override
- TypeMirror parameterType(TypeMirror type, DaggerElements elements) {
- return type;
- }
-
- @Override
- CodeBlock potentiallyCast(CodeBlock instance, TypeMirror instanceType) {
- return instance;
- }
- },
- ;
-
- abstract TypeMirror parameterType(TypeMirror type, DaggerElements elements);
- abstract CodeBlock potentiallyCast(CodeBlock instance, TypeMirror instanceType);
- }
-
- private static CodeBlock instanceWithPotentialCast(CodeBlock instance, TypeMirror instanceType) {
- return isRawTypePubliclyAccessible(instanceType)
- ? instance
- : CodeBlock.of("(($T) $L)", instanceType, instance);
- }
-
- private enum CheckNotNullPolicy {
- IGNORE, CHECK_FOR_NULL;
- CodeBlock checkForNull(CodeBlock maybeNull) {
- if (this.equals(IGNORE)) {
- return maybeNull;
- }
- return checkNotNullProvidesMethod(maybeNull);
- }
-
- static CheckNotNullPolicy get(ProvisionBinding binding, CompilerOptions compilerOptions) {
- return binding.shouldCheckForNull(compilerOptions) ? CHECK_FOR_NULL : IGNORE;
- }
- }
-
- private static InjectionMethod methodProxy(
- ClassName proxyEnclosingClass,
- ExecutableElement method,
- String methodName,
- ReceiverAccessibility receiverAccessibility,
- CheckNotNullPolicy checkNotNullPolicy,
- DaggerElements elements) {
- TypeElement enclosingType = MoreElements.asType(method.getEnclosingElement());
- InjectionMethod.Builder injectionMethod =
- InjectionMethod.builder(elements).name(methodName).enclosingClass(proxyEnclosingClass);
- ParameterSpec instance = null;
- if (!method.getModifiers().contains(STATIC)) {
- instance =
- injectionMethod.addParameter(
- "instance", receiverAccessibility.parameterType(enclosingType.asType(), elements));
- }
-
- CodeBlock arguments = injectionMethod.copyParameters(method);
- if (!method.getReturnType().getKind().equals(VOID)) {
- injectionMethod
- .returnType(method.getReturnType())
- .nullableAnnotation(getNullableType(method));
- injectionMethod.methodBodyBuilder().add("return ");
- }
- CodeBlock.Builder proxyInvocation = CodeBlock.builder();
- if (method.getModifiers().contains(STATIC)) {
- proxyInvocation.add("$T", rawTypeName(TypeName.get(enclosingType.asType())));
- } else {
- injectionMethod.copyTypeParameters(enclosingType);
- proxyInvocation.add(
- receiverAccessibility.potentiallyCast(
- CodeBlock.of("$N", instance), enclosingType.asType()));
- }
-
- injectionMethod
- .copyTypeParameters(method)
- .copyThrows(method);
-
- proxyInvocation.add(".$N($L)", method.getSimpleName(), arguments);
- injectionMethod
- .methodBodyBuilder()
- .add(checkNotNullPolicy.checkForNull(proxyInvocation.build()))
- .add(";\n");
- return injectionMethod.build();
- }
-
- private static InjectionMethod fieldProxy(
- ClassName proxyEnclosingClass,
- VariableElement field,
- String methodName,
- DaggerElements elements) {
- TypeElement enclosingType = MoreElements.asType(field.getEnclosingElement());
- InjectionMethod.Builder injectionMethod =
- InjectionMethod.builder(elements).name(methodName).enclosingClass(proxyEnclosingClass);
- injectionMethod.copyTypeParameters(enclosingType);
-
- ParameterSpec instance =
- injectionMethod.addParameter("instance", accessibleType(enclosingType.asType(), elements));
- CodeBlock parameter = injectionMethod.copyParameter(field);
- injectionMethod
- .methodBodyBuilder()
- .addStatement(
- "$L.$L = $L",
- instanceWithPotentialCast(CodeBlock.of("$N", instance), enclosingType.asType()),
- field.getSimpleName(),
- parameter);
- return injectionMethod.build();
- }
-}
diff --git a/java/dagger/internal/codegen/InjectionOrProvisionProviderCreationExpression.java b/java/dagger/internal/codegen/InjectionOrProvisionProviderCreationExpression.java
deleted file mode 100644
index 2d2e8cb11..000000000
--- a/java/dagger/internal/codegen/InjectionOrProvisionProviderCreationExpression.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
-import static dagger.model.BindingKind.INJECTION;
-
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
-import dagger.internal.codegen.javapoet.CodeBlocks;
-import javax.inject.Provider;
-
-/**
- * A {@link Provider} creation expression for an {@link javax.inject.Inject @Inject}-constructed
- * class or a {@link dagger.Provides @Provides}-annotated module method.
- */
-// TODO(dpb): Resolve with ProducerCreationExpression.
-final class InjectionOrProvisionProviderCreationExpression
- implements FrameworkInstanceCreationExpression {
-
- private final ContributionBinding binding;
- private final ComponentBindingExpressions componentBindingExpressions;
-
- InjectionOrProvisionProviderCreationExpression(
- ContributionBinding binding, ComponentBindingExpressions componentBindingExpressions) {
- this.binding = checkNotNull(binding);
- this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
- }
-
- @Override
- public CodeBlock creationExpression() {
- CodeBlock createFactory =
- CodeBlock.of(
- "$T.create($L)",
- generatedClassNameForBinding(binding),
- componentBindingExpressions.getCreateMethodArgumentsCodeBlock(binding));
-
- // When scoping a parameterized factory for an @Inject class, Java 7 cannot always infer the
- // type properly, so cast to a raw framework type before scoping.
- if (binding.kind().equals(INJECTION)
- && binding.unresolved().isPresent()
- && binding.scope().isPresent()) {
- return CodeBlocks.cast(createFactory, Provider.class);
- } else {
- return createFactory;
- }
- }
-}
diff --git a/java/dagger/internal/codegen/InjectionSiteFactory.java b/java/dagger/internal/codegen/InjectionSiteFactory.java
deleted file mode 100644
index d09f91d3e..000000000
--- a/java/dagger/internal/codegen/InjectionSiteFactory.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static dagger.internal.codegen.MembersInjectionBinding.InjectionSite.Kind.METHOD;
-import static dagger.internal.codegen.langmodel.DaggerElements.DECLARATION_ORDER;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.STATIC;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableSortedSet;
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.SetMultimap;
-import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import javax.inject.Inject;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementVisitor;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.ElementKindVisitor8;
-
-/** A factory for {@link Binding} objects. */
-final class InjectionSiteFactory {
- private final ElementVisitor<Optional<InjectionSite>, DeclaredType> injectionSiteVisitor =
- new ElementKindVisitor8<Optional<InjectionSite>, DeclaredType>(Optional.empty()) {
- @Override
- public Optional<InjectionSite> visitExecutableAsMethod(
- ExecutableElement method, DeclaredType type) {
- ExecutableType resolved = MoreTypes.asExecutable(types.asMemberOf(type, method));
- return Optional.of(
- InjectionSite.method(
- method,
- dependencyRequestFactory.forRequiredResolvedVariables(
- method.getParameters(), resolved.getParameterTypes())));
- }
-
- @Override
- public Optional<InjectionSite> visitVariableAsField(
- VariableElement field, DeclaredType type) {
- if (!isAnnotationPresent(field, Inject.class)
- || field.getModifiers().contains(PRIVATE)
- || field.getModifiers().contains(STATIC)) {
- return Optional.empty();
- }
- TypeMirror resolved = types.asMemberOf(type, field);
- return Optional.of(
- InjectionSite.field(
- field, dependencyRequestFactory.forRequiredResolvedVariable(field, resolved)));
- }
- };
-
- private final DaggerTypes types;
- private final DaggerElements elements;
- private final DependencyRequestFactory dependencyRequestFactory;
-
- @Inject
- InjectionSiteFactory(
- DaggerTypes types,
- DaggerElements elements,
- DependencyRequestFactory dependencyRequestFactory) {
- this.types = types;
- this.elements = elements;
- this.dependencyRequestFactory = dependencyRequestFactory;
- }
-
- /** Returns the injection sites for a type. */
- ImmutableSortedSet<InjectionSite> getInjectionSites(DeclaredType declaredType) {
- Set<InjectionSite> injectionSites = new HashSet<>();
- List<TypeElement> ancestors = new ArrayList<>();
- SetMultimap<String, ExecutableElement> overriddenMethodMap = LinkedHashMultimap.create();
- for (Optional<DeclaredType> currentType = Optional.of(declaredType);
- currentType.isPresent();
- currentType = types.nonObjectSuperclass(currentType.get())) {
- DeclaredType type = currentType.get();
- ancestors.add(MoreElements.asType(type.asElement()));
- for (Element enclosedElement : type.asElement().getEnclosedElements()) {
- Optional<InjectionSite> maybeInjectionSite =
- injectionSiteVisitor.visit(enclosedElement, type);
- if (maybeInjectionSite.isPresent()) {
- InjectionSite injectionSite = maybeInjectionSite.get();
- if (shouldBeInjected(injectionSite.element(), overriddenMethodMap)) {
- injectionSites.add(injectionSite);
- }
- if (injectionSite.kind().equals(METHOD)) {
- ExecutableElement injectionSiteMethod =
- MoreElements.asExecutable(injectionSite.element());
- overriddenMethodMap.put(
- injectionSiteMethod.getSimpleName().toString(), injectionSiteMethod);
- }
- }
- }
- }
- return ImmutableSortedSet.copyOf(
- // supertypes before subtypes
- Comparator.comparing(
- (InjectionSite injectionSite) ->
- ancestors.indexOf(injectionSite.element().getEnclosingElement()))
- .reversed()
- // fields before methods
- .thenComparing(injectionSite -> injectionSite.element().getKind())
- // then sort by whichever element comes first in the parent
- // this isn't necessary, but makes the processor nice and predictable
- .thenComparing(InjectionSite::element, DECLARATION_ORDER),
- injectionSites);
- }
-
- private boolean shouldBeInjected(
- Element injectionSite, SetMultimap<String, ExecutableElement> overriddenMethodMap) {
- if (!isAnnotationPresent(injectionSite, Inject.class)
- || injectionSite.getModifiers().contains(PRIVATE)
- || injectionSite.getModifiers().contains(STATIC)) {
- return false;
- }
-
- if (injectionSite.getKind().isField()) { // Inject all fields (self and ancestors)
- return true;
- }
-
- // For each method with the same name belonging to any descendant class, return false if any
- // method has already overridden the injectionSite method. To decrease the number of methods
- // that are checked, we store the already injected methods in a SetMultimap and only
- // check the methods with the same name.
- ExecutableElement injectionSiteMethod = MoreElements.asExecutable(injectionSite);
- TypeElement injectionSiteType = MoreElements.asType(injectionSite.getEnclosingElement());
- for (ExecutableElement method :
- overriddenMethodMap.get(injectionSiteMethod.getSimpleName().toString())) {
- if (elements.overrides(method, injectionSiteMethod, injectionSiteType)) {
- return false;
- }
- }
- return true;
- }
-}
diff --git a/java/dagger/internal/codegen/InnerSwitchingProviders.java b/java/dagger/internal/codegen/InnerSwitchingProviders.java
deleted file mode 100644
index 74c4366e1..000000000
--- a/java/dagger/internal/codegen/InnerSwitchingProviders.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.squareup.javapoet.MethodSpec.constructorBuilder;
-import static dagger.internal.codegen.BindingRequest.bindingRequest;
-import static dagger.model.RequestKind.INSTANCE;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
-
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.TypeName;
-import com.squareup.javapoet.TypeSpec;
-import dagger.internal.codegen.javapoet.Expression;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.Key;
-import javax.inject.Provider;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * Generates {@linkplain BindingExpression binding expressions} for a binding that is represented by
- * an inner {@code SwitchingProvider} class.
- */
-final class InnerSwitchingProviders extends SwitchingProviders {
- private final ComponentBindingExpressions componentBindingExpressions;
- private final DaggerTypes types;
-
- InnerSwitchingProviders(
- ComponentImplementation componentImplementation,
- ComponentBindingExpressions componentBindingExpressions,
- DaggerTypes types) {
- super(componentImplementation, types);
- this.componentBindingExpressions = componentBindingExpressions;
- this.types = types;
- }
-
- /**
- * Returns the binding expression for a binding that satisfies a {@link Provider} requests with a
- * inner {@code SwitchingProvider} class.
- */
- BindingExpression newBindingExpression(ContributionBinding binding) {
- return new BindingExpression() {
- @Override
- Expression getDependencyExpression(ClassName requestingClass) {
- return getProviderExpression(new SwitchCase(binding, requestingClass));
- }
- };
- }
-
- @Override
- protected TypeSpec createSwitchingProviderType(TypeSpec.Builder builder) {
- return builder
- .addModifiers(PRIVATE, FINAL)
- .addField(TypeName.INT, "id", PRIVATE, FINAL)
- .addMethod(
- constructorBuilder()
- .addParameter(TypeName.INT, "id")
- .addStatement("this.id = id")
- .build())
- .build();
- }
-
- private final class SwitchCase implements SwitchingProviders.SwitchCase {
- private final ContributionBinding binding;
- private final ClassName requestingClass;
-
- SwitchCase(ContributionBinding binding, ClassName requestingClass) {
- this.binding = binding;
- this.requestingClass = requestingClass;
- }
-
- @Override
- public Key key() {
- return binding.key();
- }
-
- @Override
- public Expression getProviderExpression(ClassName switchingProviderClass, int switchId) {
- TypeMirror instanceType = types.accessibleType(binding.contributedType(), requestingClass);
- return Expression.create(
- types.wrapType(instanceType, Provider.class),
- CodeBlock.of("new $T<>($L)", switchingProviderClass, switchId));
- }
-
- @Override
- public Expression getReturnExpression(ClassName switchingProviderClass) {
- return componentBindingExpressions.getDependencyExpression(
- bindingRequest(binding.key(), INSTANCE), switchingProviderClass);
- }
- }
-}
diff --git a/java/dagger/internal/codegen/InstanceFactoryCreationExpression.java b/java/dagger/internal/codegen/InstanceFactoryCreationExpression.java
deleted file mode 100644
index c9b26d755..000000000
--- a/java/dagger/internal/codegen/InstanceFactoryCreationExpression.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.InstanceFactory;
-import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
-import java.util.function.Supplier;
-
-/**
- * A {@link FrameworkInstanceCreationExpression} that creates an {@link InstanceFactory} for an
- * instance.
- */
-final class InstanceFactoryCreationExpression implements FrameworkInstanceCreationExpression {
-
- private final boolean nullable;
- private final Supplier<CodeBlock> instanceExpression;
-
- InstanceFactoryCreationExpression(Supplier<CodeBlock> instanceExpression) {
- this(false, instanceExpression);
- }
-
- InstanceFactoryCreationExpression(boolean nullable, Supplier<CodeBlock> instanceExpression) {
- this.nullable = nullable;
- this.instanceExpression = checkNotNull(instanceExpression);
- }
-
- @Override
- public CodeBlock creationExpression() {
- return CodeBlock.of(
- "$T.$L($L)",
- InstanceFactory.class,
- nullable ? "createNullable" : "create",
- instanceExpression.get());
- }
-
- @Override
- public boolean useInnerSwitchingProvider() {
- return false;
- }
-}
diff --git a/java/dagger/internal/codegen/JavacPluginModule.java b/java/dagger/internal/codegen/JavacPluginModule.java
deleted file mode 100644
index bc5b66e30..000000000
--- a/java/dagger/internal/codegen/JavacPluginModule.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.ValidationType.NONE;
-import static javax.tools.Diagnostic.Kind.NOTE;
-
-import com.sun.tools.javac.model.JavacElements;
-import com.sun.tools.javac.model.JavacTypes;
-import com.sun.tools.javac.util.Context;
-import dagger.Binds;
-import dagger.Module;
-import dagger.Provides;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import javax.annotation.processing.Messager;
-import javax.inject.Inject;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.util.Types;
-import javax.tools.Diagnostic;
-
-/**
- * A module that provides a {@link BindingGraphFactory} and {@link ComponentDescriptorFactory} for
- * use in {@code javac} plugins. Requires a binding for the {@code javac} {@link Context}.
- */
-@Module(includes = InjectBindingRegistryModule.class)
-abstract class JavacPluginModule {
- @Provides
- static CompilerOptions compilerOptions() {
- return new CompilerOptions() {
- @Override
- boolean usesProducers() {
- return true;
- }
-
- @Override
- boolean fastInit() {
- return false;
- }
-
- @Override
- boolean formatGeneratedSource() {
- return false;
- }
-
- @Override
- boolean writeProducerNameInToken() {
- return true;
- }
-
- @Override
- Diagnostic.Kind nullableValidationKind() {
- return NOTE;
- }
-
- @Override
- Diagnostic.Kind privateMemberValidationKind() {
- return NOTE;
- }
-
- @Override
- Diagnostic.Kind staticMemberValidationKind() {
- return NOTE;
- }
-
- @Override
- boolean ignorePrivateAndStaticInjectionForComponent() {
- return false;
- }
-
- @Override
- ValidationType scopeCycleValidationType() {
- return NONE;
- }
-
- @Override
- boolean warnIfInjectionFactoryNotGeneratedUpstream() {
- return false;
- }
-
- @Override
- boolean headerCompilation() {
- return false;
- }
-
- @Override
- boolean aheadOfTimeSubcomponents() {
- return false;
- }
-
- @Override
- boolean forceUseSerializedComponentImplementations() {
- return false;
- }
-
- @Override
- boolean emitModifiableMetadataAnnotations() {
- return false;
- }
-
- @Override
- boolean useGradleIncrementalProcessing() {
- return false;
- }
-
- @Override
- ValidationType fullBindingGraphValidationType(TypeElement element) {
- return NONE;
- }
-
- @Override
- Diagnostic.Kind moduleHasDifferentScopesDiagnosticKind() {
- return NOTE;
- }
-
- @Override
- ValidationType explicitBindingConflictsWithInjectValidationType() {
- return NONE;
- }
- };
- }
-
- @Binds
- abstract Messager messager(NullMessager nullMessager);
-
- static final class NullMessager implements Messager {
-
- @Inject
- NullMessager() {}
-
- @Override
- public void printMessage(Diagnostic.Kind kind, CharSequence charSequence) {}
-
- @Override
- public void printMessage(Diagnostic.Kind kind, CharSequence charSequence, Element element) {}
-
- @Override
- public void printMessage(
- Diagnostic.Kind kind,
- CharSequence charSequence,
- Element element,
- AnnotationMirror annotationMirror) {}
-
- @Override
- public void printMessage(
- Diagnostic.Kind kind,
- CharSequence charSequence,
- Element element,
- AnnotationMirror annotationMirror,
- AnnotationValue annotationValue) {}
- }
-
- @Provides
- static DaggerElements daggerElements(Context javaContext) {
- return new DaggerElements(
- JavacElements.instance(javaContext), JavacTypes.instance(javaContext));
- }
-
- @Provides
- static DaggerTypes daggerTypes(Context javaContext, DaggerElements elements) {
- return new DaggerTypes(JavacTypes.instance(javaContext), elements);
- }
-
- @Binds abstract Types types(DaggerTypes daggerTypes);
-
- private JavacPluginModule() {}
-}
diff --git a/java/dagger/internal/codegen/KeyFactory.java b/java/dagger/internal/codegen/KeyFactory.java
deleted file mode 100644
index b6ac27fca..000000000
--- a/java/dagger/internal/codegen/KeyFactory.java
+++ /dev/null
@@ -1,469 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.auto.common.MoreTypes.asExecutable;
-import static com.google.auto.common.MoreTypes.isType;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.InjectionAnnotations.getQualifier;
-import static dagger.internal.codegen.MapKeys.getMapKey;
-import static dagger.internal.codegen.MapKeys.mapKeyType;
-import static dagger.internal.codegen.Optionals.firstPresent;
-import static dagger.internal.codegen.RequestKinds.extractKeyType;
-import static dagger.internal.codegen.RequestKinds.getRequestKind;
-import static dagger.internal.codegen.langmodel.DaggerTypes.isFutureType;
-import static java.util.Arrays.asList;
-import static javax.lang.model.element.ElementKind.METHOD;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableSet;
-import dagger.Binds;
-import dagger.BindsOptionalOf;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.internal.codegen.serialization.KeyProto;
-import dagger.model.Key;
-import dagger.model.Key.MultibindingContributionIdentifier;
-import dagger.model.RequestKind;
-import dagger.multibindings.Multibinds;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
-import dagger.producers.Production;
-import dagger.producers.internal.ProductionImplementation;
-import dagger.producers.monitoring.ProductionComponentMonitor;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.Executor;
-import java.util.stream.Stream;
-import javax.inject.Inject;
-import javax.inject.Provider;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.PrimitiveType;
-import javax.lang.model.type.TypeMirror;
-
-/** A factory for {@link Key}s. */
-final class KeyFactory {
- private final DaggerTypes types;
- private final DaggerElements elements;
- private final TypeProtoConverter typeProtoConverter;
- private final AnnotationProtoConverter annotationProtoConverter;
-
- @Inject
- KeyFactory(
- DaggerTypes types,
- DaggerElements elements,
- TypeProtoConverter typeProtoConverter,
- AnnotationProtoConverter annotationProtoConverter) {
- this.types = checkNotNull(types);
- this.elements = checkNotNull(elements);
- this.typeProtoConverter = typeProtoConverter;
- this.annotationProtoConverter = annotationProtoConverter;
- }
-
- private TypeMirror boxPrimitives(TypeMirror type) {
- return type.getKind().isPrimitive() ? types.boxedClass((PrimitiveType) type).asType() : type;
- }
-
- private DeclaredType setOf(TypeMirror elementType) {
- return types.getDeclaredType(elements.getTypeElement(Set.class), boxPrimitives(elementType));
- }
-
- private DeclaredType mapOf(TypeMirror keyType, TypeMirror valueType) {
- return types.getDeclaredType(
- elements.getTypeElement(Map.class), boxPrimitives(keyType), boxPrimitives(valueType));
- }
-
- /** Returns {@code Map<KeyType, FrameworkType<ValueType>>}. */
- private TypeMirror mapOfFrameworkType(
- TypeMirror keyType, TypeElement frameworkType, TypeMirror valueType) {
- return mapOf(keyType, types.getDeclaredType(frameworkType, boxPrimitives(valueType)));
- }
-
- Key forComponentMethod(ExecutableElement componentMethod) {
- checkArgument(componentMethod.getKind().equals(METHOD));
- return forMethod(componentMethod, componentMethod.getReturnType());
- }
-
- Key forProductionComponentMethod(ExecutableElement componentMethod) {
- checkArgument(componentMethod.getKind().equals(METHOD));
- TypeMirror returnType = componentMethod.getReturnType();
- TypeMirror keyType =
- isFutureType(returnType)
- ? getOnlyElement(MoreTypes.asDeclared(returnType).getTypeArguments())
- : returnType;
- return forMethod(componentMethod, keyType);
- }
-
- Key forSubcomponentCreatorMethod(
- ExecutableElement subcomponentCreatorMethod, DeclaredType declaredContainer) {
- checkArgument(subcomponentCreatorMethod.getKind().equals(METHOD));
- ExecutableType resolvedMethod =
- asExecutable(types.asMemberOf(declaredContainer, subcomponentCreatorMethod));
- return Key.builder(resolvedMethod.getReturnType()).build();
- }
-
- Key forSubcomponentCreator(TypeMirror creatorType) {
- return Key.builder(creatorType).build();
- }
-
- Key forProvidesMethod(ExecutableElement method, TypeElement contributingModule) {
- return forBindingMethod(
- method, contributingModule, Optional.of(elements.getTypeElement(Provider.class)));
- }
-
- Key forProducesMethod(ExecutableElement method, TypeElement contributingModule) {
- return forBindingMethod(
- method, contributingModule, Optional.of(elements.getTypeElement(Producer.class)));
- }
-
- /** Returns the key bound by a {@link Binds} method. */
- Key forBindsMethod(ExecutableElement method, TypeElement contributingModule) {
- checkArgument(isAnnotationPresent(method, Binds.class));
- return forBindingMethod(method, contributingModule, Optional.empty());
- }
-
- /** Returns the base key bound by a {@link BindsOptionalOf} method. */
- Key forBindsOptionalOfMethod(ExecutableElement method, TypeElement contributingModule) {
- checkArgument(isAnnotationPresent(method, BindsOptionalOf.class));
- return forBindingMethod(method, contributingModule, Optional.empty());
- }
-
- private Key forBindingMethod(
- ExecutableElement method,
- TypeElement contributingModule,
- Optional<TypeElement> frameworkType) {
- checkArgument(method.getKind().equals(METHOD));
- ExecutableType methodType =
- MoreTypes.asExecutable(
- types.asMemberOf(MoreTypes.asDeclared(contributingModule.asType()), method));
- ContributionType contributionType = ContributionType.fromBindingElement(method);
- TypeMirror returnType = methodType.getReturnType();
- if (frameworkType.isPresent()
- && frameworkType.get().equals(elements.getTypeElement(Producer.class))
- && isType(returnType)) {
- if (isFutureType(methodType.getReturnType())) {
- returnType = getOnlyElement(MoreTypes.asDeclared(returnType).getTypeArguments());
- } else if (contributionType.equals(ContributionType.SET_VALUES)
- && SetType.isSet(returnType)) {
- SetType setType = SetType.from(returnType);
- if (isFutureType(setType.elementType())) {
- returnType =
- types.getDeclaredType(
- elements.getTypeElement(Set.class), types.unwrapType(setType.elementType()));
- }
- }
- }
- TypeMirror keyType = bindingMethodKeyType(returnType, method, contributionType, frameworkType);
- Key key = forMethod(method, keyType);
- return contributionType.equals(ContributionType.UNIQUE)
- ? key
- : key.toBuilder()
- .multibindingContributionIdentifier(
- new MultibindingContributionIdentifier(method, contributingModule))
- .build();
- }
-
- /**
- * Returns the key for a {@link Multibinds @Multibinds} method.
- *
- * <p>The key's type is either {@code Set<T>} or {@code Map<K, Provider<V>>}. The latter works
- * even for maps used by {@code Producer}s.
- */
- Key forMultibindsMethod(ExecutableType executableType, ExecutableElement method) {
- checkArgument(method.getKind().equals(METHOD), "%s must be a method", method);
- TypeMirror returnType = executableType.getReturnType();
- TypeMirror keyType =
- MapType.isMap(returnType)
- ? mapOfFrameworkType(
- MapType.from(returnType).keyType(),
- elements.getTypeElement(Provider.class),
- MapType.from(returnType).valueType())
- : returnType;
- return forMethod(method, keyType);
- }
-
- private TypeMirror bindingMethodKeyType(
- TypeMirror returnType,
- ExecutableElement method,
- ContributionType contributionType,
- Optional<TypeElement> frameworkType) {
- switch (contributionType) {
- case UNIQUE:
- return returnType;
- case SET:
- return setOf(returnType);
- case MAP:
- TypeMirror mapKeyType = mapKeyType(getMapKey(method).get(), types);
- return frameworkType.isPresent()
- ? mapOfFrameworkType(mapKeyType, frameworkType.get(), returnType)
- : mapOf(mapKeyType, returnType);
- case SET_VALUES:
- // TODO(gak): do we want to allow people to use "covariant return" here?
- checkArgument(SetType.isSet(returnType));
- return returnType;
- }
- throw new AssertionError();
- }
-
- /**
- * Returns the key for a binding associated with a {@link DelegateDeclaration}.
- *
- * <p>If {@code delegateDeclaration} is {@code @IntoMap}, transforms the {@code Map<K, V>} key
- * from {@link DelegateDeclaration#key()} to {@code Map<K, FrameworkType<V>>}. If {@code
- * delegateDeclaration} is not a map contribution, its key is returned.
- */
- Key forDelegateBinding(DelegateDeclaration delegateDeclaration, Class<?> frameworkType) {
- return delegateDeclaration.contributionType().equals(ContributionType.MAP)
- ? wrapMapValue(delegateDeclaration.key(), frameworkType)
- : delegateDeclaration.key();
- }
-
- private Key forMethod(ExecutableElement method, TypeMirror keyType) {
- return forQualifiedType(getQualifier(method), keyType);
- }
-
- Key forInjectConstructorWithResolvedType(TypeMirror type) {
- return Key.builder(type).build();
- }
-
- // TODO(ronshapiro): Remove these conveniences which are simple wrappers around Key.Builder
- Key forType(TypeMirror type) {
- return Key.builder(type).build();
- }
-
- Key forMembersInjectedType(TypeMirror type) {
- return Key.builder(type).build();
- }
-
- Key forQualifiedType(Optional<AnnotationMirror> qualifier, TypeMirror type) {
- return Key.builder(boxPrimitives(type)).qualifier(qualifier).build();
- }
-
- Key forProductionExecutor() {
- return Key.builder(elements.getTypeElement(Executor.class).asType())
- .qualifier(SimpleAnnotationMirror.of(elements.getTypeElement(Production.class)))
- .build();
- }
-
- Key forProductionImplementationExecutor() {
- return Key.builder(elements.getTypeElement(Executor.class).asType())
- .qualifier(SimpleAnnotationMirror.of(elements.getTypeElement(ProductionImplementation.class)))
- .build();
- }
-
- Key forProductionComponentMonitor() {
- return Key.builder(elements.getTypeElement(ProductionComponentMonitor.class).asType()).build();
- }
-
- /**
- * If {@code requestKey} is for a {@code Map<K, V>} or {@code Map<K, Produced<V>>}, returns keys
- * for {@code Map<K, Provider<V>>} and {@code Map<K, Producer<V>>} (if Dagger-Producers is on
- * the classpath).
- */
- ImmutableSet<Key> implicitFrameworkMapKeys(Key requestKey) {
- return Stream.of(implicitMapProviderKeyFrom(requestKey), implicitMapProducerKeyFrom(requestKey))
- .filter(Optional::isPresent)
- .map(Optional::get)
- .collect(toImmutableSet());
- }
-
- /**
- * Optionally extract a {@link Key} for the underlying provision binding(s) if such a valid key
- * can be inferred from the given key. Specifically, if the key represents a {@link Map}{@code
- * <K, V>} or {@code Map<K, Producer<V>>}, a key of {@code Map<K, Provider<V>>} will be
- * returned.
- */
- Optional<Key> implicitMapProviderKeyFrom(Key possibleMapKey) {
- return firstPresent(
- rewrapMapKey(possibleMapKey, Produced.class, Provider.class),
- wrapMapKey(possibleMapKey, Provider.class));
- }
-
- /**
- * Optionally extract a {@link Key} for the underlying production binding(s) if such a
- * valid key can be inferred from the given key. Specifically, if the key represents a
- * {@link Map}{@code <K, V>} or {@code Map<K, Produced<V>>}, a key of
- * {@code Map<K, Producer<V>>} will be returned.
- */
- Optional<Key> implicitMapProducerKeyFrom(Key possibleMapKey) {
- return firstPresent(
- rewrapMapKey(possibleMapKey, Produced.class, Producer.class),
- wrapMapKey(possibleMapKey, Producer.class));
- }
-
- /**
- * If {@code key}'s type is {@code Map<K, Provider<V>>}, {@code Map<K, Producer<V>>}, or {@code
- * Map<K, Produced<V>>}, returns a key with the same qualifier and {@link
- * Key#multibindingContributionIdentifier()} whose type is simply {@code Map<K, V>}.
- *
- * <p>Otherwise, returns {@code key}.
- */
- Key unwrapMapValueType(Key key) {
- if (MapType.isMap(key)) {
- MapType mapType = MapType.from(key);
- if (!mapType.isRawType()) {
- for (Class<?> frameworkClass : asList(Provider.class, Producer.class, Produced.class)) {
- if (mapType.valuesAreTypeOf(frameworkClass)) {
- return key.toBuilder()
- .type(mapOf(mapType.keyType(), mapType.unwrappedValueType(frameworkClass)))
- .build();
- }
- }
- }
- }
- return key;
- }
-
- /**
- * Converts a {@link Key} of type {@code Map<K, V>} to {@code Map<K, Provider<V>>}.
- */
- private Key wrapMapValue(Key key, Class<?> newWrappingClass) {
- checkArgument(
- FrameworkTypes.isFrameworkType(elements.getTypeElement(newWrappingClass).asType()));
- return wrapMapKey(key, newWrappingClass).get();
- }
-
- /**
- * If {@code key}'s type is {@code Map<K, CurrentWrappingClass<Bar>>}, returns a key with type
- * {@code Map<K, NewWrappingClass<Bar>>} with the same qualifier. Otherwise returns {@link
- * Optional#empty()}.
- *
- * <p>Returns {@link Optional#empty()} if {@code newWrappingClass} is not in the classpath.
- *
- * @throws IllegalArgumentException if {@code newWrappingClass} is the same as {@code
- * currentWrappingClass}
- */
- Optional<Key> rewrapMapKey(
- Key possibleMapKey, Class<?> currentWrappingClass, Class<?> newWrappingClass) {
- checkArgument(!currentWrappingClass.equals(newWrappingClass));
- if (MapType.isMap(possibleMapKey)) {
- MapType mapType = MapType.from(possibleMapKey);
- if (!mapType.isRawType() && mapType.valuesAreTypeOf(currentWrappingClass)) {
- TypeElement wrappingElement = elements.getTypeElement(newWrappingClass);
- if (wrappingElement == null) {
- // This target might not be compiled with Producers, so wrappingClass might not have an
- // associated element.
- return Optional.empty();
- }
- DeclaredType wrappedValueType =
- types.getDeclaredType(
- wrappingElement, mapType.unwrappedValueType(currentWrappingClass));
- return Optional.of(
- possibleMapKey.toBuilder().type(mapOf(mapType.keyType(), wrappedValueType)).build());
- }
- }
- return Optional.empty();
- }
-
- /**
- * If {@code key}'s type is {@code Map<K, Foo>} and {@code Foo} is not {@code WrappingClass
- * <Bar>}, returns a key with type {@code Map<K, WrappingClass<Foo>>} with the same qualifier.
- * Otherwise returns {@link Optional#empty()}.
- *
- * <p>Returns {@link Optional#empty()} if {@code WrappingClass} is not in the classpath.
- */
- private Optional<Key> wrapMapKey(Key possibleMapKey, Class<?> wrappingClass) {
- if (MapType.isMap(possibleMapKey)) {
- MapType mapType = MapType.from(possibleMapKey);
- if (!mapType.isRawType() && !mapType.valuesAreTypeOf(wrappingClass)) {
- TypeElement wrappingElement = elements.getTypeElement(wrappingClass);
- if (wrappingElement == null) {
- // This target might not be compiled with Producers, so wrappingClass might not have an
- // associated element.
- return Optional.empty();
- }
- DeclaredType wrappedValueType = types.getDeclaredType(wrappingElement, mapType.valueType());
- return Optional.of(
- possibleMapKey.toBuilder().type(mapOf(mapType.keyType(), wrappedValueType)).build());
- }
- }
- return Optional.empty();
- }
-
- /**
- * If {@code key}'s type is {@code Set<WrappingClass<Bar>>}, returns a key with type {@code Set
- * <Bar>} with the same qualifier. Otherwise returns {@link Optional#empty()}.
- */
- Optional<Key> unwrapSetKey(Key key, Class<?> wrappingClass) {
- if (SetType.isSet(key)) {
- SetType setType = SetType.from(key);
- if (!setType.isRawType() && setType.elementsAreTypeOf(wrappingClass)) {
- return Optional.of(
- key.toBuilder().type(setOf(setType.unwrappedElementType(wrappingClass))).build());
- }
- }
- return Optional.empty();
- }
-
- /**
- * If {@code key}'s type is {@code Optional<T>} for some {@code T}, returns a key with the same
- * qualifier whose type is {@linkplain RequestKinds#extractKeyType(RequestKind, TypeMirror)}
- * extracted} from {@code T}.
- */
- Optional<Key> unwrapOptional(Key key) {
- if (!OptionalType.isOptional(key)) {
- return Optional.empty();
- }
-
- TypeMirror optionalValueType = OptionalType.from(key).valueType();
- return Optional.of(
- key.toBuilder()
- .type(extractKeyType(getRequestKind(optionalValueType), optionalValueType))
- .build());
- }
-
- /** Translates a {@link Key} to a proto representation. */
- static KeyProto toProto(Key key) {
- KeyProto.Builder builder =
- KeyProto.newBuilder().setType(TypeProtoConverter.toProto(key.type()));
- key.qualifier().map(AnnotationProtoConverter::toProto).ifPresent(builder::setQualifier);
- key.multibindingContributionIdentifier()
- .ifPresent(
- mci ->
- builder
- .getMultibindingContributionIdentifierBuilder()
- .setModule(mci.module())
- .setBindingElement(mci.bindingElement()));
- return builder.build();
- }
-
- /** Creates a {@link Key} from its proto representation. */
- Key fromProto(KeyProto key) {
- Key.Builder builder = Key.builder(typeProtoConverter.fromProto(key.getType()));
- if (key.hasQualifier()) {
- builder.qualifier(annotationProtoConverter.fromProto(key.getQualifier()));
- }
- if (key.hasMultibindingContributionIdentifier()) {
- KeyProto.MultibindingContributionIdentifier multibindingContributionIdentifier =
- key.getMultibindingContributionIdentifier();
- builder.multibindingContributionIdentifier(
- new MultibindingContributionIdentifier(
- multibindingContributionIdentifier.getBindingElement(),
- multibindingContributionIdentifier.getModule()));
- }
- return builder.build();
- }
-}
diff --git a/java/dagger/internal/codegen/KeyVariableNamer.java b/java/dagger/internal/codegen/KeyVariableNamer.java
deleted file mode 100644
index 407f208ca..000000000
--- a/java/dagger/internal/codegen/KeyVariableNamer.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.CaseFormat.LOWER_CAMEL;
-import static com.google.common.base.CaseFormat.UPPER_CAMEL;
-import static dagger.internal.codegen.SourceFiles.protectAgainstKeywords;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableSet;
-import dagger.model.DependencyRequest;
-import dagger.model.Key;
-import java.util.Iterator;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.ArrayType;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.PrimitiveType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.type.TypeVisitor;
-import javax.lang.model.util.SimpleTypeVisitor8;
-
-/**
- * Suggests a variable name for a type based on a {@link Key}. Prefer {@link
- * DependencyVariableNamer} for cases where a specific {@link DependencyRequest} is present.
- */
-final class KeyVariableNamer {
- /** Simple names that are very common. Inspired by https://errorprone.info/bugpattern/BadImport */
- private static final ImmutableSet<String> VERY_SIMPLE_NAMES =
- ImmutableSet.of(
- "Builder",
- "Factory",
- "Component",
- "Subcomponent",
- "Injector");
-
- private static final TypeVisitor<Void, StringBuilder> TYPE_NAMER =
- new SimpleTypeVisitor8<Void, StringBuilder>() {
- @Override
- public Void visitDeclared(DeclaredType declaredType, StringBuilder builder) {
- TypeElement element = MoreTypes.asTypeElement(declaredType);
- if (element.getNestingKind().isNested()
- && VERY_SIMPLE_NAMES.contains(element.getSimpleName().toString())) {
- builder.append(element.getEnclosingElement().getSimpleName());
- }
-
- builder.append(element.getSimpleName());
- Iterator<? extends TypeMirror> argumentIterator =
- declaredType.getTypeArguments().iterator();
- if (argumentIterator.hasNext()) {
- builder.append("Of");
- TypeMirror first = argumentIterator.next();
- first.accept(this, builder);
- while (argumentIterator.hasNext()) {
- builder.append("And");
- argumentIterator.next().accept(this, builder);
- }
- }
- return null;
- }
-
- @Override
- public Void visitPrimitive(PrimitiveType type, StringBuilder builder) {
- builder.append(LOWER_CAMEL.to(UPPER_CAMEL, type.toString()));
- return null;
- }
-
- @Override
- public Void visitArray(ArrayType type, StringBuilder builder) {
- type.getComponentType().accept(this, builder);
- builder.append("Array");
- return null;
- }
- };
-
- private KeyVariableNamer() {}
-
- static String name(Key key) {
- if (key.multibindingContributionIdentifier().isPresent()) {
- return key.multibindingContributionIdentifier().get().bindingElement();
- }
-
- StringBuilder builder = new StringBuilder();
-
- if (key.qualifier().isPresent()) {
- // TODO(gak): Use a better name for fields with qualifiers with members.
- builder.append(key.qualifier().get().getAnnotationType().asElement().getSimpleName());
- }
-
- key.type().accept(TYPE_NAMER, builder);
-
- return protectAgainstKeywords(UPPER_CAMEL.to(LOWER_CAMEL, builder.toString()));
- }
-}
diff --git a/java/dagger/internal/codegen/Keys.java b/java/dagger/internal/codegen/Keys.java
deleted file mode 100644
index 670fda78a..000000000
--- a/java/dagger/internal/codegen/Keys.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.Key;
-import java.util.Optional;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.SimpleTypeVisitor6;
-
-/** Utility methods related to {@link Key}s. */
-final class Keys {
- static boolean isValidMembersInjectionKey(Key key) {
- return !key.qualifier().isPresent()
- && !key.multibindingContributionIdentifier().isPresent()
- && key.type().getKind().equals(TypeKind.DECLARED);
- }
-
- /**
- * Returns {@code true} if this is valid as an implicit key (that is, if it's valid for a
- * just-in-time binding by discovering an {@code @Inject} constructor).
- */
- static boolean isValidImplicitProvisionKey(Key key, DaggerTypes types) {
- return isValidImplicitProvisionKey(key.qualifier(), key.type(), types);
- }
-
- /**
- * Returns {@code true} if a key with {@code qualifier} and {@code type} is valid as an implicit
- * key (that is, if it's valid for a just-in-time binding by discovering an {@code @Inject}
- * constructor).
- */
- static boolean isValidImplicitProvisionKey(
- Optional<? extends AnnotationMirror> qualifier, TypeMirror type, final DaggerTypes types) {
- // Qualifiers disqualify implicit provisioning.
- if (qualifier.isPresent()) {
- return false;
- }
-
- return type.accept(
- new SimpleTypeVisitor6<Boolean, Void>(false) {
- @Override
- public Boolean visitDeclared(DeclaredType type, Void ignored) {
- // Non-classes or abstract classes aren't allowed.
- TypeElement element = MoreElements.asType(type.asElement());
- if (!element.getKind().equals(ElementKind.CLASS)
- || element.getModifiers().contains(Modifier.ABSTRACT)) {
- return false;
- }
-
- // If the key has type arguments, validate that each type argument is declared.
- // Otherwise the type argument may be a wildcard (or other type), and we can't
- // resolve that to actual types.
- for (TypeMirror arg : type.getTypeArguments()) {
- if (arg.getKind() != TypeKind.DECLARED) {
- return false;
- }
- }
-
- // Also validate that the key is not the erasure of a generic type.
- // If it is, that means the user referred to Foo<T> as just 'Foo',
- // which we don't allow. (This is a judgement call -- we *could*
- // allow it and instantiate the type bounds... but we don't.)
- return MoreTypes.asDeclared(element.asType()).getTypeArguments().isEmpty()
- || !types.isSameType(types.erasure(element.asType()), type);
- }
- },
- null);
- }
-}
diff --git a/java/dagger/internal/codegen/MapBindingExpression.java b/java/dagger/internal/codegen/MapBindingExpression.java
deleted file mode 100644
index 526c493ec..000000000
--- a/java/dagger/internal/codegen/MapBindingExpression.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.BindingRequest.bindingRequest;
-import static dagger.internal.codegen.MapKeys.getMapKeyExpression;
-import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
-import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
-import static dagger.model.BindingKind.MULTIBOUND_MAP;
-import static javax.lang.model.util.ElementFilter.methodsIn;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.MapBuilder;
-import dagger.internal.codegen.javapoet.Expression;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.BindingKind;
-import dagger.model.DependencyRequest;
-import java.util.Collections;
-import java.util.Optional;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-
-/** A {@link BindingExpression} for multibound maps. */
-final class MapBindingExpression extends MultibindingExpression {
- /** Maximum number of key-value pairs that can be passed to ImmutableMap.of(K, V, K, V, ...). */
- private static final int MAX_IMMUTABLE_MAP_OF_KEY_VALUE_PAIRS = 5;
-
- private final ProvisionBinding binding;
- private final ImmutableMap<DependencyRequest, ContributionBinding> dependencies;
- private final ComponentBindingExpressions componentBindingExpressions;
- private final DaggerTypes types;
- private final DaggerElements elements;
-
- MapBindingExpression(
- ResolvedBindings resolvedBindings,
- ComponentImplementation componentImplementation,
- BindingGraph graph,
- ComponentBindingExpressions componentBindingExpressions,
- DaggerTypes types,
- DaggerElements elements) {
- super(resolvedBindings, componentImplementation);
- this.binding = (ProvisionBinding) resolvedBindings.contributionBinding();
- BindingKind bindingKind = this.binding.kind();
- checkArgument(bindingKind.equals(MULTIBOUND_MAP), bindingKind);
- this.componentBindingExpressions = componentBindingExpressions;
- this.types = types;
- this.elements = elements;
- this.dependencies =
- Maps.toMap(
- binding.dependencies(),
- dep -> graph.contributionBindings().get(dep.key()).contributionBinding());
- }
-
- @Override
- protected Expression buildDependencyExpression(ClassName requestingClass) {
- Optional<CodeBlock> superMethodCall = superMethodCall();
- // TODO(ronshapiro): We should also make an ImmutableMap version of MapFactory
- boolean isImmutableMapAvailable = isImmutableMapAvailable();
- // TODO(ronshapiro, gak): Use Maps.immutableEnumMap() if it's available?
- if (isImmutableMapAvailable
- && dependencies.size() <= MAX_IMMUTABLE_MAP_OF_KEY_VALUE_PAIRS
- && !superMethodCall.isPresent()) {
- return Expression.create(
- immutableMapType(),
- CodeBlock.builder()
- .add("$T.", ImmutableMap.class)
- .add(maybeTypeParameters(requestingClass))
- .add(
- "of($L)",
- dependencies
- .keySet()
- .stream()
- .map(dependency -> keyAndValueExpression(dependency, requestingClass))
- .collect(toParametersCodeBlock()))
- .build());
- }
- switch (dependencies.size()) {
- case 0:
- return collectionsStaticFactoryInvocation(requestingClass, CodeBlock.of("emptyMap()"));
- case 1:
- return collectionsStaticFactoryInvocation(
- requestingClass,
- CodeBlock.of(
- "singletonMap($L)",
- keyAndValueExpression(getOnlyElement(dependencies.keySet()), requestingClass)));
- default:
- CodeBlock.Builder instantiation = CodeBlock.builder();
- instantiation
- .add("$T.", isImmutableMapAvailable ? ImmutableMap.class : MapBuilder.class)
- .add(maybeTypeParameters(requestingClass));
- if (isImmutableMapBuilderWithExpectedSizeAvailable()) {
- instantiation.add("builderWithExpectedSize($L)", dependencies.size());
- } else if (isImmutableMapAvailable) {
- instantiation.add("builder()");
- } else {
- instantiation.add("newMapBuilder($L)", dependencies.size());
- }
- for (DependencyRequest dependency : getNewContributions(dependencies.keySet())) {
- instantiation.add(".put($L)", keyAndValueExpression(dependency, requestingClass));
- }
- if (superMethodCall.isPresent()) {
- instantiation.add(CodeBlock.of(".putAll($L)", superMethodCall.get()));
- }
- return Expression.create(
- isImmutableMapAvailable ? immutableMapType() : binding.key().type(),
- instantiation.add(".build()").build());
- }
- }
-
- private DeclaredType immutableMapType() {
- MapType mapType = MapType.from(binding.key());
- return types.getDeclaredType(
- elements.getTypeElement(ImmutableMap.class), mapType.keyType(), mapType.valueType());
- }
-
- private CodeBlock keyAndValueExpression(DependencyRequest dependency, ClassName requestingClass) {
- return CodeBlock.of(
- "$L, $L",
- getMapKeyExpression(dependencies.get(dependency), requestingClass, elements),
- componentBindingExpressions
- .getDependencyExpression(bindingRequest(dependency), requestingClass)
- .codeBlock());
- }
-
- private Expression collectionsStaticFactoryInvocation(
- ClassName requestingClass, CodeBlock methodInvocation) {
- return Expression.create(
- binding.key().type(),
- CodeBlock.builder()
- .add("$T.", Collections.class)
- .add(maybeTypeParameters(requestingClass))
- .add(methodInvocation)
- .build());
- }
-
- private CodeBlock maybeTypeParameters(ClassName requestingClass) {
- TypeMirror bindingKeyType = binding.key().type();
- MapType mapType = MapType.from(binding.key());
- return isTypeAccessibleFrom(bindingKeyType, requestingClass.packageName())
- ? CodeBlock.of("<$T, $T>", mapType.keyType(), mapType.valueType())
- : CodeBlock.of("");
- }
-
- private boolean isImmutableMapBuilderWithExpectedSizeAvailable() {
- if (isImmutableMapAvailable()) {
- return methodsIn(elements.getTypeElement(ImmutableMap.class).getEnclosedElements())
- .stream()
- .anyMatch(method -> method.getSimpleName().contentEquals("builderWithExpectedSize"));
- }
- return false;
- }
-
- private boolean isImmutableMapAvailable() {
- return elements.getTypeElement(ImmutableMap.class) != null;
- }
-}
diff --git a/java/dagger/internal/codegen/MapFactoryCreationExpression.java b/java/dagger/internal/codegen/MapFactoryCreationExpression.java
deleted file mode 100644
index 187b792bb..000000000
--- a/java/dagger/internal/codegen/MapFactoryCreationExpression.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static dagger.internal.codegen.MapKeys.getMapKeyExpression;
-import static dagger.internal.codegen.SourceFiles.mapFactoryClassName;
-
-import com.google.common.collect.ImmutableSet;
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.model.DependencyRequest;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
-import javax.inject.Provider;
-import javax.lang.model.type.TypeMirror;
-
-/** A factory creation expression for a multibound map. */
-final class MapFactoryCreationExpression extends MultibindingFactoryCreationExpression {
-
- private final ComponentImplementation componentImplementation;
- private final BindingGraph graph;
- private final ContributionBinding binding;
- private final DaggerElements elements;
-
- MapFactoryCreationExpression(
- ContributionBinding binding,
- ComponentImplementation componentImplementation,
- ComponentBindingExpressions componentBindingExpressions,
- BindingGraph graph,
- DaggerElements elements) {
- super(binding, componentImplementation, componentBindingExpressions);
- this.binding = checkNotNull(binding);
- this.componentImplementation = checkNotNull(componentImplementation);
- this.graph = checkNotNull(graph);
- this.elements = checkNotNull(elements);
- }
-
- @Override
- public CodeBlock creationExpression() {
- CodeBlock.Builder builder = CodeBlock.builder().add("$T.", mapFactoryClassName(binding));
- if (!useRawType()) {
- MapType mapType = MapType.from(binding.key().type());
- // TODO(ronshapiro): either inline this into mapFactoryClassName, or add a
- // mapType.unwrappedValueType() method that doesn't require a framework type
- TypeMirror valueType = mapType.valueType();
- for (Class<?> frameworkClass :
- ImmutableSet.of(Provider.class, Producer.class, Produced.class)) {
- if (mapType.valuesAreTypeOf(frameworkClass)) {
- valueType = mapType.unwrappedValueType(frameworkClass);
- break;
- }
- }
- builder.add("<$T, $T>", mapType.keyType(), valueType);
- }
-
- builder.add("builder($L)", binding.dependencies().size());
-
- superContributions()
- .ifPresent(superContributions -> builder.add(".putAll($L)", superContributions));
-
- for (DependencyRequest dependency : dependenciesToImplement()) {
- ContributionBinding contributionBinding =
- graph.contributionBindings().get(dependency.key()).contributionBinding();
- builder.add(
- ".put($L, $L)",
- getMapKeyExpression(contributionBinding, componentImplementation.name(), elements),
- multibindingDependencyExpression(dependency));
- }
- builder.add(".build()");
-
- componentImplementation.registerImplementedMultibinding(binding, bindingRequest());
-
- return builder.build();
- }
-}
diff --git a/java/dagger/internal/codegen/MapKeyAccessibility.java b/java/dagger/internal/codegen/MapKeyAccessibility.java
deleted file mode 100644
index ce2787746..000000000
--- a/java/dagger/internal/codegen/MapKeyAccessibility.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
-
-import dagger.internal.codegen.langmodel.Accessibility;
-import java.util.Collection;
-import java.util.List;
-import java.util.function.Predicate;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.SimpleAnnotationValueVisitor8;
-
-final class MapKeyAccessibility extends SimpleAnnotationValueVisitor8<Boolean, Void> {
- private final Predicate<TypeMirror> accessibilityChecker;
-
- private MapKeyAccessibility(Predicate<TypeMirror> accessibilityChecker) {
- this.accessibilityChecker = accessibilityChecker;
- }
-
- @Override
- public Boolean visitAnnotation(AnnotationMirror annotation, Void aVoid) {
- // The annotation type is not checked, as the generated code will refer to the @AutoAnnotation
- // generated type which is always public
- return visitValues(annotation.getElementValues().values());
- }
-
- @Override
- public Boolean visitArray(List<? extends AnnotationValue> values, Void aVoid) {
- return visitValues(values);
- }
-
- private boolean visitValues(Collection<? extends AnnotationValue> values) {
- return values.stream().allMatch(value -> value.accept(this, null));
- }
-
- @Override
- public Boolean visitEnumConstant(VariableElement enumConstant, Void aVoid) {
- return accessibilityChecker.test(enumConstant.getEnclosingElement().asType());
- }
-
- @Override
- public Boolean visitType(TypeMirror type, Void aVoid) {
- return accessibilityChecker.test(type);
- }
-
- @Override
- protected Boolean defaultAction(Object o, Void aVoid) {
- return true;
- }
-
- static boolean isMapKeyAccessibleFrom(AnnotationMirror annotation, String accessingPackage) {
- return new MapKeyAccessibility(type -> isTypeAccessibleFrom(type, accessingPackage))
- .visitAnnotation(annotation, null);
- }
-
- static boolean isMapKeyPubliclyAccessible(AnnotationMirror annotation) {
- return new MapKeyAccessibility(Accessibility::isTypePubliclyAccessible)
- .visitAnnotation(annotation, null);
- }
-}
diff --git a/java/dagger/internal/codegen/MapKeyProcessingStep.java b/java/dagger/internal/codegen/MapKeyProcessingStep.java
index 19975a730..50693da14 100644
--- a/java/dagger/internal/codegen/MapKeyProcessingStep.java
+++ b/java/dagger/internal/codegen/MapKeyProcessingStep.java
@@ -16,7 +16,7 @@
package dagger.internal.codegen;
-import static dagger.internal.codegen.MapKeys.getUnwrappedMapKeyType;
+import static dagger.internal.codegen.binding.MapKeys.getUnwrappedMapKeyType;
import static javax.lang.model.element.ElementKind.ANNOTATION_TYPE;
import com.google.auto.common.MoreElements;
@@ -24,6 +24,11 @@ import com.google.auto.common.MoreTypes;
import com.google.common.collect.ImmutableSet;
import dagger.MapKey;
import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.validation.MapKeyValidator;
+import dagger.internal.codegen.validation.TypeCheckingProcessingStep;
+import dagger.internal.codegen.validation.ValidationReport;
+import dagger.internal.codegen.writing.AnnotationCreatorGenerator;
+import dagger.internal.codegen.writing.UnwrappedMapKeyGenerator;
import java.lang.annotation.Annotation;
import java.util.Set;
import javax.annotation.processing.Messager;
@@ -37,7 +42,7 @@ import javax.lang.model.type.DeclaredType;
* The annotation processor responsible for validating the mapKey annotation and auto-generate
* implementation of annotations marked with {@link MapKey @MapKey} where necessary.
*/
-public class MapKeyProcessingStep extends TypeCheckingProcessingStep<TypeElement> {
+final class MapKeyProcessingStep extends TypeCheckingProcessingStep<TypeElement> {
private final Messager messager;
private final DaggerTypes types;
private final MapKeyValidator mapKeyValidator;
diff --git a/java/dagger/internal/codegen/MapKeyValidator.java b/java/dagger/internal/codegen/MapKeyValidator.java
deleted file mode 100644
index f2568efdc..000000000
--- a/java/dagger/internal/codegen/MapKeyValidator.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static javax.lang.model.util.ElementFilter.methodsIn;
-
-import dagger.MapKey;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import java.util.List;
-import javax.inject.Inject;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.TypeKind;
-
-/**
- * A validator for {@link MapKey} annotations.
- */
-// TODO(dpb,gak): Should unwrapped MapKeys be required to have their single member be named "value"?
-final class MapKeyValidator {
- private final DaggerElements elements;
-
- @Inject
- MapKeyValidator(DaggerElements elements) {
- this.elements = elements;
- }
-
- ValidationReport<Element> validate(Element element) {
- ValidationReport.Builder<Element> builder = ValidationReport.about(element);
- List<ExecutableElement> members = methodsIn(((TypeElement) element).getEnclosedElements());
- if (members.isEmpty()) {
- builder.addError("Map key annotations must have members", element);
- } else if (element.getAnnotation(MapKey.class).unwrapValue()) {
- if (members.size() > 1) {
- builder.addError(
- "Map key annotations with unwrapped values must have exactly one member", element);
- } else if (members.get(0).getReturnType().getKind() == TypeKind.ARRAY) {
- builder.addError("Map key annotations with unwrapped values cannot use arrays", element);
- }
- } else if (autoAnnotationIsMissing()) {
- builder.addError(
- "@AutoAnnotation is a necessary dependency if @MapKey(unwrapValue = false). Add a "
- + "dependency on com.google.auto.value:auto-value:<current version>");
- }
- return builder.build();
- }
-
- private boolean autoAnnotationIsMissing() {
- return elements.getTypeElement("com.google.auto.value.AutoAnnotation") == null;
- }
-}
diff --git a/java/dagger/internal/codegen/MapKeys.java b/java/dagger/internal/codegen/MapKeys.java
deleted file mode 100644
index d8da6af52..000000000
--- a/java/dagger/internal/codegen/MapKeys.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
-import static com.google.auto.common.AnnotationMirrors.getAnnotationValuesWithDefaults;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static dagger.internal.codegen.MapKeyAccessibility.isMapKeyPubliclyAccessible;
-import static dagger.internal.codegen.SourceFiles.elementBasedClassName;
-import static javax.lang.model.element.Modifier.PUBLIC;
-import static javax.lang.model.element.Modifier.STATIC;
-import static javax.lang.model.util.ElementFilter.methodsIn;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableSet;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.TypeName;
-import dagger.MapKey;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import java.util.NoSuchElementException;
-import java.util.Optional;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.ArrayType;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.PrimitiveType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.SimpleTypeVisitor6;
-
-/**
- * Methods for extracting {@link MapKey} annotations and key code blocks from binding elements.
- */
-final class MapKeys {
-
- /**
- * If {@code bindingElement} is annotated with a {@link MapKey} annotation, returns it.
- *
- * @throws IllegalArgumentException if the element is annotated with more than one {@code MapKey}
- * annotation
- */
- static Optional<AnnotationMirror> getMapKey(Element bindingElement) {
- ImmutableSet<? extends AnnotationMirror> mapKeys = getMapKeys(bindingElement);
- return mapKeys.isEmpty()
- ? Optional.empty()
- : Optional.<AnnotationMirror>of(getOnlyElement(mapKeys));
- }
-
- /**
- * Returns all of the {@link MapKey} annotations that annotate {@code bindingElement}.
- */
- static ImmutableSet<? extends AnnotationMirror> getMapKeys(Element bindingElement) {
- return getAnnotatedAnnotations(bindingElement, MapKey.class);
- }
-
- /**
- * Returns the annotation value if {@code mapKey}'s type is annotated with
- * {@link MapKey @MapKey(unwrapValue = true)}.
- *
- * @throws IllegalArgumentException if {@code mapKey}'s type is not annotated with
- * {@link MapKey @MapKey} at all.
- */
- static Optional<? extends AnnotationValue> unwrapValue(AnnotationMirror mapKey) {
- MapKey mapKeyAnnotation = mapKey.getAnnotationType().asElement().getAnnotation(MapKey.class);
- checkArgument(
- mapKeyAnnotation != null, "%s is not annotated with @MapKey", mapKey.getAnnotationType());
- return mapKeyAnnotation.unwrapValue()
- ? Optional.of(getOnlyElement(getAnnotationValuesWithDefaults(mapKey).values()))
- : Optional.empty();
- }
-
- static TypeMirror mapKeyType(AnnotationMirror mapKeyAnnotation, DaggerTypes types) {
- return unwrapValue(mapKeyAnnotation).isPresent()
- ? getUnwrappedMapKeyType(mapKeyAnnotation.getAnnotationType(), types)
- : mapKeyAnnotation.getAnnotationType();
- }
-
- /**
- * Returns the map key type for an unwrapped {@link MapKey} annotation type. If the single member
- * type is primitive, returns the boxed type.
- *
- * @throws IllegalArgumentException if {@code mapKeyAnnotationType} is not an annotation type or
- * has more than one member, or if its single member is an array
- * @throws NoSuchElementException if the annotation has no members
- */
- static DeclaredType getUnwrappedMapKeyType(
- final DeclaredType mapKeyAnnotationType, final DaggerTypes types) {
- checkArgument(
- MoreTypes.asTypeElement(mapKeyAnnotationType).getKind() == ElementKind.ANNOTATION_TYPE,
- "%s is not an annotation type",
- mapKeyAnnotationType);
-
- final ExecutableElement onlyElement =
- getOnlyElement(methodsIn(mapKeyAnnotationType.asElement().getEnclosedElements()));
-
- SimpleTypeVisitor6<DeclaredType, Void> keyTypeElementVisitor =
- new SimpleTypeVisitor6<DeclaredType, Void>() {
-
- @Override
- public DeclaredType visitArray(ArrayType t, Void p) {
- throw new IllegalArgumentException(
- mapKeyAnnotationType + "." + onlyElement.getSimpleName() + " cannot be an array");
- }
-
- @Override
- public DeclaredType visitPrimitive(PrimitiveType t, Void p) {
- return MoreTypes.asDeclared(types.boxedClass(t).asType());
- }
-
- @Override
- public DeclaredType visitDeclared(DeclaredType t, Void p) {
- return t;
- }
- };
- return keyTypeElementVisitor.visit(onlyElement.getReturnType());
- }
-
- /**
- * Returns a code block for {@code binding}'s {@link ContributionBinding#mapKeyAnnotation() map
- * key}. If for whatever reason the map key is not accessible from within {@code requestingClass}
- * (i.e. it has a package-private {@code enum} from a different package), this will return an
- * invocation of a proxy-method giving it access.
- *
- * @throws IllegalStateException if {@code binding} is not a {@link dagger.multibindings.IntoMap
- * map} contribution.
- */
- static CodeBlock getMapKeyExpression(
- ContributionBinding binding, ClassName requestingClass, DaggerElements elements) {
- AnnotationMirror mapKeyAnnotation = binding.mapKeyAnnotation().get();
- return MapKeyAccessibility.isMapKeyAccessibleFrom(
- mapKeyAnnotation, requestingClass.packageName())
- ? directMapKeyExpression(mapKeyAnnotation, elements)
- : CodeBlock.of("$T.create()", mapKeyProxyClassName(binding));
- }
-
- /**
- * Returns a code block for the map key annotation {@code mapKey}.
- *
- * <p>This method assumes the map key will be accessible in the context that the returned {@link
- * CodeBlock} is used. Use {@link #getMapKeyExpression(ContributionBinding, ClassName,
- * DaggerElements)} when that assumption is not guaranteed.
- *
- * @throws IllegalArgumentException if the element is annotated with more than one {@code MapKey}
- * annotation
- * @throws IllegalStateException if {@code bindingElement} is not annotated with a {@code MapKey}
- * annotation
- */
- private static CodeBlock directMapKeyExpression(
- AnnotationMirror mapKey, DaggerElements elements) {
- Optional<? extends AnnotationValue> unwrappedValue = unwrapValue(mapKey);
- AnnotationExpression annotationExpression = new AnnotationExpression(mapKey);
-
- if (MoreTypes.asTypeElement(mapKey.getAnnotationType())
- .getQualifiedName()
- .contentEquals("dagger.android.AndroidInjectionKey")) {
- TypeElement unwrappedType =
- elements.checkTypePresent((String) unwrappedValue.get().getValue());
- return CodeBlock.of(
- "$T.of($S)",
- ClassName.get("dagger.android.internal", "AndroidInjectionKeys"),
- ClassName.get(unwrappedType).reflectionName());
- }
-
- if (unwrappedValue.isPresent()) {
- TypeMirror unwrappedValueType =
- getOnlyElement(getAnnotationValuesWithDefaults(mapKey).keySet()).getReturnType();
- return annotationExpression.getValueExpression(unwrappedValueType, unwrappedValue.get());
- } else {
- return annotationExpression.getAnnotationInstanceExpression();
- }
- }
-
- /**
- * Returns the {@link ClassName} in which {@link #mapKeyFactoryMethod(ContributionBinding,
- * DaggerTypes, DaggerElements)} is generated.
- */
- static ClassName mapKeyProxyClassName(ContributionBinding binding) {
- return elementBasedClassName(
- MoreElements.asExecutable(binding.bindingElement().get()), "MapKey");
- }
-
- /**
- * A {@code static create()} method to be added to {@link
- * #mapKeyProxyClassName(ContributionBinding)} when the {@code @MapKey} annotation is not publicly
- * accessible.
- */
- static Optional<MethodSpec> mapKeyFactoryMethod(
- ContributionBinding binding, DaggerTypes types, DaggerElements elements) {
- return binding
- .mapKeyAnnotation()
- .filter(mapKey -> !isMapKeyPubliclyAccessible(mapKey))
- .map(
- mapKey ->
- methodBuilder("create")
- .addModifiers(PUBLIC, STATIC)
- .returns(TypeName.get(mapKeyType(mapKey, types)))
- .addStatement("return $L", directMapKeyExpression(mapKey, elements))
- .build());
- }
-
- private MapKeys() {}
-}
diff --git a/java/dagger/internal/codegen/MapMultibindingValidator.java b/java/dagger/internal/codegen/MapMultibindingValidator.java
deleted file mode 100644
index 346d27145..000000000
--- a/java/dagger/internal/codegen/MapMultibindingValidator.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.Multimaps.filterKeys;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSetMultimap;
-import static dagger.internal.codegen.Formatter.INDENT;
-import static dagger.model.BindingKind.MULTIBOUND_MAP;
-import static javax.tools.Diagnostic.Kind.ERROR;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.base.Equivalence;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.Multimaps;
-import com.google.common.collect.SetMultimap;
-import dagger.model.BindingGraph;
-import dagger.model.Key;
-import dagger.producers.Producer;
-import dagger.spi.BindingGraphPlugin;
-import dagger.spi.DiagnosticReporter;
-import java.util.Set;
-import javax.inject.Inject;
-import javax.inject.Provider;
-import javax.lang.model.type.DeclaredType;
-
-/**
- * Reports an error for any map binding with either more than one contribution with the same map key
- * or contributions with inconsistent map key annotation types.
- */
-final class MapMultibindingValidator implements BindingGraphPlugin {
-
- private final BindingDeclarationFormatter bindingDeclarationFormatter;
- private final KeyFactory keyFactory;
-
- @Inject
- MapMultibindingValidator(
- BindingDeclarationFormatter bindingDeclarationFormatter, KeyFactory keyFactory) {
- this.bindingDeclarationFormatter = bindingDeclarationFormatter;
- this.keyFactory = keyFactory;
- }
-
- @Override
- public String pluginName() {
- return "Dagger/MapKeys";
- }
-
- @Override
- public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
- mapMultibindings(bindingGraph)
- .forEach(
- binding -> {
- ImmutableSet<ContributionBinding> contributions =
- mapBindingContributions(binding, bindingGraph);
- checkForDuplicateMapKeys(binding, contributions, diagnosticReporter);
- checkForInconsistentMapKeyAnnotationTypes(binding, contributions, diagnosticReporter);
- });
- }
-
- /**
- * Returns the map multibindings in the binding graph. If a graph contains bindings for more than
- * one of the following for the same {@code K} and {@code V}, then only the first one found will
- * be returned so we don't report the same map contribution problem more than once.
- *
- * <ol>
- * <li>{@code Map<K, V>}
- * <li>{@code Map<K, Provider<V>>}
- * <li>{@code Map<K, Producer<V>>}
- * </ol>
- */
- private ImmutableSet<dagger.model.Binding> mapMultibindings(BindingGraph bindingGraph) {
- ImmutableSetMultimap<Key, dagger.model.Binding> mapMultibindings =
- bindingGraph.bindings().stream()
- .filter(node -> node.kind().equals(MULTIBOUND_MAP))
- .collect(toImmutableSetMultimap(dagger.model.Binding::key, node -> node));
-
- // Mutlbindings for Map<K, V>
- SetMultimap<Key, dagger.model.Binding> plainValueMapMultibindings =
- filterKeys(mapMultibindings, key -> !MapType.from(key).valuesAreFrameworkType());
-
- // Multibindings for Map<K, Provider<V>> where Map<K, V> isn't in plainValueMapMultibindings
- SetMultimap<Key, dagger.model.Binding> providerValueMapMultibindings =
- filterKeys(
- mapMultibindings,
- key ->
- MapType.from(key).valuesAreTypeOf(Provider.class)
- && !plainValueMapMultibindings.containsKey(keyFactory.unwrapMapValueType(key)));
-
- // Multibindings for Map<K, Producer<V>> where Map<K, V> isn't in plainValueMapMultibindings and
- // Map<K, Provider<V>> isn't in providerValueMapMultibindings
- SetMultimap<Key, dagger.model.Binding> producerValueMapMultibindings =
- filterKeys(
- mapMultibindings,
- key ->
- MapType.from(key).valuesAreTypeOf(Producer.class)
- && !plainValueMapMultibindings.containsKey(keyFactory.unwrapMapValueType(key))
- && !providerValueMapMultibindings.containsKey(
- keyFactory.rewrapMapKey(key, Producer.class, Provider.class).get()));
-
- return new ImmutableSet.Builder<dagger.model.Binding>()
- .addAll(plainValueMapMultibindings.values())
- .addAll(providerValueMapMultibindings.values())
- .addAll(producerValueMapMultibindings.values())
- .build();
- }
-
- private ImmutableSet<ContributionBinding> mapBindingContributions(
- dagger.model.Binding binding, BindingGraph bindingGraph) {
- checkArgument(binding.kind().equals(MULTIBOUND_MAP));
- return bindingGraph.requestedBindings(binding).stream()
- .map(b -> (BindingNode) b)
- .map(b -> (ContributionBinding) b.delegate())
- .collect(toImmutableSet());
- }
-
- private void checkForDuplicateMapKeys(
- dagger.model.Binding multiboundMapBinding,
- ImmutableSet<ContributionBinding> contributions,
- DiagnosticReporter diagnosticReporter) {
- ImmutableSetMultimap<Object, ContributionBinding> contributionsByMapKey =
- ImmutableSetMultimap.copyOf(Multimaps.index(contributions, ContributionBinding::mapKey));
-
- for (Set<ContributionBinding> contributionsForOneMapKey :
- Multimaps.asMap(contributionsByMapKey).values()) {
- if (contributionsForOneMapKey.size() > 1) {
- diagnosticReporter.reportBinding(
- ERROR,
- multiboundMapBinding,
- duplicateMapKeyErrorMessage(contributionsForOneMapKey, multiboundMapBinding.key()));
- }
- }
- }
-
- private void checkForInconsistentMapKeyAnnotationTypes(
- dagger.model.Binding multiboundMapBinding,
- ImmutableSet<ContributionBinding> contributions,
- DiagnosticReporter diagnosticReporter) {
- ImmutableSetMultimap<Equivalence.Wrapper<DeclaredType>, ContributionBinding>
- contributionsByMapKeyAnnotationType = indexByMapKeyAnnotationType(contributions);
-
- if (contributionsByMapKeyAnnotationType.keySet().size() > 1) {
- diagnosticReporter.reportBinding(
- ERROR,
- multiboundMapBinding,
- inconsistentMapKeyAnnotationTypesErrorMessage(
- contributionsByMapKeyAnnotationType, multiboundMapBinding.key()));
- }
- }
-
- private static ImmutableSetMultimap<Equivalence.Wrapper<DeclaredType>, ContributionBinding>
- indexByMapKeyAnnotationType(ImmutableSet<ContributionBinding> contributions) {
- return ImmutableSetMultimap.copyOf(
- Multimaps.index(
- contributions,
- mapBinding ->
- MoreTypes.equivalence()
- .wrap(mapBinding.mapKeyAnnotation().get().getAnnotationType())));
- }
-
- private String inconsistentMapKeyAnnotationTypesErrorMessage(
- ImmutableSetMultimap<Equivalence.Wrapper<DeclaredType>, ContributionBinding>
- contributionsByMapKeyAnnotationType,
- Key mapBindingKey) {
- StringBuilder message =
- new StringBuilder(mapBindingKey.toString())
- .append(" uses more than one @MapKey annotation type");
- Multimaps.asMap(contributionsByMapKeyAnnotationType)
- .forEach(
- (annotationType, contributions) -> {
- message.append('\n').append(INDENT).append(annotationType.get()).append(':');
- bindingDeclarationFormatter.formatIndentedList(message, contributions, 2);
- });
- return message.toString();
- }
-
- private String duplicateMapKeyErrorMessage(
- Set<ContributionBinding> contributionsForOneMapKey, Key mapBindingKey) {
- StringBuilder message =
- new StringBuilder("The same map key is bound more than once for ").append(mapBindingKey);
- bindingDeclarationFormatter.formatIndentedList(message, contributionsForOneMapKey, 1);
- return message.toString();
- }
-}
diff --git a/java/dagger/internal/codegen/MapType.java b/java/dagger/internal/codegen/MapType.java
deleted file mode 100644
index 73ecdbfa3..000000000
--- a/java/dagger/internal/codegen/MapType.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Equivalence;
-import dagger.model.Key;
-import java.util.Map;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * Information about a {@link Map} {@link TypeMirror}.
- */
-@AutoValue
-abstract class MapType {
- /**
- * The map type itself, wrapped using {@link MoreTypes#equivalence()}. Use
- * {@link #declaredMapType()} instead.
- */
- protected abstract Equivalence.Wrapper<DeclaredType> wrappedDeclaredMapType();
-
- /**
- * The map type itself.
- */
- DeclaredType declaredMapType() {
- return wrappedDeclaredMapType().get();
- }
-
- /**
- * {@code true} if the map type is the raw {@link Map} type.
- */
- boolean isRawType() {
- return declaredMapType().getTypeArguments().isEmpty();
- }
-
- /**
- * The map key type.
- *
- * @throws IllegalStateException if {@link #isRawType()} is true.
- */
- TypeMirror keyType() {
- checkState(!isRawType());
- return declaredMapType().getTypeArguments().get(0);
- }
-
- /**
- * The map value type.
- *
- * @throws IllegalStateException if {@link #isRawType()} is true.
- */
- TypeMirror valueType() {
- checkState(!isRawType());
- return declaredMapType().getTypeArguments().get(1);
- }
-
- /**
- * {@code true} if {@link #valueType()} is a {@code clazz}.
- *
- * @throws IllegalStateException if {@link #isRawType()} is true.
- */
- boolean valuesAreTypeOf(Class<?> clazz) {
- return MoreTypes.isType(valueType()) && MoreTypes.isTypeOf(clazz, valueType());
- }
-
- /**
- * Returns {@code true} if the {@linkplain #valueType() value type} of the {@link Map} is a
- * {@linkplain FrameworkTypes#isFrameworkType(TypeMirror) framework type}.
- */
- boolean valuesAreFrameworkType() {
- return FrameworkTypes.isFrameworkType(valueType());
- }
-
- /**
- * {@code V} if {@link #valueType()} is a framework type like {@code Provider<V>} or {@code
- * Producer<V>}.
- *
- * @throws IllegalStateException if {@link #isRawType()} is true or {@link #valueType()} is not a
- * framework type
- */
- TypeMirror unwrappedFrameworkValueType() {
- checkState(
- valuesAreFrameworkType(), "called unwrappedFrameworkValueType() on %s", declaredMapType());
- return uncheckedUnwrappedValueType();
- }
-
- /**
- * {@code V} if {@link #valueType()} is a {@code WrappingClass<V>}.
- *
- * @throws IllegalStateException if {@link #isRawType()} is true or {@link #valueType()} is not a
- * {@code WrappingClass<V>}
- * @throws IllegalArgumentException if {@code wrappingClass} does not have exactly one type
- * parameter
- */
- TypeMirror unwrappedValueType(Class<?> wrappingClass) {
- checkArgument(
- wrappingClass.getTypeParameters().length == 1,
- "%s must have exactly one type parameter",
- wrappingClass);
- checkState(valuesAreTypeOf(wrappingClass), "expected values to be %s: %s", wrappingClass, this);
- return uncheckedUnwrappedValueType();
- }
-
- private TypeMirror uncheckedUnwrappedValueType() {
- return MoreTypes.asDeclared(valueType()).getTypeArguments().get(0);
- }
-
- /**
- * {@code true} if {@code type} is a {@link Map} type.
- */
- static boolean isMap(TypeMirror type) {
- return MoreTypes.isType(type) && MoreTypes.isTypeOf(Map.class, type);
- }
-
- /**
- * {@code true} if {@code key.type()} is a {@link Map} type.
- */
- static boolean isMap(Key key) {
- return isMap(key.type());
- }
-
- /**
- * Returns a {@link MapType} for {@code type}.
- *
- * @throws IllegalArgumentException if {@code type} is not a {@link Map} type
- */
- static MapType from(TypeMirror type) {
- checkArgument(isMap(type), "%s is not a Map", type);
- return new AutoValue_MapType(MoreTypes.equivalence().wrap(MoreTypes.asDeclared(type)));
- }
-
- /**
- * Returns a {@link MapType} for {@code key}'s {@link Key#type() type}.
- *
- * @throws IllegalArgumentException if {@code key.type()} is not a {@link Map} type
- */
- static MapType from(Key key) {
- return from(key.type());
- }
-}
diff --git a/java/dagger/internal/codegen/MemberSelect.java b/java/dagger/internal/codegen/MemberSelect.java
deleted file mode 100644
index c42c7c9ae..000000000
--- a/java/dagger/internal/codegen/MemberSelect.java
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.SINGLETON_INSTANCE;
-import static dagger.internal.codegen.SourceFiles.bindingTypeElementTypeVariableNames;
-import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
-import static dagger.internal.codegen.SourceFiles.setFactoryClassName;
-import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
-import static dagger.internal.codegen.javapoet.TypeNames.FACTORY;
-import static dagger.internal.codegen.javapoet.TypeNames.MAP_FACTORY;
-import static dagger.internal.codegen.javapoet.TypeNames.PRODUCER;
-import static dagger.internal.codegen.javapoet.TypeNames.PRODUCERS;
-import static dagger.internal.codegen.javapoet.TypeNames.PROVIDER;
-import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
-import static javax.lang.model.type.TypeKind.DECLARED;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableList;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.TypeVariableName;
-import dagger.internal.codegen.javapoet.CodeBlocks;
-import java.util.List;
-import java.util.Optional;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * Represents a {@link com.sun.source.tree.MemberSelectTree} as a {@link CodeBlock}.
- */
-abstract class MemberSelect {
-
- /**
- * Returns a {@link MemberSelect} that accesses the field given by {@code fieldName} owned by
- * {@code owningClass}. In this context "local" refers to the fact that the field is owned by the
- * type (or an enclosing type) from which the code block will be used. The returned
- * {@link MemberSelect} will not be valid for accessing the field from a different class
- * (regardless of accessibility).
- */
- static MemberSelect localField(ClassName owningClass, String fieldName) {
- return new LocalField(owningClass, fieldName);
- }
-
- private static final class LocalField extends MemberSelect {
- final String fieldName;
-
- LocalField(ClassName owningClass, String fieldName) {
- super(owningClass, false);
- this.fieldName = checkNotNull(fieldName);
- }
-
- @Override
- CodeBlock getExpressionFor(ClassName usingClass) {
- return owningClass().equals(usingClass)
- ? CodeBlock.of("$N", fieldName)
- : CodeBlock.of("$T.this.$N", owningClass(), fieldName);
- }
- }
-
- /**
- * Returns a {@link MemberSelect} that accesses the method given by {@code methodName} owned by
- * {@code owningClass}. In this context "local" refers to the fact that the method is owned by the
- * type (or an enclosing type) from which the code block will be used. The returned {@link
- * MemberSelect} will not be valid for accessing the method from a different class (regardless of
- * accessibility).
- */
- static MemberSelect localMethod(ClassName owningClass, String methodName) {
- return new LocalMethod(owningClass, methodName);
- }
-
- private static final class LocalMethod extends MemberSelect {
- final String methodName;
-
- LocalMethod(ClassName owningClass, String methodName) {
- super(owningClass, false);
- this.methodName = checkNotNull(methodName);
- }
-
- @Override
- CodeBlock getExpressionFor(ClassName usingClass) {
- return owningClass().equals(usingClass)
- ? CodeBlock.of("$N()", methodName)
- : CodeBlock.of("$T.this.$N()", owningClass(), methodName);
- }
- }
-
- /**
- * If {@code resolvedBindings} is an unscoped provision binding with no factory arguments or a
- * no-op members injection binding, then we don't need a field to hold its factory. In that case,
- * this method returns the static member select that returns the factory or no-op members
- * injector.
- */
- static Optional<MemberSelect> staticFactoryCreation(ResolvedBindings resolvedBindings) {
- if (resolvedBindings.contributionBindings().isEmpty()) {
- throw new AssertionError(
- "Expected a contribution binding, but none found. *THIS IS A DAGGER BUG* - please "
- + "report it on Github with as much context as you can provide. Thanks!"
- + "\n\nKey: "
- + resolvedBindings.key()
- + "\nMultibinding declarations: "
- + resolvedBindings.multibindingDeclarations()
- + "\nSubcomponent declarations: "
- + resolvedBindings.subcomponentDeclarations()
- + "\nOptional binding declarations: "
- + resolvedBindings.optionalBindingDeclarations());
- }
- ContributionBinding contributionBinding = resolvedBindings.contributionBinding();
- if (contributionBinding.factoryCreationStrategy().equals(SINGLETON_INSTANCE)
- && !contributionBinding.scope().isPresent()) {
- switch (contributionBinding.kind()) {
- case MULTIBOUND_MAP:
- return Optional.of(emptyMapFactory(contributionBinding));
-
- case MULTIBOUND_SET:
- return Optional.of(emptySetFactory(contributionBinding));
-
- case INJECTION:
- case PROVISION:
- TypeMirror keyType = resolvedBindings.key().type();
- if (keyType.getKind().equals(DECLARED)) {
- ImmutableList<TypeVariableName> typeVariables =
- bindingTypeElementTypeVariableNames(contributionBinding);
- if (!typeVariables.isEmpty()) {
- List<? extends TypeMirror> typeArguments =
- ((DeclaredType) keyType).getTypeArguments();
- return Optional.of(
- MemberSelect.parameterizedFactoryCreateMethod(
- generatedClassNameForBinding(contributionBinding), typeArguments));
- }
- }
- // fall through
-
- default:
- return Optional.of(
- new StaticMethod(
- generatedClassNameForBinding(contributionBinding), CodeBlock.of("create()")));
- }
- }
-
- return Optional.empty();
- }
-
- /**
- * Returns a {@link MemberSelect} for the instance of a {@code create()} method on a factory. This
- * only applies for factories that do not have any dependencies.
- */
- private static MemberSelect parameterizedFactoryCreateMethod(
- ClassName owningClass, List<? extends TypeMirror> parameters) {
- return new ParameterizedStaticMethod(
- owningClass, ImmutableList.copyOf(parameters), CodeBlock.of("create()"), FACTORY);
- }
-
- private static final class StaticMethod extends MemberSelect {
- final CodeBlock methodCodeBlock;
-
- StaticMethod(ClassName owningClass, CodeBlock methodCodeBlock) {
- super(owningClass, true);
- this.methodCodeBlock = checkNotNull(methodCodeBlock);
- }
-
- @Override
- CodeBlock getExpressionFor(ClassName usingClass) {
- return owningClass().equals(usingClass)
- ? methodCodeBlock
- : CodeBlock.of("$T.$L", owningClass(), methodCodeBlock);
- }
- }
-
- /** A {@link MemberSelect} for a factory of an empty map. */
- private static MemberSelect emptyMapFactory(ContributionBinding contributionBinding) {
- BindingType bindingType = contributionBinding.bindingType();
- ImmutableList<TypeMirror> typeParameters =
- ImmutableList.copyOf(
- MoreTypes.asDeclared(contributionBinding.key().type()).getTypeArguments());
- if (bindingType.equals(BindingType.PRODUCTION)) {
- return new ParameterizedStaticMethod(
- PRODUCERS, typeParameters, CodeBlock.of("emptyMapProducer()"), PRODUCER);
- } else {
- return new ParameterizedStaticMethod(
- MAP_FACTORY, typeParameters, CodeBlock.of("emptyMapProvider()"), PROVIDER);
- }
- }
-
- /**
- * A static member select for an empty set factory. Calls {@link
- * dagger.internal.SetFactory#empty()}, {@link dagger.producers.internal.SetProducer#empty()}, or
- * {@link dagger.producers.internal.SetOfProducedProducer#empty()}, depending on the set bindings.
- */
- private static MemberSelect emptySetFactory(ContributionBinding binding) {
- return new ParameterizedStaticMethod(
- setFactoryClassName(binding),
- ImmutableList.of(SetType.from(binding.key()).elementType()),
- CodeBlock.of("empty()"),
- FACTORY);
- }
-
- private static final class ParameterizedStaticMethod extends MemberSelect {
- final ImmutableList<TypeMirror> typeParameters;
- final CodeBlock methodCodeBlock;
- final ClassName rawReturnType;
-
- ParameterizedStaticMethod(
- ClassName owningClass,
- ImmutableList<TypeMirror> typeParameters,
- CodeBlock methodCodeBlock,
- ClassName rawReturnType) {
- super(owningClass, true);
- this.typeParameters = typeParameters;
- this.methodCodeBlock = methodCodeBlock;
- this.rawReturnType = rawReturnType;
- }
-
- @Override
- CodeBlock getExpressionFor(ClassName usingClass) {
- boolean accessible = true;
- for (TypeMirror typeParameter : typeParameters) {
- accessible &= isTypeAccessibleFrom(typeParameter, usingClass.packageName());
- }
-
- if (accessible) {
- return CodeBlock.of(
- "$T.<$L>$L",
- owningClass(),
- typeParameters.stream().map(CodeBlocks::type).collect(toParametersCodeBlock()),
- methodCodeBlock);
- } else {
- return CodeBlock.of("(($T) $T.$L)", rawReturnType, owningClass(), methodCodeBlock);
- }
- }
- }
-
- private final ClassName owningClass;
- private final boolean staticMember;
-
- MemberSelect(ClassName owningClass, boolean staticMemeber) {
- this.owningClass = owningClass;
- this.staticMember = staticMemeber;
- }
-
- /** Returns the class that owns the member being selected. */
- ClassName owningClass() {
- return owningClass;
- }
-
- /**
- * Returns true if the member being selected is static and does not require an instance of
- * {@link #owningClass()}.
- */
- boolean staticMember() {
- return staticMember;
- }
-
- /**
- * Returns a {@link CodeBlock} suitable for accessing the member from the given {@code
- * usingClass}.
- */
- abstract CodeBlock getExpressionFor(ClassName usingClass);
-}
diff --git a/java/dagger/internal/codegen/MembersInjectionBinding.java b/java/dagger/internal/codegen/MembersInjectionBinding.java
deleted file mode 100644
index 4918fa1fc..000000000
--- a/java/dagger/internal/codegen/MembersInjectionBinding.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static java.util.stream.Collectors.toList;
-
-import com.google.auto.value.AutoValue;
-import com.google.auto.value.extension.memoized.Memoized;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSortedSet;
-import dagger.model.BindingKind;
-import dagger.model.DependencyRequest;
-import java.util.Optional;
-import javax.inject.Inject;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-
-/**
- * Represents the full members injection of a particular type.
- */
-@AutoValue
-abstract class MembersInjectionBinding extends Binding {
- @Override
- public final Optional<Element> bindingElement() {
- return Optional.of(membersInjectedType());
- }
-
- abstract TypeElement membersInjectedType();
-
- @Override
- abstract Optional<MembersInjectionBinding> unresolved();
-
- @Override
- public Optional<TypeElement> contributingModule() {
- return Optional.empty();
- }
-
- /** The set of individual sites where {@link Inject} is applied. */
- abstract ImmutableSortedSet<InjectionSite> injectionSites();
-
- @Override
- BindingType bindingType() {
- return BindingType.MEMBERS_INJECTION;
- }
-
- @Override
- public BindingKind kind() {
- return BindingKind.MEMBERS_INJECTION;
- }
-
- @Override
- public boolean isNullable() {
- return false;
- }
-
- /**
- * Returns {@code true} if any of this binding's injection sites are directly on the bound type.
- */
- boolean hasLocalInjectionSites() {
- return injectionSites()
- .stream()
- .anyMatch(
- injectionSite ->
- injectionSite.element().getEnclosingElement().equals(membersInjectedType()));
- }
-
- @Override
- boolean requiresModuleInstance() {
- return false;
- }
-
- @Memoized
- @Override
- public abstract int hashCode();
-
- // TODO(ronshapiro,dpb): simplify the equality semantics
- @Override
- public abstract boolean equals(Object obj);
-
- @AutoValue
- abstract static class InjectionSite {
- enum Kind {
- FIELD,
- METHOD,
- }
-
- abstract Kind kind();
-
- abstract Element element();
-
- abstract ImmutableSet<DependencyRequest> dependencies();
-
- /**
- * Returns the index of {@link #element()} in its parents {@code @Inject} members that have the
- * same simple name. This method filters out private elements so that the results will be
- * consistent independent of whether the build system uses header jars or not.
- */
- @Memoized
- int indexAmongAtInjectMembersWithSameSimpleName() {
- return element()
- .getEnclosingElement()
- .getEnclosedElements()
- .stream()
- .filter(element -> isAnnotationPresent(element, Inject.class))
- .filter(element -> !element.getModifiers().contains(Modifier.PRIVATE))
- .filter(element -> element.getSimpleName().equals(this.element().getSimpleName()))
- .collect(toList())
- .indexOf(element());
- }
-
- static InjectionSite field(VariableElement element, DependencyRequest dependency) {
- return new AutoValue_MembersInjectionBinding_InjectionSite(
- Kind.FIELD, element, ImmutableSet.of(dependency));
- }
-
- static InjectionSite method(
- ExecutableElement element, Iterable<DependencyRequest> dependencies) {
- return new AutoValue_MembersInjectionBinding_InjectionSite(
- Kind.METHOD, element, ImmutableSet.copyOf(dependencies));
- }
- }
-}
diff --git a/java/dagger/internal/codegen/MembersInjectionBindingExpression.java b/java/dagger/internal/codegen/MembersInjectionBindingExpression.java
deleted file mode 100644
index e9a8ffc3d..000000000
--- a/java/dagger/internal/codegen/MembersInjectionBindingExpression.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static javax.lang.model.type.TypeKind.VOID;
-
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.ParameterSpec;
-import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.internal.codegen.javapoet.Expression;
-import javax.lang.model.element.ExecutableElement;
-
-/**
- * A binding expression for members injection component methods. See {@link
- * MembersInjectionMethods}.
- */
-final class MembersInjectionBindingExpression extends BindingExpression {
- private final MembersInjectionBinding binding;
- private final MembersInjectionMethods membersInjectionMethods;
-
- MembersInjectionBindingExpression(
- ResolvedBindings resolvedBindings, MembersInjectionMethods membersInjectionMethods) {
- this.binding = resolvedBindings.membersInjectionBinding().get();
- this.membersInjectionMethods = membersInjectionMethods;
- }
-
- @Override
- Expression getDependencyExpression(ClassName requestingClass) {
- throw new UnsupportedOperationException(binding.toString());
- }
-
- // TODO(ronshapiro): This class doesn't need to be a BindingExpression, as
- // getDependencyExpression() should never be called for members injection methods. It's probably
- // better suited as a method on MembersInjectionMethods
- @Override
- protected CodeBlock getComponentMethodImplementation(
- ComponentMethodDescriptor componentMethod, ComponentImplementation component) {
- ExecutableElement methodElement = componentMethod.methodElement();
- ParameterSpec parameter = ParameterSpec.get(getOnlyElement(methodElement.getParameters()));
-
- if (binding.injectionSites().isEmpty()) {
- return methodElement.getReturnType().getKind().equals(VOID)
- ? CodeBlock.of("")
- : CodeBlock.of("return $N;", parameter);
- } else {
- return methodElement.getReturnType().getKind().equals(VOID)
- ? CodeBlock.of("$L;", membersInjectionInvocation(parameter))
- : CodeBlock.of("return $L;", membersInjectionInvocation(parameter));
- }
- }
-
- CodeBlock membersInjectionInvocation(ParameterSpec target) {
- return CodeBlock.of("$N($N)", membersInjectionMethods.getOrCreate(binding.key()), target);
- }
-}
diff --git a/java/dagger/internal/codegen/MembersInjectionMethods.java b/java/dagger/internal/codegen/MembersInjectionMethods.java
deleted file mode 100644
index 1e046697c..000000000
--- a/java/dagger/internal/codegen/MembersInjectionMethods.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.MEMBERS_INJECTION_METHOD;
-import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
-import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
-import static javax.lang.model.element.Modifier.PRIVATE;
-
-import com.google.common.collect.ImmutableSet;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.ParameterSpec;
-import com.squareup.javapoet.TypeName;
-import dagger.internal.codegen.InjectionMethods.InjectionSiteMethod;
-import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.Key;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import javax.lang.model.element.Name;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.TypeMirror;
-
-/** Manages the member injection methods for a component. */
-final class MembersInjectionMethods {
- private final Map<Key, MethodSpec> membersInjectionMethods = new LinkedHashMap<>();
- private final ComponentImplementation componentImplementation;
- private final ComponentBindingExpressions bindingExpressions;
- private final BindingGraph graph;
- private final DaggerElements elements;
- private final DaggerTypes types;
-
- MembersInjectionMethods(
- ComponentImplementation componentImplementation,
- ComponentBindingExpressions bindingExpressions,
- BindingGraph graph,
- DaggerElements elements,
- DaggerTypes types) {
- this.componentImplementation = checkNotNull(componentImplementation);
- this.bindingExpressions = checkNotNull(bindingExpressions);
- this.graph = checkNotNull(graph);
- this.elements = checkNotNull(elements);
- this.types = checkNotNull(types);
- }
-
- /**
- * Returns the members injection {@link MethodSpec} for the given {@link Key}, creating it if
- * necessary.
- */
- MethodSpec getOrCreate(Key key) {
- return reentrantComputeIfAbsent(membersInjectionMethods, key, this::membersInjectionMethod);
- }
-
- private MethodSpec membersInjectionMethod(Key key) {
- ResolvedBindings resolvedBindings =
- graph.membersInjectionBindings().getOrDefault(key, graph.contributionBindings().get(key));
- Binding binding = resolvedBindings.binding();
- TypeMirror keyType = binding.key().type();
- TypeMirror membersInjectedType =
- isTypeAccessibleFrom(keyType, componentImplementation.name().packageName())
- ? keyType
- : elements.getTypeElement(Object.class).asType();
- TypeName membersInjectedTypeName = TypeName.get(membersInjectedType);
- Name bindingTypeName = binding.bindingTypeElement().get().getSimpleName();
- // TODO(ronshapiro): include type parameters in this name e.g. injectFooOfT, and outer class
- // simple names Foo.Builder -> injectFooBuilder
- String methodName = componentImplementation.getUniqueMethodName("inject" + bindingTypeName);
- ParameterSpec parameter = ParameterSpec.builder(membersInjectedTypeName, "instance").build();
- MethodSpec.Builder methodBuilder =
- methodBuilder(methodName)
- .addModifiers(PRIVATE)
- .returns(membersInjectedTypeName)
- .addParameter(parameter);
- TypeElement canIgnoreReturnValue =
- elements.getTypeElement("com.google.errorprone.annotations.CanIgnoreReturnValue");
- if (canIgnoreReturnValue != null) {
- methodBuilder.addAnnotation(ClassName.get(canIgnoreReturnValue));
- }
- CodeBlock instance = CodeBlock.of("$N", parameter);
- methodBuilder.addCode(
- InjectionSiteMethod.invokeAll(
- injectionSites(binding),
- componentImplementation.name(),
- instance,
- membersInjectedType,
- types,
- request ->
- bindingExpressions
- .getDependencyArgumentExpression(request, componentImplementation.name())
- .codeBlock(),
- elements));
- methodBuilder.addStatement("return $L", instance);
-
- MethodSpec method = methodBuilder.build();
- componentImplementation.addMethod(MEMBERS_INJECTION_METHOD, method);
- return method;
- }
-
- private static ImmutableSet<InjectionSite> injectionSites(Binding binding) {
- if (binding instanceof ProvisionBinding) {
- return ((ProvisionBinding) binding).injectionSites();
- } else if (binding instanceof MembersInjectionBinding) {
- return ((MembersInjectionBinding) binding).injectionSites();
- }
- throw new IllegalArgumentException(binding.key().toString());
- }
-}
diff --git a/java/dagger/internal/codegen/MembersInjectionValidator.java b/java/dagger/internal/codegen/MembersInjectionValidator.java
deleted file mode 100644
index 036315ea9..000000000
--- a/java/dagger/internal/codegen/MembersInjectionValidator.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static dagger.internal.codegen.InjectionAnnotations.getQualifiers;
-
-import com.google.auto.common.MoreElements;
-import javax.inject.Inject;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.type.ArrayType;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.PrimitiveType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.type.TypeVisitor;
-import javax.lang.model.util.SimpleTypeVisitor8;
-
-/**
- * Validates members injection requests (members injection methods on components and requests for
- * {@code MembersInjector<Foo>}).
- */
-final class MembersInjectionValidator {
-
- @Inject
- MembersInjectionValidator() {}
-
- /** Reports errors if a request for a {@code MembersInjector<Foo>}) is invalid. */
- ValidationReport<Element> validateMembersInjectionRequest(
- Element requestElement, TypeMirror membersInjectedType) {
- ValidationReport.Builder<Element> report = ValidationReport.about(requestElement);
- checkQualifiers(report, requestElement);
- membersInjectedType.accept(VALIDATE_MEMBERS_INJECTED_TYPE, report);
- return report.build();
- }
-
- /**
- * Reports errors if a members injection method on a component is invalid.
- *
- * @throws IllegalArgumentException if the method doesn't have exactly one parameter
- */
- ValidationReport<ExecutableElement> validateMembersInjectionMethod(
- ExecutableElement method, TypeMirror membersInjectedType) {
- checkArgument(
- method.getParameters().size() == 1, "expected a method with one parameter: %s", method);
-
- ValidationReport.Builder<ExecutableElement> report = ValidationReport.about(method);
- checkQualifiers(report, method);
- checkQualifiers(report, method.getParameters().get(0));
- membersInjectedType.accept(VALIDATE_MEMBERS_INJECTED_TYPE, report);
- return report.build();
- }
-
- private void checkQualifiers(ValidationReport.Builder<?> report, Element element) {
- for (AnnotationMirror qualifier : getQualifiers(element)) {
- report.addError("Cannot inject members into qualified types", element, qualifier);
- break; // just report on the first qualifier, in case there is more than one
- }
- }
-
- private static final TypeVisitor<Void, ValidationReport.Builder<?>>
- VALIDATE_MEMBERS_INJECTED_TYPE =
- new SimpleTypeVisitor8<Void, ValidationReport.Builder<?>>() {
- // Only declared types can be members-injected.
- @Override
- protected Void defaultAction(TypeMirror type, ValidationReport.Builder<?> report) {
- report.addError("Cannot inject members into " + type);
- return null;
- }
-
- @Override
- public Void visitDeclared(DeclaredType type, ValidationReport.Builder<?> report) {
- if (type.getTypeArguments().isEmpty()) {
- // If the type is the erasure of a generic type, that means the user referred to
- // Foo<T> as just 'Foo', which we don't allow. (This is a judgement call; we
- // *could* allow it and instantiate the type bounds, but we don't.)
- if (!MoreElements.asType(type.asElement()).getTypeParameters().isEmpty()) {
- report.addError("Cannot inject members into raw type " + type);
- }
- } else {
- // If the type has arguments, validate that each type argument is declared.
- // Otherwise the type argument may be a wildcard (or other type), and we can't
- // resolve that to actual types. For array type arguments, validate the type of the
- // array.
- for (TypeMirror arg : type.getTypeArguments()) {
- if (!arg.accept(DECLARED_OR_ARRAY, null)) {
- report.addError(
- "Cannot inject members into types with unbounded type arguments: " + type);
- }
- }
- }
- return null;
- }
- };
-
- // TODO(dpb): Can this be inverted so it explicitly rejects wildcards or type variables?
- // This logic is hard to describe.
- private static final TypeVisitor<Boolean, Void> DECLARED_OR_ARRAY =
- new SimpleTypeVisitor8<Boolean, Void>(false) {
- @Override
- public Boolean visitArray(ArrayType arrayType, Void p) {
- return arrayType
- .getComponentType()
- .accept(
- new SimpleTypeVisitor8<Boolean, Void>(false) {
- @Override
- public Boolean visitDeclared(DeclaredType declaredType, Void p) {
- for (TypeMirror arg : declaredType.getTypeArguments()) {
- if (!arg.accept(this, null)) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- public Boolean visitArray(ArrayType arrayType, Void p) {
- return arrayType.getComponentType().accept(this, null);
- }
-
- @Override
- public Boolean visitPrimitive(PrimitiveType primitiveType, Void p) {
- return true;
- }
- },
- null);
- }
-
- @Override
- public Boolean visitDeclared(DeclaredType t, Void p) {
- return true;
- }
- };
-}
diff --git a/java/dagger/internal/codegen/MembersInjectorGenerator.java b/java/dagger/internal/codegen/MembersInjectorGenerator.java
deleted file mode 100644
index 4360af7f8..000000000
--- a/java/dagger/internal/codegen/MembersInjectorGenerator.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkState;
-import static com.squareup.javapoet.MethodSpec.constructorBuilder;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static com.squareup.javapoet.TypeSpec.classBuilder;
-import static dagger.internal.codegen.GwtCompatibility.gwtIncompatibleAnnotation;
-import static dagger.internal.codegen.SourceFiles.bindingTypeElementTypeVariableNames;
-import static dagger.internal.codegen.SourceFiles.frameworkFieldUsages;
-import static dagger.internal.codegen.SourceFiles.generateBindingFieldsForDependencies;
-import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
-import static dagger.internal.codegen.SourceFiles.parameterizedGeneratedTypeNameForBinding;
-import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
-import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
-import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings;
-import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
-import static dagger.internal.codegen.javapoet.TypeNames.membersInjectorOf;
-import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PUBLIC;
-import static javax.lang.model.element.Modifier.STATIC;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.FieldSpec;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.ParameterSpec;
-import com.squareup.javapoet.TypeName;
-import com.squareup.javapoet.TypeSpec;
-import com.squareup.javapoet.TypeVariableName;
-import dagger.MembersInjector;
-import dagger.internal.codegen.InjectionMethods.InjectionSiteMethod;
-import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.Key;
-import java.util.Map.Entry;
-import java.util.Optional;
-import javax.annotation.processing.Filer;
-import javax.inject.Inject;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
-
-/**
- * Generates {@link MembersInjector} implementations from {@link MembersInjectionBinding} instances.
- */
-final class MembersInjectorGenerator extends SourceFileGenerator<MembersInjectionBinding> {
- private final DaggerTypes types;
- private final DaggerElements elements;
-
- @Inject
- MembersInjectorGenerator(
- Filer filer, DaggerElements elements, DaggerTypes types, SourceVersion sourceVersion) {
- super(filer, elements, sourceVersion);
- this.types = types;
- this.elements = elements;
- }
-
- @Override
- ClassName nameGeneratedType(MembersInjectionBinding binding) {
- return membersInjectorNameForType(binding.membersInjectedType());
- }
-
- @Override
- Element originatingElement(MembersInjectionBinding binding) {
- return binding.membersInjectedType();
- }
-
- @Override
- Optional<TypeSpec.Builder> write(ClassName generatedTypeName, MembersInjectionBinding binding) {
- // Empty members injection bindings are special and don't need source files.
- if (binding.injectionSites().isEmpty()) {
- return Optional.empty();
- }
- // We don't want to write out resolved bindings -- we want to write out the generic version.
- checkState(
- !binding.unresolved().isPresent(),
- "tried to generate a MembersInjector for a binding of a resolved generic type: %s",
- binding);
-
- ImmutableList<TypeVariableName> typeParameters = bindingTypeElementTypeVariableNames(binding);
- TypeSpec.Builder injectorTypeBuilder =
- classBuilder(generatedTypeName)
- .addModifiers(PUBLIC, FINAL)
- .addTypeVariables(typeParameters);
-
- TypeName injectedTypeName = TypeName.get(binding.key().type());
- TypeName implementedType = membersInjectorOf(injectedTypeName);
- injectorTypeBuilder.addSuperinterface(implementedType);
-
- MethodSpec.Builder injectMembersBuilder =
- methodBuilder("injectMembers")
- .addModifiers(PUBLIC)
- .addAnnotation(Override.class)
- .addParameter(injectedTypeName, "instance");
-
- ImmutableMap<Key, FrameworkField> fields = generateBindingFieldsForDependencies(binding);
-
- ImmutableMap.Builder<Key, FieldSpec> dependencyFieldsBuilder = ImmutableMap.builder();
-
- MethodSpec.Builder constructorBuilder = constructorBuilder().addModifiers(PUBLIC);
-
- // We use a static create method so that generated components can avoid having
- // to refer to the generic types of the factory.
- // (Otherwise they may have visibility problems referring to the types.)
- MethodSpec.Builder createMethodBuilder =
- methodBuilder("create")
- .returns(implementedType)
- .addModifiers(PUBLIC, STATIC)
- .addTypeVariables(typeParameters);
-
- createMethodBuilder.addCode(
- "return new $T(", parameterizedGeneratedTypeNameForBinding(binding));
- ImmutableList.Builder<CodeBlock> constructorInvocationParameters = ImmutableList.builder();
-
- boolean usesRawFrameworkTypes = false;
- UniqueNameSet fieldNames = new UniqueNameSet();
- for (Entry<Key, FrameworkField> fieldEntry : fields.entrySet()) {
- Key dependencyKey = fieldEntry.getKey();
- FrameworkField bindingField = fieldEntry.getValue();
-
- // If the dependency type is not visible to this members injector, then use the raw framework
- // type for the field.
- boolean useRawFrameworkType =
- !isTypeAccessibleFrom(dependencyKey.type(), generatedTypeName.packageName());
-
- String fieldName = fieldNames.getUniqueName(bindingField.name());
- TypeName fieldType = useRawFrameworkType ? bindingField.type().rawType : bindingField.type();
- FieldSpec.Builder fieldBuilder = FieldSpec.builder(fieldType, fieldName, PRIVATE, FINAL);
- ParameterSpec.Builder parameterBuilder = ParameterSpec.builder(fieldType, fieldName);
-
- // If we're using the raw type for the field, then suppress the injectMembers method's
- // unchecked-type warning and the field's and the constructor and create-method's
- // parameters' raw-type warnings.
- if (useRawFrameworkType) {
- usesRawFrameworkTypes = true;
- fieldBuilder.addAnnotation(suppressWarnings(RAWTYPES));
- parameterBuilder.addAnnotation(suppressWarnings(RAWTYPES));
- }
- constructorBuilder.addParameter(parameterBuilder.build());
- createMethodBuilder.addParameter(parameterBuilder.build());
-
- FieldSpec field = fieldBuilder.build();
- injectorTypeBuilder.addField(field);
- constructorBuilder.addStatement("this.$1N = $1N", field);
- dependencyFieldsBuilder.put(dependencyKey, field);
- constructorInvocationParameters.add(CodeBlock.of("$N", field));
- }
-
- createMethodBuilder.addCode(
- constructorInvocationParameters.build().stream().collect(toParametersCodeBlock()));
- createMethodBuilder.addCode(");");
-
- injectorTypeBuilder.addMethod(constructorBuilder.build());
- injectorTypeBuilder.addMethod(createMethodBuilder.build());
-
- ImmutableMap<Key, FieldSpec> dependencyFields = dependencyFieldsBuilder.build();
-
- injectMembersBuilder.addCode(
- InjectionSiteMethod.invokeAll(
- binding.injectionSites(),
- generatedTypeName,
- CodeBlock.of("instance"),
- binding.key().type(),
- types,
- frameworkFieldUsages(binding.dependencies(), dependencyFields)::get,
- elements));
-
- if (usesRawFrameworkTypes) {
- injectMembersBuilder.addAnnotation(suppressWarnings(UNCHECKED));
- }
- injectorTypeBuilder.addMethod(injectMembersBuilder.build());
-
- for (InjectionSite injectionSite : binding.injectionSites()) {
- if (injectionSite.element().getEnclosingElement().equals(binding.membersInjectedType())) {
- injectorTypeBuilder.addMethod(
- InjectionSiteMethod.create(injectionSite, elements).toMethodSpec());
- }
- }
-
- gwtIncompatibleAnnotation(binding).ifPresent(injectorTypeBuilder::addAnnotation);
-
- return Optional.of(injectorTypeBuilder);
- }
-}
diff --git a/java/dagger/internal/codegen/MembersInjectorProviderCreationExpression.java b/java/dagger/internal/codegen/MembersInjectorProviderCreationExpression.java
deleted file mode 100644
index 8e863e526..000000000
--- a/java/dagger/internal/codegen/MembersInjectorProviderCreationExpression.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
-import static dagger.internal.codegen.javapoet.TypeNames.INSTANCE_FACTORY;
-import static dagger.internal.codegen.javapoet.TypeNames.MEMBERS_INJECTORS;
-
-import com.google.auto.common.MoreTypes;
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
-import javax.lang.model.type.TypeMirror;
-
-/** A {@code Provider<MembersInjector<Foo>>} creation expression. */
-final class MembersInjectorProviderCreationExpression
- implements FrameworkInstanceCreationExpression {
-
- private final ComponentBindingExpressions componentBindingExpressions;
- private final ProvisionBinding binding;
-
- MembersInjectorProviderCreationExpression(
- ProvisionBinding binding, ComponentBindingExpressions componentBindingExpressions) {
- this.binding = checkNotNull(binding);
- this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
- }
-
- @Override
- public CodeBlock creationExpression() {
- TypeMirror membersInjectedType =
- getOnlyElement(MoreTypes.asDeclared(binding.key().type()).getTypeArguments());
-
- CodeBlock membersInjector =
- binding.injectionSites().isEmpty()
- ? CodeBlock.of("$T.<$T>noOp()", MEMBERS_INJECTORS, membersInjectedType)
- : CodeBlock.of(
- "$T.create($L)",
- membersInjectorNameForType(MoreTypes.asTypeElement(membersInjectedType)),
- componentBindingExpressions.getCreateMethodArgumentsCodeBlock(binding));
-
- // TODO(ronshapiro): consider adding a MembersInjectorBindingExpression to return this directly
- // (as it's rarely requested as a Provider).
- return CodeBlock.of("$T.create($L)", INSTANCE_FACTORY, membersInjector);
- }
-
- @Override
- public boolean useInnerSwitchingProvider() {
- return !binding.injectionSites().isEmpty();
- }
-}
diff --git a/java/dagger/internal/codegen/MethodBindingExpression.java b/java/dagger/internal/codegen/MethodBindingExpression.java
deleted file mode 100644
index 2120e8063..000000000
--- a/java/dagger/internal/codegen/MethodBindingExpression.java
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static dagger.internal.codegen.ComponentImplementation.FieldSpecKind.PRIVATE_METHOD_SCOPED_FIELD;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.VOLATILE;
-
-import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.FieldSpec;
-import com.squareup.javapoet.TypeName;
-import dagger.internal.DoubleCheck;
-import dagger.internal.MemoizedSentinel;
-import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
-import dagger.internal.codegen.javapoet.Expression;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.RequestKind;
-import java.util.Optional;
-import javax.lang.model.type.TypeMirror;
-
-/** A binding expression that wraps another in a nullary method on the component. */
-abstract class MethodBindingExpression extends BindingExpression {
- private final BindingRequest request;
- private final ResolvedBindings resolvedBindings;
- private final ContributionBinding binding;
- private final BindingMethodImplementation bindingMethodImplementation;
- private final ComponentImplementation componentImplementation;
- private final ProducerEntryPointView producerEntryPointView;
- private final BindingExpression wrappedBindingExpression;
- private final DaggerTypes types;
-
- protected MethodBindingExpression(
- BindingRequest request,
- ResolvedBindings resolvedBindings,
- MethodImplementationStrategy methodImplementationStrategy,
- BindingExpression wrappedBindingExpression,
- ComponentImplementation componentImplementation,
- DaggerTypes types) {
- this.request = checkNotNull(request);
- this.resolvedBindings = resolvedBindings;
- this.binding = resolvedBindings.contributionBinding();
- this.bindingMethodImplementation = bindingMethodImplementation(methodImplementationStrategy);
- this.wrappedBindingExpression = checkNotNull(wrappedBindingExpression);
- this.componentImplementation = checkNotNull(componentImplementation);
- this.producerEntryPointView = new ProducerEntryPointView(types);
- this.types = checkNotNull(types);
- }
-
- @Override
- Expression getDependencyExpression(ClassName requestingClass) {
- if (request.frameworkType().isPresent()) {
- // Initializing a framework instance that participates in a cycle requires that the underlying
- // FrameworkInstanceBindingExpression is invoked in order for a cycle to be detected properly.
- // When a MethodBindingExpression wraps a FrameworkInstanceBindingExpression, the wrapped
- // expression will only be invoked once to implement the method body. This is a hack to work
- // around that weirdness - methodImplementation.body() will invoke the framework instance
- // initialization again in case the field is not fully initialized.
- // TODO(b/121196706): use a less hacky approach to fix this bug
- Object unused = methodBody();
- }
-
- addMethod();
- return Expression.create(
- returnType(),
- requestingClass.equals(componentImplementation.name())
- ? CodeBlock.of("$N()", methodName())
- : CodeBlock.of("$T.this.$N()", componentImplementation.name(), methodName()));
- }
-
- @Override
- final CodeBlock getModifiableBindingMethodImplementation(
- ModifiableBindingMethod modifiableBindingMethod,
- ComponentImplementation component,
- DaggerTypes types) {
- // A matching modifiable binding method means that we have previously created the binding method
- // and we are now implementing it. If there is no matching method we need to first create the
- // method. We create the method by deferring to getDependencyExpression (defined above) via a
- // call to super.getModifiableBindingMethodImplementation().
- if (supertypeModifiableBindingMethod().isPresent()) {
- checkState(
- supertypeModifiableBindingMethod().get().fulfillsSameRequestAs(modifiableBindingMethod));
- return methodBody();
- }
- return super.getModifiableBindingMethodImplementation(
- modifiableBindingMethod, component, types);
- }
-
- protected final Optional<ModifiableBindingMethod> supertypeModifiableBindingMethod() {
- return componentImplementation.supertypeModifiableBindingMethod(request);
- }
-
- @Override
- Expression getDependencyExpressionForComponentMethod(ComponentMethodDescriptor componentMethod,
- ComponentImplementation component) {
- return producerEntryPointView
- .getProducerEntryPointField(this, componentMethod, component)
- .orElseGet(
- () -> super.getDependencyExpressionForComponentMethod(componentMethod, component));
- }
-
- /** Adds the method to the component (if necessary) the first time it's called. */
- protected abstract void addMethod();
-
- /** Returns the name of the method to call. */
- protected abstract String methodName();
-
- /**
- * Returns {@code true} if the method of this binding expression is modifiable and is not a
- * component method.
- */
- protected boolean isModifiableImplementationMethod() {
- return false;
- }
-
- /** The method's body. */
- protected final CodeBlock methodBody() {
- return implementation(
- wrappedBindingExpression.getDependencyExpression(componentImplementation.name())
- ::codeBlock);
- }
-
- /** The method's body if this method is a component method. */
- protected final CodeBlock methodBodyForComponentMethod(
- ComponentMethodDescriptor componentMethod) {
- return implementation(
- wrappedBindingExpression.getDependencyExpressionForComponentMethod(
- componentMethod, componentImplementation)
- ::codeBlock);
- }
-
- private CodeBlock implementation(Supplier<CodeBlock> simpleBindingExpression) {
- return bindingMethodImplementation.implementation(simpleBindingExpression);
- }
-
- private BindingMethodImplementation bindingMethodImplementation(
- MethodImplementationStrategy methodImplementationStrategy) {
- switch (methodImplementationStrategy) {
- case SIMPLE:
- return new SimpleMethodImplementation();
- case SINGLE_CHECK:
- return new SingleCheckedMethodImplementation();
- case DOUBLE_CHECK:
- return new DoubleCheckedMethodImplementation();
- }
- throw new AssertionError(methodImplementationStrategy);
- }
-
- /** Returns the return type for the dependency request. */
- protected TypeMirror returnType() {
- if (request.isRequestKind(RequestKind.INSTANCE)
- && binding.contributedPrimitiveType().isPresent()) {
- return binding.contributedPrimitiveType().get();
- }
-
- if (matchingComponentMethod().isPresent()) {
- // Component methods are part of the user-defined API, and thus we must use the user-defined
- // type.
- return matchingComponentMethod().get().resolvedReturnType(types);
- }
-
- // If the component is abstract, this method may be overridden by another implementation in a
- // different package for which requestedType is inaccessible. In order to make that method
- // overridable, we use the publicly accessible type. If the method is private, we don't need to
- // worry about this, and instead just need to check accessibility of the file we're about to
- // write
- TypeMirror requestedType = request.requestedType(binding.contributedType(), types);
- return isModifiableImplementationMethod()
- ? types.publiclyAccessibleType(requestedType)
- : types.accessibleType(requestedType, componentImplementation.name());
- }
-
- private Optional<ComponentMethodDescriptor> matchingComponentMethod() {
- return componentImplementation.componentDescriptor().firstMatchingComponentMethod(request);
- }
-
- /** Strateg for implementing the body of this method. */
- enum MethodImplementationStrategy {
- SIMPLE,
- SINGLE_CHECK,
- DOUBLE_CHECK,
- ;
- }
-
- private abstract static class BindingMethodImplementation {
- /**
- * Returns the method body, which contains zero or more statements (including semicolons).
- *
- * <p>If the implementation has a non-void return type, the body will also include the {@code
- * return} statement.
- *
- * @param simpleBindingExpression the expression to retrieve an instance of this binding without
- * the wrapping method.
- */
- abstract CodeBlock implementation(Supplier<CodeBlock> simpleBindingExpression);
- }
-
- /** Returns the {@code wrappedBindingExpression} directly. */
- private static final class SimpleMethodImplementation extends BindingMethodImplementation {
- @Override
- CodeBlock implementation(Supplier<CodeBlock> simpleBindingExpression) {
- return CodeBlock.of("return $L;", simpleBindingExpression.get());
- }
- }
-
- /**
- * Defines a method body for single checked caching of the given {@code wrappedBindingExpression}.
- */
- private final class SingleCheckedMethodImplementation extends BindingMethodImplementation {
- private final Supplier<FieldSpec> field = Suppliers.memoize(this::createField);
-
- @Override
- CodeBlock implementation(Supplier<CodeBlock> simpleBindingExpression) {
- String fieldExpression = field.get().name.equals("local") ? "this.local" : field.get().name;
-
- CodeBlock.Builder builder = CodeBlock.builder()
- .addStatement("Object local = $N", fieldExpression);
-
- if (isNullable()) {
- builder.beginControlFlow("if (local instanceof $T)", MemoizedSentinel.class);
- } else {
- builder.beginControlFlow("if (local == null)");
- }
-
- return builder
- .addStatement("local = $L", simpleBindingExpression.get())
- .addStatement("$N = ($T) local", fieldExpression, returnType())
- .endControlFlow()
- .addStatement("return ($T) local", returnType())
- .build();
- }
-
- FieldSpec createField() {
- String name =
- componentImplementation.getUniqueFieldName(
- request.isRequestKind(RequestKind.INSTANCE)
- ? KeyVariableNamer.name(binding.key())
- // TODO(ronshapiro): Use KeyVariableNamer directly so we don't need to use a
- // ResolvedBindings instance and construct a whole framework field just to get the
- // name
- : FrameworkField.forResolvedBindings(resolvedBindings, Optional.empty()).name());
-
- FieldSpec.Builder builder = FieldSpec.builder(fieldType(), name, PRIVATE, VOLATILE);
- if (isNullable()) {
- builder.initializer("new $T()", MemoizedSentinel.class);
- }
-
- FieldSpec field = builder.build();
- componentImplementation.addField(PRIVATE_METHOD_SCOPED_FIELD, field);
- return field;
- }
-
- TypeName fieldType() {
- if (isNullable()) {
- // Nullable instances use `MemoizedSentinel` instead of `null` as the initialization value,
- // so the field type must accept that and the return type
- return TypeName.OBJECT;
- }
- TypeName returnType = TypeName.get(returnType());
- return returnType.isPrimitive() ? returnType.box() : returnType;
- }
-
- private boolean isNullable() {
- return request.isRequestKind(RequestKind.INSTANCE) && binding.isNullable();
- }
- }
-
- /**
- * Defines a method body for double checked caching of the given {@code wrappedBindingExpression}.
- */
- private final class DoubleCheckedMethodImplementation extends BindingMethodImplementation {
- private final Supplier<String> fieldName = Suppliers.memoize(this::createField);
-
- @Override
- CodeBlock implementation(Supplier<CodeBlock> simpleBindingExpression) {
- String fieldExpression = fieldName.get().equals("local") ? "this.local" : fieldName.get();
- return CodeBlock.builder()
- .addStatement("$T local = $L", TypeName.OBJECT, fieldExpression)
- .beginControlFlow("if (local instanceof $T)", MemoizedSentinel.class)
- .beginControlFlow("synchronized (local)")
- .addStatement("local = $L", fieldExpression)
- .beginControlFlow("if (local instanceof $T)", MemoizedSentinel.class)
- .addStatement("local = $L", simpleBindingExpression.get())
- .addStatement("$1L = $2T.reentrantCheck($1L, local)", fieldExpression, DoubleCheck.class)
- .endControlFlow()
- .endControlFlow()
- .endControlFlow()
- .addStatement("return ($T) local", returnType())
- .build();
- }
-
- private String createField() {
- String name =
- componentImplementation.getUniqueFieldName(KeyVariableNamer.name(binding.key()));
- componentImplementation.addField(
- PRIVATE_METHOD_SCOPED_FIELD,
- FieldSpec.builder(TypeName.OBJECT, name, PRIVATE, VOLATILE)
- .initializer("new $T()", MemoizedSentinel.class)
- .build());
- return name;
- }
- }
-
-}
diff --git a/java/dagger/internal/codegen/MethodSignature.java b/java/dagger/internal/codegen/MethodSignature.java
deleted file mode 100644
index ef5c9c516..000000000
--- a/java/dagger/internal/codegen/MethodSignature.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.DaggerStreams.toImmutableList;
-
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Equivalence;
-import com.google.common.collect.ImmutableList;
-import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import java.util.List;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeMirror;
-
-@AutoValue
-abstract class MethodSignature {
-
- abstract String name();
-
- abstract ImmutableList<? extends Equivalence.Wrapper<? extends TypeMirror>> parameterTypes();
-
- abstract ImmutableList<? extends Equivalence.Wrapper<? extends TypeMirror>> thrownTypes();
-
- static MethodSignature forComponentMethod(
- ComponentMethodDescriptor componentMethod, DeclaredType componentType, DaggerTypes types) {
- ExecutableType methodType =
- MoreTypes.asExecutable(types.asMemberOf(componentType, componentMethod.methodElement()));
- return new AutoValue_MethodSignature(
- componentMethod.methodElement().getSimpleName().toString(),
- wrapInEquivalence(methodType.getParameterTypes()),
- wrapInEquivalence(methodType.getThrownTypes()));
- }
-
- private static ImmutableList<? extends Equivalence.Wrapper<? extends TypeMirror>>
- wrapInEquivalence(List<? extends TypeMirror> types) {
- return types.stream().map(MoreTypes.equivalence()::wrap).collect(toImmutableList());
- }
-}
diff --git a/java/dagger/internal/codegen/MethodSignatureFormatter.java b/java/dagger/internal/codegen/MethodSignatureFormatter.java
deleted file mode 100644
index 012d5b013..000000000
--- a/java/dagger/internal/codegen/MethodSignatureFormatter.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkState;
-import static dagger.internal.codegen.DiagnosticFormatting.stripCommonTypePrefixes;
-import static dagger.internal.codegen.InjectionAnnotations.getQualifier;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Optional;
-import javax.inject.Inject;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * Formats the signature of an {@link ExecutableElement} suitable for use in error messages.
- */
-final class MethodSignatureFormatter extends Formatter<ExecutableElement> {
- private final DaggerTypes types;
-
- @Inject
- MethodSignatureFormatter(DaggerTypes types) {
- this.types = types;
- }
-
- /**
- * A formatter that uses the type where the method is declared for the annotations and name of the
- * method, but the method's resolved type as a member of {@code declaredType} for the key.
- */
- Formatter<ExecutableElement> typedFormatter(DeclaredType declaredType) {
- return new Formatter<ExecutableElement>() {
- @Override
- public String format(ExecutableElement method) {
- return MethodSignatureFormatter.this.format(
- method,
- MoreTypes.asExecutable(types.asMemberOf(declaredType, method)),
- MoreElements.asType(method.getEnclosingElement()));
- }
- };
- }
-
- @Override public String format(ExecutableElement method) {
- return format(method, Optional.empty());
- }
-
- /**
- * Formats an ExecutableElement as if it were contained within the container, if the container is
- * present.
- */
- public String format(ExecutableElement method, Optional<DeclaredType> container) {
- TypeElement type = MoreElements.asType(method.getEnclosingElement());
- ExecutableType executableType = MoreTypes.asExecutable(method.asType());
- if (container.isPresent()) {
- executableType = MoreTypes.asExecutable(types.asMemberOf(container.get(), method));
- type = MoreElements.asType(container.get().asElement());
- }
- return format(method, executableType, type);
- }
-
- private String format(
- ExecutableElement method, ExecutableType methodType, TypeElement declaringType) {
- StringBuilder builder = new StringBuilder();
- // TODO(cgruber): AnnotationMirror formatter.
- List<? extends AnnotationMirror> annotations = method.getAnnotationMirrors();
- if (!annotations.isEmpty()) {
- Iterator<? extends AnnotationMirror> annotationIterator = annotations.iterator();
- for (int i = 0; annotationIterator.hasNext(); i++) {
- if (i > 0) {
- builder.append(' ');
- }
- builder.append(formatAnnotation(annotationIterator.next()));
- }
- builder.append(' ');
- }
- if (method.getSimpleName().contentEquals("<init>")) {
- builder.append(declaringType.getQualifiedName());
- } else {
- builder
- .append(nameOfType(methodType.getReturnType()))
- .append(' ')
- .append(declaringType.getQualifiedName())
- .append('.')
- .append(method.getSimpleName());
- }
- builder.append('(');
- checkState(method.getParameters().size() == methodType.getParameterTypes().size());
- Iterator<? extends VariableElement> parameters = method.getParameters().iterator();
- Iterator<? extends TypeMirror> parameterTypes = methodType.getParameterTypes().iterator();
- for (int i = 0; parameters.hasNext(); i++) {
- if (i > 0) {
- builder.append(", ");
- }
- appendParameter(builder, parameters.next(), parameterTypes.next());
- }
- builder.append(')');
- return builder.toString();
- }
-
- private static void appendParameter(StringBuilder builder, VariableElement parameter,
- TypeMirror type) {
- getQualifier(parameter)
- .ifPresent(
- qualifier -> {
- builder.append(formatAnnotation(qualifier)).append(' ');
- });
- builder.append(nameOfType(type));
- }
-
- private static String nameOfType(TypeMirror type) {
- return stripCommonTypePrefixes(type.toString());
- }
-
- private static String formatAnnotation(AnnotationMirror annotation) {
- return stripCommonTypePrefixes(annotation.toString());
- }
-}
diff --git a/java/dagger/internal/codegen/MissingBindingExpression.java b/java/dagger/internal/codegen/MissingBindingExpression.java
deleted file mode 100644
index 610d0525f..000000000
--- a/java/dagger/internal/codegen/MissingBindingExpression.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import java.util.Optional;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * A {@link ModifiableAbstractMethodBindingExpression} for a binding that is missing when generating
- * the abstract base class implementation of a subcomponent. The (unimplemented) method is added to
- * the {@link ComponentImplementation} when the dependency expression is requested. The method is
- * overridden when generating the implementation of an ancestor component.
- */
-final class MissingBindingExpression extends ModifiableAbstractMethodBindingExpression {
- private final ComponentImplementation componentImplementation;
- private final BindingRequest request;
-
- MissingBindingExpression(
- ComponentImplementation componentImplementation,
- BindingRequest request,
- Optional<ModifiableBindingMethod> matchingModifiableBindingMethod,
- Optional<ComponentMethodDescriptor> matchingComponentMethod,
- DaggerTypes types) {
- super(
- componentImplementation,
- ModifiableBindingType.MISSING,
- request,
- matchingModifiableBindingMethod,
- matchingComponentMethod,
- types);
- this.componentImplementation = componentImplementation;
- this.request = request;
- }
-
- @Override
- String chooseMethodName() {
- return componentImplementation.getUniqueMethodName(request);
- }
-
- @Override
- protected TypeMirror contributedType() {
- return request.key().type();
- }
-}
diff --git a/java/dagger/internal/codegen/MissingBindingValidator.java b/java/dagger/internal/codegen/MissingBindingValidator.java
deleted file mode 100644
index f39361d3f..000000000
--- a/java/dagger/internal/codegen/MissingBindingValidator.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Verify.verify;
-import static dagger.internal.codegen.DaggerStreams.instancesOf;
-import static dagger.internal.codegen.Keys.isValidImplicitProvisionKey;
-import static dagger.internal.codegen.Keys.isValidMembersInjectionKey;
-import static dagger.internal.codegen.RequestKinds.canBeSatisfiedByProductionBinding;
-import static javax.tools.Diagnostic.Kind.ERROR;
-
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.BindingGraph;
-import dagger.model.BindingGraph.ComponentNode;
-import dagger.model.BindingGraph.DependencyEdge;
-import dagger.model.BindingGraph.MissingBinding;
-import dagger.model.BindingGraph.Node;
-import dagger.model.Key;
-import dagger.spi.BindingGraphPlugin;
-import dagger.spi.DiagnosticReporter;
-import javax.inject.Inject;
-import javax.lang.model.type.TypeKind;
-
-/** Reports errors for missing bindings. */
-final class MissingBindingValidator implements BindingGraphPlugin {
-
- private final DaggerTypes types;
- private final InjectBindingRegistry injectBindingRegistry;
-
- @Inject
- MissingBindingValidator(
- DaggerTypes types, InjectBindingRegistry injectBindingRegistry) {
- this.types = types;
- this.injectBindingRegistry = injectBindingRegistry;
- }
-
- @Override
- public String pluginName() {
- return "Dagger/MissingBinding";
- }
-
- @Override
- public void visitGraph(BindingGraph graph, DiagnosticReporter diagnosticReporter) {
- // Don't report missing bindings when validating a full binding graph or a graph built from a
- // subcomponent.
- if (graph.isFullBindingGraph() || graph.rootComponentNode().isSubcomponent()) {
- return;
- }
- graph
- .missingBindings()
- .forEach(missingBinding -> reportMissingBinding(missingBinding, graph, diagnosticReporter));
- }
-
- private void reportMissingBinding(
- MissingBinding missingBinding, BindingGraph graph, DiagnosticReporter diagnosticReporter) {
- diagnosticReporter.reportBinding(
- ERROR, missingBinding, missingBindingErrorMessage(missingBinding, graph));
- }
-
- private String missingBindingErrorMessage(MissingBinding missingBinding, BindingGraph graph) {
- Key key = missingBinding.key();
- StringBuilder errorMessage = new StringBuilder();
- // Wildcards should have already been checked by DependencyRequestValidator.
- verify(!key.type().getKind().equals(TypeKind.WILDCARD), "unexpected wildcard request: %s", key);
- // TODO(ronshapiro): replace "provided" with "satisfied"?
- errorMessage.append(key).append(" cannot be provided without ");
- if (isValidImplicitProvisionKey(key, types)) {
- errorMessage.append("an @Inject constructor or ");
- }
- errorMessage.append("an @Provides-"); // TODO(dpb): s/an/a
- if (allIncomingDependenciesCanUseProduction(missingBinding, graph)) {
- errorMessage.append(" or @Produces-");
- }
- errorMessage.append("annotated method.");
- if (isValidMembersInjectionKey(key) && typeHasInjectionSites(key)) {
- errorMessage.append(
- " This type supports members injection but cannot be implicitly provided.");
- }
- graph.bindings(key).stream()
- .map(binding -> binding.componentPath().currentComponent())
- .distinct()
- .forEach(
- component ->
- errorMessage
- .append("\nA binding with matching key exists in component: ")
- .append(component.getQualifiedName()));
- return errorMessage.toString();
- }
-
- private boolean allIncomingDependenciesCanUseProduction(
- MissingBinding missingBinding, BindingGraph graph) {
- return graph.network().inEdges(missingBinding).stream()
- .flatMap(instancesOf(DependencyEdge.class))
- .allMatch(edge -> dependencyCanBeProduction(edge, graph));
- }
-
- // TODO(ronshapiro): merge with
- // ProvisionDependencyOnProduerBindingValidator.dependencyCanUseProduction
- private boolean dependencyCanBeProduction(DependencyEdge edge, BindingGraph graph) {
- Node source = graph.network().incidentNodes(edge).source();
- if (source instanceof ComponentNode) {
- return canBeSatisfiedByProductionBinding(edge.dependencyRequest().kind());
- }
- if (source instanceof dagger.model.Binding) {
- return ((dagger.model.Binding) source).isProduction();
- }
- throw new IllegalArgumentException(
- "expected a dagger.model.Binding or ComponentNode: " + source);
- }
-
- private boolean typeHasInjectionSites(Key key) {
- return injectBindingRegistry
- .getOrFindMembersInjectionBinding(key)
- .map(binding -> !binding.injectionSites().isEmpty())
- .orElse(false);
- }
-}
diff --git a/java/dagger/internal/codegen/ModifiableAbstractMethodBindingExpression.java b/java/dagger/internal/codegen/ModifiableAbstractMethodBindingExpression.java
deleted file mode 100644
index 412acae54..000000000
--- a/java/dagger/internal/codegen/ModifiableAbstractMethodBindingExpression.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.PROTECTED;
-
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.TypeName;
-import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
-import dagger.internal.codegen.javapoet.Expression;
-import dagger.internal.codegen.langmodel.Accessibility;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import java.util.Optional;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * A {@link BindingExpression} that invokes a method that encapsulates a binding that cannot be
- * satisfied when generating the abstract base class implementation of a subcomponent. The
- * (unimplemented) method is added to the {@link ComponentImplementation} when the dependency
- * expression is requested. The method is overridden when generating the implementation of an
- * ancestor component.
- */
-abstract class ModifiableAbstractMethodBindingExpression extends BindingExpression {
- private final ComponentImplementation componentImplementation;
- private final ModifiableBindingType modifiableBindingType;
- private final BindingRequest request;
- private final Optional<ComponentMethodDescriptor> matchingComponentMethod;
- private final DaggerTypes types;
- private Optional<String> methodName;
-
- ModifiableAbstractMethodBindingExpression(
- ComponentImplementation componentImplementation,
- ModifiableBindingType modifiableBindingType,
- BindingRequest request,
- Optional<ModifiableBindingMethod> matchingModifiableBindingMethod,
- Optional<ComponentMethodDescriptor> matchingComponentMethod,
- DaggerTypes types) {
- this.componentImplementation = componentImplementation;
- this.modifiableBindingType = modifiableBindingType;
- this.request = request;
- this.matchingComponentMethod = matchingComponentMethod;
- this.types = types;
- this.methodName =
- initializeMethodName(matchingComponentMethod, matchingModifiableBindingMethod);
- }
-
- /**
- * If this binding corresponds to an existing component method, or a known modifiable binding
- * method, use them to initialize the method name, which is a signal to call the existing method
- * rather than emit an abstract method.
- */
- private static Optional<String> initializeMethodName(
- Optional<ComponentMethodDescriptor> matchingComponentMethod,
- Optional<ModifiableBindingMethod> matchingModifiableBindingMethod) {
- if (matchingComponentMethod.isPresent()) {
- return Optional.of(matchingComponentMethod.get().methodElement().getSimpleName().toString());
- }
- if (matchingModifiableBindingMethod.isPresent()) {
- return Optional.of(matchingModifiableBindingMethod.get().methodSpec().name);
- }
- return Optional.empty();
- }
-
- @Override
- final Expression getDependencyExpression(ClassName requestingClass) {
- addUnimplementedMethod();
- return Expression.create(
- returnType(),
- componentImplementation.name().equals(requestingClass)
- ? CodeBlock.of("$N()", methodName.get())
- : CodeBlock.of("$T.this.$N()", componentImplementation.name(), methodName.get()));
- }
-
- private void addUnimplementedMethod() {
- if (!methodName.isPresent()) {
- // Only add the method once in case of repeated references to the missing binding.
- methodName = Optional.of(chooseMethodName());
- TypeMirror returnType = returnType();
- componentImplementation.addModifiableBindingMethod(
- modifiableBindingType,
- request,
- returnType,
- MethodSpec.methodBuilder(methodName.get())
- .addModifiers(PROTECTED, ABSTRACT)
- .returns(TypeName.get(returnType))
- .build(),
- false /* finalized */);
- }
- }
-
- /**
- * The return type of this abstract method expression:
- *
- * <ul>
- * <li>If there's a {@code matchingComponentMethod}, use its return type.
- * <li>Otherwise, use the {@linkplain DaggerTypes#publiclyAccessibleType(TypeMirror) publicly
- * accessible type} of the request. We can't use the {@linkplain
- * Accessibility#isTypeAccessibleFrom(TypeMirror, String) type accessible from the current
- * implementation's package} because a subclass implementation may be in a different package
- * from which the request type is not accessible.
- * </ul>
- */
- private TypeMirror returnType() {
- if (matchingComponentMethod.isPresent()) {
- return matchingComponentMethod.get().resolvedReturnType(types);
- }
-
- TypeMirror requestedType = request.requestedType(contributedType(), types);
- return types.publiclyAccessibleType(requestedType);
- }
-
- /**
- * The {@link ContributionBinding#contributedType() type contributed} by the binding of this
- * expression. For missing bindings, this will be the key type.
- */
- protected abstract TypeMirror contributedType();
-
- /** Returns a unique 'getter' method name for the current component. */
- abstract String chooseMethodName();
-}
diff --git a/java/dagger/internal/codegen/ModifiableBindingExpressions.java b/java/dagger/internal/codegen/ModifiableBindingExpressions.java
deleted file mode 100644
index 76bcb8bc5..000000000
--- a/java/dagger/internal/codegen/ModifiableBindingExpressions.java
+++ /dev/null
@@ -1,526 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.BindingRequest.bindingRequest;
-import static java.util.stream.Collectors.toList;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PROTECTED;
-import static javax.lang.model.element.Modifier.PUBLIC;
-
-import com.google.common.collect.ImmutableSet;
-import com.squareup.javapoet.MethodSpec;
-import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.internal.codegen.ComponentImplementation.MethodSpecKind;
-import dagger.internal.codegen.MethodBindingExpression.MethodImplementationStrategy;
-import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.BindingKind;
-import dagger.model.DependencyRequest;
-import java.util.Optional;
-
-/**
- * A central repository of code expressions used to access modifiable bindings available to a
- * component. A binding is modifiable if it can be modified across implementations of a
- * subcomponent. This is only relevant for ahead-of-time subcomponents.
- */
-final class ModifiableBindingExpressions {
- private final Optional<ModifiableBindingExpressions> parent;
- private final ComponentBindingExpressions bindingExpressions;
- private final BindingGraph graph;
- private final ComponentImplementation componentImplementation;
- private final CompilerOptions compilerOptions;
- private final DaggerTypes types;
-
- ModifiableBindingExpressions(
- Optional<ModifiableBindingExpressions> parent,
- ComponentBindingExpressions bindingExpressions,
- BindingGraph graph,
- ComponentImplementation componentImplementation,
- CompilerOptions compilerOptions,
- DaggerTypes types) {
- this.parent = parent;
- this.bindingExpressions = bindingExpressions;
- this.graph = graph;
- this.componentImplementation = componentImplementation;
- this.compilerOptions = compilerOptions;
- this.types = types;
- }
-
- /**
- * Adds {@code method} to the component implementation. If the binding for the method is
- * modifiable, also registers the relevant modifiable binding information.
- */
- void addPossiblyModifiableComponentMethod(
- ComponentMethodDescriptor componentMethod, MethodSpec method) {
- BindingRequest request = bindingRequest(componentMethod.dependencyRequest().get());
- ModifiableBindingType modifiableBindingType = getModifiableBindingType(request);
- if (modifiableBindingType.isModifiable()) {
- componentImplementation.addModifiableComponentMethod(
- modifiableBindingType,
- request,
- componentMethod.resolvedReturnType(types),
- method,
- newModifiableBindingWillBeFinalized(modifiableBindingType, request));
- } else {
- componentImplementation.addMethod(MethodSpecKind.COMPONENT_METHOD, method);
- }
- }
-
- /**
- * Returns the implementation of a modifiable binding method originally defined in a supertype
- * implementation of this subcomponent. Returns {@link Optional#empty()} when the binding cannot
- * or should not be modified by the current binding graph.
- */
- Optional<ModifiableBindingMethod> possiblyReimplementedMethod(
- ModifiableBindingMethod modifiableBindingMethod) {
- checkState(componentImplementation.superclassImplementation().isPresent());
- BindingRequest request = modifiableBindingMethod.request();
- ModifiableBindingType newModifiableBindingType = getModifiableBindingType(request);
- ModifiableBindingType oldModifiableBindingType = modifiableBindingMethod.type();
- boolean modifiableBindingTypeChanged =
- !newModifiableBindingType.equals(oldModifiableBindingType);
-
- ResolvedBindings resolvedBindings = graph.resolvedBindings(request);
- // Don't reimplement modifiable bindings that were perceived to be provision bindings in a
- // superclass implementation but are now production bindings.
- if ((modifiableBindingTypeChanged
- // Optional bindings don't need the same treatment since the only transition they can
- // make is empty -> present. In that case, the Producer<Optional<T>> will be overridden
- // and the absentOptionalProvider() will be a dangling reference that is never attempted
- // to be overridden.
- || newModifiableBindingType.equals(ModifiableBindingType.MULTIBINDING))
- && resolvedBindings != null
- && resolvedBindings.bindingType().equals(BindingType.PRODUCTION)
- && !request.canBeSatisfiedByProductionBinding()) {
- return oldModifiableBindingType.hasBaseClassImplementation()
- ? Optional.empty()
- : Optional.of(
- reimplementedMethod(
- modifiableBindingMethod,
- newModifiableBindingType,
- new PrunedConcreteMethodBindingExpression(),
- componentImplementation.isAbstract()));
- }
-
- if (modifiableBindingTypeChanged
- && !newModifiableBindingType.hasBaseClassImplementation()
- && (oldModifiableBindingType.hasBaseClassImplementation()
- || componentImplementation.isAbstract())) {
- // We don't want to override one abstract method with another one. However, If the component
- // is not abstract (such as a transition from GENERATED_INSTANCE -> MISSING), we must provide
- // an implementation like normal.
- return Optional.empty();
- }
-
- if (modifiableBindingTypeChanged
- || shouldModifyImplementation(newModifiableBindingType, request)) {
- boolean markMethodFinal =
- knownModifiableBindingWillBeFinalized(modifiableBindingMethod)
- // no need to mark the method final if the component implementation will be final
- && componentImplementation.isAbstract();
- return Optional.of(
- reimplementedMethod(
- modifiableBindingMethod,
- newModifiableBindingType,
- bindingExpressions.getBindingExpression(request),
- markMethodFinal));
- }
- return Optional.empty();
- }
-
- /**
- * Returns a new {@link ModifiableBindingMethod} that overrides {@code supertypeMethod} and is
- * implemented with {@code bindingExpression}.
- */
- private ModifiableBindingMethod reimplementedMethod(
- ModifiableBindingMethod supertypeMethod,
- ModifiableBindingType newModifiableBindingType,
- BindingExpression bindingExpression,
- boolean markMethodFinal) {
- MethodSpec baseMethod = supertypeMethod.methodSpec();
- return supertypeMethod.reimplement(
- newModifiableBindingType,
- MethodSpec.methodBuilder(baseMethod.name)
- .addModifiers(baseMethod.modifiers.contains(PUBLIC) ? PUBLIC : PROTECTED)
- .addModifiers(markMethodFinal ? ImmutableSet.of(FINAL) : ImmutableSet.of())
- .returns(baseMethod.returnType)
- .addAnnotation(Override.class)
- .addCode(
- bindingExpression.getModifiableBindingMethodImplementation(
- supertypeMethod, componentImplementation, types))
- .build(),
- markMethodFinal);
- }
-
- /**
- * Returns true if a modifiable binding method that was registered in a superclass implementation
- * of this subcomponent should be marked as "finalized" if it is being overridden by this
- * subcomponent implementation. "Finalized" means we should not attempt to modify the binding in
- * any subcomponent subclass.
- */
- private boolean knownModifiableBindingWillBeFinalized(
- ModifiableBindingMethod modifiableBindingMethod) {
- ModifiableBindingType newModifiableBindingType =
- getModifiableBindingType(modifiableBindingMethod.request());
- if (!newModifiableBindingType.isModifiable()) {
- // If a modifiable binding has become non-modifiable it is final by definition.
- return true;
- }
- return modifiableBindingWillBeFinalized(
- newModifiableBindingType,
- shouldModifyImplementation(newModifiableBindingType, modifiableBindingMethod.request()));
- }
-
- /**
- * Returns true if a newly discovered modifiable binding method, once it is defined in this
- * subcomponent implementation, should be marked as "finalized", meaning we should not attempt to
- * modify the binding in any subcomponent subclass.
- */
- private boolean newModifiableBindingWillBeFinalized(
- ModifiableBindingType modifiableBindingType, BindingRequest request) {
- return modifiableBindingWillBeFinalized(
- modifiableBindingType, shouldModifyImplementation(modifiableBindingType, request));
- }
-
- /**
- * Returns true if we shouldn't attempt to further modify a modifiable binding once we complete
- * the implementation for the current subcomponent.
- */
- private boolean modifiableBindingWillBeFinalized(
- ModifiableBindingType modifiableBindingType, boolean modifyingBinding) {
- switch (modifiableBindingType) {
- case MISSING:
- case BINDS_METHOD_WITH_MISSING_DEPENDENCY:
- case GENERATED_INSTANCE:
- case OPTIONAL:
- case INJECTION:
- // Once we modify any of the above a single time, then they are finalized.
- return modifyingBinding;
- case MULTIBINDING:
- return false;
- default:
- throw new IllegalStateException(
- String.format(
- "Building binding expression for unsupported ModifiableBindingType [%s].",
- modifiableBindingType));
- }
- }
-
- /**
- * Creates a binding expression for a binding if it may be modified across implementations of a
- * subcomponent.
- */
- Optional<BindingExpression> maybeCreateModifiableBindingExpression(BindingRequest request) {
- ModifiableBindingType type = getModifiableBindingType(request);
- if (!type.isModifiable()) {
- return Optional.empty();
- }
- return Optional.of(createModifiableBindingExpression(type, request));
- }
-
- /** Creates a binding expression for a modifiable binding. */
- private BindingExpression createModifiableBindingExpression(
- ModifiableBindingType type, BindingRequest request) {
- ResolvedBindings resolvedBindings = graph.resolvedBindings(request);
- Optional<ModifiableBindingMethod> matchingModifiableBindingMethod =
- componentImplementation.getModifiableBindingMethod(request);
- Optional<ComponentMethodDescriptor> matchingComponentMethod =
- graph.componentDescriptor().firstMatchingComponentMethod(request);
- switch (type) {
- case GENERATED_INSTANCE:
- // If the subcomponent is abstract then we need to define an (un-implemented)
- // DeferredModifiableBindingExpression.
- if (componentImplementation.isAbstract()) {
- return new DeferredModifiableBindingExpression(
- componentImplementation,
- type,
- resolvedBindings.contributionBinding(),
- request,
- matchingModifiableBindingMethod,
- matchingComponentMethod,
- types);
- }
- // Otherwise return a concrete implementation.
- return bindingExpressions.createBindingExpression(resolvedBindings, request);
-
- case MISSING:
- // If we need an expression for a missing binding and the current implementation is
- // abstract, then we need an (un-implemented) MissingBindingExpression.
- if (componentImplementation.isAbstract()) {
- return new MissingBindingExpression(
- componentImplementation,
- request,
- matchingModifiableBindingMethod,
- matchingComponentMethod,
- types);
- }
- // Otherwise we assume that it is valid to have a missing binding as it is part of a
- // dependency chain that has been passively pruned.
- // TODO(b/117833324): Identify pruned bindings when generating the subcomponent
- // implementation in which the bindings are pruned. If we hold a reference to the binding
- // graph used to generate a given implementation then we can compare a implementation's
- // graph with its superclass implementation's graph to detect pruned dependency branches.
- return new PrunedConcreteMethodBindingExpression();
-
- case BINDS_METHOD_WITH_MISSING_DEPENDENCY:
- checkState(componentImplementation.isAbstract());
- return new DeferredModifiableBindingExpression(
- componentImplementation,
- type,
- resolvedBindings.contributionBinding(),
- request,
- matchingModifiableBindingMethod,
- matchingComponentMethod,
- types);
-
- case OPTIONAL:
- case MULTIBINDING:
- case INJECTION:
- return bindingExpressions.wrapInMethod(
- resolvedBindings,
- request,
- bindingExpressions.createBindingExpression(resolvedBindings, request));
- default:
- throw new IllegalStateException(
- String.format(
- "Building binding expression for unsupported ModifiableBindingType [%s].", type));
- }
- }
-
- /**
- * The reason why a binding may need to be modified across implementations of a subcomponent, if
- * at all.
- */
- ModifiableBindingType getModifiableBindingType(BindingRequest request) {
- if (!compilerOptions.aheadOfTimeSubcomponents()) {
- return ModifiableBindingType.NONE;
- }
-
- // When generating a component the binding is not considered modifiable. Bindings are modifiable
- // only across subcomponent implementations.
- if (!componentImplementation.componentDescriptor().isSubcomponent()) {
- return ModifiableBindingType.NONE;
- }
-
- if (request.requestKind().filter(RequestKinds::isDerivedFromProvider).isPresent()) {
- return ModifiableBindingType.NONE;
- }
-
- if (resolvedInThisComponent(request)) {
- ResolvedBindings resolvedBindings = graph.resolvedBindings(request);
- if (resolvedBindings.contributionBindings().isEmpty()) {
- // TODO(ronshapiro): Confirm whether a resolved binding must have a single contribution
- // binding.
- return ModifiableBindingType.NONE;
- }
-
- ContributionBinding binding = resolvedBindings.contributionBinding();
- if (binding.requiresGeneratedInstance()) {
- return ModifiableBindingType.GENERATED_INSTANCE;
- }
-
- if (binding.kind().equals(BindingKind.DELEGATE)
- && graph
- .contributionBindings()
- .get(getOnlyElement(binding.dependencies()).key())
- .isEmpty()) {
- return ModifiableBindingType.BINDS_METHOD_WITH_MISSING_DEPENDENCY;
- }
-
- if (binding.kind().equals(BindingKind.OPTIONAL) && binding.dependencies().isEmpty()) {
- // only empty optional bindings can be modified
- return ModifiableBindingType.OPTIONAL;
- }
-
- if (binding.isSyntheticMultibinding()) {
- return ModifiableBindingType.MULTIBINDING;
- }
-
- if (binding.kind().equals(BindingKind.INJECTION)) {
- return ModifiableBindingType.INJECTION;
- }
- } else if (!resolvableBinding(request)) {
- return ModifiableBindingType.MISSING;
- }
-
- return ModifiableBindingType.NONE;
- }
-
- /**
- * Returns true if the current binding graph can, and should, modify a binding by overriding a
- * modifiable binding method.
- */
- private boolean shouldModifyImplementation(
- ModifiableBindingType modifiableBindingType, BindingRequest request) {
- ResolvedBindings resolvedBindings = graph.resolvedBindings(request);
- if (request.requestKind().isPresent()) {
- switch (request.requestKind().get()) {
- case FUTURE:
- // Futures backed by production bindings are always requested by a Producer.get() call, so
- // if the binding is modifiable, the producer will be wrapped in a modifiable method and
- // the future can refer to that method; even if the producer binding is modified,
- // getModifiableProducer().get() will never need to be modified. Furthermore, because
- // cancellation is treated by wrapped producers, and those producers point to the
- // modifiable producer wrapper methods, we never need or want to change the access of
- // these wrapped producers for entry methods
- //
- // Futures backed by provision bindings are inlined and contain no wrapping producer, so
- // if the binding is modifiable and is resolved as a provision binding in a superclass
- // but later resolved as a production binding, we can't take the same shortcut as before.
- Optional<ComponentImplementation> superclassImplementation =
- componentImplementation.superclassImplementation();
- if (superclassImplementation.isPresent()) {
- if (superclassImplementation.get().isDeserializedImplementation()) {
- // TODO(b/117833324): consider serializing the binding type so that we don't need to
- // branch here. Or, instead, consider removing this optimization entirely if there
- // aren't that many FUTURE entry point methods to justify the extra code.
- break;
- } else {
- return bindingTypeChanged(request, resolvedBindings);
- }
- }
- return false;
-
- case LAZY:
- case PROVIDER_OF_LAZY:
- // Lazy and ProviderOfLazy are always created from a Provider, and therefore this request
- // never needs to be modifiable. It will refer (via DoubleCheck.lazy() or
- // ProviderOfLazy.create()) to the modifiable method and not the framework instance.
- return false;
-
- case MEMBERS_INJECTION:
- case PRODUCED:
- // MEMBERS_INJECTION has a completely different code path for binding expressions, and
- // PRODUCED requests are only requestable in @Produces methods, which are hidden from
- // generated components inside Producer factories
- throw new AssertionError(request);
-
- case INSTANCE:
- case PROVIDER:
- case PRODUCER:
- // These may be modifiable, so run through the regular logic. They're spelled out
- // explicitly so that ErrorProne will detect if a new enum value is created and missing
- // from this list.
- break;
- }
- }
-
- switch (modifiableBindingType) {
- case GENERATED_INSTANCE:
- return !componentImplementation.isAbstract();
-
- case MISSING:
- // TODO(b/117833324): investigate beder@'s comment about having intermediate component
- // ancestors satisfy missing bindings of their children with their own missing binding
- // methods so that we can minimize the cases where we need to reach into doubly-nested
- // descendant component implementations.
-
- // Implement a missing binding if it is resolvable, or if we're generating a concrete
- // subcomponent implementation. If a binding is still missing when the subcomponent
- // implementation is concrete then it is assumed to be part of a dependency that would have
- // been passively pruned when implementing the full component hierarchy.
- return resolvableBinding(request) || !componentImplementation.isAbstract();
-
- case BINDS_METHOD_WITH_MISSING_DEPENDENCY:
- DependencyRequest dependency =
- getOnlyElement(resolvedBindings.contributionBinding().dependencies());
- return !graph.contributionBindings().get(dependency.key()).isEmpty();
-
- case OPTIONAL:
- // Only override optional binding methods if we have a non-empty binding.
- return !resolvedBindings.contributionBinding().dependencies().isEmpty();
-
- case MULTIBINDING:
- // Only modify a multibinding if there are new contributions.
- return !componentImplementation
- .superclassContributionsMade(request)
- .containsAll(
- resolvedBindings.contributionBinding().dependencies().stream()
- .map(DependencyRequest::key)
- .collect(toList()));
-
- case INJECTION:
- return !resolvedBindings.contributionBinding().kind().equals(BindingKind.INJECTION);
-
- default:
- throw new IllegalStateException(
- String.format(
- "Overriding modifiable binding method with unsupported ModifiableBindingType [%s].",
- modifiableBindingType));
- }
- }
-
- /**
- * Returns {@code true} if the {@link BindingType} for {@code request} is not the same in this
- * implementation and it's superclass implementation.
- */
- private boolean bindingTypeChanged(BindingRequest request, ResolvedBindings resolvedBindings) {
- BindingGraph superclassGraph =
- componentImplementation.superclassImplementation().get().graph();
- ResolvedBindings superclassBindings = superclassGraph.resolvedBindings(request);
- return superclassBindings != null
- && resolvedBindings != null
- && !superclassBindings.bindingType().equals(resolvedBindings.bindingType());
- }
-
- /**
- * Returns true if the binding can be resolved by the graph for this component or any parent
- * component.
- */
- private boolean resolvableBinding(BindingRequest request) {
- for (ModifiableBindingExpressions expressions = this;
- expressions != null;
- expressions = expressions.parent.orElse(null)) {
- if (expressions.resolvedInThisComponent(request)) {
- return true;
- }
- }
- return false;
- }
-
- /** Returns true if the binding can be resolved by the graph for this component. */
- private boolean resolvedInThisComponent(BindingRequest request) {
- ResolvedBindings resolvedBindings = graph.resolvedBindings(request);
- return resolvedBindings != null
- && !resolvedBindings.bindingsOwnedBy(graph.componentDescriptor()).isEmpty();
- }
-
- /**
- * Wraps a modifiable binding expression in a method that can be overridden in a subclass
- * implementation.
- */
- BindingExpression wrapInModifiableMethodBindingExpression(
- BindingRequest request,
- ResolvedBindings resolvedBindings,
- MethodImplementationStrategy methodImplementationStrategy,
- BindingExpression wrappedBindingExpression) {
- ModifiableBindingType modifiableBindingType = getModifiableBindingType(request);
- checkState(modifiableBindingType.isModifiable());
- return new ModifiableConcreteMethodBindingExpression(
- request,
- resolvedBindings,
- methodImplementationStrategy,
- wrappedBindingExpression,
- modifiableBindingType,
- componentImplementation,
- newModifiableBindingWillBeFinalized(modifiableBindingType, request),
- types);
- }
-}
diff --git a/java/dagger/internal/codegen/ModifiableBindingMethods.java b/java/dagger/internal/codegen/ModifiableBindingMethods.java
deleted file mode 100644
index ead708df6..000000000
--- a/java/dagger/internal/codegen/ModifiableBindingMethods.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Verify.verify;
-
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Equivalence;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import com.squareup.javapoet.MethodSpec;
-import java.util.Map;
-import java.util.Optional;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * A registry for those methods which each wrap a binding whose definition may be modified across
- * each class in the class hierarchy implementing a subcomponent. Subcomponent implementations are
- * spread across a class hierarchy when generating ahead-of-time subcomponents. There is one
- * subcomponent implementation class for each of the subcomponent's ancestor components. An instance
- * of {@link ModifiableBindingMethod} is associated with a single class in this hierarchy. For a
- * given subcomponent implementation class we can use the {@link ModifiableBindingMethod}s of its
- * superclasses to know what binding methods to attempt to modify.
- */
-final class ModifiableBindingMethods {
- private final Map<BindingRequest, ModifiableBindingMethod> methods = Maps.newLinkedHashMap();
-
- /** Registers a new method encapsulating a modifiable binding. */
- void addModifiableMethod(
- ModifiableBindingType type,
- BindingRequest request,
- TypeMirror returnType,
- MethodSpec method,
- boolean finalized) {
- // It's ok for the type to not be modifiable, since it could be overriding a previously
- // modifiable method (such as with addReimplementedMethod).
- addMethod(ModifiableBindingMethod.create(type, request, returnType, method, finalized));
- }
-
- /** Registers a reimplemented modifiable method. */
- void addReimplementedMethod(ModifiableBindingMethod method) {
- addMethod(method);
- }
-
- private void addMethod(ModifiableBindingMethod method) {
- ModifiableBindingMethod previousMethod = methods.put(method.request(), method);
- verify(
- previousMethod == null,
- "registering %s but %s is already registered for the same binding request",
- method,
- previousMethod);
- }
-
- /** Returns all {@link ModifiableBindingMethod}s that have not been marked as finalized. */
- ImmutableMap<BindingRequest, ModifiableBindingMethod> getNonFinalizedMethods() {
- return ImmutableMap.copyOf(Maps.filterValues(methods, m -> !m.finalized()));
- }
-
- /** Returns the {@link ModifiableBindingMethod} for the given binding if present. */
- Optional<ModifiableBindingMethod> getMethod(BindingRequest request) {
- return Optional.ofNullable(methods.get(request));
- }
-
- /** Returns all of the {@link ModifiableBindingMethod}s. */
- ImmutableList<ModifiableBindingMethod> allMethods() {
- return ImmutableList.copyOf(methods.values());
- }
-
- /** Whether a given binding has been marked as finalized. */
- // TODO(ronshapiro): possibly rename this to something that indicates that the BindingRequest for
- // `method` has been finalized in *this* component implementation?
- boolean finalized(ModifiableBindingMethod method) {
- ModifiableBindingMethod storedMethod = methods.get(method.request());
- return storedMethod != null && storedMethod.finalized();
- }
-
- @AutoValue
- abstract static class ModifiableBindingMethod {
- private static ModifiableBindingMethod create(
- ModifiableBindingType type,
- BindingRequest request,
- TypeMirror returnType,
- MethodSpec methodSpec,
- boolean finalized) {
- return new AutoValue_ModifiableBindingMethods_ModifiableBindingMethod(
- type, request, MoreTypes.equivalence().wrap(returnType), methodSpec, finalized);
- }
-
- /** Creates a {@ModifiableBindingMethod} that reimplements the current method. */
- ModifiableBindingMethod reimplement(
- ModifiableBindingType newModifiableBindingType,
- MethodSpec newImplementation,
- boolean finalized) {
- return new AutoValue_ModifiableBindingMethods_ModifiableBindingMethod(
- newModifiableBindingType, request(), returnTypeWrapper(), newImplementation, finalized);
- }
-
- abstract ModifiableBindingType type();
-
- abstract BindingRequest request();
-
- final TypeMirror returnType() {
- return returnTypeWrapper().get();
- }
-
- abstract Equivalence.Wrapper<TypeMirror> returnTypeWrapper();
-
- abstract MethodSpec methodSpec();
-
- abstract boolean finalized();
-
- /** Whether a {@link ModifiableBindingMethod} is for the same binding request. */
- boolean fulfillsSameRequestAs(ModifiableBindingMethod other) {
- return request().equals(other.request());
- }
- }
-}
diff --git a/java/dagger/internal/codegen/ModifiableBindingType.java b/java/dagger/internal/codegen/ModifiableBindingType.java
deleted file mode 100644
index 7e43ec159..000000000
--- a/java/dagger/internal/codegen/ModifiableBindingType.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import com.google.common.collect.ImmutableSet;
-
-/**
- * A label for a binding indicating whether, and how, it may be redefined across implementations of
- * a subcomponent.
- *
- * <p>A subcomponent has multiple implementations only when generating ahead-of-time subcomponents.
- * Specifically, each subcomponent type in a component hierarchy is implemented as an abstract
- * class, and descendent components are implemented as abstract inner classes. A consequence of this
- * is that a given subcomponent has an implementation for each ancestor component. Each
- * implementation represents a different sub-binding-graph of the full subcomponent. A binding is
- * modifiable if it's definition may change depending on the characteristics of its ancestor
- * components.
- */
-enum ModifiableBindingType {
- /** A binding that is not modifiable */
- NONE,
-
- /**
- * A binding that is missing when generating the abstract base class implementation of a
- * subcomponent.
- */
- MISSING,
-
- /**
- * A binding that requires an instance of a generated type. These binding are modifiable in the
- * sense that they are encapsulated in a method when they are first required, possibly in an
- * abstract implementation of a subcomponent, where, in general, no concrete instances of
- * generated types are available, and the method is satisfied in a final concrete implementation.
- */
- GENERATED_INSTANCE,
-
- /**
- * Multibindings may have contributions come from any ancestor component. Therefore, each
- * implementation of a subcomponent may have newly available contributions, and so the binding
- * method is reimplemented with each subcomponent implementation.
- */
- MULTIBINDING,
-
- /**
- * A Optional binding that may be empty when looking at a partial binding graph, but bound to a
- * value when considering the complete binding graph, thus modifiable across subcomponent
- * implementations.
- */
- OPTIONAL,
-
- /**
- * If a binding is defined according to an {@code @Inject} annotated constructor on the object it
- * is valid for that binding to be redefined a single time by an {@code @Provides} annotated
- * module method. It is possible that the {@code @Provides} binding isn't available in a partial
- * binding graph, but becomes available when considering a more complete binding graph, therefore
- * such bindings are modifiable across subcomponent implementations.
- */
- INJECTION,
-
- /**
- * A {@link dagger.Binds} method whose dependency is {@link #MISSING}.
- *
- * <p>There's not much to do for @Binds bindings if the dependency is missing - at best, if the
- * dependency is a weaker scope/unscoped, we save only a few lines that implement the scoping. But
- * it's also possible, if the dependency is the same or stronger scope, that no extra code is
- * necessary, in which case we'd be overriding a method that just returns another.
- */
- BINDS_METHOD_WITH_MISSING_DEPENDENCY,
- ;
-
- private static final ImmutableSet<ModifiableBindingType> TYPES_WITH_BASE_CLASS_IMPLEMENTATIONS =
- ImmutableSet.of(NONE, INJECTION, MULTIBINDING, OPTIONAL);
-
- boolean isModifiable() {
- return !equals(NONE);
- }
-
- /**
- * Returns true if the method encapsulating the modifiable binding should have a concrete
- * implementation in the abstract base class for a subcomponent.
- */
- boolean hasBaseClassImplementation() {
- return TYPES_WITH_BASE_CLASS_IMPLEMENTATIONS.contains(this);
- }
-}
diff --git a/java/dagger/internal/codegen/ModifiableConcreteMethodBindingExpression.java b/java/dagger/internal/codegen/ModifiableConcreteMethodBindingExpression.java
deleted file mode 100644
index 907466b29..000000000
--- a/java/dagger/internal/codegen/ModifiableConcreteMethodBindingExpression.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PROTECTED;
-
-import com.squareup.javapoet.TypeName;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import java.util.Optional;
-
-/**
- * A binding expression that wraps a modifiable binding expression in a public, no-arg method.
- *
- * <p>Dependents of this binding expression will just call the modifiable binding method.
- */
-final class ModifiableConcreteMethodBindingExpression extends MethodBindingExpression {
-
- private final BindingRequest request;
- private final ModifiableBindingType modifiableBindingType;
- private final ComponentImplementation componentImplementation;
- private final boolean bindingCannotBeModified;
- private Optional<String> methodName = Optional.empty();
-
- ModifiableConcreteMethodBindingExpression(
- BindingRequest request,
- ResolvedBindings resolvedBindings,
- MethodImplementationStrategy methodImplementationStrategy,
- BindingExpression wrappedBindingExpression,
- ModifiableBindingType modifiableBindingType,
- ComponentImplementation componentImplementation,
- boolean bindingCannotBeModified,
- DaggerTypes types) {
- super(
- request,
- resolvedBindings,
- methodImplementationStrategy,
- wrappedBindingExpression,
- componentImplementation,
- types);
- this.request = checkNotNull(request);
- this.modifiableBindingType = checkNotNull(modifiableBindingType);
- this.componentImplementation = checkNotNull(componentImplementation);
- this.bindingCannotBeModified = bindingCannotBeModified;
- }
-
- @Override
- protected void addMethod() {
- if (methodName.isPresent()) {
- return;
- }
-
- if (supertypeModifiableBindingMethod().isPresent()) {
- methodName = supertypeModifiableBindingMethod().map(method -> method.methodSpec().name);
- return;
- }
-
- // Add the modifiable binding method to the component if we haven't already.
- methodName = Optional.of(componentImplementation.getUniqueMethodName(request));
- componentImplementation.addModifiableBindingMethod(
- modifiableBindingType,
- request,
- returnType(),
- methodBuilder(methodName.get())
- .addModifiers(bindingCannotBeModified ? PRIVATE : PROTECTED)
- .returns(TypeName.get(returnType()))
- .addCode(methodBody())
- .build(),
- bindingCannotBeModified);
- }
-
- @Override
- protected String methodName() {
- checkState(methodName.isPresent(), "addMethod() must be called before methodName().");
- return methodName.get();
- }
-
- @Override
- protected boolean isModifiableImplementationMethod() {
- return true;
- }
-}
diff --git a/java/dagger/internal/codegen/ModuleAnnotation.java b/java/dagger/internal/codegen/ModuleAnnotation.java
deleted file mode 100644
index 27dd071f2..000000000
--- a/java/dagger/internal/codegen/ModuleAnnotation.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2019 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
-import static com.google.auto.common.MoreTypes.asTypeElement;
-import static com.google.common.base.Preconditions.checkArgument;
-import static dagger.internal.codegen.DaggerStreams.toImmutableList;
-import static dagger.internal.codegen.MoreAnnotationValues.asAnnotationValues;
-import static dagger.internal.codegen.langmodel.DaggerElements.getAnyAnnotation;
-
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.auto.value.extension.memoized.Memoized;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import dagger.Module;
-import dagger.producers.ProducerModule;
-import java.lang.annotation.Annotation;
-import java.util.Optional;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.TypeElement;
-
-/** A {@code @Module} or {@code @ProducerModule} annotation. */
-@AutoValue
-abstract class ModuleAnnotation {
- private static final ImmutableSet<Class<? extends Annotation>> MODULE_ANNOTATIONS =
- ImmutableSet.of(Module.class, ProducerModule.class);
-
- /** The annotation itself. */
- // This does not use AnnotationMirrors.equivalence() because we want the actual annotation
- // instance.
- abstract AnnotationMirror annotation();
-
- /** The type of the annotation. */
- @Memoized
- Class<?> annotationClass() {
- try {
- return Class.forName(
- asTypeElement(annotation().getAnnotationType()).getQualifiedName().toString());
- } catch (ClassNotFoundException e) {
- AssertionError assertionError = new AssertionError();
- assertionError.initCause(e);
- throw assertionError;
- }
- }
-
- /**
- * The types specified in the {@code includes} attribute.
- *
- * @throws IllegalArgumentException if any of the values are error types
- */
- @Memoized
- ImmutableList<TypeElement> includes() {
- return includesAsAnnotationValues().stream()
- .map(MoreAnnotationValues::asType)
- .map(MoreTypes::asTypeElement)
- .collect(toImmutableList());
- }
-
- /** The values specified in the {@code includes} attribute. */
- @Memoized
- ImmutableList<AnnotationValue> includesAsAnnotationValues() {
- return asAnnotationValues(getAnnotationValue(annotation(), "includes"));
- }
-
- /**
- * The types specified in the {@code subcomponents} attribute.
- *
- * @throws IllegalArgumentException if any of the values are error types
- */
- @Memoized
- ImmutableList<TypeElement> subcomponents() {
- return subcomponentsAsAnnotationValues().stream()
- .map(MoreAnnotationValues::asType)
- .map(MoreTypes::asTypeElement)
- .collect(toImmutableList());
- }
-
- /** The values specified in the {@code subcomponents} attribute. */
- @Memoized
- ImmutableList<AnnotationValue> subcomponentsAsAnnotationValues() {
- return asAnnotationValues(getAnnotationValue(annotation(), "subcomponents"));
- }
-
- /** Returns {@code true} if the argument is a {@code @Module} or {@code @ProducerModule}. */
- static boolean isModuleAnnotation(AnnotationMirror annotation) {
- return MODULE_ANNOTATIONS.stream()
- .map(Class::getCanonicalName)
- .anyMatch(asTypeElement(annotation.getAnnotationType()).getQualifiedName()::contentEquals);
- }
-
- /** The module annotation types. */
- static ImmutableSet<Class<? extends Annotation>> moduleAnnotations() {
- return MODULE_ANNOTATIONS;
- }
-
- /**
- * Creates an object that represents a {@code @Module} or {@code @ProducerModule}.
- *
- * @throws IllegalArgumentException if {@link #isModuleAnnotation(AnnotationMirror)} returns
- * {@code false}
- */
- static ModuleAnnotation moduleAnnotation(AnnotationMirror annotation) {
- checkArgument(
- isModuleAnnotation(annotation),
- "%s is not a Module or ProducerModule annotation",
- annotation);
- return new AutoValue_ModuleAnnotation(annotation);
- }
-
- /**
- * Returns an object representing the {@code @Module} or {@code @ProducerModule} annotation if one
- * annotates {@code typeElement}.
- */
- static Optional<ModuleAnnotation> moduleAnnotation(TypeElement typeElement) {
- return getAnyAnnotation(typeElement, Module.class, ProducerModule.class)
- .map(ModuleAnnotation::moduleAnnotation);
- }
-}
diff --git a/java/dagger/internal/codegen/ModuleConstructorProxyGenerator.java b/java/dagger/internal/codegen/ModuleConstructorProxyGenerator.java
deleted file mode 100644
index 5f0d6871b..000000000
--- a/java/dagger/internal/codegen/ModuleConstructorProxyGenerator.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.squareup.javapoet.MethodSpec.constructorBuilder;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static com.squareup.javapoet.TypeSpec.classBuilder;
-import static dagger.internal.codegen.ModuleKind.checkIsModule;
-import static dagger.internal.codegen.ModuleProxies.nonPublicNullaryConstructor;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PUBLIC;
-import static javax.lang.model.element.Modifier.STATIC;
-
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.TypeSpec;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import java.util.Optional;
-import javax.annotation.processing.Filer;
-import javax.inject.Inject;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-
-/**
- * Generates a {@code public static} method that calls {@code new SomeModule()} for modules that
- * don't have {@linkplain ModuleProxies#nonPublicNullaryConstructor(TypeElement,
- * DaggerElements) publicly accessible constructors}.
- */
-// TODO(dpb): See if this can become a SourceFileGenerator<ModuleDescriptor> instead. Doing so may
-// cause ModuleProcessingStep to defer elements multiple times.
-final class ModuleConstructorProxyGenerator extends SourceFileGenerator<TypeElement> {
- private final DaggerElements elements;
-
- @Inject
- ModuleConstructorProxyGenerator(
- Filer filer, DaggerElements elements, SourceVersion sourceVersion) {
- super(filer, elements, sourceVersion);
- this.elements = elements;
- }
-
- @Override
- ClassName nameGeneratedType(TypeElement moduleElement) {
- return ModuleProxies.constructorProxyTypeName(moduleElement);
- }
-
- @Override
- Element originatingElement(TypeElement moduleElement) {
- return moduleElement;
- }
-
- @Override
- Optional<TypeSpec.Builder> write(ClassName generatedTypeName, TypeElement moduleElement) {
- checkIsModule(moduleElement);
- return nonPublicNullaryConstructor(moduleElement, elements).isPresent()
- ? Optional.of(buildProxy(generatedTypeName, moduleElement))
- : Optional.empty();
- }
-
- private TypeSpec.Builder buildProxy(ClassName generatedTypeName, TypeElement moduleElement) {
- return classBuilder(generatedTypeName)
- .addModifiers(PUBLIC, FINAL)
- .addMethod(constructorBuilder().addModifiers(PRIVATE).build())
- .addMethod(
- methodBuilder("newInstance")
- .addModifiers(PUBLIC, STATIC)
- .returns(ClassName.get(moduleElement))
- .addStatement("return new $T()", moduleElement)
- .build());
- }
-}
diff --git a/java/dagger/internal/codegen/ModuleDescriptor.java b/java/dagger/internal/codegen/ModuleDescriptor.java
deleted file mode 100644
index ac7a4b772..000000000
--- a/java/dagger/internal/codegen/ModuleDescriptor.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreElements.getPackage;
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.common.base.CaseFormat.LOWER_CAMEL;
-import static com.google.common.base.CaseFormat.UPPER_CAMEL;
-import static com.google.common.base.Verify.verify;
-import static com.google.common.collect.Iterables.transform;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.ModuleAnnotation.moduleAnnotation;
-import static dagger.internal.codegen.SourceFiles.classFileName;
-import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
-import static dagger.internal.codegen.langmodel.DaggerElements.isAnnotationPresent;
-import static javax.lang.model.type.TypeKind.DECLARED;
-import static javax.lang.model.type.TypeKind.NONE;
-import static javax.lang.model.util.ElementFilter.methodsIn;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.auto.value.extension.memoized.Memoized;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.graph.Traverser;
-import com.google.errorprone.annotations.CanIgnoreReturnValue;
-import com.squareup.javapoet.ClassName;
-import dagger.Binds;
-import dagger.BindsOptionalOf;
-import dagger.Module;
-import dagger.Provides;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.model.Key;
-import dagger.multibindings.Multibinds;
-import dagger.producers.Produces;
-import java.util.HashMap;
-import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Set;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.TypeMirror;
-
-@AutoValue
-abstract class ModuleDescriptor {
-
- abstract TypeElement moduleElement();
-
- abstract ImmutableSet<TypeElement> includedModules();
-
- abstract ImmutableSet<ContributionBinding> bindings();
-
- /** The multibinding declarations contained in this module. */
- abstract ImmutableSet<MultibindingDeclaration> multibindingDeclarations();
-
- /** The {@link Module#subcomponents() subcomponent declarations} contained in this module. */
- abstract ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations();
-
- /** The {@link Binds} method declarations that define delegate bindings. */
- abstract ImmutableSet<DelegateDeclaration> delegateDeclarations();
-
- /** The {@link BindsOptionalOf} method declarations that define optional bindings. */
- abstract ImmutableSet<OptionalBindingDeclaration> optionalDeclarations();
-
- /** The kind of the module. */
- abstract ModuleKind kind();
-
- /** Returns all of the bindings declared in this module. */
- @Memoized
- ImmutableSet<BindingDeclaration> allBindingDeclarations() {
- return ImmutableSet.<BindingDeclaration>builder()
- .addAll(bindings())
- .addAll(delegateDeclarations())
- .addAll(multibindingDeclarations())
- .addAll(optionalDeclarations())
- .addAll(subcomponentDeclarations())
- .build();
- }
-
- /** Returns the keys of all bindings declared by this module. */
- ImmutableSet<Key> allBindingKeys() {
- return allBindingDeclarations().stream().map(BindingDeclaration::key).collect(toImmutableSet());
- }
-
- @Singleton
- static final class Factory implements ClearableCache {
- private final DaggerElements elements;
- private final BindingFactory bindingFactory;
- private final MultibindingDeclaration.Factory multibindingDeclarationFactory;
- private final DelegateDeclaration.Factory bindingDelegateDeclarationFactory;
- private final SubcomponentDeclaration.Factory subcomponentDeclarationFactory;
- private final OptionalBindingDeclaration.Factory optionalBindingDeclarationFactory;
- private final Map<TypeElement, ModuleDescriptor> cache = new HashMap<>();
-
- @Inject
- Factory(
- DaggerElements elements,
- BindingFactory bindingFactory,
- MultibindingDeclaration.Factory multibindingDeclarationFactory,
- DelegateDeclaration.Factory bindingDelegateDeclarationFactory,
- SubcomponentDeclaration.Factory subcomponentDeclarationFactory,
- OptionalBindingDeclaration.Factory optionalBindingDeclarationFactory) {
- this.elements = elements;
- this.bindingFactory = bindingFactory;
- this.multibindingDeclarationFactory = multibindingDeclarationFactory;
- this.bindingDelegateDeclarationFactory = bindingDelegateDeclarationFactory;
- this.subcomponentDeclarationFactory = subcomponentDeclarationFactory;
- this.optionalBindingDeclarationFactory = optionalBindingDeclarationFactory;
- }
-
- ModuleDescriptor create(TypeElement moduleElement) {
- return reentrantComputeIfAbsent(cache, moduleElement, this::createUncached);
- }
-
- ModuleDescriptor createUncached(TypeElement moduleElement) {
- ImmutableSet.Builder<ContributionBinding> bindings = ImmutableSet.builder();
- ImmutableSet.Builder<DelegateDeclaration> delegates = ImmutableSet.builder();
- ImmutableSet.Builder<MultibindingDeclaration> multibindingDeclarations =
- ImmutableSet.builder();
- ImmutableSet.Builder<OptionalBindingDeclaration> optionalDeclarations =
- ImmutableSet.builder();
-
- for (ExecutableElement moduleMethod : methodsIn(elements.getAllMembers(moduleElement))) {
- if (isAnnotationPresent(moduleMethod, Provides.class)) {
- bindings.add(bindingFactory.providesMethodBinding(moduleMethod, moduleElement));
- }
- if (isAnnotationPresent(moduleMethod, Produces.class)) {
- bindings.add(bindingFactory.producesMethodBinding(moduleMethod, moduleElement));
- }
- if (isAnnotationPresent(moduleMethod, Binds.class)) {
- delegates.add(bindingDelegateDeclarationFactory.create(moduleMethod, moduleElement));
- }
- if (isAnnotationPresent(moduleMethod, Multibinds.class)) {
- multibindingDeclarations.add(
- multibindingDeclarationFactory.forMultibindsMethod(moduleMethod, moduleElement));
- }
- if (isAnnotationPresent(moduleMethod, BindsOptionalOf.class)) {
- optionalDeclarations.add(
- optionalBindingDeclarationFactory.forMethod(moduleMethod, moduleElement));
- }
- }
-
- return new AutoValue_ModuleDescriptor(
- moduleElement,
- ImmutableSet.copyOf(collectIncludedModules(new LinkedHashSet<>(), moduleElement)),
- bindings.build(),
- multibindingDeclarations.build(),
- subcomponentDeclarationFactory.forModule(moduleElement),
- delegates.build(),
- optionalDeclarations.build(),
- ModuleKind.forAnnotatedElement(moduleElement).get());
- }
-
- /** Returns all the modules transitively included by given modules, including the arguments. */
- ImmutableSet<ModuleDescriptor> transitiveModules(Iterable<TypeElement> modules) {
- return ImmutableSet.copyOf(
- Traverser.forGraph(
- (ModuleDescriptor module) -> transform(module.includedModules(), this::create))
- .depthFirstPreOrder(transform(modules, this::create)));
- }
-
- @CanIgnoreReturnValue
- private Set<TypeElement> collectIncludedModules(
- Set<TypeElement> includedModules, TypeElement moduleElement) {
- TypeMirror superclass = moduleElement.getSuperclass();
- if (!superclass.getKind().equals(NONE)) {
- verify(superclass.getKind().equals(DECLARED));
- TypeElement superclassElement = MoreTypes.asTypeElement(superclass);
- if (!superclassElement.getQualifiedName().contentEquals(Object.class.getCanonicalName())) {
- collectIncludedModules(includedModules, superclassElement);
- }
- }
- moduleAnnotation(moduleElement)
- .ifPresent(
- moduleAnnotation -> {
- includedModules.addAll(moduleAnnotation.includes());
- includedModules.addAll(implicitlyIncludedModules(moduleElement));
- });
- return includedModules;
- }
-
- // @ContributesAndroidInjector generates a module that is implicitly included in the enclosing
- // module
- private ImmutableSet<TypeElement> implicitlyIncludedModules(TypeElement moduleElement) {
- TypeElement contributesAndroidInjector =
- elements.getTypeElement("dagger.android.ContributesAndroidInjector");
- if (contributesAndroidInjector == null) {
- return ImmutableSet.of();
- }
- return methodsIn(moduleElement.getEnclosedElements()).stream()
- .filter(method -> isAnnotationPresent(method, contributesAndroidInjector.asType()))
- .map(method -> elements.checkTypePresent(implicitlyIncludedModuleName(method)))
- .collect(toImmutableSet());
- }
-
- private String implicitlyIncludedModuleName(ExecutableElement method) {
- return getPackage(method).getQualifiedName()
- + "."
- + classFileName(ClassName.get(MoreElements.asType(method.getEnclosingElement())))
- + "_"
- + LOWER_CAMEL.to(UPPER_CAMEL, method.getSimpleName().toString());
- }
-
- @Override
- public void clearCache() {
- cache.clear();
- }
- }
-}
diff --git a/java/dagger/internal/codegen/ModuleGenerator.java b/java/dagger/internal/codegen/ModuleGenerator.java
deleted file mode 100644
index 161b47b4a..000000000
--- a/java/dagger/internal/codegen/ModuleGenerator.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import java.lang.annotation.Retention;
-import javax.inject.Qualifier;
-
-/** Qualifier for a {@link SourceFileGenerator} for modules. */
-@Qualifier
-@Retention(RUNTIME)
-@interface ModuleGenerator {}
diff --git a/java/dagger/internal/codegen/ModuleKind.java b/java/dagger/internal/codegen/ModuleKind.java
deleted file mode 100644
index c93d8ce79..000000000
--- a/java/dagger/internal/codegen/ModuleKind.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
-
-import com.google.auto.common.MoreElements;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import dagger.Module;
-import dagger.producers.ProducerModule;
-import java.lang.annotation.Annotation;
-import java.util.EnumSet;
-import java.util.Optional;
-import java.util.Set;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.TypeElement;
-
-/** Enumeration of the kinds of modules. */
-enum ModuleKind {
- /** {@code @Module} */
- MODULE(Module.class),
-
- /** {@code @ProducerModule} */
- PRODUCER_MODULE(ProducerModule.class);
-
- /** Returns the annotations for modules of the given kinds. */
- static ImmutableSet<Class<? extends Annotation>> annotationsFor(Set<ModuleKind> kinds) {
- return kinds.stream().map(ModuleKind::annotation).collect(toImmutableSet());
- }
-
- /**
- * Returns the kind of an annotated element if it is annotated with one of the module {@linkplain
- * #annotation() annotations}.
- *
- * @throws IllegalArgumentException if the element is annotated with more than one of the module
- * annotations
- */
- static Optional<ModuleKind> forAnnotatedElement(TypeElement element) {
- Set<ModuleKind> kinds = EnumSet.noneOf(ModuleKind.class);
- for (ModuleKind kind : values()) {
- if (MoreElements.isAnnotationPresent(element, kind.annotation())) {
- kinds.add(kind);
- }
- }
-
- if (kinds.size() > 1) {
- throw new IllegalArgumentException(
- element + " cannot be annotated with more than one of " + annotationsFor(kinds));
- }
- return kinds.stream().findAny();
- }
-
- static void checkIsModule(TypeElement moduleElement) {
- checkArgument(forAnnotatedElement(moduleElement).isPresent());
- }
-
- private final Class<? extends Annotation> moduleAnnotation;
-
- ModuleKind(Class<? extends Annotation> moduleAnnotation) {
- this.moduleAnnotation = moduleAnnotation;
- }
-
- /**
- * Returns the annotation mirror for this module kind on the given type.
- *
- * @throws IllegalArgumentException if the annotation is not present on the type
- */
- AnnotationMirror getModuleAnnotation(TypeElement element) {
- Optional<AnnotationMirror> result = getAnnotationMirror(element, moduleAnnotation);
- checkArgument(
- result.isPresent(), "annotation %s is not present on type %s", moduleAnnotation, element);
- return result.get();
- }
-
- /** Returns the annotation that marks a module of this kind. */
- Class<? extends Annotation> annotation() {
- return moduleAnnotation;
- }
-
- /** Returns the kinds of modules that a module of this kind is allowed to include. */
- ImmutableSet<ModuleKind> legalIncludedModuleKinds() {
- switch (this) {
- case MODULE:
- return Sets.immutableEnumSet(MODULE);
- case PRODUCER_MODULE:
- return Sets.immutableEnumSet(MODULE, PRODUCER_MODULE);
- }
- throw new AssertionError(this);
- }
-}
diff --git a/java/dagger/internal/codegen/ModuleProcessingStep.java b/java/dagger/internal/codegen/ModuleProcessingStep.java
index 27cd531fc..2bf33545c 100644
--- a/java/dagger/internal/codegen/ModuleProcessingStep.java
+++ b/java/dagger/internal/codegen/ModuleProcessingStep.java
@@ -28,7 +28,19 @@ import com.google.common.collect.Sets;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
-import dagger.internal.codegen.DelegateDeclaration.Factory;
+import dagger.internal.codegen.base.SourceFileGenerator;
+import dagger.internal.codegen.binding.BindingFactory;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.binding.DelegateDeclaration;
+import dagger.internal.codegen.binding.DelegateDeclaration.Factory;
+import dagger.internal.codegen.binding.ProductionBinding;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.validation.ModuleValidator;
+import dagger.internal.codegen.validation.TypeCheckingProcessingStep;
+import dagger.internal.codegen.validation.ValidationReport;
+import dagger.internal.codegen.writing.InaccessibleMapKeyProxyGenerator;
+import dagger.internal.codegen.writing.ModuleGenerator;
import dagger.producers.ProducerModule;
import dagger.producers.Produces;
import java.lang.annotation.Annotation;
@@ -53,6 +65,7 @@ final class ModuleProcessingStep extends TypeCheckingProcessingStep<TypeElement>
private final SourceFileGenerator<TypeElement> moduleConstructorProxyGenerator;
private final InaccessibleMapKeyProxyGenerator inaccessibleMapKeyProxyGenerator;
private final DelegateDeclaration.Factory delegateDeclarationFactory;
+ private final KotlinMetadataUtil metadataUtil;
private final Set<TypeElement> processedModuleElements = Sets.newLinkedHashSet();
@Inject
@@ -64,7 +77,8 @@ final class ModuleProcessingStep extends TypeCheckingProcessingStep<TypeElement>
SourceFileGenerator<ProductionBinding> producerFactoryGenerator,
@ModuleGenerator SourceFileGenerator<TypeElement> moduleConstructorProxyGenerator,
InaccessibleMapKeyProxyGenerator inaccessibleMapKeyProxyGenerator,
- Factory delegateDeclarationFactory) {
+ Factory delegateDeclarationFactory,
+ KotlinMetadataUtil metadataUtil) {
super(MoreElements::asType);
this.messager = messager;
this.moduleValidator = moduleValidator;
@@ -74,6 +88,7 @@ final class ModuleProcessingStep extends TypeCheckingProcessingStep<TypeElement>
this.moduleConstructorProxyGenerator = moduleConstructorProxyGenerator;
this.inaccessibleMapKeyProxyGenerator = inaccessibleMapKeyProxyGenerator;
this.delegateDeclarationFactory = delegateDeclarationFactory;
+ this.metadataUtil = metadataUtil;
}
@Override
@@ -95,23 +110,38 @@ final class ModuleProcessingStep extends TypeCheckingProcessingStep<TypeElement>
if (processedModuleElements.contains(module)) {
return;
}
+ // For backwards compatibility, we allow a companion object to be annotated with @Module even
+ // though it's no longer required. However, we skip processing the companion object itself
+ // because it will now be processed when processing the companion object's enclosing class.
+ if (metadataUtil.isCompanionObjectClass(module)) {
+ // TODO(danysantiago): Be strict about annotating companion objects with @Module,
+ // i.e. tell user to annotate parent instead.
+ return;
+ }
ValidationReport<TypeElement> report = moduleValidator.validate(module);
report.printMessagesTo(messager);
if (report.isClean()) {
- for (ExecutableElement method : methodsIn(module.getEnclosedElements())) {
- if (isAnnotationPresent(method, Provides.class)) {
- generate(factoryGenerator, bindingFactory.providesMethodBinding(method, module));
- } else if (isAnnotationPresent(method, Produces.class)) {
- generate(producerFactoryGenerator, bindingFactory.producesMethodBinding(method, module));
- } else if (isAnnotationPresent(method, Binds.class)) {
- inaccessibleMapKeyProxyGenerator.generate(bindsMethodBinding(module, method), messager);
- }
+ generateForMethodsIn(module);
+ if (metadataUtil.hasEnclosedCompanionObject(module)) {
+ generateForMethodsIn(metadataUtil.getEnclosedCompanionObject(module));
}
- moduleConstructorProxyGenerator.generate(module, messager);
}
processedModuleElements.add(module);
}
+ private void generateForMethodsIn(TypeElement module) {
+ for (ExecutableElement method : methodsIn(module.getEnclosedElements())) {
+ if (isAnnotationPresent(method, Provides.class)) {
+ generate(factoryGenerator, bindingFactory.providesMethodBinding(method, module));
+ } else if (isAnnotationPresent(method, Produces.class)) {
+ generate(producerFactoryGenerator, bindingFactory.producesMethodBinding(method, module));
+ } else if (isAnnotationPresent(method, Binds.class)) {
+ inaccessibleMapKeyProxyGenerator.generate(bindsMethodBinding(module, method), messager);
+ }
+ }
+ moduleConstructorProxyGenerator.generate(module, messager);
+ }
+
private <B extends ContributionBinding> void generate(
SourceFileGenerator<B> generator, B binding) {
generator.generate(binding, messager);
diff --git a/java/dagger/internal/codegen/ModuleProxies.java b/java/dagger/internal/codegen/ModuleProxies.java
deleted file mode 100644
index 6426e693e..000000000
--- a/java/dagger/internal/codegen/ModuleProxies.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.langmodel.Accessibility.isElementAccessibleFrom;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.STATIC;
-import static javax.lang.model.util.ElementFilter.constructorsIn;
-
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.codegen.langmodel.Accessibility;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import java.util.Optional;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-
-/** Convenience methods for generating and using module constructor proxy methods. */
-final class ModuleProxies {
- /** The name of the class that hosts the module constructor proxy method. */
- static ClassName constructorProxyTypeName(TypeElement moduleElement) {
- ModuleKind.checkIsModule(moduleElement);
- ClassName moduleClassName = ClassName.get(moduleElement);
- return moduleClassName
- .topLevelClassName()
- .peerClass(SourceFiles.classFileName(moduleClassName) + "_Proxy");
- }
-
- /**
- * The module constructor being proxied. A proxy is generated if it is not publicly accessible and
- * has no arguments. If an implicit reference to the enclosing class exists, or the module is
- * abstract, no proxy method can be generated.
- */
- // TODO(ronshapiro): make this an @Injectable class that injects DaggerElements
- static Optional<ExecutableElement> nonPublicNullaryConstructor(
- TypeElement moduleElement, DaggerElements elements) {
- ModuleKind.checkIsModule(moduleElement);
- if (moduleElement.getModifiers().contains(ABSTRACT)
- || (moduleElement.getNestingKind().isNested()
- && !moduleElement.getModifiers().contains(STATIC))) {
- return Optional.empty();
- }
- return constructorsIn(elements.getAllMembers(moduleElement)).stream()
- .filter(constructor -> !Accessibility.isElementPubliclyAccessible(constructor))
- .filter(constructor -> !constructor.getModifiers().contains(PRIVATE))
- .filter(constructor -> constructor.getParameters().isEmpty())
- .findAny();
- }
-
- /**
- * Returns a code block that creates a new module instance, either by invoking the nullary
- * constructor if it's accessible from {@code requestingClass} or else by invoking the
- * constructor's generated proxy method.
- */
- static CodeBlock newModuleInstance(
- TypeElement moduleElement, ClassName requestingClass, DaggerElements elements) {
- ModuleKind.checkIsModule(moduleElement);
- String packageName = requestingClass.packageName();
- return nonPublicNullaryConstructor(moduleElement, elements)
- .filter(constructor -> !isElementAccessibleFrom(constructor, packageName))
- .map(
- constructor ->
- CodeBlock.of("$T.newInstance()", constructorProxyTypeName(moduleElement)))
- .orElse(CodeBlock.of("new $T()", moduleElement));
- }
-}
diff --git a/java/dagger/internal/codegen/ModuleValidator.java b/java/dagger/internal/codegen/ModuleValidator.java
deleted file mode 100644
index 094bf34d5..000000000
--- a/java/dagger/internal/codegen/ModuleValidator.java
+++ /dev/null
@@ -1,644 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
-import static com.google.auto.common.Visibility.PRIVATE;
-import static com.google.auto.common.Visibility.PUBLIC;
-import static com.google.auto.common.Visibility.effectiveVisibilityOfElement;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.ComponentAnnotation.componentAnnotation;
-import static dagger.internal.codegen.ComponentAnnotation.isComponentAnnotation;
-import static dagger.internal.codegen.ComponentAnnotation.subcomponentAnnotation;
-import static dagger.internal.codegen.ComponentCreatorAnnotation.getCreatorAnnotations;
-import static dagger.internal.codegen.ConfigurationAnnotations.getSubcomponentCreator;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.ModuleAnnotation.isModuleAnnotation;
-import static dagger.internal.codegen.ModuleAnnotation.moduleAnnotation;
-import static dagger.internal.codegen.MoreAnnotationMirrors.simpleName;
-import static dagger.internal.codegen.MoreAnnotationValues.asType;
-import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
-import static dagger.internal.codegen.ValidationType.NONE;
-import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
-import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
-import static java.util.EnumSet.noneOf;
-import static java.util.stream.Collectors.joining;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.STATIC;
-import static javax.lang.model.util.ElementFilter.methodsIn;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.auto.common.Visibility;
-import com.google.common.base.Joiner;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.Sets;
-import com.google.errorprone.annotations.FormatMethod;
-import dagger.Module;
-import dagger.Subcomponent;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.BindingGraph;
-import dagger.producers.ProducerModule;
-import dagger.producers.ProductionSubcomponent;
-import java.lang.annotation.Annotation;
-import java.util.Collection;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Optional;
-import java.util.Set;
-import javax.inject.Inject;
-import javax.inject.Scope;
-import javax.inject.Singleton;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.SimpleAnnotationValueVisitor8;
-import javax.lang.model.util.SimpleTypeVisitor8;
-
-/**
- * A {@linkplain ValidationReport validator} for {@link Module}s or {@link ProducerModule}s.
- */
-@Singleton
-final class ModuleValidator {
- private static final ImmutableSet<Class<? extends Annotation>> SUBCOMPONENT_TYPES =
- ImmutableSet.of(Subcomponent.class, ProductionSubcomponent.class);
- private static final ImmutableSet<Class<? extends Annotation>> SUBCOMPONENT_CREATOR_TYPES =
- ImmutableSet.of(
- Subcomponent.Builder.class,
- Subcomponent.Factory.class,
- ProductionSubcomponent.Builder.class,
- ProductionSubcomponent.Factory.class);
- private static final Optional<Class<?>> ANDROID_PROCESSOR;
- private static final String CONTRIBUTES_ANDROID_INJECTOR_NAME =
- "dagger.android.ContributesAndroidInjector";
- private static final String ANDROID_PROCESSOR_NAME = "dagger.android.processor.AndroidProcessor";
-
- static {
- Class<?> clazz;
- try {
- clazz = Class.forName(ANDROID_PROCESSOR_NAME, false, ModuleValidator.class.getClassLoader());
- } catch (ClassNotFoundException ignored) {
- clazz = null;
- }
- ANDROID_PROCESSOR = Optional.ofNullable(clazz);
- }
-
- private final DaggerTypes types;
- private final DaggerElements elements;
- private final AnyBindingMethodValidator anyBindingMethodValidator;
- private final MethodSignatureFormatter methodSignatureFormatter;
- private final ComponentDescriptorFactory componentDescriptorFactory;
- private final BindingGraphFactory bindingGraphFactory;
- private final BindingGraphConverter bindingGraphConverter;
- private final BindingGraphValidator bindingGraphValidator;
- private final CompilerOptions compilerOptions;
- private final Map<TypeElement, ValidationReport<TypeElement>> cache = new HashMap<>();
- private final Set<TypeElement> knownModules = new HashSet<>();
-
- @Inject
- ModuleValidator(
- DaggerTypes types,
- DaggerElements elements,
- AnyBindingMethodValidator anyBindingMethodValidator,
- MethodSignatureFormatter methodSignatureFormatter,
- ComponentDescriptorFactory componentDescriptorFactory,
- BindingGraphFactory bindingGraphFactory,
- BindingGraphConverter bindingGraphConverter,
- BindingGraphValidator bindingGraphValidator,
- CompilerOptions compilerOptions) {
- this.types = types;
- this.elements = elements;
- this.anyBindingMethodValidator = anyBindingMethodValidator;
- this.methodSignatureFormatter = methodSignatureFormatter;
- this.componentDescriptorFactory = componentDescriptorFactory;
- this.bindingGraphFactory = bindingGraphFactory;
- this.bindingGraphConverter = bindingGraphConverter;
- this.bindingGraphValidator = bindingGraphValidator;
- this.compilerOptions = compilerOptions;
- }
-
- /**
- * Adds {@code modules} to the set of module types that will be validated during this compilation
- * step. If a component or module includes a module that is not in this set, that included module
- * is assumed to be valid because it was processed in a previous compilation step. If it were
- * invalid, that previous compilation step would have failed and blocked this one.
- *
- * <p>This logic depends on this method being called before {@linkplain #validate(TypeElement)
- * validating} any module or {@linkplain #validateReferencedModules(TypeElement, AnnotationMirror,
- * ImmutableSet, Set) component}.
- */
- void addKnownModules(Collection<TypeElement> modules) {
- knownModules.addAll(modules);
- }
-
- /** Returns a validation report for a module type. */
- ValidationReport<TypeElement> validate(TypeElement module) {
- return validate(module, new HashSet<>());
- }
-
- private ValidationReport<TypeElement> validate(
- TypeElement module, Set<TypeElement> visitedModules) {
- if (visitedModules.add(module)) {
- return reentrantComputeIfAbsent(cache, module, m -> validateUncached(module, visitedModules));
- }
- return ValidationReport.about(module).build();
- }
-
- private ValidationReport<TypeElement> validateUncached(
- TypeElement module, Set<TypeElement> visitedModules) {
- ValidationReport.Builder<TypeElement> builder = ValidationReport.about(module);
- ModuleKind moduleKind = ModuleKind.forAnnotatedElement(module).get();
-
- ListMultimap<String, ExecutableElement> allMethodsByName = ArrayListMultimap.create();
- ListMultimap<String, ExecutableElement> bindingMethodsByName = ArrayListMultimap.create();
-
- Set<ModuleMethodKind> methodKinds = noneOf(ModuleMethodKind.class);
- TypeElement contributesAndroidInjectorElement =
- elements.getTypeElement(CONTRIBUTES_ANDROID_INJECTOR_NAME);
- TypeMirror contributesAndroidInjector =
- contributesAndroidInjectorElement != null
- ? contributesAndroidInjectorElement.asType()
- : null;
- for (ExecutableElement moduleMethod : methodsIn(module.getEnclosedElements())) {
- if (anyBindingMethodValidator.isBindingMethod(moduleMethod)) {
- builder.addSubreport(anyBindingMethodValidator.validate(moduleMethod));
- bindingMethodsByName.put(moduleMethod.getSimpleName().toString(), moduleMethod);
- methodKinds.add(ModuleMethodKind.ofMethod(moduleMethod));
- }
- allMethodsByName.put(moduleMethod.getSimpleName().toString(), moduleMethod);
-
- for (AnnotationMirror annotation : moduleMethod.getAnnotationMirrors()) {
- if (!ANDROID_PROCESSOR.isPresent()
- && MoreTypes.equivalence()
- .equivalent(contributesAndroidInjector, annotation.getAnnotationType())) {
- builder.addSubreport(
- ValidationReport.about(moduleMethod)
- .addError(
- String.format(
- "@%s was used, but %s was not found on the processor path",
- CONTRIBUTES_ANDROID_INJECTOR_NAME, ANDROID_PROCESSOR_NAME))
- .build());
- break;
- }
- }
- }
-
- if (methodKinds.containsAll(
- EnumSet.of(ModuleMethodKind.ABSTRACT_DECLARATION, ModuleMethodKind.INSTANCE_BINDING))) {
- builder.addError(
- String.format(
- "A @%s may not contain both non-static and abstract binding methods",
- moduleKind.annotation().getSimpleName()));
- }
-
- validateModuleVisibility(module, moduleKind, builder);
- validateMethodsWithSameName(builder, bindingMethodsByName);
- if (module.getKind() != ElementKind.INTERFACE) {
- validateBindingMethodOverrides(module, builder, allMethodsByName, bindingMethodsByName);
- }
- validateModifiers(module, builder);
- validateReferencedModules(module, moduleKind, visitedModules, builder);
- validateReferencedSubcomponents(module, moduleKind, builder);
- validateNoScopeAnnotationsOnModuleElement(module, moduleKind, builder);
- validateSelfCycles(module, builder);
-
- if (builder.build().isClean()
- && !compilerOptions.fullBindingGraphValidationType(module).equals(NONE)) {
- validateModuleBindings(module, builder);
- }
-
- return builder.build();
- }
-
- private void validateReferencedSubcomponents(
- final TypeElement subject,
- ModuleKind moduleKind,
- final ValidationReport.Builder<TypeElement> builder) {
- // TODO(ronshapiro): use validateTypesAreDeclared when it is checked in
- ModuleAnnotation moduleAnnotation = moduleAnnotation(moduleKind.getModuleAnnotation(subject));
- for (AnnotationValue subcomponentAttribute :
- moduleAnnotation.subcomponentsAsAnnotationValues()) {
- asType(subcomponentAttribute)
- .accept(
- new SimpleTypeVisitor8<Void, Void>() {
- @Override
- protected Void defaultAction(TypeMirror e, Void aVoid) {
- builder.addError(
- e + " is not a valid subcomponent type",
- subject,
- moduleAnnotation.annotation(),
- subcomponentAttribute);
- return null;
- }
-
- @Override
- public Void visitDeclared(DeclaredType declaredType, Void aVoid) {
- TypeElement attributeType = MoreTypes.asTypeElement(declaredType);
- if (isAnyAnnotationPresent(attributeType, SUBCOMPONENT_TYPES)) {
- validateSubcomponentHasBuilder(
- attributeType, moduleAnnotation.annotation(), builder);
- } else {
- builder.addError(
- isAnyAnnotationPresent(attributeType, SUBCOMPONENT_CREATOR_TYPES)
- ? moduleSubcomponentsIncludesCreator(attributeType)
- : moduleSubcomponentsIncludesNonSubcomponent(attributeType),
- subject,
- moduleAnnotation.annotation(),
- subcomponentAttribute);
- }
-
- return null;
- }
- },
- null);
- }
- }
-
- private static String moduleSubcomponentsIncludesNonSubcomponent(TypeElement notSubcomponent) {
- return notSubcomponent.getQualifiedName()
- + " is not a @Subcomponent or @ProductionSubcomponent";
- }
-
- private static String moduleSubcomponentsIncludesCreator(
- TypeElement moduleSubcomponentsAttribute) {
- TypeElement subcomponentType =
- MoreElements.asType(moduleSubcomponentsAttribute.getEnclosingElement());
- ComponentCreatorAnnotation creatorAnnotation =
- getOnlyElement(getCreatorAnnotations(moduleSubcomponentsAttribute));
- return String.format(
- "%s is a @%s.%s. Did you mean to use %s?",
- moduleSubcomponentsAttribute.getQualifiedName(),
- subcomponentAnnotation(subcomponentType).get().simpleName(),
- creatorAnnotation.creatorKind().typeName(),
- subcomponentType.getQualifiedName());
- }
-
- private static void validateSubcomponentHasBuilder(
- TypeElement subcomponentAttribute,
- AnnotationMirror moduleAnnotation,
- ValidationReport.Builder<TypeElement> builder) {
- if (getSubcomponentCreator(subcomponentAttribute).isPresent()) {
- return;
- }
- builder.addError(
- moduleSubcomponentsDoesntHaveCreator(subcomponentAttribute, moduleAnnotation),
- builder.getSubject(),
- moduleAnnotation);
- }
-
- private static String moduleSubcomponentsDoesntHaveCreator(
- TypeElement subcomponent, AnnotationMirror moduleAnnotation) {
- return String.format(
- "%1$s doesn't have a @%2$s.Builder or @%2$s.Factory, which is required when used with "
- + "@%3$s.subcomponents",
- subcomponent.getQualifiedName(),
- subcomponentAnnotation(subcomponent).get().simpleName(),
- simpleName(moduleAnnotation));
- }
-
- enum ModuleMethodKind {
- ABSTRACT_DECLARATION,
- INSTANCE_BINDING,
- STATIC_BINDING,
- ;
-
- static ModuleMethodKind ofMethod(ExecutableElement moduleMethod) {
- if (moduleMethod.getModifiers().contains(STATIC)) {
- return STATIC_BINDING;
- } else if (moduleMethod.getModifiers().contains(ABSTRACT)) {
- return ABSTRACT_DECLARATION;
- } else {
- return INSTANCE_BINDING;
- }
- }
- }
-
- private void validateModifiers(
- TypeElement subject, ValidationReport.Builder<TypeElement> builder) {
- // This coupled with the check for abstract modules in ComponentValidator guarantees that
- // only modules without type parameters are referenced from @Component(modules={...}).
- if (!subject.getTypeParameters().isEmpty() && !subject.getModifiers().contains(ABSTRACT)) {
- builder.addError("Modules with type parameters must be abstract", subject);
- }
- }
-
- private void validateMethodsWithSameName(
- ValidationReport.Builder<TypeElement> builder,
- ListMultimap<String, ExecutableElement> bindingMethodsByName) {
- for (Entry<String, Collection<ExecutableElement>> entry :
- bindingMethodsByName.asMap().entrySet()) {
- if (entry.getValue().size() > 1) {
- for (ExecutableElement offendingMethod : entry.getValue()) {
- builder.addError(
- String.format(
- "Cannot have more than one binding method with the same name in a single module"),
- offendingMethod);
- }
- }
- }
- }
-
- private void validateReferencedModules(
- TypeElement subject,
- ModuleKind moduleKind,
- Set<TypeElement> visitedModules,
- ValidationReport.Builder<TypeElement> builder) {
- // Validate that all the modules we include are valid for inclusion.
- AnnotationMirror mirror = moduleKind.getModuleAnnotation(subject);
- builder.addSubreport(
- validateReferencedModules(
- subject, mirror, moduleKind.legalIncludedModuleKinds(), visitedModules));
- }
-
- /**
- * Validates modules included in a given module or installed in a given component.
- *
- * <p>Checks that the referenced modules are non-generic types annotated with {@code @Module} or
- * {@code @ProducerModule}.
- *
- * <p>If the referenced module is in the {@linkplain #addKnownModules(Collection) known modules
- * set} and has errors, reports an error at that module's inclusion.
- *
- * @param annotatedType the annotated module or component
- * @param annotation the annotation specifying the referenced modules ({@code @Component},
- * {@code @ProductionComponent}, {@code @Subcomponent}, {@code @ProductionSubcomponent},
- * {@code @Module}, or {@code @ProducerModule})
- * @param validModuleKinds the module kinds that the annotated type is permitted to include
- */
- ValidationReport<TypeElement> validateReferencedModules(
- TypeElement annotatedType,
- AnnotationMirror annotation,
- ImmutableSet<ModuleKind> validModuleKinds,
- Set<TypeElement> visitedModules) {
- ValidationReport.Builder<TypeElement> subreport = ValidationReport.about(annotatedType);
- ImmutableSet<? extends Class<? extends Annotation>> validModuleAnnotations =
- validModuleKinds.stream().map(ModuleKind::annotation).collect(toImmutableSet());
-
- for (AnnotationValue includedModule : getModules(annotation)) {
- asType(includedModule)
- .accept(
- new SimpleTypeVisitor8<Void, Void>() {
- @Override
- protected Void defaultAction(TypeMirror mirror, Void p) {
- reportError("%s is not a valid module type.", mirror);
- return null;
- }
-
- @Override
- public Void visitDeclared(DeclaredType t, Void p) {
- TypeElement module = MoreElements.asType(t.asElement());
- if (!t.getTypeArguments().isEmpty()) {
- reportError(
- "%s is listed as a module, but has type parameters",
- module.getQualifiedName());
- }
- if (!isAnyAnnotationPresent(module, validModuleAnnotations)) {
- reportError(
- "%s is listed as a module, but is not annotated with %s",
- module.getQualifiedName(),
- (validModuleAnnotations.size() > 1 ? "one of " : "")
- + validModuleAnnotations
- .stream()
- .map(otherClass -> "@" + otherClass.getSimpleName())
- .collect(joining(", ")));
- } else if (knownModules.contains(module)
- && !validate(module, visitedModules).isClean()) {
- reportError("%s has errors", module.getQualifiedName());
- }
- return null;
- }
-
- @FormatMethod
- private void reportError(String format, Object... args) {
- subreport.addError(
- String.format(format, args), annotatedType, annotation, includedModule);
- }
- },
- null);
- }
- return subreport.build();
- }
-
- private static ImmutableList<AnnotationValue> getModules(AnnotationMirror annotation) {
- if (isModuleAnnotation(annotation)) {
- return moduleAnnotation(annotation).includesAsAnnotationValues();
- }
- if (isComponentAnnotation(annotation)) {
- return componentAnnotation(annotation).moduleValues();
- }
- throw new IllegalArgumentException(String.format("unsupported annotation: %s", annotation));
- }
-
- private void validateBindingMethodOverrides(
- TypeElement subject,
- ValidationReport.Builder<TypeElement> builder,
- ListMultimap<String, ExecutableElement> allMethodsByName,
- ListMultimap<String, ExecutableElement> bindingMethodsByName) {
- // For every binding method, confirm it overrides nothing *and* nothing overrides it.
- // Consider the following hierarchy:
- // class Parent {
- // @Provides Foo a() {}
- // @Provides Foo b() {}
- // Foo c() {}
- // }
- // class Child extends Parent {
- // @Provides Foo a() {}
- // Foo b() {}
- // @Provides Foo c() {}
- // }
- // In each of those cases, we want to fail. "a" is clear, "b" because Child is overriding
- // a binding method in Parent, and "c" because Child is defining a binding method that overrides
- // Parent.
- TypeElement currentClass = subject;
- TypeMirror objectType = elements.getTypeElement(Object.class).asType();
- // We keep track of methods that failed so we don't spam with multiple failures.
- Set<ExecutableElement> failedMethods = Sets.newHashSet();
- while (!types.isSameType(currentClass.getSuperclass(), objectType)) {
- currentClass = MoreElements.asType(types.asElement(currentClass.getSuperclass()));
- List<ExecutableElement> superclassMethods = methodsIn(currentClass.getEnclosedElements());
- for (ExecutableElement superclassMethod : superclassMethods) {
- String name = superclassMethod.getSimpleName().toString();
- // For each method in the superclass, confirm our binding methods don't override it
- for (ExecutableElement bindingMethod : bindingMethodsByName.get(name)) {
- if (failedMethods.add(bindingMethod)
- && elements.overrides(bindingMethod, superclassMethod, subject)) {
- builder.addError(
- String.format(
- "Binding methods may not override another method. Overrides: %s",
- methodSignatureFormatter.format(superclassMethod)),
- bindingMethod);
- }
- }
- // For each binding method in superclass, confirm our methods don't override it.
- if (anyBindingMethodValidator.isBindingMethod(superclassMethod)) {
- for (ExecutableElement method : allMethodsByName.get(name)) {
- if (failedMethods.add(method)
- && elements.overrides(method, superclassMethod, subject)) {
- builder.addError(
- String.format(
- "Binding methods may not be overridden in modules. Overrides: %s",
- methodSignatureFormatter.format(superclassMethod)),
- method);
- }
- }
- }
- allMethodsByName.put(superclassMethod.getSimpleName().toString(), superclassMethod);
- }
- }
- }
-
- private void validateModuleVisibility(
- final TypeElement moduleElement,
- ModuleKind moduleKind,
- final ValidationReport.Builder<?> reportBuilder) {
- ModuleAnnotation moduleAnnotation =
- moduleAnnotation(getAnnotationMirror(moduleElement, moduleKind.annotation()).get());
- Visibility moduleVisibility = Visibility.ofElement(moduleElement);
- Visibility moduleEffectiveVisibility = effectiveVisibilityOfElement(moduleElement);
- if (moduleVisibility.equals(PRIVATE)) {
- reportBuilder.addError("Modules cannot be private.", moduleElement);
- } else if (moduleEffectiveVisibility.equals(PRIVATE)) {
- reportBuilder.addError("Modules cannot be enclosed in private types.", moduleElement);
- }
-
- switch (moduleElement.getNestingKind()) {
- case ANONYMOUS:
- throw new IllegalStateException("Can't apply @Module to an anonymous class");
- case LOCAL:
- throw new IllegalStateException("Local classes shouldn't show up in the processor");
- case MEMBER:
- case TOP_LEVEL:
- if (moduleEffectiveVisibility.equals(PUBLIC)) {
- ImmutableSet<TypeElement> invalidVisibilityIncludes =
- getModuleIncludesWithInvalidVisibility(moduleAnnotation);
- if (!invalidVisibilityIncludes.isEmpty()) {
- reportBuilder.addError(
- String.format(
- "This module is public, but it includes non-public (or effectively non-public) "
- + "modules (%s) that have non-static, non-abstract binding methods. Either "
- + "reduce the visibility of this module, make the included modules "
- + "public, or make all of the binding methods on the included modules "
- + "abstract or static.",
- formatListForErrorMessage(invalidVisibilityIncludes.asList())),
- moduleElement);
- }
- }
- }
- }
-
- private ImmutableSet<TypeElement> getModuleIncludesWithInvalidVisibility(
- ModuleAnnotation moduleAnnotation) {
- return moduleAnnotation.includes().stream()
- .filter(include -> !effectiveVisibilityOfElement(include).equals(PUBLIC))
- .filter(this::requiresModuleInstance)
- .collect(toImmutableSet());
- }
-
- /**
- * Returns {@code true} if a module instance is needed for any of the binding methods on the
- * given {@code module}. This is the case when the module has any binding methods that are neither
- * {@code abstract} nor {@code static}.
- */
- private boolean requiresModuleInstance(TypeElement module) {
- // Note elements.getAllMembers(module) rather than module.getEnclosedElements() here: we need to
- // include binding methods declared in supertypes because unlike most other validations being
- // done in this class, which assume that supertype binding methods will be validated in a
- // separate call to the validator since the supertype itself must be a @Module, we need to look
- // at all the binding methods in the module's type hierarchy here.
- return methodsIn(elements.getAllMembers(module)).stream()
- .filter(method -> anyBindingMethodValidator.isBindingMethod(method))
- .map(ExecutableElement::getModifiers)
- .anyMatch(modifiers -> !modifiers.contains(ABSTRACT) && !modifiers.contains(STATIC));
- }
-
- private void validateNoScopeAnnotationsOnModuleElement(
- TypeElement module, ModuleKind moduleKind, ValidationReport.Builder<TypeElement> report) {
- for (AnnotationMirror scope : getAnnotatedAnnotations(module, Scope.class)) {
- report.addError(
- String.format(
- "@%ss cannot be scoped. Did you mean to scope a method instead?",
- moduleKind.annotation().getSimpleName()),
- module,
- scope);
- }
- }
-
- private void validateSelfCycles(
- TypeElement module, ValidationReport.Builder<TypeElement> builder) {
- ModuleAnnotation moduleAnnotation = moduleAnnotation(module).get();
- moduleAnnotation
- .includesAsAnnotationValues()
- .forEach(
- value ->
- value.accept(
- new SimpleAnnotationValueVisitor8<Void, Void>() {
- @Override
- public Void visitType(TypeMirror includedModule, Void aVoid) {
- if (MoreTypes.equivalence().equivalent(module.asType(), includedModule)) {
- String moduleKind = moduleAnnotation.annotationClass().getSimpleName();
- builder.addError(
- String.format("@%s cannot include themselves.", moduleKind),
- module,
- moduleAnnotation.annotation(),
- value);
- }
- return null;
- }
- },
- null));
- }
-
- private void validateModuleBindings(
- TypeElement module, ValidationReport.Builder<TypeElement> report) {
- BindingGraph bindingGraph =
- bindingGraphConverter.convert(
- bindingGraphFactory.create(
- componentDescriptorFactory.moduleComponentDescriptor(module), true));
- if (!bindingGraphValidator.isValid(bindingGraph)) {
- // Since the validator uses a DiagnosticReporter to report errors, the ValdiationReport won't
- // have any Items for them. We have to tell the ValidationReport that some errors were
- // reported for the subject.
- report.markDirty();
- }
- }
-
- private static String formatListForErrorMessage(List<?> things) {
- switch (things.size()) {
- case 0:
- return "";
- case 1:
- return things.get(0).toString();
- default:
- StringBuilder output = new StringBuilder();
- Joiner.on(", ").appendTo(output, things.subList(0, things.size() - 1));
- output.append(" and ").append(things.get(things.size() - 1));
- return output.toString();
- }
- }
-}
diff --git a/java/dagger/internal/codegen/MonitoringModuleGenerator.java b/java/dagger/internal/codegen/MonitoringModuleGenerator.java
deleted file mode 100644
index 1738fe9a7..000000000
--- a/java/dagger/internal/codegen/MonitoringModuleGenerator.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.squareup.javapoet.MethodSpec.constructorBuilder;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static com.squareup.javapoet.TypeSpec.classBuilder;
-import static dagger.internal.codegen.javapoet.TypeNames.PRODUCTION_COMPONENT_MONITOR_FACTORY;
-import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
-import static dagger.internal.codegen.javapoet.TypeNames.setOf;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.STATIC;
-
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.TypeSpec;
-import dagger.Module;
-import dagger.Provides;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.multibindings.Multibinds;
-import dagger.producers.ProductionScope;
-import dagger.producers.monitoring.ProductionComponentMonitor;
-import dagger.producers.monitoring.internal.Monitors;
-import java.util.Optional;
-import javax.annotation.processing.Filer;
-import javax.inject.Inject;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-
-/** Generates a monitoring module for use with production components. */
-final class MonitoringModuleGenerator extends SourceFileGenerator<TypeElement> {
-
- @Inject
- MonitoringModuleGenerator(Filer filer, DaggerElements elements, SourceVersion sourceVersion) {
- super(filer, elements, sourceVersion);
- }
-
- @Override
- ClassName nameGeneratedType(TypeElement componentElement) {
- return SourceFiles.generatedMonitoringModuleName(componentElement);
- }
-
- @Override
- Element originatingElement(TypeElement componentElement) {
- return componentElement;
- }
-
- @Override
- Optional<TypeSpec.Builder> write(ClassName generatedTypeName, TypeElement componentElement) {
- return Optional.of(
- classBuilder(generatedTypeName)
- .addAnnotation(Module.class)
- .addModifiers(ABSTRACT)
- .addMethod(privateConstructor())
- .addMethod(setOfFactories())
- .addMethod(monitor(componentElement)));
- }
-
- private MethodSpec privateConstructor() {
- return constructorBuilder().addModifiers(PRIVATE).build();
- }
-
- private MethodSpec setOfFactories() {
- return methodBuilder("setOfFactories")
- .addAnnotation(Multibinds.class)
- .addModifiers(ABSTRACT)
- .returns(setOf(PRODUCTION_COMPONENT_MONITOR_FACTORY))
- .build();
- }
-
- private MethodSpec monitor(TypeElement componentElement) {
- return methodBuilder("monitor")
- .returns(ProductionComponentMonitor.class)
- .addModifiers(STATIC)
- .addAnnotation(Provides.class)
- .addAnnotation(ProductionScope.class)
- .addParameter(providerOf(ClassName.get(componentElement.asType())), "component")
- .addParameter(
- providerOf(setOf(PRODUCTION_COMPONENT_MONITOR_FACTORY)), "factories")
- .addStatement(
- "return $T.createMonitorForComponent(component, factories)", Monitors.class)
- .build();
- }
-}
diff --git a/java/dagger/internal/codegen/MonitoringModuleProcessingStep.java b/java/dagger/internal/codegen/MonitoringModuleProcessingStep.java
deleted file mode 100644
index 55ae57941..000000000
--- a/java/dagger/internal/codegen/MonitoringModuleProcessingStep.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import com.google.auto.common.MoreElements;
-import com.google.common.collect.ImmutableSet;
-import dagger.producers.ProductionComponent;
-import dagger.producers.ProductionSubcomponent;
-import java.lang.annotation.Annotation;
-import java.util.Set;
-import javax.annotation.processing.Messager;
-import javax.inject.Inject;
-import javax.lang.model.element.TypeElement;
-
-/**
- * A processing step that is responsible for generating a special module for a {@link
- * ProductionComponent} or {@link ProductionSubcomponent}.
- */
-final class MonitoringModuleProcessingStep extends TypeCheckingProcessingStep<TypeElement> {
- private final Messager messager;
- private final MonitoringModuleGenerator monitoringModuleGenerator;
-
- @Inject
- MonitoringModuleProcessingStep(
- Messager messager, MonitoringModuleGenerator monitoringModuleGenerator) {
- super(MoreElements::asType);
- this.messager = messager;
- this.monitoringModuleGenerator = monitoringModuleGenerator;
- }
-
- @Override
- public Set<? extends Class<? extends Annotation>> annotations() {
- return ImmutableSet.of(ProductionComponent.class, ProductionSubcomponent.class);
- }
-
- @Override
- protected void process(
- TypeElement element, ImmutableSet<Class<? extends Annotation>> annotations) {
- monitoringModuleGenerator.generate(MoreElements.asType(element), messager);
- }
-}
diff --git a/java/dagger/internal/codegen/MoreAnnotationMirrors.java b/java/dagger/internal/codegen/MoreAnnotationMirrors.java
deleted file mode 100644
index 92825a06e..000000000
--- a/java/dagger/internal/codegen/MoreAnnotationMirrors.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
-import static dagger.internal.codegen.DaggerStreams.toImmutableList;
-import static dagger.internal.codegen.MoreAnnotationValues.asAnnotationValues;
-
-import com.google.auto.common.AnnotationMirrors;
-import com.google.common.base.Equivalence;
-import com.google.common.collect.ImmutableList;
-import java.util.Optional;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Name;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * A utility class for working with {@link AnnotationMirror} instances, similar to {@link
- * AnnotationMirrors}.
- */
-final class MoreAnnotationMirrors {
-
- private MoreAnnotationMirrors() {}
-
- /**
- * Wraps an {@link Optional} of a type in an {@code Optional} of a {@link Equivalence.Wrapper} for
- * that type.
- */
- static Optional<Equivalence.Wrapper<AnnotationMirror>> wrapOptionalInEquivalence(
- Optional<AnnotationMirror> optional) {
- return optional.map(AnnotationMirrors.equivalence()::wrap);
- }
-
- /**
- * Unwraps an {@link Optional} of a {@link Equivalence.Wrapper} into an {@code Optional} of the
- * underlying type.
- */
- static Optional<AnnotationMirror> unwrapOptionalEquivalence(
- Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedOptional) {
- return wrappedOptional.map(Equivalence.Wrapper::get);
- }
-
- static Name simpleName(AnnotationMirror annotationMirror) {
- return annotationMirror.getAnnotationType().asElement().getSimpleName();
- }
-
- /**
- * Returns the list of types that is the value named {@code name} from {@code annotationMirror}.
- *
- * @throws IllegalArgumentException unless that member represents an array of types
- */
- static ImmutableList<TypeMirror> getTypeListValue(
- AnnotationMirror annotationMirror, String name) {
- return asAnnotationValues(getAnnotationValue(annotationMirror, name))
- .stream()
- .map(MoreAnnotationValues::asType)
- .collect(toImmutableList());
- }
-}
diff --git a/java/dagger/internal/codegen/MoreAnnotationValues.java b/java/dagger/internal/codegen/MoreAnnotationValues.java
deleted file mode 100644
index 84a4d94b0..000000000
--- a/java/dagger/internal/codegen/MoreAnnotationValues.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2013 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import com.google.common.collect.ImmutableList;
-import java.util.List;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.AnnotationValueVisitor;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.SimpleAnnotationValueVisitor8;
-
-/** Utility methods for working with {@link AnnotationValue} instances. */
-final class MoreAnnotationValues {
- /**
- * Returns the list of values represented by an array annotation value.
- *
- * @throws IllegalArgumentException unless {@code annotationValue} represents an array
- */
- static ImmutableList<AnnotationValue> asAnnotationValues(AnnotationValue annotationValue) {
- return annotationValue.accept(AS_ANNOTATION_VALUES, null);
- }
-
- private static final AnnotationValueVisitor<ImmutableList<AnnotationValue>, String>
- AS_ANNOTATION_VALUES =
- new SimpleAnnotationValueVisitor8<ImmutableList<AnnotationValue>, String>() {
- @Override
- public ImmutableList<AnnotationValue> visitArray(
- List<? extends AnnotationValue> vals, String elementName) {
- return ImmutableList.copyOf(vals);
- }
-
- @Override
- protected ImmutableList<AnnotationValue> defaultAction(Object o, String elementName) {
- throw new IllegalArgumentException(elementName + " is not an array: " + o);
- }
- };
-
- /**
- * Returns the type represented by an annotation value.
- *
- * @throws IllegalArgumentException unless {@code annotationValue} represents a single type
- */
- static TypeMirror asType(AnnotationValue annotationValue) {
- return AS_TYPE.visit(annotationValue);
- }
-
- private static final AnnotationValueVisitor<TypeMirror, Void> AS_TYPE =
- new SimpleAnnotationValueVisitor8<TypeMirror, Void>() {
- @Override
- public TypeMirror visitType(TypeMirror t, Void p) {
- return t;
- }
-
- @Override
- protected TypeMirror defaultAction(Object o, Void p) {
- throw new TypeNotPresentException(o.toString(), null);
- }
- };
-
- private MoreAnnotationValues() {}
-}
diff --git a/java/dagger/internal/codegen/MultibindingAnnotations.java b/java/dagger/internal/codegen/MultibindingAnnotations.java
deleted file mode 100644
index b478704fa..000000000
--- a/java/dagger/internal/codegen/MultibindingAnnotations.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.langmodel.DaggerElements.getAllAnnotations;
-
-import com.google.common.collect.ImmutableSet;
-import dagger.multibindings.ElementsIntoSet;
-import dagger.multibindings.IntoMap;
-import dagger.multibindings.IntoSet;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-
-/**
- * Utility methods related to processing {@link IntoSet}, {@link ElementsIntoSet}, and {@link
- * IntoMap}.
- */
-final class MultibindingAnnotations {
- static ImmutableSet<AnnotationMirror> forElement(Element method) {
- return getAllAnnotations(method, IntoSet.class, ElementsIntoSet.class, IntoMap.class);
- }
-}
diff --git a/java/dagger/internal/codegen/MultibindingAnnotationsProcessingStep.java b/java/dagger/internal/codegen/MultibindingAnnotationsProcessingStep.java
deleted file mode 100644
index 2bb0a7e37..000000000
--- a/java/dagger/internal/codegen/MultibindingAnnotationsProcessingStep.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
-import static javax.tools.Diagnostic.Kind.ERROR;
-
-import com.google.auto.common.MoreElements;
-import com.google.common.collect.ImmutableSet;
-import dagger.multibindings.ElementsIntoSet;
-import dagger.multibindings.IntoMap;
-import dagger.multibindings.IntoSet;
-import java.lang.annotation.Annotation;
-import java.util.Set;
-import javax.annotation.processing.Messager;
-import javax.inject.Inject;
-import javax.lang.model.element.ExecutableElement;
-
-/**
- * Processing step that verifies that {@link IntoSet}, {@link ElementsIntoSet} and {@link IntoMap}
- * are not present on non-binding methods.
- */
-final class MultibindingAnnotationsProcessingStep
- extends TypeCheckingProcessingStep<ExecutableElement> {
- private final AnyBindingMethodValidator anyBindingMethodValidator;
- private final Messager messager;
-
- @Inject
- MultibindingAnnotationsProcessingStep(
- AnyBindingMethodValidator anyBindingMethodValidator, Messager messager) {
- super(MoreElements::asExecutable);
- this.anyBindingMethodValidator = anyBindingMethodValidator;
- this.messager = messager;
- }
-
- @Override
- public Set<? extends Class<? extends Annotation>> annotations() {
- return ImmutableSet.of(IntoSet.class, ElementsIntoSet.class, IntoMap.class);
- }
-
- @Override
- protected void process(
- ExecutableElement method, ImmutableSet<Class<? extends Annotation>> annotations) {
- if (!anyBindingMethodValidator.isBindingMethod(method)) {
- annotations.forEach(
- annotation ->
- messager.printMessage(
- ERROR,
- "Multibinding annotations may only be on @Provides, @Produces, or @Binds methods",
- method,
- getAnnotationMirror(method, annotation).get()));
- }
- }
-}
diff --git a/java/dagger/internal/codegen/MultibindingDeclaration.java b/java/dagger/internal/codegen/MultibindingDeclaration.java
deleted file mode 100644
index c3724dc77..000000000
--- a/java/dagger/internal/codegen/MultibindingDeclaration.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.common.base.Preconditions.checkArgument;
-
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.auto.value.extension.memoized.Memoized;
-import dagger.internal.codegen.ContributionType.HasContributionType;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.Key;
-import dagger.multibindings.Multibinds;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import javax.inject.Inject;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.ExecutableType;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * A declaration that a multibinding with a certain key is available to be injected in a component
- * even if the component has no multibindings for that key. Identified by a map- or set-returning
- * method annotated with {@link Multibinds @Multibinds}.
- */
-@AutoValue
-abstract class MultibindingDeclaration extends BindingDeclaration implements HasContributionType {
-
- /**
- * The map or set key whose availability is declared. For maps, this will be {@code Map<K,
- * Provider<V>>}. For sets, this will be {@code Set<T>}.
- */
- @Override
- public abstract Key key();
-
- /**
- * {@link ContributionType#SET} if the declared type is a {@link Set}, or
- * {@link ContributionType#MAP} if it is a {@link Map}.
- */
- @Override
- public abstract ContributionType contributionType();
-
- @Memoized
- @Override
- public abstract int hashCode();
-
- @Override
- public abstract boolean equals(Object obj);
-
- /**
- * A factory for {@link MultibindingDeclaration}s.
- */
- static final class Factory {
- private final DaggerTypes types;
- private final KeyFactory keyFactory;
-
- @Inject
- Factory(DaggerTypes types, KeyFactory keyFactory) {
- this.types = types;
- this.keyFactory = keyFactory;
- }
-
- /** A multibinding declaration for a {@link Multibinds @Multibinds} method. */
- MultibindingDeclaration forMultibindsMethod(
- ExecutableElement moduleMethod, TypeElement moduleElement) {
- checkArgument(isAnnotationPresent(moduleMethod, Multibinds.class));
- return forDeclaredMethod(
- moduleMethod,
- MoreTypes.asExecutable(
- types.asMemberOf(MoreTypes.asDeclared(moduleElement.asType()), moduleMethod)),
- moduleElement);
- }
-
- private MultibindingDeclaration forDeclaredMethod(
- ExecutableElement method,
- ExecutableType methodType,
- TypeElement contributingType) {
- TypeMirror returnType = methodType.getReturnType();
- checkArgument(
- SetType.isSet(returnType) || MapType.isMap(returnType),
- "%s must return a set or map",
- method);
- return new AutoValue_MultibindingDeclaration(
- Optional.<Element>of(method),
- Optional.of(contributingType),
- keyFactory.forMultibindsMethod(methodType, method),
- contributionType(returnType));
- }
-
- private ContributionType contributionType(TypeMirror returnType) {
- if (MapType.isMap(returnType)) {
- return ContributionType.MAP;
- } else if (SetType.isSet(returnType)) {
- return ContributionType.SET;
- } else {
- throw new IllegalArgumentException("Must be Map or Set: " + returnType);
- }
- }
- }
-}
diff --git a/java/dagger/internal/codegen/MultibindingExpression.java b/java/dagger/internal/codegen/MultibindingExpression.java
deleted file mode 100644
index f0523eb41..000000000
--- a/java/dagger/internal/codegen/MultibindingExpression.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
-import dagger.internal.codegen.javapoet.Expression;
-import dagger.model.DependencyRequest;
-import dagger.model.Key;
-import dagger.model.RequestKind;
-import java.util.Optional;
-import java.util.Set;
-
-/** An abstract base class for multibinding {@link BindingExpression}s. */
-abstract class MultibindingExpression extends SimpleInvocationBindingExpression {
- private final ProvisionBinding binding;
- private final ComponentImplementation componentImplementation;
-
- MultibindingExpression(
- ResolvedBindings resolvedBindings, ComponentImplementation componentImplementation) {
- super(resolvedBindings);
- this.componentImplementation = componentImplementation;
- this.binding = (ProvisionBinding) resolvedBindings.contributionBinding();
- }
-
- @Override
- Expression getDependencyExpression(ClassName requestingClass) {
- Expression expression = buildDependencyExpression(requestingClass);
- componentImplementation.registerImplementedMultibinding(binding, bindingRequest());
- return expression;
- }
-
- /**
- * Returns an expression that evaluates to the value of a multibinding request for the given
- * requesting class.
- */
- protected abstract Expression buildDependencyExpression(ClassName requestingClass);
-
- /**
- * Returns the subset of {@code dependencies} that represent multibinding contributions that were
- * not included in a superclass implementation of this multibinding method. This is relevant only
- * for ahead-of-time subcomponents. When not generating ahead-of-time subcomponents there is only
- * one implementation of a multibinding expression and all {@link DependencyRequest}s from the
- * argment are returned.
- */
- protected Set<DependencyRequest> getNewContributions(
- ImmutableSet<DependencyRequest> dependencies) {
- ImmutableSet<Key> superclassContributions = superclassContributions();
- return Sets.filter(
- dependencies, dependency -> !superclassContributions.contains(dependency.key()));
- }
-
- /**
- * Returns the {@link CodeBlock} representing a call to a superclass implementation of the
- * modifiable binding method that encapsulates this binding, if it exists. This is only possible
- * when generating ahead-of-time subcomponents.
- */
- protected Optional<CodeBlock> superMethodCall() {
- if (componentImplementation.superclassImplementation().isPresent()) {
- Optional<ModifiableBindingMethod> method =
- componentImplementation.getModifiableBindingMethod(bindingRequest());
- if (method.isPresent()) {
- if (!superclassContributions().isEmpty()) {
- return Optional.of(CodeBlock.of("super.$L()", method.get().methodSpec().name));
- }
- }
- }
- return Optional.empty();
- }
-
- private BindingRequest bindingRequest() {
- return BindingRequest.bindingRequest(binding.key(), RequestKind.INSTANCE);
- }
-
- private ImmutableSet<Key> superclassContributions() {
- return componentImplementation.superclassContributionsMade(bindingRequest());
- }
-}
diff --git a/java/dagger/internal/codegen/MultibindingFactoryCreationExpression.java b/java/dagger/internal/codegen/MultibindingFactoryCreationExpression.java
deleted file mode 100644
index abe161a4a..000000000
--- a/java/dagger/internal/codegen/MultibindingFactoryCreationExpression.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-
-import com.google.common.collect.ImmutableSet;
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
-import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
-import dagger.internal.codegen.javapoet.CodeBlocks;
-import dagger.model.DependencyRequest;
-import dagger.model.Key;
-import java.util.Optional;
-
-/** An abstract factory creation expression for multibindings. */
-abstract class MultibindingFactoryCreationExpression
- implements FrameworkInstanceCreationExpression {
- private final ComponentImplementation componentImplementation;
- private final ComponentBindingExpressions componentBindingExpressions;
- private final ContributionBinding binding;
-
- MultibindingFactoryCreationExpression(
- ContributionBinding binding,
- ComponentImplementation componentImplementation,
- ComponentBindingExpressions componentBindingExpressions) {
- this.binding = checkNotNull(binding);
- this.componentImplementation = checkNotNull(componentImplementation);
- this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
- }
-
- /** Returns the expression for a dependency of this multibinding. */
- protected final CodeBlock multibindingDependencyExpression(DependencyRequest dependency) {
- CodeBlock expression =
- componentBindingExpressions
- .getDependencyExpression(
- BindingRequest.bindingRequest(dependency.key(), binding.frameworkType()),
- componentImplementation.name())
- .codeBlock();
-
- return useRawType()
- ? CodeBlocks.cast(expression, binding.frameworkType().frameworkClass())
- : expression;
- }
-
- protected final ImmutableSet<DependencyRequest> dependenciesToImplement() {
- ImmutableSet<Key> alreadyImplementedKeys =
- componentImplementation.superclassContributionsMade(bindingRequest());
- return binding.dependencies().stream()
- .filter(dependency -> !alreadyImplementedKeys.contains(dependency.key()))
- .collect(toImmutableSet());
- }
-
- protected Optional<CodeBlock> superContributions() {
- if (dependenciesToImplement().size() == binding.dependencies().size()) {
- return Optional.empty();
- }
- ModifiableBindingMethod superMethod =
- componentImplementation.getModifiableBindingMethod(bindingRequest()).get();
- return Optional.of(CodeBlock.of("super.$N()", superMethod.methodSpec().name));
- }
-
- /** The binding request for this framework instance. */
- protected final BindingRequest bindingRequest() {
- return BindingRequest.bindingRequest(binding.key(), binding.frameworkType());
- }
-
- /**
- * Returns true if the {@linkplain ContributionBinding#key() key type} is inaccessible from the
- * component, and therefore a raw type must be used.
- */
- protected final boolean useRawType() {
- return !componentImplementation.isTypeAccessible(binding.key().type());
- }
-
- @Override
- public final boolean useInnerSwitchingProvider() {
- return !binding.dependencies().isEmpty();
- }
-}
diff --git a/java/dagger/internal/codegen/MultibindsMethodValidator.java b/java/dagger/internal/codegen/MultibindsMethodValidator.java
deleted file mode 100644
index bc97d3060..000000000
--- a/java/dagger/internal/codegen/MultibindsMethodValidator.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.BindingElementValidator.AllowsMultibindings.NO_MULTIBINDINGS;
-import static dagger.internal.codegen.BindingElementValidator.AllowsScoping.NO_SCOPING;
-import static dagger.internal.codegen.BindingMethodValidator.Abstractness.MUST_BE_ABSTRACT;
-import static dagger.internal.codegen.BindingMethodValidator.ExceptionSuperclass.NO_EXCEPTIONS;
-import static dagger.internal.codegen.FrameworkTypes.isFrameworkType;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableSet;
-import dagger.Module;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.multibindings.Multibinds;
-import dagger.producers.ProducerModule;
-import javax.inject.Inject;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.type.TypeMirror;
-
-/** A validator for {@link Multibinds} methods. */
-class MultibindsMethodValidator extends BindingMethodValidator {
-
- /** Creates a validator for {@link Multibinds @Multibinds} methods. */
- @Inject
- MultibindsMethodValidator(
- DaggerElements elements,
- DaggerTypes types,
- DependencyRequestValidator dependencyRequestValidator) {
- super(
- elements,
- types,
- Multibinds.class,
- ImmutableSet.of(Module.class, ProducerModule.class),
- dependencyRequestValidator,
- MUST_BE_ABSTRACT,
- NO_EXCEPTIONS,
- NO_MULTIBINDINGS,
- NO_SCOPING);
- }
-
- @Override
- protected ElementValidator elementValidator(ExecutableElement element) {
- return new Validator(element);
- }
-
- private class Validator extends MethodValidator {
- Validator(ExecutableElement element) {
- super(element);
- }
-
- @Override
- protected void checkParameters() {
- if (!element.getParameters().isEmpty()) {
- report.addError(bindingMethods("cannot have parameters"));
- }
- }
-
- /** Adds an error unless the method returns a {@code Map<K, V>} or {@code Set<T>}. */
- @Override
- protected void checkType() {
- if (!isPlainMap(element.getReturnType())
- && !isPlainSet(element.getReturnType())) {
- report.addError(bindingMethods("must return Map<K, V> or Set<T>"));
- }
- }
-
- private boolean isPlainMap(TypeMirror returnType) {
- if (!MapType.isMap(returnType)) {
- return false;
- }
- MapType mapType = MapType.from(returnType);
- return !mapType.isRawType()
- && MoreTypes.isType(mapType.valueType()) // No wildcards.
- && !isFrameworkType(mapType.valueType());
- }
-
- private boolean isPlainSet(TypeMirror returnType) {
- if (!SetType.isSet(returnType)) {
- return false;
- }
- SetType setType = SetType.from(returnType);
- return !setType.isRawType()
- && MoreTypes.isType(setType.elementType()) // No wildcards.
- && !isFrameworkType(setType.elementType());
- }
- }
-}
diff --git a/java/dagger/internal/codegen/NullableBindingValidator.java b/java/dagger/internal/codegen/NullableBindingValidator.java
deleted file mode 100644
index 1452c3ff8..000000000
--- a/java/dagger/internal/codegen/NullableBindingValidator.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.DaggerStreams.instancesOf;
-import static dagger.internal.codegen.DaggerStreams.toImmutableList;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import dagger.model.BindingGraph;
-import dagger.model.BindingGraph.DependencyEdge;
-import dagger.spi.BindingGraphPlugin;
-import dagger.spi.DiagnosticReporter;
-import javax.inject.Inject;
-
-/**
- * Reports errors or warnings (depending on the {@code -Adagger.nullableValidation} value) for each
- * non-nullable dependency request that is satisfied by a nullable binding.
- */
-final class NullableBindingValidator implements BindingGraphPlugin {
-
- private final CompilerOptions compilerOptions;
-
- @Inject
- NullableBindingValidator(CompilerOptions compilerOptions) {
- this.compilerOptions = compilerOptions;
- }
-
- @Override
- public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
- for (dagger.model.Binding binding : nullableBindings(bindingGraph)) {
- for (DependencyEdge dependencyEdge : nonNullableDependencies(bindingGraph, binding)) {
- diagnosticReporter.reportDependency(
- compilerOptions.nullableValidationKind(),
- dependencyEdge,
- nullableToNonNullable(
- binding.key().toString(),
- binding.toString())); // binding.toString() will include the @Nullable
- }
- }
- }
-
- @Override
- public String pluginName() {
- return "Dagger/Nullable";
- }
-
- private ImmutableList<dagger.model.Binding> nullableBindings(BindingGraph bindingGraph) {
- return bindingGraph.bindings().stream()
- .filter(binding -> binding.isNullable())
- .collect(toImmutableList());
- }
-
- private ImmutableSet<DependencyEdge> nonNullableDependencies(
- BindingGraph bindingGraph, dagger.model.Binding binding) {
- return bindingGraph.network().inEdges(binding).stream()
- .flatMap(instancesOf(DependencyEdge.class))
- .filter(edge -> !edge.dependencyRequest().isNullable())
- .collect(toImmutableSet());
- }
-
- @VisibleForTesting
- static String nullableToNonNullable(String key, String binding) {
- return String.format("%s is not nullable, but is being provided by %s", key, binding);
- }
-}
diff --git a/java/dagger/internal/codegen/OptionalBindingDeclaration.java b/java/dagger/internal/codegen/OptionalBindingDeclaration.java
deleted file mode 100644
index b26ab9175..000000000
--- a/java/dagger/internal/codegen/OptionalBindingDeclaration.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.common.base.Preconditions.checkArgument;
-
-import com.google.auto.value.AutoValue;
-import com.google.auto.value.extension.memoized.Memoized;
-import dagger.BindsOptionalOf;
-import dagger.model.Key;
-import java.util.Optional;
-import javax.inject.Inject;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-
-/** A {@link BindsOptionalOf} declaration. */
-@AutoValue
-abstract class OptionalBindingDeclaration extends BindingDeclaration {
-
- /**
- * {@inheritDoc}
- *
- * <p>The key's type is the method's return type, even though the synthetic bindings will be for
- * {@code Optional} of derived types.
- */
- @Override
- public abstract Key key();
-
- @Memoized
- @Override
- public abstract int hashCode();
-
- @Override
- public abstract boolean equals(Object obj);
-
- static class Factory {
- private final KeyFactory keyFactory;
-
- @Inject
- Factory(KeyFactory keyFactory) {
- this.keyFactory = keyFactory;
- }
-
- OptionalBindingDeclaration forMethod(ExecutableElement method, TypeElement contributingModule) {
- checkArgument(isAnnotationPresent(method, BindsOptionalOf.class));
- return new AutoValue_OptionalBindingDeclaration(
- Optional.<Element>of(method),
- Optional.of(contributingModule),
- keyFactory.forBindsOptionalOfMethod(method, contributingModule));
- }
- }
-}
diff --git a/java/dagger/internal/codegen/OptionalBindingExpression.java b/java/dagger/internal/codegen/OptionalBindingExpression.java
deleted file mode 100644
index dbb0e374c..000000000
--- a/java/dagger/internal/codegen/OptionalBindingExpression.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.BindingRequest.bindingRequest;
-import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
-
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.codegen.OptionalType.OptionalKind;
-import dagger.internal.codegen.javapoet.Expression;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.DependencyRequest;
-import javax.inject.Inject;
-import javax.lang.model.SourceVersion;
-
-/** A binding expression for optional bindings. */
-final class OptionalBindingExpression extends SimpleInvocationBindingExpression {
- private final ProvisionBinding binding;
- private final ComponentBindingExpressions componentBindingExpressions;
- private final DaggerTypes types;
- private final SourceVersion sourceVersion;
-
- @Inject
- OptionalBindingExpression(
- ResolvedBindings resolvedBindings,
- ComponentBindingExpressions componentBindingExpressions,
- DaggerTypes types,
- SourceVersion sourceVersion) {
- super(resolvedBindings);
- this.binding = (ProvisionBinding) resolvedBindings.contributionBinding();
- this.componentBindingExpressions = componentBindingExpressions;
- this.types = types;
- this.sourceVersion = sourceVersion;
- }
-
- @Override
- Expression getDependencyExpression(ClassName requestingClass) {
- OptionalType optionalType = OptionalType.from(binding.key());
- OptionalKind optionalKind = optionalType.kind();
- if (binding.dependencies().isEmpty()) {
- if (sourceVersion.compareTo(SourceVersion.RELEASE_7) <= 0) {
- // When compiling with -source 7, javac's type inference isn't strong enough to detect
- // Futures.immediateFuture(Optional.absent()) for keys that aren't Object. It also has
- // issues
- // when used as an argument to some members injection proxy methods (see
- // https://github.com/google/dagger/issues/916)
- if (isTypeAccessibleFrom(binding.key().type(), requestingClass.packageName())) {
- return Expression.create(
- binding.key().type(), optionalKind.parameterizedAbsentValueExpression(optionalType));
- }
- }
- return Expression.create(binding.key().type(), optionalKind.absentValueExpression());
- }
- DependencyRequest dependency = getOnlyElement(binding.dependencies());
-
- CodeBlock dependencyExpression =
- componentBindingExpressions
- .getDependencyExpression(bindingRequest(dependency), requestingClass)
- .codeBlock();
-
- // If the dependency type is inaccessible, then we have to use Optional.<Object>of(...), or else
- // we will get "incompatible types: inference variable has incompatible bounds.
- return isTypeAccessibleFrom(dependency.key().type(), requestingClass.packageName())
- ? Expression.create(
- binding.key().type(), optionalKind.presentExpression(dependencyExpression))
- : Expression.create(
- types.erasure(binding.key().type()),
- optionalKind.presentObjectExpression(dependencyExpression));
- }
-
- @Override
- boolean requiresMethodEncapsulation() {
- // TODO(dpb): Maybe require it for present bindings.
- return false;
- }
-}
diff --git a/java/dagger/internal/codegen/OptionalFactories.java b/java/dagger/internal/codegen/OptionalFactories.java
deleted file mode 100644
index 51c9939a3..000000000
--- a/java/dagger/internal/codegen/OptionalFactories.java
+++ /dev/null
@@ -1,439 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.CaseFormat.UPPER_CAMEL;
-import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
-import static com.google.common.base.Verify.verify;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static com.squareup.javapoet.MethodSpec.constructorBuilder;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static com.squareup.javapoet.TypeSpec.anonymousClassBuilder;
-import static com.squareup.javapoet.TypeSpec.classBuilder;
-import static dagger.internal.codegen.ComponentImplementation.FieldSpecKind.ABSENT_OPTIONAL_FIELD;
-import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.ABSENT_OPTIONAL_METHOD;
-import static dagger.internal.codegen.ComponentImplementation.TypeSpecKind.PRESENT_FACTORY;
-import static dagger.internal.codegen.RequestKinds.requestTypeName;
-import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
-import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
-import static dagger.internal.codegen.javapoet.TypeNames.PROVIDER;
-import static dagger.internal.codegen.javapoet.TypeNames.abstractProducerOf;
-import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf;
-import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PUBLIC;
-import static javax.lang.model.element.Modifier.STATIC;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Function;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.MoreExecutors;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.FieldSpec;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.ParameterSpec;
-import com.squareup.javapoet.ParameterizedTypeName;
-import com.squareup.javapoet.TypeName;
-import com.squareup.javapoet.TypeSpec;
-import com.squareup.javapoet.TypeVariableName;
-import dagger.internal.InstanceFactory;
-import dagger.internal.Preconditions;
-import dagger.internal.codegen.OptionalType.OptionalKind;
-import dagger.internal.codegen.javapoet.AnnotationSpecs;
-import dagger.model.RequestKind;
-import dagger.producers.Producer;
-import dagger.producers.internal.Producers;
-import java.util.Comparator;
-import java.util.Map;
-import java.util.Optional;
-import java.util.TreeMap;
-import java.util.concurrent.Executor;
-import javax.inject.Inject;
-import javax.inject.Provider;
-
-/** The nested class and static methods required by the component to implement optional bindings. */
-// TODO(dpb): Name members simply if a component uses only one of Guava or JDK Optional.
-@PerGeneratedFile
-final class OptionalFactories {
- private final ComponentImplementation componentImplementation;
-
- @Inject OptionalFactories(@TopLevel ComponentImplementation componentImplementation) {
- this.componentImplementation = componentImplementation;
- }
-
- /**
- * The factory classes that implement {@code Provider<Optional<T>>} or {@code
- * Producer<Optional<T>>} for present optional bindings for a given kind of dependency request
- * within the component.
- *
- * <p>The key is the {@code Provider<Optional<T>>} type.
- */
- private final Map<PresentFactorySpec, TypeSpec> presentFactoryClasses =
- new TreeMap<>(
- Comparator.comparing(PresentFactorySpec::valueKind)
- .thenComparing(PresentFactorySpec::frameworkType)
- .thenComparing(PresentFactorySpec::optionalKind));
-
- /**
- * The static methods that return a {@code Provider<Optional<T>>} that always returns an absent
- * value.
- */
- private final Map<OptionalKind, MethodSpec> absentOptionalProviderMethods = new TreeMap<>();
-
- /**
- * The static fields for {@code Provider<Optional<T>>} objects that always return an absent value.
- */
- private final Map<OptionalKind, FieldSpec> absentOptionalProviderFields = new TreeMap<>();
-
- /**
- * Returns an expression that calls a static method that returns a {@code Provider<Optional<T>>}
- * for absent optional bindings.
- */
- CodeBlock absentOptionalProvider(ContributionBinding binding) {
- verify(
- binding.bindingType().equals(BindingType.PROVISION),
- "Absent optional bindings should be provisions: %s",
- binding);
- OptionalKind optionalKind = OptionalType.from(binding.key()).kind();
- return CodeBlock.of(
- "$N()",
- absentOptionalProviderMethods.computeIfAbsent(
- optionalKind,
- kind -> {
- MethodSpec method = absentOptionalProviderMethod(kind);
- componentImplementation.addMethod(ABSENT_OPTIONAL_METHOD, method);
- return method;
- }));
- }
-
- /**
- * Creates a method specification for a {@code Provider<Optional<T>>} that always returns an
- * absent value.
- */
- private MethodSpec absentOptionalProviderMethod(OptionalKind optionalKind) {
- TypeVariableName typeVariable = TypeVariableName.get("T");
- return methodBuilder(
- String.format(
- "absent%sProvider", UPPER_UNDERSCORE.to(UPPER_CAMEL, optionalKind.name())))
- .addModifiers(PRIVATE, STATIC)
- .addTypeVariable(typeVariable)
- .returns(providerOf(optionalKind.of(typeVariable)))
- .addJavadoc(
- "Returns a {@link $T} that returns {@code $L}.",
- Provider.class,
- optionalKind.absentValueExpression())
- .addCode("$L // safe covariant cast\n", AnnotationSpecs.suppressWarnings(UNCHECKED))
- .addCode(
- "$1T provider = ($1T) $2N;",
- providerOf(optionalKind.of(typeVariable)),
- absentOptionalProviderFields.computeIfAbsent(
- optionalKind,
- kind -> {
- FieldSpec field = absentOptionalProviderField(kind);
- componentImplementation.addField(ABSENT_OPTIONAL_FIELD, field);
- return field;
- }))
- .addCode("return provider;")
- .build();
- }
-
- /**
- * Creates a field specification for a {@code Provider<Optional<T>>} that always returns an absent
- * value.
- */
- private FieldSpec absentOptionalProviderField(OptionalKind optionalKind) {
- return FieldSpec.builder(
- PROVIDER,
- String.format("ABSENT_%s_PROVIDER", optionalKind.name()),
- PRIVATE,
- STATIC,
- FINAL)
- .addAnnotation(AnnotationSpecs.suppressWarnings(RAWTYPES))
- .initializer("$T.create($L)", InstanceFactory.class, optionalKind.absentValueExpression())
- .addJavadoc(
- "A {@link $T} that returns {@code $L}.",
- Provider.class,
- optionalKind.absentValueExpression())
- .build();
- }
-
- /** Information about the type of a factory for present bindings. */
- @AutoValue
- abstract static class PresentFactorySpec {
- /** Whether the factory is a {@link Provider} or a {@link Producer}. */
- abstract FrameworkType frameworkType();
-
- /** What kind of {@code Optional} is returned. */
- abstract OptionalKind optionalKind();
-
- /** The kind of request satisfied by the value of the {@code Optional}. */
- abstract RequestKind valueKind();
-
- /** The type variable for the factory class. */
- TypeVariableName typeVariable() {
- return TypeVariableName.get("T");
- }
-
- /** The type contained by the {@code Optional}. */
- TypeName valueType() {
- return requestTypeName(valueKind(), typeVariable());
- }
-
- /** The type provided or produced by the factory. */
- ParameterizedTypeName optionalType() {
- return optionalKind().of(valueType());
- }
-
- /** The type of the factory. */
- ParameterizedTypeName factoryType() {
- return frameworkType().frameworkClassOf(optionalType());
- }
-
- /** The type of the delegate provider or producer. */
- ParameterizedTypeName delegateType() {
- return frameworkType().frameworkClassOf(typeVariable());
- }
-
- /** Returns the superclass the generated factory should have, if any. */
- Optional<ParameterizedTypeName> superclass() {
- switch (frameworkType()) {
- case PRODUCER_NODE:
- // TODO(cgdecker): This probably isn't a big issue for now, but it's possible this
- // shouldn't be an AbstractProducer:
- // - As AbstractProducer, it'll only call the delegate's get() method once and then cache
- // that result (essentially) rather than calling the delegate's get() method each time
- // its get() method is called (which was what it did before the cancellation change).
- // - It's not 100% clear to me whether the view-creation methods should return a view of
- // the same view created by the delegate or if they should just return their own views.
- return Optional.of(abstractProducerOf(optionalType()));
- default:
- return Optional.empty();
- }
- }
-
- /** Returns the superinterface the generated factory should have, if any. */
- Optional<ParameterizedTypeName> superinterface() {
- switch (frameworkType()) {
- case PROVIDER:
- return Optional.of(factoryType());
- default:
- return Optional.empty();
- }
- }
-
- /** Returns the name of the factory method to generate. */
- String factoryMethodName() {
- switch (frameworkType()) {
- case PROVIDER:
- return "get";
- case PRODUCER_NODE:
- return "compute";
- }
- throw new AssertionError(frameworkType());
- }
-
- /** The name of the factory class. */
- String factoryClassName() {
- return new StringBuilder("Present")
- .append(UPPER_UNDERSCORE.to(UPPER_CAMEL, optionalKind().name()))
- .append(UPPER_UNDERSCORE.to(UPPER_CAMEL, valueKind().toString()))
- .append(frameworkType().frameworkClass().getSimpleName())
- .toString();
- }
-
- private static PresentFactorySpec of(ContributionBinding binding) {
- return new AutoValue_OptionalFactories_PresentFactorySpec(
- FrameworkType.forBindingType(binding.bindingType()),
- OptionalType.from(binding.key()).kind(),
- getOnlyElement(binding.dependencies()).kind());
- }
- }
-
- /**
- * Returns an expression for an instance of a nested class that implements {@code
- * Provider<Optional<T>>} or {@code Producer<Optional<T>>} for a present optional binding, where
- * {@code T} represents dependency requests of that kind.
- *
- * <ul>
- * <li>If {@code optionalRequestKind} is {@link RequestKind#INSTANCE}, the class implements
- * {@code ProviderOrProducer<Optional<T>>}.
- * <li>If {@code optionalRequestKind} is {@link RequestKind#PROVIDER}, the class implements
- * {@code Provider<Optional<Provider<T>>>}.
- * <li>If {@code optionalRequestKind} is {@link RequestKind#LAZY}, the class implements {@code
- * Provider<Optional<Lazy<T>>>}.
- * <li>If {@code optionalRequestKind} is {@link RequestKind#PROVIDER_OF_LAZY}, the class
- * implements {@code Provider<Optional<Provider<Lazy<T>>>>}.
- * <li>If {@code optionalRequestKind} is {@link RequestKind#PRODUCER}, the class implements
- * {@code Producer<Optional<Producer<T>>>}.
- * <li>If {@code optionalRequestKind} is {@link RequestKind#PRODUCED}, the class implements
- * {@code Producer<Optional<Produced<T>>>}.
- * </ul>
- *
- * @param delegateFactory an expression for a {@link Provider} or {@link Producer} of the
- * underlying type
- */
- CodeBlock presentOptionalFactory(ContributionBinding binding, CodeBlock delegateFactory) {
- return CodeBlock.of(
- "$N.of($L)",
- presentFactoryClasses.computeIfAbsent(
- PresentFactorySpec.of(binding),
- spec -> {
- TypeSpec type = presentOptionalFactoryClass(spec);
- componentImplementation.addType(PRESENT_FACTORY, type);
- return type;
- }),
- delegateFactory);
- }
-
- private TypeSpec presentOptionalFactoryClass(PresentFactorySpec spec) {
- FieldSpec delegateField =
- FieldSpec.builder(spec.delegateType(), "delegate", PRIVATE, FINAL).build();
- ParameterSpec delegateParameter = ParameterSpec.builder(delegateField.type, "delegate").build();
- TypeSpec.Builder factoryClassBuilder =
- classBuilder(spec.factoryClassName())
- .addTypeVariable(spec.typeVariable())
- .addModifiers(PRIVATE, STATIC, FINAL)
- .addJavadoc(
- "A {@code $T} that uses a delegate {@code $T}.",
- spec.factoryType(),
- delegateField.type);
-
- spec.superclass().ifPresent(factoryClassBuilder::superclass);
- spec.superinterface().ifPresent(factoryClassBuilder::addSuperinterface);
-
- return factoryClassBuilder
- .addField(delegateField)
- .addMethod(
- constructorBuilder()
- .addModifiers(PRIVATE)
- .addParameter(delegateParameter)
- .addCode(
- "this.$N = $T.checkNotNull($N);",
- delegateField,
- Preconditions.class,
- delegateParameter)
- .build())
- .addMethod(presentOptionalFactoryGetMethod(spec, delegateField))
- .addMethod(
- methodBuilder("of")
- .addModifiers(PRIVATE, STATIC)
- .addTypeVariable(spec.typeVariable())
- .returns(spec.factoryType())
- .addParameter(delegateParameter)
- .addCode(
- "return new $L<$T>($N);",
- spec.factoryClassName(),
- spec.typeVariable(),
- delegateParameter)
- .build())
- .build();
- }
-
- private MethodSpec presentOptionalFactoryGetMethod(
- PresentFactorySpec spec, FieldSpec delegateField) {
- MethodSpec.Builder getMethodBuilder =
- methodBuilder(spec.factoryMethodName()).addAnnotation(Override.class).addModifiers(PUBLIC);
-
- switch (spec.frameworkType()) {
- case PROVIDER:
- return getMethodBuilder
- .returns(spec.optionalType())
- .addCode(
- "return $L;",
- spec.optionalKind()
- .presentExpression(
- FrameworkType.PROVIDER.to(
- spec.valueKind(), CodeBlock.of("$N", delegateField))))
- .build();
-
- case PRODUCER_NODE:
- getMethodBuilder.returns(listenableFutureOf(spec.optionalType()));
-
- switch (spec.valueKind()) {
- case FUTURE: // return a ListenableFuture<Optional<ListenableFuture<T>>>
- case PRODUCER: // return a ListenableFuture<Optional<Producer<T>>>
- return getMethodBuilder
- .addCode(
- "return $T.immediateFuture($L);",
- Futures.class,
- spec.optionalKind()
- .presentExpression(
- FrameworkType.PRODUCER_NODE.to(
- spec.valueKind(), CodeBlock.of("$N", delegateField))))
- .build();
-
- case INSTANCE: // return a ListenableFuture<Optional<T>>
- return getMethodBuilder
- .addCode(
- "return $L;",
- transformFutureToOptional(
- spec.optionalKind(),
- spec.typeVariable(),
- CodeBlock.of("$N.get()", delegateField)))
- .build();
-
- case PRODUCED: // return a ListenableFuture<Optional<Produced<T>>>
- return getMethodBuilder
- .addCode(
- "return $L;",
- transformFutureToOptional(
- spec.optionalKind(),
- spec.valueType(),
- CodeBlock.of(
- "$T.createFutureProduced($N.get())", Producers.class, delegateField)))
- .build();
-
- default:
- throw new UnsupportedOperationException(
- spec.factoryType() + " objects are not supported");
- }
- }
- throw new AssertionError(spec.frameworkType());
- }
-
- /**
- * An expression that uses {@link Futures#transform(ListenableFuture, Function, Executor)} to
- * transform a {@code ListenableFuture<inputType>} into a {@code
- * ListenableFuture<Optional<inputType>>}.
- *
- * @param inputFuture an expression of type {@code ListenableFuture<inputType>}
- */
- private static CodeBlock transformFutureToOptional(
- OptionalKind optionalKind, TypeName inputType, CodeBlock inputFuture) {
- return CodeBlock.of(
- "$T.transform($L, $L, $T.directExecutor())",
- Futures.class,
- inputFuture,
- anonymousClassBuilder("")
- .addSuperinterface(
- ParameterizedTypeName.get(
- ClassName.get(Function.class), inputType, optionalKind.of(inputType)))
- .addMethod(
- methodBuilder("apply")
- .addAnnotation(Override.class)
- .addModifiers(PUBLIC)
- .returns(optionalKind.of(inputType))
- .addParameter(inputType, "input")
- .addCode("return $L;", optionalKind.presentExpression(CodeBlock.of("input")))
- .build())
- .build(),
- MoreExecutors.class);
- }
-}
diff --git a/java/dagger/internal/codegen/OptionalFactoryInstanceCreationExpression.java b/java/dagger/internal/codegen/OptionalFactoryInstanceCreationExpression.java
deleted file mode 100644
index ba9e25f0f..000000000
--- a/java/dagger/internal/codegen/OptionalFactoryInstanceCreationExpression.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.BindingRequest.bindingRequest;
-
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
-
-/**
- * A {@link FrameworkInstanceCreationExpression} for {@link dagger.model.BindingKind#OPTIONAL
- * optional bindings}.
- */
-final class OptionalFactoryInstanceCreationExpression
- implements FrameworkInstanceCreationExpression {
- private final OptionalFactories optionalFactories;
- private final ContributionBinding binding;
- private final ComponentImplementation componentImplementation;
- private final ComponentBindingExpressions componentBindingExpressions;
-
- OptionalFactoryInstanceCreationExpression(
- OptionalFactories optionalFactories,
- ContributionBinding binding,
- ComponentImplementation componentImplementation,
- ComponentBindingExpressions componentBindingExpressions) {
- this.optionalFactories = optionalFactories;
- this.binding = binding;
- this.componentImplementation = componentImplementation;
- this.componentBindingExpressions = componentBindingExpressions;
- }
-
- @Override
- public CodeBlock creationExpression() {
- return binding.dependencies().isEmpty()
- ? optionalFactories.absentOptionalProvider(binding)
- : optionalFactories.presentOptionalFactory(
- binding,
- componentBindingExpressions
- .getDependencyExpression(
- bindingRequest(
- getOnlyElement(binding.dependencies()).key(), binding.frameworkType()),
- componentImplementation.name())
- .codeBlock());
- }
-
- @Override
- public boolean useInnerSwitchingProvider() {
- // Share providers for empty optionals from OptionalFactories so we don't have numerous
- // switch cases that all return Optional.empty().
- return !binding.dependencies().isEmpty();
- }
-}
diff --git a/java/dagger/internal/codegen/OptionalType.java b/java/dagger/internal/codegen/OptionalType.java
deleted file mode 100644
index 0fdbf685d..000000000
--- a/java/dagger/internal/codegen/OptionalType.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Equivalence;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.ParameterizedTypeName;
-import com.squareup.javapoet.TypeName;
-import dagger.model.Key;
-import java.util.Optional;
-import javax.lang.model.element.Name;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.type.TypeVisitor;
-import javax.lang.model.util.SimpleTypeVisitor8;
-
-/**
- * Information about an {@code Optional} {@link TypeMirror}.
- *
- * <p>{@link com.google.common.base.Optional} and {@link java.util.Optional} are supported.
- */
-@AutoValue
-abstract class OptionalType {
-
- /** A variant of {@code Optional}. */
- enum OptionalKind {
- /** {@link com.google.common.base.Optional}. */
- GUAVA_OPTIONAL(com.google.common.base.Optional.class, "absent"),
-
- /** {@link java.util.Optional}. */
- JDK_OPTIONAL(java.util.Optional.class, "empty"),
- ;
-
- private final Class<?> clazz;
- private final String absentFactoryMethodName;
-
- OptionalKind(Class<?> clazz, String absentFactoryMethodName) {
- this.clazz = clazz;
- this.absentFactoryMethodName = absentFactoryMethodName;
- }
-
- /** Returns {@code valueType} wrapped in the correct class. */
- ParameterizedTypeName of(TypeName valueType) {
- return ParameterizedTypeName.get(ClassName.get(clazz), valueType);
- }
-
- /** Returns an expression for the absent/empty value. */
- CodeBlock absentValueExpression() {
- return CodeBlock.of("$T.$L()", clazz, absentFactoryMethodName);
- }
-
- /**
- * Returns an expression for the absent/empty value, parameterized with {@link #valueType()}.
- */
- CodeBlock parameterizedAbsentValueExpression(OptionalType optionalType) {
- return CodeBlock.of("$T.<$T>$L()", clazz, optionalType.valueType(), absentFactoryMethodName);
- }
-
- /** Returns an expression for the present {@code value}. */
- CodeBlock presentExpression(CodeBlock value) {
- return CodeBlock.of("$T.of($L)", clazz, value);
- }
-
- /**
- * Returns an expression for the present {@code value}, returning {@code Optional<Object>} no
- * matter what type the value is.
- */
- CodeBlock presentObjectExpression(CodeBlock value) {
- return CodeBlock.of("$T.<$T>of($L)", clazz, Object.class, value);
- }
- }
-
- private static final TypeVisitor<Optional<OptionalKind>, Void> OPTIONAL_KIND =
- new SimpleTypeVisitor8<Optional<OptionalKind>, Void>(Optional.empty()) {
- @Override
- public Optional<OptionalKind> visitDeclared(DeclaredType t, Void p) {
- for (OptionalKind optionalKind : OptionalKind.values()) {
- Name qualifiedName = MoreElements.asType(t.asElement()).getQualifiedName();
- if (qualifiedName.contentEquals(optionalKind.clazz.getCanonicalName())) {
- return Optional.of(optionalKind);
- }
- }
- return Optional.empty();
- }
- };
-
- /**
- * The optional type itself, wrapped using {@link MoreTypes#equivalence()}.
- *
- * @deprecated Use {@link #declaredOptionalType()} instead.
- */
- @Deprecated
- protected abstract Equivalence.Wrapper<DeclaredType> wrappedDeclaredOptionalType();
-
- /** The optional type itself. */
- @SuppressWarnings("deprecation")
- DeclaredType declaredOptionalType() {
- return wrappedDeclaredOptionalType().get();
- }
-
- /** Which {@code Optional} type is used. */
- OptionalKind kind() {
- return declaredOptionalType().accept(OPTIONAL_KIND, null).get();
- }
-
- /** The value type. */
- TypeMirror valueType() {
- return declaredOptionalType().getTypeArguments().get(0);
- }
-
- /** Returns {@code true} if {@code type} is an {@code Optional} type. */
- static boolean isOptional(TypeMirror type) {
- return type.accept(OPTIONAL_KIND, null).isPresent();
- }
-
- /** Returns {@code true} if {@code key.type()} is an {@code Optional} type. */
- static boolean isOptional(Key key) {
- return isOptional(key.type());
- }
-
- /**
- * Returns a {@link OptionalType} for {@code type}.
- *
- * @throws IllegalArgumentException if {@code type} is not an {@code Optional} type
- */
- static OptionalType from(TypeMirror type) {
- checkArgument(isOptional(type), "%s must be an Optional", type);
- return new AutoValue_OptionalType(MoreTypes.equivalence().wrap(MoreTypes.asDeclared(type)));
- }
-
- /**
- * Returns a {@link OptionalType} for {@code key}'s {@link Key#type() type}.
- *
- * @throws IllegalArgumentException if {@code key.type()} is not an {@code Optional} type
- */
- static OptionalType from(Key key) {
- return from(key.type());
- }
-}
diff --git a/java/dagger/internal/codegen/Optionals.java b/java/dagger/internal/codegen/Optionals.java
deleted file mode 100644
index 1021c35ed..000000000
--- a/java/dagger/internal/codegen/Optionals.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.collect.Lists.asList;
-
-import java.util.Comparator;
-import java.util.Optional;
-import java.util.function.Function;
-
-/** Utilities for {@link Optional}s. */
-final class Optionals {
- /**
- * A {@link Comparator} that puts empty {@link Optional}s before present ones, and compares
- * present {@link Optional}s by their values.
- */
- static <C extends Comparable<C>> Comparator<Optional<C>> optionalComparator() {
- return Comparator.comparing((Optional<C> optional) -> optional.isPresent())
- .thenComparing(Optional::get);
- }
-
- static <T> Comparator<Optional<T>> emptiesLast(Comparator<? super T> valueComparator) {
- checkNotNull(valueComparator);
- return Comparator.comparing(o -> o.orElse(null), Comparator.nullsLast(valueComparator));
- }
-
- /** Returns the first argument that is present, or empty if none are. */
- @SafeVarargs
- static <T> Optional<T> firstPresent(Optional<T> first, Optional<T> second, Optional<T>... rest) {
- return asList(first, second, rest)
- .stream()
- .filter(Optional::isPresent)
- .findFirst()
- .orElse(Optional.empty());
- }
-
- /**
- * Walks a chain of present optionals as defined by successive calls to {@code nextFunction},
- * returning the value of the final optional that is present. The first optional in the chain is
- * the result of {@code nextFunction(start)}.
- */
- static <T> T rootmostValue(T start, Function<T, Optional<T>> nextFunction) {
- T current = start;
- for (Optional<T> next = nextFunction.apply(start);
- next.isPresent();
- next = nextFunction.apply(current)) {
- current = next.get();
- }
- return current;
- }
-
- private Optionals() {}
-}
diff --git a/java/dagger/internal/codegen/ParentComponent.java b/java/dagger/internal/codegen/ParentComponent.java
deleted file mode 100644
index 2d2b583e4..000000000
--- a/java/dagger/internal/codegen/ParentComponent.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import java.lang.annotation.Retention;
-import javax.inject.Qualifier;
-
-/**
- * A {@link Qualifier} for bindings that are associated with a component implementation's
- * parent component.
- */
-@Retention(RUNTIME)
-@Qualifier
-@interface ParentComponent {}
diff --git a/java/dagger/internal/codegen/PerComponentImplementation.java b/java/dagger/internal/codegen/PerComponentImplementation.java
deleted file mode 100644
index 5d4ba1807..000000000
--- a/java/dagger/internal/codegen/PerComponentImplementation.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import java.lang.annotation.Retention;
-import javax.inject.Scope;
-
-/** A {@link Scope} that encompasses a single component implementation. */
-@Retention(RUNTIME)
-@Scope
-@interface PerComponentImplementation {}
diff --git a/java/dagger/internal/codegen/PerGeneratedFile.java b/java/dagger/internal/codegen/PerGeneratedFile.java
deleted file mode 100644
index c30e67a5e..000000000
--- a/java/dagger/internal/codegen/PerGeneratedFile.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import java.lang.annotation.Retention;
-import javax.inject.Scope;
-
-/**
- * A {@link Scope} that encompasses a top-level component implementation and any of its inner
- * descendant component implementations in the same generated file.
- */
-@Retention(RUNTIME)
-@Scope
-@interface PerGeneratedFile {}
diff --git a/java/dagger/internal/codegen/PrivateMethodBindingExpression.java b/java/dagger/internal/codegen/PrivateMethodBindingExpression.java
deleted file mode 100644
index 482c12320..000000000
--- a/java/dagger/internal/codegen/PrivateMethodBindingExpression.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static dagger.internal.codegen.ComponentImplementation.MethodSpecKind.PRIVATE_METHOD;
-import static javax.lang.model.element.Modifier.PRIVATE;
-
-import com.squareup.javapoet.TypeName;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-
-/**
- * A binding expression that wraps the dependency expressions in a private, no-arg method.
- *
- * <p>Dependents of this binding expression will just call the no-arg private method.
- */
-final class PrivateMethodBindingExpression extends MethodBindingExpression {
- private final BindingRequest request;
- private final ComponentImplementation componentImplementation;
- private String methodName;
-
- PrivateMethodBindingExpression(
- BindingRequest request,
- ResolvedBindings resolvedBindings,
- MethodImplementationStrategy methodImplementationStrategy,
- BindingExpression wrappedBindingExpression,
- ComponentImplementation componentImplementation,
- DaggerTypes types) {
- super(
- request,
- resolvedBindings,
- methodImplementationStrategy,
- wrappedBindingExpression,
- componentImplementation,
- types);
- this.request = checkNotNull(request);
- this.componentImplementation = checkNotNull(componentImplementation);
- }
-
- @Override
- protected void addMethod() {
- if (methodName == null) {
- // Have to set methodName field before implementing the method in order to handle recursion.
- methodName = componentImplementation.getUniqueMethodName(request);
- // TODO(user): Fix the order that these generated methods are written to the component.
- componentImplementation.addMethod(
- PRIVATE_METHOD,
- methodBuilder(methodName)
- .addModifiers(PRIVATE)
- .returns(TypeName.get(returnType()))
- .addCode(methodBody())
- .build());
- }
- }
-
- @Override
- protected String methodName() {
- checkState(methodName != null, "addMethod() must be called before methodName()");
- return methodName;
- }
-}
diff --git a/java/dagger/internal/codegen/ProcessingEnvironmentCompilerOptions.java b/java/dagger/internal/codegen/ProcessingEnvironmentCompilerOptions.java
deleted file mode 100644
index cf2475de7..000000000
--- a/java/dagger/internal/codegen/ProcessingEnvironmentCompilerOptions.java
+++ /dev/null
@@ -1,484 +0,0 @@
-/*
- * Copyright (C) 2019 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.CaseFormat.LOWER_CAMEL;
-import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.Sets.immutableEnumSet;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.FeatureStatus.DISABLED;
-import static dagger.internal.codegen.FeatureStatus.ENABLED;
-import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.EMIT_MODIFIABLE_METADATA_ANNOTATIONS;
-import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.EXPERIMENTAL_AHEAD_OF_TIME_SUBCOMPONENTS;
-import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.EXPERIMENTAL_ANDROID_MODE;
-import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.FAST_INIT;
-import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.FLOATING_BINDS_METHODS;
-import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.FORCE_USE_SERIALIZED_COMPONENT_IMPLEMENTATIONS;
-import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.FORMAT_GENERATED_SOURCE;
-import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.IGNORE_PRIVATE_AND_STATIC_INJECTION_FOR_COMPONENT;
-import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.WARN_IF_INJECTION_FACTORY_NOT_GENERATED_UPSTREAM;
-import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Feature.WRITE_PRODUCER_NAME_IN_TOKEN;
-import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.KeyOnlyOption.HEADER_COMPILATION;
-import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.KeyOnlyOption.USE_GRADLE_INCREMENTAL_PROCESSING;
-import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Validation.DISABLE_INTER_COMPONENT_SCOPE_VALIDATION;
-import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Validation.EXPLICIT_BINDING_CONFLICTS_WITH_INJECT;
-import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Validation.FULL_BINDING_GRAPH_VALIDATION;
-import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Validation.MODULE_HAS_DIFFERENT_SCOPES_VALIDATION;
-import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Validation.NULLABLE_VALIDATION;
-import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Validation.PRIVATE_MEMBER_VALIDATION;
-import static dagger.internal.codegen.ProcessingEnvironmentCompilerOptions.Validation.STATIC_MEMBER_VALIDATION;
-import static dagger.internal.codegen.ValidationType.ERROR;
-import static dagger.internal.codegen.ValidationType.NONE;
-import static dagger.internal.codegen.ValidationType.WARNING;
-import static java.util.stream.Collectors.joining;
-import static java.util.stream.Stream.concat;
-
-import com.google.common.base.Ascii;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import dagger.producers.Produces;
-import java.util.Arrays;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Stream;
-import javax.annotation.processing.ProcessingEnvironment;
-import javax.lang.model.element.TypeElement;
-import javax.tools.Diagnostic;
-
-final class ProcessingEnvironmentCompilerOptions extends CompilerOptions {
- /** Returns a valid {@link CompilerOptions} parsed from the processing environment. */
- static CompilerOptions create(ProcessingEnvironment processingEnvironment) {
- return new ProcessingEnvironmentCompilerOptions(processingEnvironment).checkValid();
- }
-
- private final ProcessingEnvironment processingEnvironment;
- private final Map<EnumOption<?>, Object> enumOptions = new HashMap<>();
- private final Map<EnumOption<?>, ImmutableMap<String, ? extends Enum<?>>> allCommandLineOptions =
- new HashMap<>();
-
- private ProcessingEnvironmentCompilerOptions(ProcessingEnvironment processingEnvironment) {
- this.processingEnvironment = processingEnvironment;
- }
-
- @Override
- boolean usesProducers() {
- return processingEnvironment.getElementUtils().getTypeElement(Produces.class.getCanonicalName())
- != null;
- }
-
- @Override
- boolean headerCompilation() {
- return isEnabled(HEADER_COMPILATION);
- }
-
- @Override
- boolean fastInit() {
- return isEnabled(FAST_INIT);
- }
-
- @Override
- boolean formatGeneratedSource() {
- return isEnabled(FORMAT_GENERATED_SOURCE);
- }
-
- @Override
- boolean writeProducerNameInToken() {
- return isEnabled(WRITE_PRODUCER_NAME_IN_TOKEN);
- }
-
- @Override
- Diagnostic.Kind nullableValidationKind() {
- return diagnosticKind(NULLABLE_VALIDATION);
- }
-
- @Override
- Diagnostic.Kind privateMemberValidationKind() {
- return diagnosticKind(PRIVATE_MEMBER_VALIDATION);
- }
-
- @Override
- Diagnostic.Kind staticMemberValidationKind() {
- return diagnosticKind(STATIC_MEMBER_VALIDATION);
- }
-
- @Override
- boolean ignorePrivateAndStaticInjectionForComponent() {
- return isEnabled(IGNORE_PRIVATE_AND_STATIC_INJECTION_FOR_COMPONENT);
- }
-
- @Override
- ValidationType scopeCycleValidationType() {
- return parseOption(DISABLE_INTER_COMPONENT_SCOPE_VALIDATION);
- }
-
- @Override
- boolean warnIfInjectionFactoryNotGeneratedUpstream() {
- return isEnabled(WARN_IF_INJECTION_FACTORY_NOT_GENERATED_UPSTREAM);
- }
-
- @Override
- boolean aheadOfTimeSubcomponents() {
- return isEnabled(EXPERIMENTAL_AHEAD_OF_TIME_SUBCOMPONENTS);
- }
-
- @Override
- boolean forceUseSerializedComponentImplementations() {
- return isEnabled(FORCE_USE_SERIALIZED_COMPONENT_IMPLEMENTATIONS);
- }
-
- @Override
- boolean emitModifiableMetadataAnnotations() {
- return isEnabled(EMIT_MODIFIABLE_METADATA_ANNOTATIONS);
- }
-
- @Override
- boolean useGradleIncrementalProcessing() {
- return isEnabled(USE_GRADLE_INCREMENTAL_PROCESSING);
- }
-
- @Override
- ValidationType fullBindingGraphValidationType(TypeElement element) {
- return fullBindingGraphValidationType();
- }
-
- private ValidationType fullBindingGraphValidationType() {
- return parseOption(FULL_BINDING_GRAPH_VALIDATION);
- }
-
- @Override
- Diagnostic.Kind moduleHasDifferentScopesDiagnosticKind() {
- return diagnosticKind(MODULE_HAS_DIFFERENT_SCOPES_VALIDATION);
- }
-
- @Override
- ValidationType explicitBindingConflictsWithInjectValidationType() {
- return parseOption(EXPLICIT_BINDING_CONFLICTS_WITH_INJECT);
- }
-
- private boolean isEnabled(KeyOnlyOption keyOnlyOption) {
- return processingEnvironment.getOptions().containsKey(keyOnlyOption.toString());
- }
-
- private boolean isEnabled(Feature feature) {
- return parseOption(feature).equals(ENABLED);
- }
-
- private Diagnostic.Kind diagnosticKind(Validation validation) {
- return parseOption(validation).diagnosticKind().get();
- }
-
- @SuppressWarnings("CheckReturnValue")
- private ProcessingEnvironmentCompilerOptions checkValid() {
- for (KeyOnlyOption keyOnlyOption : KeyOnlyOption.values()) {
- isEnabled(keyOnlyOption);
- }
- for (Feature feature : Feature.values()) {
- parseOption(feature);
- }
- for (Validation validation : Validation.values()) {
- parseOption(validation);
- }
- noLongerRecognized(EXPERIMENTAL_ANDROID_MODE);
- noLongerRecognized(FLOATING_BINDS_METHODS);
- return this;
- }
-
- private void noLongerRecognized(CommandLineOption commandLineOption) {
- if (processingEnvironment.getOptions().containsKey(commandLineOption.toString())) {
- processingEnvironment
- .getMessager()
- .printMessage(
- Diagnostic.Kind.WARNING, commandLineOption + " is no longer recognized by Dagger");
- }
- }
-
- private interface CommandLineOption {
- /** The key of the option (appears after "-A"). */
- @Override
- String toString();
-
- /**
- * Returns all aliases besides {@link #toString()}, such as old names for an option, in order of
- * precedence.
- */
- default ImmutableList<String> aliases() {
- return ImmutableList.of();
- }
-
- /** All the command-line names for this option, in order of precedence. */
- default Stream<String> allNames() {
- return concat(Stream.of(toString()), aliases().stream());
- }
- }
-
- /** An option that can be set on the command line. */
- private interface EnumOption<E extends Enum<E>> extends CommandLineOption {
- /** The default value for this option. */
- E defaultValue();
-
- /** The valid values for this option. */
- Set<E> validValues();
- }
-
- enum KeyOnlyOption implements CommandLineOption {
- HEADER_COMPILATION {
- @Override
- public String toString() {
- return "experimental_turbine_hjar";
- }
- },
-
- USE_GRADLE_INCREMENTAL_PROCESSING {
- @Override
- public String toString() {
- return "dagger.gradle.incremental";
- }
- },
- }
-
- /**
- * A feature that can be enabled or disabled on the command line by setting {@code -Akey=ENABLED}
- * or {@code -Akey=DISABLED}.
- */
- enum Feature implements EnumOption<FeatureStatus> {
- FAST_INIT,
-
- EXPERIMENTAL_ANDROID_MODE,
-
- FORMAT_GENERATED_SOURCE,
-
- WRITE_PRODUCER_NAME_IN_TOKEN,
-
- WARN_IF_INJECTION_FACTORY_NOT_GENERATED_UPSTREAM,
-
- IGNORE_PRIVATE_AND_STATIC_INJECTION_FOR_COMPONENT,
-
- EXPERIMENTAL_AHEAD_OF_TIME_SUBCOMPONENTS,
-
- FORCE_USE_SERIALIZED_COMPONENT_IMPLEMENTATIONS,
-
- EMIT_MODIFIABLE_METADATA_ANNOTATIONS(ENABLED),
-
- FLOATING_BINDS_METHODS,
- ;
-
- final FeatureStatus defaultValue;
-
- Feature() {
- this(DISABLED);
- }
-
- Feature(FeatureStatus defaultValue) {
- this.defaultValue = defaultValue;
- }
-
- @Override
- public FeatureStatus defaultValue() {
- return defaultValue;
- }
-
- @Override
- public Set<FeatureStatus> validValues() {
- return EnumSet.allOf(FeatureStatus.class);
- }
-
- @Override
- public String toString() {
- return optionName(this);
- }
- }
-
- /** The diagnostic kind or validation type for a kind of validation. */
- enum Validation implements EnumOption<ValidationType> {
- DISABLE_INTER_COMPONENT_SCOPE_VALIDATION(),
-
- NULLABLE_VALIDATION(ERROR, WARNING),
-
- PRIVATE_MEMBER_VALIDATION(ERROR, WARNING),
-
- STATIC_MEMBER_VALIDATION(ERROR, WARNING),
-
- /** Whether to validate full binding graphs for components, subcomponents, and modules. */
- FULL_BINDING_GRAPH_VALIDATION(NONE, ERROR, WARNING) {
- @Override
- public ImmutableList<String> aliases() {
- return ImmutableList.of("dagger.moduleBindingValidation");
- }
- },
-
- /**
- * How to report conflicting scoped bindings when validating partial binding graphs associated
- * with modules.
- */
- MODULE_HAS_DIFFERENT_SCOPES_VALIDATION(ERROR, WARNING),
-
- /**
- * How to report that an explicit binding in a subcomponent conflicts with an {@code @Inject}
- * constructor used in an ancestor component.
- */
- EXPLICIT_BINDING_CONFLICTS_WITH_INJECT(WARNING, ERROR, NONE),
- ;
-
- final ValidationType defaultType;
- final ImmutableSet<ValidationType> validTypes;
-
- Validation() {
- this(ERROR, WARNING, NONE);
- }
-
- Validation(ValidationType defaultType, ValidationType... moreValidTypes) {
- this.defaultType = defaultType;
- this.validTypes = immutableEnumSet(defaultType, moreValidTypes);
- }
-
- @Override
- public ValidationType defaultValue() {
- return defaultType;
- }
-
- @Override
- public Set<ValidationType> validValues() {
- return validTypes;
- }
-
- @Override
- public String toString() {
- return optionName(this);
- }
- }
-
- private static String optionName(Enum<? extends EnumOption<?>> option) {
- return "dagger." + UPPER_UNDERSCORE.to(LOWER_CAMEL, option.name());
- }
-
- /** The supported command-line options. */
- static ImmutableSet<String> supportedOptions() {
- // need explicit type parameter to avoid a runtime stream error
- return Stream.<CommandLineOption[]>of(
- KeyOnlyOption.values(), Feature.values(), Validation.values())
- .flatMap(Arrays::stream)
- .flatMap(CommandLineOption::allNames)
- .collect(toImmutableSet());
- }
-
- /**
- * Returns the value for the option as set on the command line by any name, or the default value
- * if not set.
- *
- * <p>If more than one name is used to set the value, but all names specify the same value,
- * reports a warning and returns that value.
- *
- * <p>If more than one name is used to set the value, and not all names specify the same value,
- * reports an error and returns the default value.
- */
- private <T extends Enum<T>> T parseOption(EnumOption<T> option) {
- @SuppressWarnings("unchecked") // we only put covariant values into the map
- T value = (T) enumOptions.computeIfAbsent(option, this::parseOptionUncached);
- return value;
- }
-
- private <T extends Enum<T>> T parseOptionUncached(EnumOption<T> option) {
- ImmutableMap<String, T> values = parseOptionWithAllNames(option);
-
- // If no value is specified, return the default value.
- if (values.isEmpty()) {
- return option.defaultValue();
- }
-
- // If all names have the same value, return that.
- if (values.asMultimap().inverse().keySet().size() == 1) {
- // Warn if an option was set with more than one name. That would be an error if the values
- // differed.
- if (values.size() > 1) {
- reportUseOfDifferentNamesForOption(Diagnostic.Kind.WARNING, option, values.keySet());
- }
- return values.values().asList().get(0);
- }
-
- // If different names have different values, report an error and return the default
- // value.
- reportUseOfDifferentNamesForOption(Diagnostic.Kind.ERROR, option, values.keySet());
- return option.defaultValue();
- }
-
- private void reportUseOfDifferentNamesForOption(
- Diagnostic.Kind diagnosticKind, EnumOption<?> option, ImmutableSet<String> usedNames) {
- processingEnvironment
- .getMessager()
- .printMessage(
- diagnosticKind,
- String.format(
- "Only one of the equivalent options (%s) should be used; prefer -A%s",
- usedNames.stream().map(name -> "-A" + name).collect(joining(", ")), option));
- }
-
- private <T extends Enum<T>> ImmutableMap<String, T> parseOptionWithAllNames(
- EnumOption<T> option) {
- @SuppressWarnings("unchecked") // map is covariant
- ImmutableMap<String, T> aliasValues =
- (ImmutableMap<String, T>)
- allCommandLineOptions.computeIfAbsent(option, this::parseOptionWithAllNamesUncached);
- return aliasValues;
- }
-
- private <T extends Enum<T>> ImmutableMap<String, T> parseOptionWithAllNamesUncached(
- EnumOption<T> option) {
- ImmutableMap.Builder<String, T> values = ImmutableMap.builder();
- getUsedNames(option)
- .forEach(
- name -> parseOptionWithName(option, name).ifPresent(value -> values.put(name, value)));
- return values.build();
- }
-
- private <T extends Enum<T>> Optional<T> parseOptionWithName(EnumOption<T> option, String key) {
- checkArgument(processingEnvironment.getOptions().containsKey(key), "key %s not found", key);
- String stringValue = processingEnvironment.getOptions().get(key);
- if (stringValue == null) {
- processingEnvironment
- .getMessager()
- .printMessage(Diagnostic.Kind.ERROR, "Processor option -A" + key + " needs a value");
- } else {
- try {
- T value =
- Enum.valueOf(option.defaultValue().getDeclaringClass(), Ascii.toUpperCase(stringValue));
- if (option.validValues().contains(value)) {
- return Optional.of(value);
- }
- } catch (IllegalArgumentException e) {
- // handled below
- }
- processingEnvironment
- .getMessager()
- .printMessage(
- Diagnostic.Kind.ERROR,
- String.format(
- "Processor option -A%s may only have the values %s "
- + "(case insensitive), found: %s",
- key, option.validValues(), stringValue));
- }
- return Optional.empty();
- }
-
- private Stream<String> getUsedNames(CommandLineOption option) {
- return option.allNames().filter(name -> processingEnvironment.getOptions().containsKey(name));
- }
-}
diff --git a/java/dagger/internal/codegen/ProcessingEnvironmentModule.java b/java/dagger/internal/codegen/ProcessingEnvironmentModule.java
index 173057438..8a535fe13 100644
--- a/java/dagger/internal/codegen/ProcessingEnvironmentModule.java
+++ b/java/dagger/internal/codegen/ProcessingEnvironmentModule.java
@@ -16,15 +16,18 @@
package dagger.internal.codegen;
-import static com.google.common.base.Preconditions.checkNotNull;
-
import com.google.googlejavaformat.java.filer.FormattingFiler;
+import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.Reusable;
+import dagger.internal.codegen.SpiModule.ProcessorClassLoader;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions;
+import dagger.internal.codegen.compileroption.ProcessingOptions;
import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.spi.BindingGraphPlugin;
import java.util.Map;
-import java.util.Optional;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
@@ -33,27 +36,25 @@ import javax.lang.model.util.Types;
/** Bindings that depend on the {@link ProcessingEnvironment}. */
@Module
-final class ProcessingEnvironmentModule {
-
- private final ProcessingEnvironment processingEnvironment;
-
- ProcessingEnvironmentModule(ProcessingEnvironment processingEnvironment) {
- this.processingEnvironment = checkNotNull(processingEnvironment);
- }
+interface ProcessingEnvironmentModule {
+ @Binds
+ @Reusable // to avoid parsing options more than once
+ CompilerOptions bindCompilerOptions(
+ ProcessingEnvironmentCompilerOptions processingEnvironmentCompilerOptions);
@Provides
@ProcessingOptions
- Map<String, String> processingOptions() {
+ static Map<String, String> processingOptions(ProcessingEnvironment processingEnvironment) {
return processingEnvironment.getOptions();
}
@Provides
- Messager messager() {
+ static Messager messager(ProcessingEnvironment processingEnvironment) {
return processingEnvironment.getMessager();
}
@Provides
- Filer filer(CompilerOptions compilerOptions) {
+ static Filer filer(CompilerOptions compilerOptions, ProcessingEnvironment processingEnvironment) {
if (compilerOptions.headerCompilation() || !compilerOptions.formatGeneratedSource()) {
return processingEnvironment.getFiler();
} else {
@@ -62,28 +63,23 @@ final class ProcessingEnvironmentModule {
}
@Provides
- Types types() {
+ static Types types(ProcessingEnvironment processingEnvironment) {
return processingEnvironment.getTypeUtils();
}
@Provides
- SourceVersion sourceVersion() {
+ static SourceVersion sourceVersion(ProcessingEnvironment processingEnvironment) {
return processingEnvironment.getSourceVersion();
}
@Provides
- DaggerElements daggerElements() {
+ static DaggerElements daggerElements(ProcessingEnvironment processingEnvironment) {
return new DaggerElements(processingEnvironment);
}
@Provides
- @Reusable // to avoid parsing options more than once
- CompilerOptions compilerOptions() {
- return ProcessingEnvironmentCompilerOptions.create(processingEnvironment);
- }
-
- @Provides
- Optional<DaggerStatisticsRecorder> daggerStatisticsRecorder() {
- return Optional.empty();
+ @ProcessorClassLoader
+ static ClassLoader processorClassloader(ProcessingEnvironment processingEnvironment) {
+ return BindingGraphPlugin.class.getClassLoader();
}
}
diff --git a/java/dagger/internal/codegen/ProcessingOptions.java b/java/dagger/internal/codegen/ProcessingOptions.java
deleted file mode 100644
index 105452a6f..000000000
--- a/java/dagger/internal/codegen/ProcessingOptions.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import java.lang.annotation.Retention;
-import javax.inject.Qualifier;
-
-/**
- * A qualifier for the {@link javax.annotation.processing.ProcessingEnvironment#getOptions()
- * processing options} passed to the current invocation of {@code javac}.
- */
-@Retention(RUNTIME)
-@Qualifier
-@interface ProcessingOptions {}
diff --git a/java/dagger/internal/codegen/ProcessingRoundCacheModule.java b/java/dagger/internal/codegen/ProcessingRoundCacheModule.java
index b56cc30ac..2373cd291 100644
--- a/java/dagger/internal/codegen/ProcessingRoundCacheModule.java
+++ b/java/dagger/internal/codegen/ProcessingRoundCacheModule.java
@@ -18,6 +18,14 @@ package dagger.internal.codegen;
import dagger.Binds;
import dagger.Module;
+import dagger.internal.codegen.base.ClearableCache;
+import dagger.internal.codegen.binding.BindingGraphFactory;
+import dagger.internal.codegen.binding.ModuleDescriptor;
+import dagger.internal.codegen.kotlin.KotlinMetadataFactory;
+import dagger.internal.codegen.validation.AnyBindingMethodValidator;
+import dagger.internal.codegen.validation.ComponentCreatorValidator;
+import dagger.internal.codegen.validation.ComponentValidator;
+import dagger.internal.codegen.validation.InjectValidator;
import dagger.multibindings.IntoSet;
/**
@@ -28,6 +36,14 @@ import dagger.multibindings.IntoSet;
interface ProcessingRoundCacheModule {
@Binds
@IntoSet
+ ClearableCache anyBindingMethodValidator(AnyBindingMethodValidator cache);
+
+ @Binds
+ @IntoSet
+ ClearableCache injectValidator(InjectValidator cache);
+
+ @Binds
+ @IntoSet
ClearableCache moduleDescriptorFactory(ModuleDescriptor.Factory cache);
@Binds
@@ -36,5 +52,13 @@ interface ProcessingRoundCacheModule {
@Binds
@IntoSet
- ClearableCache componentImplementationFactory(ComponentImplementationFactory cache);
+ ClearableCache componentValidator(ComponentValidator cache);
+
+ @Binds
+ @IntoSet
+ ClearableCache componentCreatorValidator(ComponentCreatorValidator cache);
+
+ @Binds
+ @IntoSet
+ ClearableCache kotlinMetadata(KotlinMetadataFactory cache);
}
diff --git a/java/dagger/internal/codegen/ProducerCreationExpression.java b/java/dagger/internal/codegen/ProducerCreationExpression.java
deleted file mode 100644
index 1dd7a1c4b..000000000
--- a/java/dagger/internal/codegen/ProducerCreationExpression.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
-
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
-
-/**
- * A {@link dagger.producers.Producer} creation expression for a {@link
- * dagger.producers.Produces @Produces}-annotated module method.
- */
-// TODO(dpb): Resolve with InjectionOrProvisionProviderCreationExpression.
-final class ProducerCreationExpression implements FrameworkInstanceCreationExpression {
-
- private final ComponentBindingExpressions componentBindingExpressions;
- private final ContributionBinding binding;
-
- ProducerCreationExpression(
- ContributionBinding binding, ComponentBindingExpressions componentBindingExpressions) {
- this.binding = checkNotNull(binding);
- this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
- }
-
- @Override
- public CodeBlock creationExpression() {
- return CodeBlock.of(
- "$T.create($L)",
- generatedClassNameForBinding(binding),
- componentBindingExpressions.getCreateMethodArgumentsCodeBlock(binding));
- }
-}
diff --git a/java/dagger/internal/codegen/ProducerEntryPointView.java b/java/dagger/internal/codegen/ProducerEntryPointView.java
deleted file mode 100644
index 87b5a4a0f..000000000
--- a/java/dagger/internal/codegen/ProducerEntryPointView.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.ComponentImplementation.FieldSpecKind.FRAMEWORK_FIELD;
-import static javax.lang.model.element.Modifier.PRIVATE;
-
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.FieldSpec;
-import com.squareup.javapoet.TypeName;
-import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.internal.codegen.javapoet.Expression;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.RequestKind;
-import dagger.producers.Producer;
-import dagger.producers.internal.CancellationListener;
-import dagger.producers.internal.Producers;
-import java.util.Optional;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * A factory of {@linkplain Producers#entryPointViewOf(Producer, CancellationListener) entry point
- * views} of {@link Producer}s.
- */
-final class ProducerEntryPointView {
- private final DaggerTypes types;
-
- ProducerEntryPointView(DaggerTypes types) {
- this.types = types;
- }
-
- /**
- * Returns an expression for an {@linkplain Producers#entryPointViewOf(Producer,
- * CancellationListener) entry point view} of a producer if the component method returns a {@link
- * Producer} or {@link com.google.common.util.concurrent.ListenableFuture}.
- *
- * <p>This is intended to be a replacement implementation for {@link
- * BindingExpression#getDependencyExpressionForComponentMethod(ComponentMethodDescriptor,
- * ComponentImplementation)}, and in cases where {@link Optional#empty()} is returned, callers
- * should call {@code super.getDependencyExpressionForComponentMethod()}.
- */
- Optional<Expression> getProducerEntryPointField(
- BindingExpression producerExpression,
- ComponentMethodDescriptor componentMethod,
- ComponentImplementation component) {
- if (component.componentDescriptor().isProduction()
- && (componentMethod.dependencyRequest().get().kind().equals(RequestKind.FUTURE)
- || componentMethod.dependencyRequest().get().kind().equals(RequestKind.PRODUCER))) {
- return Optional.of(
- Expression.create(
- fieldType(componentMethod),
- "$N",
- createField(producerExpression, componentMethod, component)));
- } else {
- // If the component isn't a production component, it won't implement CancellationListener and
- // as such we can't create an entry point. But this binding must also just be a Producer from
- // Provider anyway in that case, so there shouldn't be an issue.
- // TODO(b/116855531): Is it really intended that a non-production component can have Producer
- // entry points?
- return Optional.empty();
- }
- }
-
- private FieldSpec createField(
- BindingExpression producerExpression,
- ComponentMethodDescriptor componentMethod,
- ComponentImplementation component) {
- // TODO(cgdecker): Use a FrameworkFieldInitializer for this?
- // Though I don't think we need the once-only behavior of that, since I think
- // getComponentMethodImplementation will only be called once anyway
- String methodName = componentMethod.methodElement().getSimpleName().toString();
- FieldSpec field =
- FieldSpec.builder(
- TypeName.get(fieldType(componentMethod)),
- component.getUniqueFieldName(methodName + "EntryPoint"),
- PRIVATE)
- .build();
- component.addField(FRAMEWORK_FIELD, field);
-
- CodeBlock fieldInitialization =
- CodeBlock.of(
- "this.$N = $T.entryPointViewOf($L, this);",
- field,
- Producers.class,
- producerExpression.getDependencyExpression(component.name()).codeBlock());
- component.addInitialization(fieldInitialization);
-
- return field;
- }
-
- // TODO(cgdecker): Can we use producerExpression.getDependencyExpression().type() instead of
- // needing to (re)compute this?
- private TypeMirror fieldType(ComponentMethodDescriptor componentMethod) {
- return types.wrapType(componentMethod.dependencyRequest().get().key().type(), Producer.class);
- }
-}
diff --git a/java/dagger/internal/codegen/ProducerFactoryGenerator.java b/java/dagger/internal/codegen/ProducerFactoryGenerator.java
deleted file mode 100644
index 3f2bef415..000000000
--- a/java/dagger/internal/codegen/ProducerFactoryGenerator.java
+++ /dev/null
@@ -1,553 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Verify.verifyNotNull;
-import static com.squareup.javapoet.ClassName.OBJECT;
-import static com.squareup.javapoet.MethodSpec.constructorBuilder;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static com.squareup.javapoet.TypeSpec.classBuilder;
-import static dagger.internal.codegen.DaggerStreams.toImmutableList;
-import static dagger.internal.codegen.GwtCompatibility.gwtIncompatibleAnnotation;
-import static dagger.internal.codegen.SourceFiles.bindingTypeElementTypeVariableNames;
-import static dagger.internal.codegen.SourceFiles.generateBindingFieldsForDependencies;
-import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
-import static dagger.internal.codegen.SourceFiles.parameterizedGeneratedTypeNameForBinding;
-import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
-import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
-import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
-import static dagger.internal.codegen.javapoet.TypeNames.FUTURES;
-import static dagger.internal.codegen.javapoet.TypeNames.PRODUCERS;
-import static dagger.internal.codegen.javapoet.TypeNames.PRODUCER_TOKEN;
-import static dagger.internal.codegen.javapoet.TypeNames.VOID_CLASS;
-import static dagger.internal.codegen.javapoet.TypeNames.listOf;
-import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf;
-import static dagger.internal.codegen.javapoet.TypeNames.producedOf;
-import static java.util.stream.Collectors.joining;
-import static javax.lang.model.element.Modifier.FINAL;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PROTECTED;
-import static javax.lang.model.element.Modifier.PUBLIC;
-import static javax.lang.model.element.Modifier.STATIC;
-
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
-import com.squareup.javapoet.AnnotationSpec;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.FieldSpec;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.ParameterizedTypeName;
-import com.squareup.javapoet.TypeName;
-import com.squareup.javapoet.TypeSpec;
-import dagger.internal.codegen.javapoet.AnnotationSpecs;
-import dagger.internal.codegen.javapoet.TypeNames;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.model.DependencyRequest;
-import dagger.model.Key;
-import dagger.model.RequestKind;
-import dagger.producers.Producer;
-import dagger.producers.internal.AbstractProducesMethodProducer;
-import dagger.producers.internal.Producers;
-import java.util.Map;
-import java.util.Optional;
-import javax.annotation.processing.Filer;
-import javax.inject.Inject;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * Generates {@link Producer} implementations from {@link ProductionBinding} instances.
- */
-final class ProducerFactoryGenerator extends SourceFileGenerator<ProductionBinding> {
- private final CompilerOptions compilerOptions;
- private final KeyFactory keyFactory;
-
- @Inject
- ProducerFactoryGenerator(
- Filer filer,
- DaggerElements elements,
- SourceVersion sourceVersion,
- CompilerOptions compilerOptions,
- KeyFactory keyFactory) {
- super(filer, elements, sourceVersion);
- this.compilerOptions = compilerOptions;
- this.keyFactory = keyFactory;
- }
-
- @Override
- ClassName nameGeneratedType(ProductionBinding binding) {
- return generatedClassNameForBinding(binding);
- }
-
- @Override
- Element originatingElement(ProductionBinding binding) {
- // we only create factories for bindings that have a binding element
- return binding.bindingElement().get();
- }
-
- @Override
- Optional<TypeSpec.Builder> write(ClassName generatedTypeName, ProductionBinding binding) {
- // We don't want to write out resolved bindings -- we want to write out the generic version.
- checkArgument(!binding.unresolved().isPresent());
- checkArgument(binding.bindingElement().isPresent());
-
- TypeName providedTypeName = TypeName.get(binding.contributedType());
- TypeName futureTypeName = listenableFutureOf(providedTypeName);
-
- TypeSpec.Builder factoryBuilder =
- classBuilder(generatedTypeName)
- .addAnnotation(
- // TODO(beder): examine if we can remove this or prevent subtypes of Future from
- // being produced
- AnnotationSpec.builder(SuppressWarnings.class)
- .addMember("value", "$S", "FutureReturnValueIgnored")
- .build())
- .addModifiers(PUBLIC, FINAL)
- .addTypeVariables(bindingTypeElementTypeVariableNames(binding));
-
- UniqueNameSet uniqueFieldNames = new UniqueNameSet();
- ImmutableMap.Builder<Key, FieldSpec> fieldsBuilder = ImmutableMap.builder();
-
- MethodSpec.Builder constructorBuilder = constructorBuilder().addModifiers(PRIVATE);
-
- Optional<FieldSpec> moduleField =
- binding.requiresModuleInstance()
- ? Optional.of(
- addFieldAndConstructorParameter(
- factoryBuilder,
- constructorBuilder,
- uniqueFieldNames.getUniqueName("module"),
- TypeName.get(binding.bindingTypeElement().get().asType())))
- : Optional.empty();
-
- String[] executorParameterName = new String[1];
- String[] monitorParameterName = new String[1];
- Map<Key, FrameworkField> bindingFieldsForDependencies =
- generateBindingFieldsForDependencies(binding);
- bindingFieldsForDependencies.forEach(
- (key, bindingField) -> {
- String fieldName = uniqueFieldNames.getUniqueName(bindingField.name());
- if (key.equals(keyFactory.forProductionImplementationExecutor())) {
- executorParameterName[0] = fieldName;
- constructorBuilder.addParameter(bindingField.type(), executorParameterName[0]);
- } else if (key.equals(keyFactory.forProductionComponentMonitor())) {
- monitorParameterName[0] = fieldName;
- constructorBuilder.addParameter(bindingField.type(), monitorParameterName[0]);
- } else {
- FieldSpec field =
- addFieldAndConstructorParameter(
- factoryBuilder, constructorBuilder, fieldName, bindingField.type());
- fieldsBuilder.put(key, field);
- }
- });
- ImmutableMap<Key, FieldSpec> fields = fieldsBuilder.build();
-
- constructorBuilder.addStatement(
- "super($N, $L, $N)",
- verifyNotNull(monitorParameterName[0]),
- producerTokenConstruction(generatedTypeName, binding),
- verifyNotNull(executorParameterName[0]));
-
- if (binding.requiresModuleInstance()) {
- assignField(constructorBuilder, moduleField.get(), null);
- }
-
- fields.forEach(
- (key, field) -> {
- ParameterizedTypeName type = bindingFieldsForDependencies.get(key).type();
- assignField(constructorBuilder, field, type);
- });
-
- MethodSpec.Builder collectDependenciesBuilder =
- methodBuilder("collectDependencies")
- .addAnnotation(Override.class)
- .addModifiers(PROTECTED);
-
- ImmutableList<DependencyRequest> asyncDependencies = asyncDependencies(binding);
- for (DependencyRequest dependency : asyncDependencies) {
- TypeName futureType = listenableFutureOf(asyncDependencyType(dependency));
- CodeBlock futureAccess = CodeBlock.of("$N.get()", fields.get(dependency.key()));
- collectDependenciesBuilder.addStatement(
- "$T $L = $L",
- futureType,
- dependencyFutureName(dependency),
- dependency.kind().equals(RequestKind.PRODUCED)
- ? CodeBlock.of("$T.createFutureProduced($L)", PRODUCERS, futureAccess)
- : futureAccess);
- }
- FutureTransform futureTransform = FutureTransform.create(fields, binding, asyncDependencies);
-
- collectDependenciesBuilder
- .returns(listenableFutureOf(futureTransform.applyArgType()))
- .addStatement("return $L", futureTransform.futureCodeBlock());
-
- MethodSpec.Builder callProducesMethod =
- methodBuilder("callProducesMethod")
- .returns(futureTypeName)
- .addAnnotation(Override.class)
- .addModifiers(PUBLIC)
- .addParameter(futureTransform.applyArgType(), futureTransform.applyArgName())
- .addExceptions(getThrownTypeNames(binding.thrownTypes()))
- .addCode(
- getInvocationCodeBlock(
- binding, providedTypeName, futureTransform.parameterCodeBlocks()));
- if (futureTransform.hasUncheckedCast()) {
- callProducesMethod.addAnnotation(AnnotationSpecs.suppressWarnings(UNCHECKED));
- }
-
- MethodSpec constructor = constructorBuilder.build();
- factoryBuilder
- .superclass(
- ParameterizedTypeName.get(
- ClassName.get(AbstractProducesMethodProducer.class),
- futureTransform.applyArgType(),
- providedTypeName))
- .addMethod(constructor)
- .addMethod(staticFactoryMethod(binding, constructor))
- .addMethod(collectDependenciesBuilder.build())
- .addMethod(callProducesMethod.build());
-
- gwtIncompatibleAnnotation(binding).ifPresent(factoryBuilder::addAnnotation);
-
- // TODO(gak): write a sensible toString
- return Optional.of(factoryBuilder);
- }
-
- private MethodSpec staticFactoryMethod(ProductionBinding binding, MethodSpec constructor) {
- return MethodSpec.methodBuilder("create")
- .addModifiers(PUBLIC, STATIC)
- .returns(parameterizedGeneratedTypeNameForBinding(binding))
- .addTypeVariables(bindingTypeElementTypeVariableNames(binding))
- .addParameters(constructor.parameters)
- .addStatement(
- "return new $T($L)",
- parameterizedGeneratedTypeNameForBinding(binding),
- constructor.parameters.stream()
- .map(p -> CodeBlock.of("$N", p.name))
- .collect(toParametersCodeBlock()))
- .build();
- }
-
- // TODO(ronshapiro): consolidate versions of these
- private static FieldSpec addFieldAndConstructorParameter(
- TypeSpec.Builder typeBuilder,
- MethodSpec.Builder constructorBuilder,
- String variableName,
- TypeName variableType) {
- FieldSpec field = FieldSpec.builder(variableType, variableName, PRIVATE, FINAL).build();
- typeBuilder.addField(field);
- constructorBuilder.addParameter(field.type, field.name);
- return field;
- }
-
- private static void assignField(
- MethodSpec.Builder constructorBuilder, FieldSpec field, ParameterizedTypeName type) {
- if (type != null && type.rawType.equals(TypeNames.PRODUCER)) {
- constructorBuilder.addStatement(
- "this.$1N = $2T.nonCancellationPropagatingViewOf($1N)", field, Producers.class);
- } else {
- constructorBuilder.addStatement("this.$1N = $1N", field);
- }
- }
-
- /** Returns a list of dependencies that are generated asynchronously. */
- private static ImmutableList<DependencyRequest> asyncDependencies(Binding binding) {
- final ImmutableMap<DependencyRequest, FrameworkDependency> frameworkDependencies =
- binding.dependenciesToFrameworkDependenciesMap();
- return FluentIterable.from(binding.dependencies())
- .filter(
- dependency ->
- isAsyncDependency(dependency)
- && frameworkDependencies
- .get(dependency)
- .frameworkClass()
- .equals(Producer.class))
- .toList();
- }
-
- private CodeBlock producerTokenConstruction(
- ClassName generatedTypeName, ProductionBinding binding) {
- CodeBlock producerTokenArgs =
- compilerOptions.writeProducerNameInToken()
- ? CodeBlock.of(
- "$S",
- String.format(
- "%s#%s",
- ClassName.get(binding.bindingTypeElement().get()),
- binding.bindingElement().get().getSimpleName()))
- : CodeBlock.of("$T.class", generatedTypeName);
- return CodeBlock.of("$T.create($L)", PRODUCER_TOKEN, producerTokenArgs);
- }
-
- /** Returns a name of the variable representing this dependency's future. */
- private static String dependencyFutureName(DependencyRequest dependency) {
- return dependency.requestElement().get().getSimpleName() + "Future";
- }
-
- /** Represents the transformation of an input future by a producer method. */
- abstract static class FutureTransform {
- protected final ImmutableMap<Key, FieldSpec> fields;
- protected final ProductionBinding binding;
-
- FutureTransform(ImmutableMap<Key, FieldSpec> fields, ProductionBinding binding) {
- this.fields = fields;
- this.binding = binding;
- }
-
- /** The code block representing the future that should be transformed. */
- abstract CodeBlock futureCodeBlock();
-
- /** The type of the argument to the apply method. */
- abstract TypeName applyArgType();
-
- /** The name of the argument to the apply method */
- abstract String applyArgName();
-
- /** The code blocks to be passed to the produces method itself. */
- abstract ImmutableList<CodeBlock> parameterCodeBlocks();
-
- /** Whether the transform method has an unchecked cast. */
- boolean hasUncheckedCast() {
- return false;
- }
-
- CodeBlock frameworkTypeUsageStatement(DependencyRequest dependency) {
- return SourceFiles.frameworkTypeUsageStatement(
- CodeBlock.of("$N", fields.get(dependency.key())), dependency.kind());
- }
-
- static FutureTransform create(
- ImmutableMap<Key, FieldSpec> fields,
- ProductionBinding binding,
- ImmutableList<DependencyRequest> asyncDependencies) {
- if (asyncDependencies.isEmpty()) {
- return new NoArgFutureTransform(fields, binding);
- } else if (asyncDependencies.size() == 1) {
- return new SingleArgFutureTransform(
- fields, binding, Iterables.getOnlyElement(asyncDependencies));
- } else {
- return new MultiArgFutureTransform(fields, binding, asyncDependencies);
- }
- }
- }
-
- static final class NoArgFutureTransform extends FutureTransform {
- NoArgFutureTransform(ImmutableMap<Key, FieldSpec> fields, ProductionBinding binding) {
- super(fields, binding);
- }
-
- @Override
- CodeBlock futureCodeBlock() {
- return CodeBlock.of("$T.<$T>immediateFuture(null)", FUTURES, VOID_CLASS);
- }
-
- @Override
- TypeName applyArgType() {
- return VOID_CLASS;
- }
-
- @Override
- String applyArgName() {
- return "ignoredVoidArg";
- }
-
- @Override
- ImmutableList<CodeBlock> parameterCodeBlocks() {
- return binding.explicitDependencies().stream()
- .map(this::frameworkTypeUsageStatement)
- .collect(toImmutableList());
- }
- }
-
- static final class SingleArgFutureTransform extends FutureTransform {
- private final DependencyRequest asyncDependency;
-
- SingleArgFutureTransform(
- ImmutableMap<Key, FieldSpec> fields,
- ProductionBinding binding,
- DependencyRequest asyncDependency) {
- super(fields, binding);
- this.asyncDependency = asyncDependency;
- }
-
- @Override
- CodeBlock futureCodeBlock() {
- return CodeBlock.of("$L", dependencyFutureName(asyncDependency));
- }
-
- @Override
- TypeName applyArgType() {
- return asyncDependencyType(asyncDependency);
- }
-
- @Override
- String applyArgName() {
- String argName = asyncDependency.requestElement().get().getSimpleName().toString();
- if (argName.equals("module")) {
- return "moduleArg";
- }
- return argName;
- }
-
- @Override
- ImmutableList<CodeBlock> parameterCodeBlocks() {
- ImmutableList.Builder<CodeBlock> parameterCodeBlocks = ImmutableList.builder();
- for (DependencyRequest dependency : binding.explicitDependencies()) {
- // We really want to compare instances here, because asyncDependency is an element in the
- // set binding.dependencies().
- if (dependency == asyncDependency) {
- parameterCodeBlocks.add(CodeBlock.of("$L", applyArgName()));
- } else {
- parameterCodeBlocks.add(frameworkTypeUsageStatement(dependency));
- }
- }
- return parameterCodeBlocks.build();
- }
- }
-
- static final class MultiArgFutureTransform extends FutureTransform {
- private final ImmutableList<DependencyRequest> asyncDependencies;
-
- MultiArgFutureTransform(
- ImmutableMap<Key, FieldSpec> fields,
- ProductionBinding binding,
- ImmutableList<DependencyRequest> asyncDependencies) {
- super(fields, binding);
- this.asyncDependencies = asyncDependencies;
- }
-
- @Override
- CodeBlock futureCodeBlock() {
- return CodeBlock.of(
- "$T.<$T>allAsList($L)",
- FUTURES,
- OBJECT,
- asyncDependencies
- .stream()
- .map(ProducerFactoryGenerator::dependencyFutureName)
- .collect(joining(", ")));
- }
-
- @Override
- TypeName applyArgType() {
- return listOf(OBJECT);
- }
-
- @Override
- String applyArgName() {
- return "args";
- }
-
- @Override
- ImmutableList<CodeBlock> parameterCodeBlocks() {
- int argIndex = 0;
- ImmutableList.Builder<CodeBlock> codeBlocks = ImmutableList.builder();
- for (DependencyRequest dependency : binding.explicitDependencies()) {
- if (isAsyncDependency(dependency)) {
- codeBlocks.add(
- CodeBlock.of(
- "($T) $L.get($L)", asyncDependencyType(dependency), applyArgName(), argIndex));
- argIndex++;
- } else {
- codeBlocks.add(frameworkTypeUsageStatement(dependency));
- }
- }
- return codeBlocks.build();
- }
-
- @Override
- boolean hasUncheckedCast() {
- return true;
- }
- }
-
- private static boolean isAsyncDependency(DependencyRequest dependency) {
- switch (dependency.kind()) {
- case INSTANCE:
- case PRODUCED:
- return true;
- default:
- return false;
- }
- }
-
- private static TypeName asyncDependencyType(DependencyRequest dependency) {
- TypeName keyName = TypeName.get(dependency.key().type());
- switch (dependency.kind()) {
- case INSTANCE:
- return keyName;
- case PRODUCED:
- return producedOf(keyName);
- default:
- throw new AssertionError();
- }
- }
-
- /**
- * Creates a code block for the invocation of the producer method from the module, which should be
- * used entirely within a method body.
- *
- * @param binding The binding to generate the invocation code block for.
- * @param providedTypeName The type name that should be provided by this producer.
- * @param parameterCodeBlocks The code blocks for all the parameters to the producer method.
- */
- private CodeBlock getInvocationCodeBlock(
- ProductionBinding binding,
- TypeName providedTypeName,
- ImmutableList<CodeBlock> parameterCodeBlocks) {
- CodeBlock moduleCodeBlock =
- CodeBlock.of(
- "$L.$L($L)",
- binding.requiresModuleInstance()
- ? "module"
- : CodeBlock.of("$T", ClassName.get(binding.bindingTypeElement().get())),
- binding.bindingElement().get().getSimpleName(),
- makeParametersCodeBlock(parameterCodeBlocks));
-
- final CodeBlock returnCodeBlock;
- switch (binding.productionKind().get()) {
- case IMMEDIATE:
- returnCodeBlock =
- CodeBlock.of("$T.<$T>immediateFuture($L)", FUTURES, providedTypeName, moduleCodeBlock);
- break;
- case FUTURE:
- returnCodeBlock = moduleCodeBlock;
- break;
- case SET_OF_FUTURE:
- returnCodeBlock = CodeBlock.of("$T.allAsSet($L)", PRODUCERS, moduleCodeBlock);
- break;
- default:
- throw new AssertionError();
- }
- return CodeBlock.of("return $L;", returnCodeBlock);
- }
-
- /**
- * Converts the list of thrown types into type names.
- *
- * @param thrownTypes the list of thrown types.
- */
- private FluentIterable<? extends TypeName> getThrownTypeNames(
- Iterable<? extends TypeMirror> thrownTypes) {
- return FluentIterable.from(thrownTypes).transform(TypeName::get);
- }
-}
diff --git a/java/dagger/internal/codegen/ProducerFromProviderCreationExpression.java b/java/dagger/internal/codegen/ProducerFromProviderCreationExpression.java
deleted file mode 100644
index aca97562e..000000000
--- a/java/dagger/internal/codegen/ProducerFromProviderCreationExpression.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static dagger.internal.codegen.BindingRequest.bindingRequest;
-
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.codegen.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
-import dagger.internal.codegen.javapoet.TypeNames;
-import dagger.model.RequestKind;
-import dagger.producers.Producer;
-import java.util.Optional;
-
-/** An {@link Producer} creation expression for provision bindings. */
-final class ProducerFromProviderCreationExpression implements FrameworkInstanceCreationExpression {
- private final ContributionBinding binding;
- private final ComponentImplementation componentImplementation;
- private final ComponentBindingExpressions componentBindingExpressions;
-
- ProducerFromProviderCreationExpression(
- ContributionBinding binding,
- ComponentImplementation componentImplementation,
- ComponentBindingExpressions componentBindingExpressions) {
- this.binding = checkNotNull(binding);
- this.componentImplementation = checkNotNull(componentImplementation);
- this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
- }
-
- @Override
- public CodeBlock creationExpression() {
- return FrameworkType.PROVIDER.to(
- RequestKind.PRODUCER,
- componentBindingExpressions
- .getDependencyExpression(
- bindingRequest(binding.key(), FrameworkType.PROVIDER),
- componentImplementation.name())
- .codeBlock());
- }
-
- @Override
- public Optional<ClassName> alternativeFrameworkClass() {
- return Optional.of(TypeNames.PRODUCER);
- }
-
- // TODO(ronshapiro): should this have a simple factory if the delegate expression is simple?
-}
diff --git a/java/dagger/internal/codegen/ProducerNodeInstanceBindingExpression.java b/java/dagger/internal/codegen/ProducerNodeInstanceBindingExpression.java
deleted file mode 100644
index 18818d56b..000000000
--- a/java/dagger/internal/codegen/ProducerNodeInstanceBindingExpression.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import com.squareup.javapoet.ClassName;
-import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
-import dagger.internal.codegen.javapoet.Expression;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.Key;
-
-/** Binding expression for producer node instances. */
-final class ProducerNodeInstanceBindingExpression extends FrameworkInstanceBindingExpression {
- /** The component defining this binding. */
- private final ComponentImplementation componentImplementation;
- private final Key key;
- private final ProducerEntryPointView producerEntryPointView;
-
- ProducerNodeInstanceBindingExpression(
- ResolvedBindings resolvedBindings,
- FrameworkInstanceSupplier frameworkInstanceSupplier,
- DaggerTypes types,
- DaggerElements elements,
- ComponentImplementation componentImplementation) {
- super(resolvedBindings, frameworkInstanceSupplier, types, elements);
- this.componentImplementation = checkNotNull(componentImplementation);
- this.key = resolvedBindings.key();
- this.producerEntryPointView = new ProducerEntryPointView(types);
- }
-
- @Override
- protected FrameworkType frameworkType() {
- return FrameworkType.PRODUCER_NODE;
- }
-
- @Override
- Expression getDependencyExpression(ClassName requestingClass) {
- Expression result = super.getDependencyExpression(requestingClass);
- componentImplementation.addCancellableProducerKey(key);
- return result;
- }
-
- @Override
- Expression getDependencyExpressionForComponentMethod(
- ComponentMethodDescriptor componentMethod, ComponentImplementation component) {
- return producerEntryPointView
- .getProducerEntryPointField(this, componentMethod, component)
- .orElseGet(
- () -> super.getDependencyExpressionForComponentMethod(componentMethod, component));
- }
-}
diff --git a/java/dagger/internal/codegen/ProducesMethodValidator.java b/java/dagger/internal/codegen/ProducesMethodValidator.java
deleted file mode 100644
index bf4594856..000000000
--- a/java/dagger/internal/codegen/ProducesMethodValidator.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.BindingElementValidator.AllowsMultibindings.ALLOWS_MULTIBINDINGS;
-import static dagger.internal.codegen.BindingElementValidator.AllowsScoping.NO_SCOPING;
-import static dagger.internal.codegen.BindingMethodValidator.Abstractness.MUST_BE_CONCRETE;
-import static dagger.internal.codegen.BindingMethodValidator.ExceptionSuperclass.EXCEPTION;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.multibindings.ElementsIntoSet;
-import dagger.producers.ProducerModule;
-import dagger.producers.Produces;
-import java.util.Optional;
-import java.util.Set;
-import javax.inject.Inject;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-
-/** A validator for {@link Produces} methods. */
-final class ProducesMethodValidator extends BindingMethodValidator {
-
- @Inject
- ProducesMethodValidator(
- DaggerElements elements,
- DaggerTypes types,
- DependencyRequestValidator dependencyRequestValidator) {
- super(
- elements,
- types,
- dependencyRequestValidator,
- Produces.class,
- ProducerModule.class,
- MUST_BE_CONCRETE,
- EXCEPTION,
- ALLOWS_MULTIBINDINGS,
- NO_SCOPING);
- }
-
- @Override
- protected String elementsIntoSetNotASetMessage() {
- return "@Produces methods of type set values must return a Set or ListenableFuture of Set";
- }
-
- @Override
- protected String badTypeMessage() {
- return "@Produces methods can return only a primitive, an array, a type variable, "
- + "a declared type, or a ListenableFuture of one of those types";
- }
-
- @Override
- protected ElementValidator elementValidator(ExecutableElement element) {
- return new Validator(element);
- }
-
- private class Validator extends MethodValidator {
- Validator(ExecutableElement element) {
- super(element);
- }
-
- @Override
- protected void checkAdditionalMethodProperties() {
- checkNullable();
- }
-
- /** Adds a warning if a {@link Produces @Produces} method is declared nullable. */
- // TODO(beder): Properly handle nullable with producer methods.
- private void checkNullable() {
- if (ConfigurationAnnotations.getNullableType(element).isPresent()) {
- report.addWarning("@Nullable on @Produces methods does not do anything");
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * <p>Allows {@code keyType} to be a {@link ListenableFuture} of an otherwise-valid key type.
- */
- @Override
- protected void checkKeyType(TypeMirror keyType) {
- Optional<TypeMirror> typeToCheck = unwrapListenableFuture(keyType);
- if (typeToCheck.isPresent()) {
- super.checkKeyType(typeToCheck.get());
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * <p>Allows an {@link ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES} method to return
- * a {@link ListenableFuture} of a {@link Set} as well.
- */
- @Override
- protected void checkSetValuesType() {
- Optional<TypeMirror> typeToCheck = unwrapListenableFuture(element.getReturnType());
- if (typeToCheck.isPresent()) {
- checkSetValuesType(typeToCheck.get());
- }
- }
-
- private Optional<TypeMirror> unwrapListenableFuture(TypeMirror type) {
- if (MoreTypes.isType(type) && MoreTypes.isTypeOf(ListenableFuture.class, type)) {
- DeclaredType declaredType = MoreTypes.asDeclared(type);
- if (declaredType.getTypeArguments().isEmpty()) {
- report.addError("@Produces methods cannot return a raw ListenableFuture");
- return Optional.empty();
- } else {
- return Optional.of((TypeMirror) getOnlyElement(declaredType.getTypeArguments()));
- }
- }
- return Optional.of(type);
- }
- }
-}
diff --git a/java/dagger/internal/codegen/ProductionBinding.java b/java/dagger/internal/codegen/ProductionBinding.java
deleted file mode 100644
index a22f21c14..000000000
--- a/java/dagger/internal/codegen/ProductionBinding.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.langmodel.DaggerTypes.isFutureType;
-
-import com.google.auto.value.AutoValue;
-import com.google.auto.value.extension.memoized.Memoized;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.errorprone.annotations.CanIgnoreReturnValue;
-import dagger.model.DependencyRequest;
-import dagger.model.Key;
-import java.util.Optional;
-import java.util.stream.Stream;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * A value object representing the mechanism by which a {@link Key} can be produced.
- */
-@AutoValue
-abstract class ProductionBinding extends ContributionBinding {
-
- @Override
- public BindingType bindingType() {
- return BindingType.PRODUCTION;
- }
-
- @Override
- abstract Optional<ProductionBinding> unresolved();
-
- @Override
- ImmutableSet<DependencyRequest> implicitDependencies() {
- return Stream.of(executorRequest(), monitorRequest())
- .filter(Optional::isPresent)
- .map(Optional::get)
- .collect(toImmutableSet());
- }
-
- /** What kind of object a {@code @Produces}-annotated method returns. */
- enum ProductionKind {
- /** A value. */
- IMMEDIATE,
- /** A {@code ListenableFuture<T>}. */
- FUTURE,
- /** A {@code Set<ListenableFuture<T>>}. */
- SET_OF_FUTURE;
-
- /** Returns the kind of object a {@code @Produces}-annotated method returns. */
- static ProductionKind fromProducesMethod(ExecutableElement producesMethod) {
- if (isFutureType(producesMethod.getReturnType())) {
- return FUTURE;
- } else if (ContributionType.fromBindingElement(producesMethod)
- .equals(ContributionType.SET_VALUES)
- && isFutureType(SetType.from(producesMethod.getReturnType()).elementType())) {
- return SET_OF_FUTURE;
- } else {
- return IMMEDIATE;
- }
- }
- }
-
- /**
- * Returns the kind of object the produces method returns. All production bindings from
- * {@code @Produces} methods will have a production kind, but synthetic production bindings may
- * not.
- */
- abstract Optional<ProductionKind> productionKind();
-
- /** Returns the list of types in the throws clause of the method. */
- abstract ImmutableList<? extends TypeMirror> thrownTypes();
-
- /**
- * If this production requires an executor, this will be the corresponding request. All
- * production bindings from {@code @Produces} methods will have an executor request, but
- * synthetic production bindings may not.
- */
- abstract Optional<DependencyRequest> executorRequest();
-
- /** If this production requires a monitor, this will be the corresponding request. All
- * production bindings from {@code @Produces} methods will have a monitor request, but synthetic
- * production bindings may not.
- */
- abstract Optional<DependencyRequest> monitorRequest();
-
- // Profiling determined that this method is called enough times that memoizing it had a measurable
- // performance improvement for large components.
- @Memoized
- @Override
- boolean requiresModuleInstance() {
- return super.requiresModuleInstance();
- }
-
- static Builder builder() {
- return new AutoValue_ProductionBinding.Builder()
- .explicitDependencies(ImmutableList.<DependencyRequest>of())
- .thrownTypes(ImmutableList.<TypeMirror>of());
- }
-
- @Memoized
- @Override
- public abstract int hashCode();
-
- // TODO(ronshapiro,dpb): simplify the equality semantics
- @Override
- public abstract boolean equals(Object obj);
-
- @AutoValue.Builder
- @CanIgnoreReturnValue
- abstract static class Builder extends ContributionBinding.Builder<ProductionBinding, Builder> {
-
- @Override
- Builder dependencies(Iterable<DependencyRequest> dependencies) {
- return explicitDependencies(dependencies);
- }
-
- abstract Builder explicitDependencies(Iterable<DependencyRequest> dependencies);
-
- abstract Builder productionKind(ProductionKind productionKind);
-
- @Override
- abstract Builder unresolved(ProductionBinding unresolved);
-
- abstract Builder thrownTypes(Iterable<? extends TypeMirror> thrownTypes);
-
- abstract Builder executorRequest(DependencyRequest executorRequest);
-
- abstract Builder monitorRequest(DependencyRequest monitorRequest);
- }
-}
diff --git a/java/dagger/internal/codegen/ProviderInstanceBindingExpression.java b/java/dagger/internal/codegen/ProviderInstanceBindingExpression.java
deleted file mode 100644
index 60166de64..000000000
--- a/java/dagger/internal/codegen/ProviderInstanceBindingExpression.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-
-/** Binding expression for provider instances. */
-final class ProviderInstanceBindingExpression extends FrameworkInstanceBindingExpression {
-
- ProviderInstanceBindingExpression(
- ResolvedBindings resolvedBindings,
- FrameworkInstanceSupplier frameworkInstanceSupplier,
- DaggerTypes types,
- DaggerElements elements) {
- super(
- resolvedBindings,
- frameworkInstanceSupplier,
- types,
- elements);
- }
-
- @Override
- protected FrameworkType frameworkType() {
- return FrameworkType.PROVIDER;
- }
-}
diff --git a/java/dagger/internal/codegen/ProvidesMethodValidator.java b/java/dagger/internal/codegen/ProvidesMethodValidator.java
deleted file mode 100644
index 01e71ae09..000000000
--- a/java/dagger/internal/codegen/ProvidesMethodValidator.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.BindingElementValidator.AllowsMultibindings.ALLOWS_MULTIBINDINGS;
-import static dagger.internal.codegen.BindingElementValidator.AllowsScoping.ALLOWS_SCOPING;
-import static dagger.internal.codegen.BindingMethodValidator.Abstractness.MUST_BE_CONCRETE;
-import static dagger.internal.codegen.BindingMethodValidator.ExceptionSuperclass.RUNTIME_EXCEPTION;
-
-import com.google.common.collect.ImmutableSet;
-import dagger.Module;
-import dagger.Provides;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.producers.ProducerModule;
-import javax.inject.Inject;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.VariableElement;
-
-/** A validator for {@link Provides} methods. */
-final class ProvidesMethodValidator extends BindingMethodValidator {
-
- private final DependencyRequestValidator dependencyRequestValidator;
-
- @Inject
- ProvidesMethodValidator(
- DaggerElements elements,
- DaggerTypes types,
- DependencyRequestValidator dependencyRequestValidator) {
- super(
- elements,
- types,
- Provides.class,
- ImmutableSet.of(Module.class, ProducerModule.class),
- dependencyRequestValidator,
- MUST_BE_CONCRETE,
- RUNTIME_EXCEPTION,
- ALLOWS_MULTIBINDINGS,
- ALLOWS_SCOPING);
- this.dependencyRequestValidator = dependencyRequestValidator;
- }
-
- @Override
- protected ElementValidator elementValidator(ExecutableElement element) {
- return new Validator(element);
- }
-
- private class Validator extends MethodValidator {
- Validator(ExecutableElement element) {
- super(element);
- }
-
- @Override
- protected void checkAdditionalMethodProperties() {
- }
-
- /** Adds an error if a {@link Provides @Provides} method depends on a producer type. */
- @Override
- protected void checkParameter(VariableElement parameter) {
- super.checkParameter(parameter);
- dependencyRequestValidator.checkNotProducer(report, parameter);
- }
- }
-}
diff --git a/java/dagger/internal/codegen/ProvisionBinding.java b/java/dagger/internal/codegen/ProvisionBinding.java
deleted file mode 100644
index 306cb1319..000000000
--- a/java/dagger/internal/codegen/ProvisionBinding.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.model.BindingKind.COMPONENT_PROVISION;
-import static dagger.model.BindingKind.PROVISION;
-
-import com.google.auto.value.AutoValue;
-import com.google.auto.value.extension.memoized.Memoized;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSortedSet;
-import com.google.errorprone.annotations.CanIgnoreReturnValue;
-import dagger.internal.codegen.MembersInjectionBinding.InjectionSite;
-import dagger.model.BindingKind;
-import dagger.model.DependencyRequest;
-import dagger.model.Key;
-import dagger.model.Scope;
-import java.util.Optional;
-
-/**
- * A value object representing the mechanism by which a {@link Key} can be provided.
- */
-@AutoValue
-abstract class ProvisionBinding extends ContributionBinding {
-
- @Override
- @Memoized
- ImmutableSet<DependencyRequest> explicitDependencies() {
- return ImmutableSet.<DependencyRequest>builder()
- .addAll(provisionDependencies())
- .addAll(membersInjectionDependencies())
- .build();
- }
-
- /**
- * Dependencies necessary to invoke an {@code @Inject} constructor or {@code @Provides} method.
- */
- abstract ImmutableSet<DependencyRequest> provisionDependencies();
-
- @Memoized
- ImmutableSet<DependencyRequest> membersInjectionDependencies() {
- return injectionSites()
- .stream()
- .flatMap(i -> i.dependencies().stream())
- .collect(toImmutableSet());
- }
-
- /**
- * {@link InjectionSite}s for all {@code @Inject} members if {@link #kind()} is {@link
- * BindingKind#INJECTION}, otherwise empty.
- */
- abstract ImmutableSortedSet<InjectionSite> injectionSites();
-
- @Override
- public BindingType bindingType() {
- return BindingType.PROVISION;
- }
-
- @Override
- abstract Optional<ProvisionBinding> unresolved();
-
- // TODO(ronshapiro): we should be able to remove this, but AutoValue barks on the Builder's scope
- // method, saying that the method doesn't correspond to a property of ProvisionBinding
- @Override
- public abstract Optional<Scope> scope();
-
- static Builder builder() {
- return new AutoValue_ProvisionBinding.Builder()
- .provisionDependencies(ImmutableSet.of())
- .injectionSites(ImmutableSortedSet.of());
- }
-
- abstract Builder toBuilder();
-
- private static final ImmutableSet<BindingKind> KINDS_TO_CHECK_FOR_NULL =
- ImmutableSet.of(PROVISION, COMPONENT_PROVISION);
-
- boolean shouldCheckForNull(CompilerOptions compilerOptions) {
- return KINDS_TO_CHECK_FOR_NULL.contains(kind())
- && !contributedPrimitiveType().isPresent()
- && !nullableType().isPresent()
- && compilerOptions.doCheckForNulls();
- }
-
- // Profiling determined that this method is called enough times that memoizing it had a measurable
- // performance improvement for large components.
- @Memoized
- @Override
- boolean requiresModuleInstance() {
- return super.requiresModuleInstance();
- }
-
- @Memoized
- @Override
- public abstract int hashCode();
-
- // TODO(ronshapiro,dpb): simplify the equality semantics
- @Override
- public abstract boolean equals(Object obj);
-
- @AutoValue.Builder
- @CanIgnoreReturnValue
- abstract static class Builder extends ContributionBinding.Builder<ProvisionBinding, Builder> {
-
- @Override
- Builder dependencies(Iterable<DependencyRequest> dependencies) {
- return provisionDependencies(dependencies);
- }
-
- abstract Builder provisionDependencies(Iterable<DependencyRequest> provisionDependencies);
-
- abstract Builder injectionSites(ImmutableSortedSet<InjectionSite> injectionSites);
-
- @Override
- abstract Builder unresolved(ProvisionBinding unresolved);
-
- abstract Builder scope(Optional<Scope> scope);
- }
-
-}
diff --git a/java/dagger/internal/codegen/ProvisionDependencyOnProducerBindingValidator.java b/java/dagger/internal/codegen/ProvisionDependencyOnProducerBindingValidator.java
deleted file mode 100644
index 2fb1a0e2a..000000000
--- a/java/dagger/internal/codegen/ProvisionDependencyOnProducerBindingValidator.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Verify.verify;
-import static dagger.internal.codegen.DaggerStreams.instancesOf;
-import static dagger.internal.codegen.RequestKinds.canBeSatisfiedByProductionBinding;
-import static javax.tools.Diagnostic.Kind.ERROR;
-
-import dagger.model.BindingGraph;
-import dagger.model.BindingGraph.DependencyEdge;
-import dagger.model.BindingGraph.Node;
-import dagger.spi.BindingGraphPlugin;
-import dagger.spi.DiagnosticReporter;
-import java.util.stream.Stream;
-import javax.inject.Inject;
-
-/**
- * Reports an error for each provision-only dependency request that is satisfied by a production
- * binding.
- */
-// TODO(b/29509141): Clarify the error.
-final class ProvisionDependencyOnProducerBindingValidator implements BindingGraphPlugin {
-
- @Inject
- ProvisionDependencyOnProducerBindingValidator() {}
-
- @Override
- public String pluginName() {
- return "Dagger/ProviderDependsOnProducer";
- }
-
- @Override
- public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
- provisionDependenciesOnProductionBindings(bindingGraph)
- .forEach(
- provisionDependent ->
- diagnosticReporter.reportDependency(
- ERROR,
- provisionDependent,
- provisionDependent.isEntryPoint()
- ? entryPointErrorMessage(provisionDependent)
- : dependencyErrorMessage(provisionDependent, bindingGraph)));
- }
-
- private Stream<DependencyEdge> provisionDependenciesOnProductionBindings(
- BindingGraph bindingGraph) {
- return bindingGraph.bindings().stream()
- .filter(binding -> binding.isProduction())
- .flatMap(binding -> incomingDependencies(binding, bindingGraph))
- .filter(edge -> !dependencyCanUseProduction(edge, bindingGraph));
- }
-
- /** Returns the dependencies on {@code binding}. */
- // TODO(dpb): Move to BindingGraph.
- private Stream<DependencyEdge> incomingDependencies(
- dagger.model.Binding binding, BindingGraph bindingGraph) {
- return bindingGraph.network().inEdges(binding).stream()
- .flatMap(instancesOf(DependencyEdge.class));
- }
-
- // TODO(ronshapiro): merge with MissingBindingValidator.dependencyCanUseProduction
- private boolean dependencyCanUseProduction(DependencyEdge edge, BindingGraph bindingGraph) {
- return edge.isEntryPoint()
- ? canBeSatisfiedByProductionBinding(edge.dependencyRequest().kind())
- : bindingRequestingDependency(edge, bindingGraph).isProduction();
- }
-
- /**
- * Returns the binding that requests a dependency.
- *
- * @throws IllegalArgumentException if {@code dependency} is an {@linkplain
- * DependencyEdge#isEntryPoint() entry point}.
- */
- // TODO(dpb): Move to BindingGraph.
- private dagger.model.Binding bindingRequestingDependency(
- DependencyEdge dependency, BindingGraph bindingGraph) {
- checkArgument(!dependency.isEntryPoint());
- Node source = bindingGraph.network().incidentNodes(dependency).source();
- verify(
- source instanceof dagger.model.Binding,
- "expected source of %s to be a binding, but was: %s",
- dependency,
- source);
- return (dagger.model.Binding) source;
- }
-
- private String entryPointErrorMessage(DependencyEdge entryPoint) {
- return String.format(
- "%s is a provision entry-point, which cannot depend on a production.",
- entryPoint.dependencyRequest().key());
- }
-
- private String dependencyErrorMessage(
- DependencyEdge dependencyOnProduction, BindingGraph bindingGraph) {
- return String.format(
- "%s is a provision, which cannot depend on a production.",
- bindingRequestingDependency(dependencyOnProduction, bindingGraph).key());
- }
-}
diff --git a/java/dagger/internal/codegen/PrunedConcreteMethodBindingExpression.java b/java/dagger/internal/codegen/PrunedConcreteMethodBindingExpression.java
deleted file mode 100644
index 6eb92ac53..000000000
--- a/java/dagger/internal/codegen/PrunedConcreteMethodBindingExpression.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.MissingBindingFactory;
-import dagger.internal.codegen.ModifiableBindingMethods.ModifiableBindingMethod;
-import dagger.internal.codegen.javapoet.Expression;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.producers.internal.MissingBindingProducer;
-import java.util.Optional;
-
-/**
- * A {@link BindingExpression} that implements a method that encapsulates a binding that is not part
- * of the binding graph when generating a final concrete implementation of a subcomponent. The
- * implementation throws an exception. It is assumed that a binding may remain missing in a valid
- * binding graph, because it's possible for there to be dependencies that are passively pruned when
- * a non-leaf binding is re-defined (such as when {@code @Provides} bindings override
- * {@code @Inject} bindings).
- *
- * <p>This method should never be invoked. If it is the exception indicates an issue within Dagger
- * itself.
- */
-final class PrunedConcreteMethodBindingExpression extends BindingExpression {
- private static final CodeBlock METHOD_IMPLEMENTATION =
- CodeBlock.of(
- "throw new $T($S);",
- UnsupportedOperationException.class,
- "This binding is not part of the final binding graph. The key was requested by a binding "
- + "that was believed to possibly be part of the graph, but is no longer requested. "
- + "If this exception is thrown, it is the result of a Dagger bug.");
-
- PrunedConcreteMethodBindingExpression() {}
-
- @Override
- CodeBlock getModifiableBindingMethodImplementation(
- ModifiableBindingMethod modifiableBindingMethod,
- ComponentImplementation component,
- DaggerTypes types) {
- Optional<FrameworkType> frameworkType = modifiableBindingMethod.request().frameworkType();
- if (frameworkType.isPresent()) {
- // If we make initializations replaceable, we can do away with these classes and this logic
- // since the pruned framework instances will no longer be initialized
- switch (frameworkType.get()) {
- case PROVIDER:
- return missingFrameworkInstance(MissingBindingFactory.class);
- case PRODUCER_NODE:
- return missingFrameworkInstance(MissingBindingProducer.class);
- }
- throw new AssertionError(frameworkType);
- }
- return METHOD_IMPLEMENTATION;
- }
-
- private static CodeBlock missingFrameworkInstance(Class<?> factoryClass) {
- return CodeBlock.builder().addStatement("return $T.create()", factoryClass).build();
- }
-
- @Override
- final Expression getDependencyExpression(ClassName requestingClass) {
- throw new UnsupportedOperationException(
- "Requesting a dependency expression for a pruned binding.");
- }
-}
diff --git a/java/dagger/internal/codegen/RequestKinds.java b/java/dagger/internal/codegen/RequestKinds.java
deleted file mode 100644
index aa17f5ead..000000000
--- a/java/dagger/internal/codegen/RequestKinds.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreTypes.asDeclared;
-import static com.google.auto.common.MoreTypes.isType;
-import static com.google.auto.common.MoreTypes.isTypeOf;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.javapoet.TypeNames.lazyOf;
-import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf;
-import static dagger.internal.codegen.javapoet.TypeNames.producedOf;
-import static dagger.internal.codegen.javapoet.TypeNames.producerOf;
-import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
-import static dagger.internal.codegen.langmodel.DaggerTypes.checkTypePresent;
-import static dagger.model.RequestKind.INSTANCE;
-import static dagger.model.RequestKind.LAZY;
-import static dagger.model.RequestKind.PRODUCED;
-import static dagger.model.RequestKind.PRODUCER;
-import static dagger.model.RequestKind.PROVIDER;
-import static dagger.model.RequestKind.PROVIDER_OF_LAZY;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.squareup.javapoet.TypeName;
-import dagger.Lazy;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.RequestKind;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
-import javax.inject.Provider;
-import javax.lang.model.type.TypeMirror;
-
-/** Utility methods for {@link RequestKind}s. */
-final class RequestKinds {
- /** Returns the type of a request of this kind for a key with a given type. */
- static TypeMirror requestType(RequestKind requestKind, TypeMirror type, DaggerTypes types) {
- switch (requestKind) {
- case INSTANCE:
- return type;
-
- case PROVIDER_OF_LAZY:
- return types.wrapType(requestType(LAZY, type, types), Provider.class);
-
- case FUTURE:
- return types.wrapType(type, ListenableFuture.class);
-
- default:
- return types.wrapType(type, frameworkClass(requestKind));
- }
- }
-
- /** Returns the type of a request of this kind for a key with a given type. */
- static TypeName requestTypeName(RequestKind requestKind, TypeName keyType) {
- switch (requestKind) {
- case INSTANCE:
- return keyType;
-
- case PROVIDER:
- return providerOf(keyType);
-
- case LAZY:
- return lazyOf(keyType);
-
- case PROVIDER_OF_LAZY:
- return providerOf(lazyOf(keyType));
-
- case PRODUCER:
- return producerOf(keyType);
-
- case PRODUCED:
- return producedOf(keyType);
-
- case FUTURE:
- return listenableFutureOf(keyType);
-
- default:
- throw new AssertionError(requestKind);
- }
- }
-
- private static final ImmutableMap<RequestKind, Class<?>> FRAMEWORK_CLASSES =
- ImmutableMap.of(
- PROVIDER, Provider.class,
- LAZY, Lazy.class,
- PRODUCER, Producer.class,
- PRODUCED, Produced.class);
-
- /** Returns the {@link RequestKind} that matches the wrapping types (if any) of {@code type}. */
- static RequestKind getRequestKind(TypeMirror type) {
- checkTypePresent(type);
- for (RequestKind kind : FRAMEWORK_CLASSES.keySet()) {
- if (matchesKind(kind, type)) {
- if (kind.equals(PROVIDER) && matchesKind(LAZY, extractKeyType(kind, type))) {
- return PROVIDER_OF_LAZY;
- }
- return kind;
- }
- }
- return INSTANCE;
- }
-
- /**
- * Returns {@code true} if {@code type} is a parameterized type of {@code kind}'s {@link
- * #frameworkClass(RequestKind) framework class}.
- */
- private static boolean matchesKind(RequestKind kind, TypeMirror type) {
- return isType(type)
- && isTypeOf(frameworkClass(kind), type)
- && !asDeclared(type).getTypeArguments().isEmpty();
- }
-
- /**
- * Unwraps the framework class(es) of {@code requestKind} from {@code type}. If {@code
- * requestKind} is {@link RequestKind#INSTANCE}, this acts as an identity function.
- *
- * @throws TypeNotPresentException if {@code type} is an {@link javax.lang.model.type.ErrorType},
- * which may mean that the type will be generated in a later round of processing
- * @throws IllegalArgumentException if {@code type} is not wrapped with {@code requestKind}'s
- * framework class(es).
- */
- static TypeMirror extractKeyType(RequestKind requestKind, TypeMirror type) {
- checkTypePresent(type);
- switch (requestKind) {
- case INSTANCE:
- return type;
- case PROVIDER_OF_LAZY:
- return extractKeyType(LAZY, extractKeyType(PROVIDER, type));
- default:
- checkArgument(isType(type) && isTypeOf(frameworkClass(requestKind), type));
- return getOnlyElement(MoreTypes.asDeclared(type).getTypeArguments());
- }
- }
-
- /**
- * A dagger- or {@code javax.inject}-defined class for {@code requestKind} that that can wrap
- * another type but share the same {@link dagger.model.Key}.
- *
- * <p>For example, {@code Provider<String>} and {@code Lazy<String>} can both be requested if a
- * key exists for {@code String}; they all share the same key.
- *
- * <p>This concept is not well defined and should probably be removed and inlined into the cases
- * that need it. For example, {@link RequestKind#PROVIDER_OF_LAZY} has <em>2</em> wrapping
- * classes, and {@link RequestKind#FUTURE} is wrapped with a {@link ListenableFuture}, but for
- * historical/implementation reasons has not had an associated framework class.
- */
- static Class<?> frameworkClass(RequestKind requestKind) {
- Class<?> result = FRAMEWORK_CLASSES.get(requestKind);
- checkArgument(result != null, "no framework class for %s", requestKind);
- return result;
- }
-
- /**
- * Returns {@code true} if requests for {@code requestKind} can be satisfied by a production
- * binding.
- */
- static boolean canBeSatisfiedByProductionBinding(RequestKind requestKind) {
- switch (requestKind) {
- case INSTANCE:
- case PROVIDER:
- case LAZY:
- case PROVIDER_OF_LAZY:
- case MEMBERS_INJECTION:
- return false;
- case PRODUCER:
- case PRODUCED:
- case FUTURE:
- return true;
- }
- throw new AssertionError();
- }
-
- /**
- * Returns true if {@code requestKind} is always derived from a {@link RequestKind#PROVIDER}
- * instance.
- */
- static boolean isDerivedFromProvider(RequestKind requestKind) {
- switch (requestKind) {
- case LAZY:
- case PROVIDER_OF_LAZY:
- return true;
- default:
- return false;
- }
- }
-
- private RequestKinds() {}
-}
diff --git a/java/dagger/internal/codegen/ResolvedBindings.java b/java/dagger/internal/codegen/ResolvedBindings.java
deleted file mode 100644
index 814995a42..000000000
--- a/java/dagger/internal/codegen/ResolvedBindings.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-
-import com.google.auto.value.AutoValue;
-import com.google.auto.value.extension.memoized.Memoized;
-import com.google.common.collect.ImmutableCollection;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Multimap;
-import dagger.internal.codegen.ContributionType.HasContributionType;
-import dagger.model.Key;
-import dagger.model.Scope;
-import java.util.Optional;
-import javax.lang.model.element.TypeElement;
-
-/**
- * The collection of bindings that have been resolved for a key. For valid graphs, contains exactly
- * one binding.
- *
- * <p>Separate {@link ResolvedBindings} instances should be used if a {@link
- * MembersInjectionBinding} and a {@link ProvisionBinding} for the same key exist in the same
- * component. (This will only happen if a type has an {@code @Inject} constructor and members, the
- * component has a members injection method, and the type is also requested normally.)
- */
-@AutoValue
-abstract class ResolvedBindings implements HasContributionType {
- /**
- * The binding key for which the {@link #bindings()} have been resolved.
- */
- abstract Key key();
-
- /**
- * The {@link ContributionBinding}s for {@link #key()} indexed by the component that owns the
- * binding. Each key in the multimap is a part of the same component ancestry.
- */
- abstract ImmutableSetMultimap<TypeElement, ContributionBinding> allContributionBindings();
-
- /**
- * The {@link MembersInjectionBinding}s for {@link #key()} indexed by the component that owns the
- * binding. Each key in the map is a part of the same component ancestry.
- */
- abstract ImmutableMap<TypeElement, MembersInjectionBinding> allMembersInjectionBindings();
-
- /** The multibinding declarations for {@link #key()}. */
- abstract ImmutableSet<MultibindingDeclaration> multibindingDeclarations();
-
- /** The subcomponent declarations for {@link #key()}. */
- abstract ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations();
-
- /**
- * The optional binding declarations for {@link #key()}.
- */
- abstract ImmutableSet<OptionalBindingDeclaration> optionalBindingDeclarations();
-
- // Computing the hash code is an expensive operation.
- @Memoized
- @Override
- public abstract int hashCode();
-
- // Suppresses ErrorProne warning that hashCode was overridden w/o equals
- @Override
- public abstract boolean equals(Object other);
-
- /** All bindings for {@link #key()}, indexed by the component that owns the binding. */
- final ImmutableSetMultimap<TypeElement, ? extends Binding> allBindings() {
- return !allMembersInjectionBindings().isEmpty()
- ? allMembersInjectionBindings().asMultimap()
- : allContributionBindings();
- }
-
- /** All bindings for {@link #key()}, regardless of which component owns them. */
- final ImmutableCollection<? extends Binding> bindings() {
- return allBindings().values();
- }
-
- /**
- * Returns the single binding.
- *
- * @throws IllegalStateException if there is not exactly one element in {@link #bindings()}, which
- * will never happen for contributions in valid graphs
- */
- final Binding binding() {
- return getOnlyElement(bindings());
- }
-
- /**
- * {@code true} if there are no {@link #bindings()}, {@link #multibindingDeclarations()}, {@link
- * #optionalBindingDeclarations()}, or {@link #subcomponentDeclarations()}.
- */
- final boolean isEmpty() {
- return allMembersInjectionBindings().isEmpty()
- && allContributionBindings().isEmpty()
- && multibindingDeclarations().isEmpty()
- && optionalBindingDeclarations().isEmpty()
- && subcomponentDeclarations().isEmpty();
- }
-
- /** All bindings for {@link #key()} that are owned by a component. */
- ImmutableSet<? extends Binding> bindingsOwnedBy(ComponentDescriptor component) {
- return allBindings().get(component.typeElement());
- }
-
- /**
- * All contribution bindings, regardless of owning component. Empty if this is a members-injection
- * binding.
- */
- @Memoized
- ImmutableSet<ContributionBinding> contributionBindings() {
- // TODO(ronshapiro): consider optimizing ImmutableSet.copyOf(Collection) for small immutable
- // collections so that it doesn't need to call toArray(). Even though this method is memoized,
- // toArray() can take ~150ms for large components, and there are surely other places in the
- // processor that can benefit from this.
- return ImmutableSet.copyOf(allContributionBindings().values());
- }
-
- /** The component that owns {@code binding}. */
- final TypeElement owningComponent(ContributionBinding binding) {
- checkArgument(
- contributionBindings().contains(binding),
- "binding is not resolved for %s: %s",
- key(),
- binding);
- return getOnlyElement(allContributionBindings().inverse().get(binding));
- }
-
- /**
- * The members-injection binding, regardless of owning component. Absent if these are contribution
- * bindings, or if there is no members-injection binding because the type fails validation.
- */
- final Optional<MembersInjectionBinding> membersInjectionBinding() {
- return allMembersInjectionBindings().isEmpty()
- ? Optional.empty()
- : Optional.of(Iterables.getOnlyElement(allMembersInjectionBindings().values()));
- }
-
- /** Creates a {@link ResolvedBindings} for contribution bindings. */
- static ResolvedBindings forContributionBindings(
- Key key,
- Multimap<TypeElement, ContributionBinding> contributionBindings,
- Iterable<MultibindingDeclaration> multibindings,
- Iterable<SubcomponentDeclaration> subcomponentDeclarations,
- Iterable<OptionalBindingDeclaration> optionalBindingDeclarations) {
- return new AutoValue_ResolvedBindings(
- key,
- ImmutableSetMultimap.copyOf(contributionBindings),
- ImmutableMap.of(),
- ImmutableSet.copyOf(multibindings),
- ImmutableSet.copyOf(subcomponentDeclarations),
- ImmutableSet.copyOf(optionalBindingDeclarations));
- }
-
- /**
- * Creates a {@link ResolvedBindings} for members injection bindings.
- */
- static ResolvedBindings forMembersInjectionBinding(
- Key key,
- ComponentDescriptor owningComponent,
- MembersInjectionBinding ownedMembersInjectionBinding) {
- return new AutoValue_ResolvedBindings(
- key,
- ImmutableSetMultimap.of(),
- ImmutableMap.of(owningComponent.typeElement(), ownedMembersInjectionBinding),
- ImmutableSet.of(),
- ImmutableSet.of(),
- ImmutableSet.of());
- }
-
- /**
- * Creates a {@link ResolvedBindings} appropriate for when there are no bindings for the key.
- */
- static ResolvedBindings noBindings(Key key) {
- return new AutoValue_ResolvedBindings(
- key,
- ImmutableSetMultimap.of(),
- ImmutableMap.of(),
- ImmutableSet.of(),
- ImmutableSet.of(),
- ImmutableSet.of());
- }
-
- /**
- * {@code true} if this is a multibinding contribution.
- */
- boolean isMultibindingContribution() {
- return contributionBindings().size() == 1
- && contributionBinding().contributionType().isMultibinding();
- }
-
- /**
- * Returns the single contribution binding.
- *
- * @throws IllegalStateException if there is not exactly one element in
- * {@link #contributionBindings()}, which will never happen for contributions in valid graphs
- */
- ContributionBinding contributionBinding() {
- return getOnlyElement(contributionBindings());
- }
-
- /**
- * The binding type for these bindings. If there are {@link #multibindingDeclarations()} or {@link
- * #subcomponentDeclarations()} but no {@link #bindings()}, returns {@link BindingType#PROVISION}.
- *
- * @throws IllegalStateException if {@link #isEmpty()} or the binding types conflict
- */
- final BindingType bindingType() {
- checkState(!isEmpty(), "empty bindings for %s", key());
- if (allBindings().isEmpty()
- && (!multibindingDeclarations().isEmpty() || !subcomponentDeclarations().isEmpty())) {
- // Only multibinding declarations, so assume provision.
- return BindingType.PROVISION;
- }
- ImmutableSet<BindingType> bindingTypes = bindingTypes();
- checkState(bindingTypes.size() == 1, "conflicting binding types: %s", bindings());
- return getOnlyElement(bindingTypes);
- }
-
- /** The binding types for {@link #bindings()}. */
- @Memoized
- ImmutableSet<BindingType> bindingTypes() {
- return bindings().stream().map(Binding::bindingType).collect(toImmutableSet());
- }
-
- /**
- * The contribution type for these bindings.
- *
- * @throws IllegalStateException if there is not exactly one element in {@link
- * #contributionBindings()}, which will never happen for contributions in valid graphs
- */
- @Override
- public ContributionType contributionType() {
- return contributionBinding().contributionType();
- }
-
- /**
- * The scope associated with the single binding.
- *
- * @throws IllegalStateException if {@link #bindings()} does not have exactly one element
- */
- Optional<Scope> scope() {
- return binding().scope();
- }
-}
diff --git a/java/dagger/internal/codegen/Scopes.java b/java/dagger/internal/codegen/Scopes.java
deleted file mode 100644
index 252f71251..000000000
--- a/java/dagger/internal/codegen/Scopes.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.DiagnosticFormatting.stripCommonTypePrefixes;
-
-import com.google.auto.common.AnnotationMirrors;
-import com.google.common.collect.ImmutableSet;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.model.Scope;
-import dagger.producers.ProductionScope;
-import java.lang.annotation.Annotation;
-import java.util.Optional;
-import javax.inject.Singleton;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-
-/** Common names and convenience methods for {@link Scope}s. */
-final class Scopes {
- /**
- * Creates a {@link Scope} object from the {@link javax.inject.Scope}-annotated annotation type.
- */
- static Scope scope(TypeElement scopeType) {
- return Scope.scope(SimpleAnnotationMirror.of(scopeType));
- }
-
- /** Returns a representation for {@link ProductionScope @ProductionScope} scope. */
- static Scope productionScope(DaggerElements elements) {
- return scope(elements, ProductionScope.class);
- }
-
- /** Returns a representation for {@link Singleton @Singleton} scope. */
- static Scope singletonScope(DaggerElements elements) {
- return scope(elements, Singleton.class);
- }
-
- private static Scope scope(
- DaggerElements elements, Class<? extends Annotation> scopeAnnotationClass) {
- return scope(elements.getTypeElement(scopeAnnotationClass));
- }
-
- /**
- * Returns at most one associated scoped annotation from the source code element, throwing an
- * exception if there are more than one.
- */
- static Optional<Scope> uniqueScopeOf(Element element) {
- // TODO(ronshapiro): Use MoreCollectors.toOptional() once we can use guava-jre
- return Optional.ofNullable(getOnlyElement(scopesOf(element), null));
- }
-
- /**
- * Returns the readable source representation (name with @ prefix) of the scope's annotation type.
- *
- * <p>It's readable source because it has had common package prefixes removed, e.g.
- * {@code @javax.inject.Singleton} is returned as {@code @Singleton}.
- */
- static String getReadableSource(Scope scope) {
- return stripCommonTypePrefixes(scope.toString());
- }
-
- /** Returns all of the associated scopes for a source code element. */
- static ImmutableSet<Scope> scopesOf(Element element) {
- return AnnotationMirrors.getAnnotatedAnnotations(element, javax.inject.Scope.class)
- .stream()
- .map(Scope::scope)
- .collect(toImmutableSet());
- }
-}
diff --git a/java/dagger/internal/codegen/SetBindingExpression.java b/java/dagger/internal/codegen/SetBindingExpression.java
deleted file mode 100644
index 5efb85fca..000000000
--- a/java/dagger/internal/codegen/SetBindingExpression.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.BindingRequest.bindingRequest;
-import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
-import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
-import static javax.lang.model.util.ElementFilter.methodsIn;
-
-import com.google.common.collect.ImmutableSet;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.SetBuilder;
-import dagger.internal.codegen.javapoet.Expression;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.DependencyRequest;
-import java.util.Collections;
-import java.util.Optional;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-
-/** A binding expression for multibound sets. */
-final class SetBindingExpression extends MultibindingExpression {
- private final ProvisionBinding binding;
- private final BindingGraph graph;
- private final ComponentBindingExpressions componentBindingExpressions;
- private final DaggerTypes types;
- private final DaggerElements elements;
-
- SetBindingExpression(
- ResolvedBindings resolvedBindings,
- ComponentImplementation componentImplementation,
- BindingGraph graph,
- ComponentBindingExpressions componentBindingExpressions,
- DaggerTypes types,
- DaggerElements elements) {
- super(resolvedBindings, componentImplementation);
- this.binding = (ProvisionBinding) resolvedBindings.contributionBinding();
- this.graph = graph;
- this.componentBindingExpressions = componentBindingExpressions;
- this.types = types;
- this.elements = elements;
- }
-
- @Override
- protected Expression buildDependencyExpression(ClassName requestingClass) {
- Optional<CodeBlock> superMethodCall = superMethodCall();
- // TODO(ronshapiro): We should also make an ImmutableSet version of SetFactory
- boolean isImmutableSetAvailable = isImmutableSetAvailable();
- // TODO(ronshapiro, gak): Use Sets.immutableEnumSet() if it's available?
- if (isImmutableSetAvailable
- && binding.dependencies().stream().allMatch(this::isSingleValue)
- && !superMethodCall.isPresent()) {
- return Expression.create(
- immutableSetType(),
- CodeBlock.builder()
- .add("$T.", ImmutableSet.class)
- .add(maybeTypeParameter(requestingClass))
- .add(
- "of($L)",
- binding
- .dependencies()
- .stream()
- .map(dependency -> getContributionExpression(dependency, requestingClass))
- .collect(toParametersCodeBlock()))
- .build());
- }
- switch (binding.dependencies().size()) {
- case 0:
- return collectionsStaticFactoryInvocation(requestingClass, CodeBlock.of("emptySet()"));
- case 1:
- {
- DependencyRequest dependency = getOnlyElement(binding.dependencies());
- CodeBlock contributionExpression = getContributionExpression(dependency, requestingClass);
- if (isSingleValue(dependency)) {
- return collectionsStaticFactoryInvocation(
- requestingClass, CodeBlock.of("singleton($L)", contributionExpression));
- } else if (isImmutableSetAvailable) {
- return Expression.create(
- immutableSetType(),
- CodeBlock.builder()
- .add("$T.", ImmutableSet.class)
- .add(maybeTypeParameter(requestingClass))
- .add("copyOf($L)", contributionExpression)
- .build());
- }
- }
- // fall through
- default:
- CodeBlock.Builder instantiation = CodeBlock.builder();
- instantiation
- .add("$T.", isImmutableSetAvailable ? ImmutableSet.class : SetBuilder.class)
- .add(maybeTypeParameter(requestingClass));
- if (isImmutableSetBuilderWithExpectedSizeAvailable()) {
- instantiation.add("builderWithExpectedSize($L)", binding.dependencies().size());
- } else if (isImmutableSetAvailable) {
- instantiation.add("builder()");
- } else {
- instantiation.add("newSetBuilder($L)", binding.dependencies().size());
- }
- for (DependencyRequest dependency : getNewContributions(binding.dependencies())) {
- String builderMethod = isSingleValue(dependency) ? "add" : "addAll";
- instantiation.add(
- ".$L($L)", builderMethod, getContributionExpression(dependency, requestingClass));
- }
- if (superMethodCall.isPresent()) {
- instantiation.add(CodeBlock.of(".addAll($L)", superMethodCall.get()));
- }
- instantiation.add(".build()");
- return Expression.create(
- isImmutableSetAvailable ? immutableSetType() : binding.key().type(),
- instantiation.build());
- }
- }
-
- private DeclaredType immutableSetType() {
- return types.getDeclaredType(
- elements.getTypeElement(ImmutableSet.class), SetType.from(binding.key()).elementType());
- }
-
- private CodeBlock getContributionExpression(
- DependencyRequest dependency, ClassName requestingClass) {
- return componentBindingExpressions
- .getDependencyExpression(bindingRequest(dependency), requestingClass)
- .codeBlock();
- }
-
- private Expression collectionsStaticFactoryInvocation(
- ClassName requestingClass, CodeBlock methodInvocation) {
- return Expression.create(
- binding.key().type(),
- CodeBlock.builder()
- .add("$T.", Collections.class)
- .add(maybeTypeParameter(requestingClass))
- .add(methodInvocation)
- .build());
- }
-
- private CodeBlock maybeTypeParameter(ClassName requestingClass) {
- TypeMirror elementType = SetType.from(binding.key()).elementType();
- return isTypeAccessibleFrom(elementType, requestingClass.packageName())
- ? CodeBlock.of("<$T>", elementType)
- : CodeBlock.of("");
- }
-
- private boolean isSingleValue(DependencyRequest dependency) {
- return graph
- .contributionBindings()
- .get(dependency.key())
- .contributionBinding()
- .contributionType()
- .equals(ContributionType.SET);
- }
-
- private boolean isImmutableSetBuilderWithExpectedSizeAvailable() {
- if (isImmutableSetAvailable()) {
- return methodsIn(elements.getTypeElement(ImmutableSet.class).getEnclosedElements())
- .stream()
- .anyMatch(method -> method.getSimpleName().contentEquals("builderWithExpectedSize"));
- }
- return false;
- }
-
- private boolean isImmutableSetAvailable() {
- return elements.getTypeElement(ImmutableSet.class) != null;
- }
-}
diff --git a/java/dagger/internal/codegen/SetFactoryCreationExpression.java b/java/dagger/internal/codegen/SetFactoryCreationExpression.java
deleted file mode 100644
index 971209170..000000000
--- a/java/dagger/internal/codegen/SetFactoryCreationExpression.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static dagger.internal.codegen.SourceFiles.setFactoryClassName;
-
-import com.squareup.javapoet.CodeBlock;
-import dagger.model.DependencyRequest;
-import dagger.producers.Produced;
-import java.util.Optional;
-
-/** A factory creation expression for a multibound set. */
-final class SetFactoryCreationExpression extends MultibindingFactoryCreationExpression {
-
- private final ComponentImplementation componentImplementation;
- private final BindingGraph graph;
- private final ContributionBinding binding;
-
- SetFactoryCreationExpression(
- ContributionBinding binding,
- ComponentImplementation componentImplementation,
- ComponentBindingExpressions componentBindingExpressions,
- BindingGraph graph) {
- super(binding, componentImplementation, componentBindingExpressions);
- this.binding = checkNotNull(binding);
- this.componentImplementation = checkNotNull(componentImplementation);
- this.graph = checkNotNull(graph);
- }
-
- @Override
- public CodeBlock creationExpression() {
- CodeBlock.Builder builder = CodeBlock.builder().add("$T.", setFactoryClassName(binding));
- if (!useRawType()) {
- SetType setType = SetType.from(binding.key());
- builder.add(
- "<$T>",
- setType.elementsAreTypeOf(Produced.class)
- ? setType.unwrappedElementType(Produced.class)
- : setType.elementType());
- }
-
- int individualProviders = 0;
- int setProviders = 0;
- CodeBlock.Builder builderMethodCalls = CodeBlock.builder();
- String methodNameSuffix =
- binding.bindingType().equals(BindingType.PROVISION) ? "Provider" : "Producer";
-
- Optional<CodeBlock> superContributions = superContributions();
- if (superContributions.isPresent()) {
- // TODO(b/117833324): consider decomposing the Provider<Set<Provider>> and adding the
- // individual contributions separately from the collection contributions. Though this may
- // actually not be doable/desirable if the super provider instance is a DelegateFactory or
- // another internal type that is not SetFactory
- builderMethodCalls.add(".addCollection$N($L)", methodNameSuffix, superContributions.get());
- setProviders++;
- }
-
- for (DependencyRequest dependency : dependenciesToImplement()) {
- ContributionType contributionType =
- graph.contributionBindings().get(dependency.key()).contributionType();
- String methodNamePrefix;
- switch (contributionType) {
- case SET:
- individualProviders++;
- methodNamePrefix = "add";
- break;
- case SET_VALUES:
- setProviders++;
- methodNamePrefix = "addCollection";
- break;
- default:
- throw new AssertionError(dependency + " is not a set multibinding");
- }
-
- builderMethodCalls.add(
- ".$N$N($L)",
- methodNamePrefix,
- methodNameSuffix,
- multibindingDependencyExpression(dependency));
- }
- builder.add("builder($L, $L)", individualProviders, setProviders);
- builder.add(builderMethodCalls.build());
-
- componentImplementation.registerImplementedMultibinding(binding, bindingRequest());
-
- return builder.add(".build()").build();
- }
-}
diff --git a/java/dagger/internal/codegen/SetType.java b/java/dagger/internal/codegen/SetType.java
deleted file mode 100644
index e4fa58469..000000000
--- a/java/dagger/internal/codegen/SetType.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-import com.google.auto.common.MoreTypes;
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Equivalence;
-import dagger.model.Key;
-import java.util.Set;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * Information about a {@link Set} {@link TypeMirror}.
- */
-@AutoValue
-abstract class SetType {
- /**
- * The set type itself, wrapped using {@link MoreTypes#equivalence()}. Use
- * {@link #declaredSetType()} instead.
- */
- protected abstract Equivalence.Wrapper<DeclaredType> wrappedDeclaredSetType();
-
- /**
- * The set type itself.
- */
- DeclaredType declaredSetType() {
- return wrappedDeclaredSetType().get();
- }
-
- /**
- * {@code true} if the set type is the raw {@link Set} type.
- */
- boolean isRawType() {
- return declaredSetType().getTypeArguments().isEmpty();
- }
-
- /**
- * The element type.
- */
- TypeMirror elementType() {
- return declaredSetType().getTypeArguments().get(0);
- }
-
- /**
- * {@code true} if {@link #elementType()} is a {@code clazz}.
- */
- boolean elementsAreTypeOf(Class<?> clazz) {
- return MoreTypes.isType(elementType()) && MoreTypes.isTypeOf(clazz, elementType());
- }
-
- /**
- * {@code T} if {@link #elementType()} is a {@code WrappingClass<T>}.
- *
- * @throws IllegalStateException if {@link #elementType()} is not a {@code WrappingClass<T>}
- * @throws IllegalArgumentException if {@code wrappingClass} does not have exactly one type
- * parameter
- */
- TypeMirror unwrappedElementType(Class<?> wrappingClass) {
- checkArgument(
- wrappingClass.getTypeParameters().length == 1,
- "%s must have exactly one type parameter",
- wrappingClass);
- checkArgument(
- elementsAreTypeOf(wrappingClass),
- "expected elements to be %s, but this type is %s",
- wrappingClass,
- declaredSetType());
- return MoreTypes.asDeclared(elementType()).getTypeArguments().get(0);
- }
-
- /**
- * {@code true} if {@code type} is a {@link Set} type.
- */
- static boolean isSet(TypeMirror type) {
- return MoreTypes.isType(type) && MoreTypes.isTypeOf(Set.class, type);
- }
-
- /**
- * {@code true} if {@code key.type()} is a {@link Set} type.
- */
- static boolean isSet(Key key) {
- return isSet(key.type());
- }
-
- /**
- * Returns a {@link SetType} for {@code type}.
- *
- * @throws IllegalArgumentException if {@code type} is not a {@link Set} type
- */
- static SetType from(TypeMirror type) {
- checkArgument(isSet(type), "%s must be a Set", type);
- return new AutoValue_SetType(MoreTypes.equivalence().wrap(MoreTypes.asDeclared(type)));
- }
-
- /**
- * Returns a {@link SetType} for {@code key}'s {@link Key#type() type}.
- *
- * @throws IllegalArgumentException if {@code key.type()} is not a {@link Set} type
- */
- static SetType from(Key key) {
- return from (key.type());
- }
-}
diff --git a/java/dagger/internal/codegen/SimpleAnnotationMirror.java b/java/dagger/internal/codegen/SimpleAnnotationMirror.java
deleted file mode 100644
index 505c8eaef..000000000
--- a/java/dagger/internal/codegen/SimpleAnnotationMirror.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static javax.lang.model.util.ElementFilter.methodsIn;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.base.Functions;
-import com.google.common.base.Joiner;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import java.util.Map;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-
-/** A representation of an annotation. */
-final class SimpleAnnotationMirror implements AnnotationMirror {
- private final TypeElement annotationType;
- private final ImmutableMap<String, ? extends AnnotationValue> namedValues;
- private final ImmutableMap<ExecutableElement, ? extends AnnotationValue> elementValues;
-
- private SimpleAnnotationMirror(
- TypeElement annotationType, Map<String, ? extends AnnotationValue> namedValues) {
- checkArgument(
- annotationType.getKind().equals(ElementKind.ANNOTATION_TYPE),
- "annotationType must be an annotation: %s",
- annotationType);
- checkArgument(
- FluentIterable.from(methodsIn(annotationType.getEnclosedElements()))
- .transform(element -> element.getSimpleName().toString())
- .toSet()
- .equals(namedValues.keySet()),
- "namedValues must have values for exactly the members in %s: %s",
- annotationType,
- namedValues);
- this.annotationType = annotationType;
- this.namedValues = ImmutableMap.copyOf(namedValues);
- this.elementValues =
- Maps.toMap(
- methodsIn(annotationType.getEnclosedElements()),
- Functions.compose(
- Functions.forMap(namedValues), element -> element.getSimpleName().toString()));
- }
-
- @Override
- public DeclaredType getAnnotationType() {
- return MoreTypes.asDeclared(annotationType.asType());
- }
-
- @Override
- public Map<ExecutableElement, ? extends AnnotationValue> getElementValues() {
- return elementValues;
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder("@").append(annotationType.getQualifiedName());
- if (!namedValues.isEmpty()) {
- builder
- .append('(')
- .append(Joiner.on(", ").withKeyValueSeparator(" = ").join(namedValues))
- .append(')');
- }
- return builder.toString();
- }
-
- /**
- * An object representing an annotation instance.
- *
- * @param annotationType must be an annotation type with no members
- */
- static AnnotationMirror of(TypeElement annotationType) {
- return of(annotationType, ImmutableMap.<String, AnnotationValue>of());
- }
-
- /**
- * An object representing an annotation instance.
- *
- * @param annotationType must be an annotation type
- * @param namedValues a value for every annotation member, including those with defaults, indexed
- * by simple name
- */
- static AnnotationMirror of(
- TypeElement annotationType, Map<String, ? extends AnnotationValue> namedValues) {
- return new SimpleAnnotationMirror(annotationType, namedValues);
- }
-}
diff --git a/java/dagger/internal/codegen/SimpleInvocationBindingExpression.java b/java/dagger/internal/codegen/SimpleInvocationBindingExpression.java
deleted file mode 100644
index 909d7ae84..000000000
--- a/java/dagger/internal/codegen/SimpleInvocationBindingExpression.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/** A simple binding expression for instance requests. Does not scope. */
-abstract class SimpleInvocationBindingExpression extends BindingExpression {
- // TODO(dpb): Take ContributionBinding instead of ResolvedBindings.
- private final ResolvedBindings resolvedBindings;
-
- SimpleInvocationBindingExpression(ResolvedBindings resolvedBindings) {
- this.resolvedBindings = checkNotNull(resolvedBindings);
- }
-
- @Override
- boolean requiresMethodEncapsulation() {
- return !resolvedBindings.contributionBinding().dependencies().isEmpty();
- }
-}
diff --git a/java/dagger/internal/codegen/SimpleMethodBindingExpression.java b/java/dagger/internal/codegen/SimpleMethodBindingExpression.java
deleted file mode 100644
index 805ac7b7e..000000000
--- a/java/dagger/internal/codegen/SimpleMethodBindingExpression.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreElements.asExecutable;
-import static com.google.common.base.Preconditions.checkArgument;
-import static dagger.internal.codegen.InjectionMethods.ProvisionMethod.requiresInjectionMethod;
-import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
-import static dagger.internal.codegen.javapoet.TypeNames.rawTypeName;
-import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
-
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.TypeName;
-import dagger.internal.codegen.InjectionMethods.ProvisionMethod;
-import dagger.internal.codegen.javapoet.Expression;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.DependencyRequest;
-import java.util.Optional;
-import java.util.function.Function;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * A binding expression that invokes methods or constructors directly (without attempting to scope)
- * {@link dagger.model.RequestKind#INSTANCE} requests.
- */
-final class SimpleMethodBindingExpression extends SimpleInvocationBindingExpression {
- private final CompilerOptions compilerOptions;
- private final ProvisionBinding provisionBinding;
- private final ComponentBindingExpressions componentBindingExpressions;
- private final MembersInjectionMethods membersInjectionMethods;
- private final ComponentRequirementExpressions componentRequirementExpressions;
- private final DaggerTypes types;
- private final DaggerElements elements;
- private final SourceVersion sourceVersion;
-
- SimpleMethodBindingExpression(
- ResolvedBindings resolvedBindings,
- CompilerOptions compilerOptions,
- ComponentBindingExpressions componentBindingExpressions,
- MembersInjectionMethods membersInjectionMethods,
- ComponentRequirementExpressions componentRequirementExpressions,
- DaggerTypes types,
- DaggerElements elements,
- SourceVersion sourceVersion) {
- super(resolvedBindings);
- this.compilerOptions = compilerOptions;
- this.provisionBinding = (ProvisionBinding) resolvedBindings.contributionBinding();
- checkArgument(
- provisionBinding.implicitDependencies().isEmpty(),
- "framework deps are not currently supported");
- checkArgument(provisionBinding.bindingElement().isPresent());
- this.componentBindingExpressions = componentBindingExpressions;
- this.membersInjectionMethods = membersInjectionMethods;
- this.componentRequirementExpressions = componentRequirementExpressions;
- this.types = types;
- this.elements = elements;
- this.sourceVersion = sourceVersion;
- }
-
- @Override
- Expression getDependencyExpression(ClassName requestingClass) {
- ImmutableMap<DependencyRequest, Expression> arguments =
- ImmutableMap.copyOf(
- Maps.asMap(
- provisionBinding.dependencies(),
- request -> dependencyArgument(request, requestingClass)));
- Function<DependencyRequest, CodeBlock> argumentsFunction =
- request -> arguments.get(request).codeBlock();
- return requiresInjectionMethod(
- provisionBinding,
- arguments.values().asList(),
- compilerOptions,
- requestingClass.packageName(),
- types)
- ? invokeInjectionMethod(argumentsFunction, requestingClass)
- : invokeMethod(argumentsFunction, requestingClass);
- }
-
- private Expression invokeMethod(
- Function<DependencyRequest, CodeBlock> argumentsFunction,
- ClassName requestingClass) {
- // TODO(dpb): align this with the contents of InlineMethods.create
- CodeBlock arguments =
- provisionBinding.dependencies().stream()
- .map(argumentsFunction)
- .collect(toParametersCodeBlock());
- ExecutableElement method = asExecutable(provisionBinding.bindingElement().get());
- CodeBlock invocation;
- switch (method.getKind()) {
- case CONSTRUCTOR:
- invocation = CodeBlock.of("new $T($L)", constructorTypeName(requestingClass), arguments);
- break;
- case METHOD:
- CodeBlock module =
- moduleReference(requestingClass)
- .orElse(CodeBlock.of("$T", provisionBinding.bindingTypeElement().get()));
- invocation = CodeBlock.of("$L.$L($L)", module, method.getSimpleName(), arguments);
- break;
- default:
- throw new IllegalStateException();
- }
-
- return Expression.create(simpleMethodReturnType(), invocation);
- }
-
- private TypeName constructorTypeName(ClassName requestingClass) {
- DeclaredType type = MoreTypes.asDeclared(provisionBinding.key().type());
- TypeName typeName = TypeName.get(type);
- if (type.getTypeArguments()
- .stream()
- .allMatch(t -> isTypeAccessibleFrom(t, requestingClass.packageName()))) {
- return typeName;
- }
- return rawTypeName(typeName);
- }
-
- private Expression invokeInjectionMethod(
- Function<DependencyRequest, CodeBlock> argumentsFunction, ClassName requestingClass) {
- return injectMembers(
- ProvisionMethod.invoke(
- provisionBinding,
- argumentsFunction,
- requestingClass,
- moduleReference(requestingClass),
- compilerOptions,
- elements));
- }
-
- private Expression dependencyArgument(DependencyRequest dependency, ClassName requestingClass) {
- return componentBindingExpressions.getDependencyArgumentExpression(dependency, requestingClass);
- }
-
- private Expression injectMembers(CodeBlock instance) {
- if (provisionBinding.injectionSites().isEmpty()) {
- return Expression.create(simpleMethodReturnType(), instance);
- }
- if (sourceVersion.compareTo(SourceVersion.RELEASE_7) <= 0) {
- // Java 7 type inference can't figure out that instance in
- // injectParameterized(Parameterized_Factory.newParameterized()) is Parameterized<T> and not
- // Parameterized<Object>
- if (!MoreTypes.asDeclared(provisionBinding.key().type()).getTypeArguments().isEmpty()) {
- TypeName keyType = TypeName.get(provisionBinding.key().type());
- instance = CodeBlock.of("($T) ($T) $L", keyType, rawTypeName(keyType), instance);
- }
- }
- MethodSpec membersInjectionMethod = membersInjectionMethods.getOrCreate(provisionBinding.key());
- TypeMirror returnType =
- membersInjectionMethod.returnType.equals(TypeName.OBJECT)
- ? elements.getTypeElement(Object.class).asType()
- : provisionBinding.key().type();
- return Expression.create(returnType, CodeBlock.of("$N($L)", membersInjectionMethod, instance));
- }
-
- private Optional<CodeBlock> moduleReference(ClassName requestingClass) {
- return provisionBinding.requiresModuleInstance()
- ? provisionBinding
- .contributingModule()
- .map(Element::asType)
- .map(ComponentRequirement::forModule)
- .map(module -> componentRequirementExpressions.getExpression(module, requestingClass))
- : Optional.empty();
- }
-
- private TypeMirror simpleMethodReturnType() {
- return provisionBinding.contributedPrimitiveType().orElse(provisionBinding.key().type());
- }
-}
diff --git a/java/dagger/internal/codegen/SimpleTypeAnnotationValue.java b/java/dagger/internal/codegen/SimpleTypeAnnotationValue.java
deleted file mode 100644
index 7f043db02..000000000
--- a/java/dagger/internal/codegen/SimpleTypeAnnotationValue.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.AnnotationValueVisitor;
-import javax.lang.model.type.TypeMirror;
-
-/** An {@link AnnotationValue} that contains a {@link TypeMirror}. */
-final class SimpleTypeAnnotationValue implements AnnotationValue {
- private final TypeMirror value;
-
- SimpleTypeAnnotationValue(TypeMirror value) {
- this.value = value;
- }
-
- @Override
- public TypeMirror getValue() {
- return value;
- }
-
- @Override
- public String toString() {
- return value + ".class";
- }
-
- @Override
- public <R, P> R accept(AnnotationValueVisitor<R, P> visitor, P parameter) {
- return visitor.visitType(getValue(), parameter);
- }
-}
diff --git a/java/dagger/internal/codegen/SourceFileGenerationException.java b/java/dagger/internal/codegen/SourceFileGenerationException.java
deleted file mode 100644
index 07c1c68d6..000000000
--- a/java/dagger/internal/codegen/SourceFileGenerationException.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static javax.tools.Diagnostic.Kind.ERROR;
-
-import com.squareup.javapoet.ClassName;
-import java.util.Optional;
-import javax.annotation.processing.Messager;
-import javax.lang.model.element.Element;
-
-/**
- * An exception thrown to indicate that a source file could not be generated.
- *
- * <p>This exception <b>should not</b> be used to report detectable, logical errors as it may mask
- * other errors that might have been caught upon further processing. Use a {@link ValidationReport}
- * for that.
- */
-final class SourceFileGenerationException extends Exception {
- private final Element associatedElement;
-
- SourceFileGenerationException(
- Optional<ClassName> generatedClassName, Throwable cause, Element associatedElement) {
- super(createMessage(generatedClassName, cause.getMessage()), cause);
- this.associatedElement = checkNotNull(associatedElement);
- }
-
- private static String createMessage(Optional<ClassName> generatedClassName, String message) {
- return String.format("Could not generate %s: %s.",
- generatedClassName.isPresent()
- ? generatedClassName.get()
- : "unknown file",
- message);
- }
-
- void printMessageTo(Messager messager) {
- messager.printMessage(ERROR, getMessage(), associatedElement);
- }
-}
diff --git a/java/dagger/internal/codegen/SourceFileGenerator.java b/java/dagger/internal/codegen/SourceFileGenerator.java
deleted file mode 100644
index 7dddc2fcd..000000000
--- a/java/dagger/internal/codegen/SourceFileGenerator.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.GeneratedAnnotations.generatedAnnotation;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import com.google.common.base.Throwables;
-import com.squareup.javapoet.AnnotationSpec;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.JavaFile;
-import com.squareup.javapoet.TypeSpec;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import java.util.Optional;
-import javax.annotation.processing.Filer;
-import javax.annotation.processing.Messager;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
-
-/**
- * A template class that provides a framework for properly handling IO while generating source files
- * from an annotation processor. Particularly, it makes a best effort to ensure that files that
- * fail to write successfully are deleted.
- *
- * @param <T> The input type from which source is to be generated.
- */
-abstract class SourceFileGenerator<T> {
- private static final String GENERATED_COMMENTS = "https://dagger.dev";
-
- private final Filer filer;
- private final DaggerElements elements;
- private final SourceVersion sourceVersion;
-
- SourceFileGenerator(Filer filer, DaggerElements elements, SourceVersion sourceVersion) {
- this.filer = checkNotNull(filer);
- this.elements = checkNotNull(elements);
- this.sourceVersion = checkNotNull(sourceVersion);
- }
-
- SourceFileGenerator(SourceFileGenerator<T> delegate) {
- this(delegate.filer, delegate.elements, delegate.sourceVersion);
- }
-
- /**
- * Generates a source file to be compiled for {@code T}. Writes any generation exception to {@code
- * messager} and does not throw.
- */
- void generate(T input, Messager messager) {
- try {
- generate(input);
- } catch (SourceFileGenerationException e) {
- e.printMessageTo(messager);
- }
- }
-
- /** Generates a source file to be compiled for {@code T}. */
- void generate(T input) throws SourceFileGenerationException {
- ClassName generatedTypeName = nameGeneratedType(input);
- Optional<TypeSpec.Builder> type = write(generatedTypeName, input);
- if (!type.isPresent()) {
- return;
- }
- try {
- buildJavaFile(generatedTypeName, input, type.get()).writeTo(filer);
- } catch (Exception e) {
- // if the code above threw a SFGE, use that
- Throwables.propagateIfPossible(e, SourceFileGenerationException.class);
- // otherwise, throw a new one
- throw new SourceFileGenerationException(
- Optional.empty(), e, originatingElement(input));
- }
- }
-
- private JavaFile buildJavaFile(
- ClassName generatedTypeName, T input, TypeSpec.Builder typeSpecBuilder) {
- typeSpecBuilder.addOriginatingElement(originatingElement(input));
- Optional<AnnotationSpec> generatedAnnotation =
- generatedAnnotation(elements, sourceVersion)
- .map(
- annotation ->
- AnnotationSpec.builder(ClassName.get(annotation))
- .addMember("value", "$S", "dagger.internal.codegen.ComponentProcessor")
- .addMember("comments", "$S", GENERATED_COMMENTS)
- .build());
- generatedAnnotation.ifPresent(typeSpecBuilder::addAnnotation);
- JavaFile.Builder javaFileBuilder =
- JavaFile.builder(generatedTypeName.packageName(), typeSpecBuilder.build())
- .skipJavaLangImports(true);
- if (!generatedAnnotation.isPresent()) {
- javaFileBuilder.addFileComment("Generated by Dagger ($L).", GENERATED_COMMENTS);
- }
- return javaFileBuilder.build();
- }
-
- /**
- * Implementations should return the {@link ClassName} for the top-level type to be generated.
- */
- abstract ClassName nameGeneratedType(T input);
-
- /** Returns the originating element of the generating type. */
- abstract Element originatingElement(T input);
-
- /**
- * Returns a {@link TypeSpec.Builder type} to be generated for {@code T}, or {@link
- * Optional#empty()} if no file should be generated.
- */
- // TODO(ronshapiro): write() makes more sense in JavaWriter where all writers are mutable.
- // consider renaming to something like typeBuilder() which conveys the mutability of the result
- abstract Optional<TypeSpec.Builder> write(ClassName generatedTypeName, T input);
-}
diff --git a/java/dagger/internal/codegen/SourceFileGeneratorsModule.java b/java/dagger/internal/codegen/SourceFileGeneratorsModule.java
index c32262adc..426afae37 100644
--- a/java/dagger/internal/codegen/SourceFileGeneratorsModule.java
+++ b/java/dagger/internal/codegen/SourceFileGeneratorsModule.java
@@ -18,48 +18,49 @@ package dagger.internal.codegen;
import dagger.Module;
import dagger.Provides;
-import dagger.internal.codegen.SourceFileGeneratorsModule.ComponentModule;
-import dagger.internal.codegen.SourceFileGeneratorsModule.MembersInjectionModule;
-import dagger.internal.codegen.SourceFileGeneratorsModule.ProductionModule;
-import dagger.internal.codegen.SourceFileGeneratorsModule.ProvisionModule;
+import dagger.internal.codegen.base.SourceFileGenerator;
+import dagger.internal.codegen.binding.MembersInjectionBinding;
+import dagger.internal.codegen.binding.ProductionBinding;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.writing.FactoryGenerator;
+import dagger.internal.codegen.writing.HjarSourceFileGenerator;
+import dagger.internal.codegen.writing.MembersInjectorGenerator;
+import dagger.internal.codegen.writing.ModuleGenerator;
+import dagger.internal.codegen.writing.ModuleProxies.ModuleConstructorProxyGenerator;
+import dagger.internal.codegen.writing.ProducerFactoryGenerator;
import javax.lang.model.element.TypeElement;
-@Module(
- includes = {
- ProvisionModule.class,
- ProductionModule.class,
- MembersInjectionModule.class,
- ComponentModule.class
- })
-interface SourceFileGeneratorsModule {
- @Module
- abstract class GeneratorModule<T, G extends SourceFileGenerator<T>> {
- @Provides
- SourceFileGenerator<T> generator(G generator, CompilerOptions compilerOptions) {
- return compilerOptions.headerCompilation()
- ? HjarSourceFileGenerator.wrap(generator)
- : generator;
- }
- }
-
- @Module
- class ProvisionModule extends GeneratorModule<ProvisionBinding, FactoryGenerator> {}
+@Module
+abstract class SourceFileGeneratorsModule {
- @Module
- class ProductionModule extends GeneratorModule<ProductionBinding, ProducerFactoryGenerator> {}
+ @Provides
+ static SourceFileGenerator<ProvisionBinding> factoryGenerator(
+ FactoryGenerator generator, CompilerOptions compilerOptions) {
+ return hjarWrapper(generator, compilerOptions);
+ }
- @Module
- class MembersInjectionModule
- extends GeneratorModule<MembersInjectionBinding, MembersInjectorGenerator> {}
+ @Provides
+ static SourceFileGenerator<ProductionBinding> producerFactoryGenerator(
+ ProducerFactoryGenerator generator, CompilerOptions compilerOptions) {
+ return hjarWrapper(generator, compilerOptions);
+ }
- @Module
- class ComponentModule extends GeneratorModule<BindingGraph, ComponentGenerator> {}
+ @Provides
+ static SourceFileGenerator<MembersInjectionBinding> membersInjectorGenerator(
+ MembersInjectorGenerator generator, CompilerOptions compilerOptions) {
+ return hjarWrapper(generator, compilerOptions);
+ }
- // the abstract module is not available because we're using a qualifier
@Provides
@ModuleGenerator
- static SourceFileGenerator<TypeElement> generator(
+ static SourceFileGenerator<TypeElement> moduleConstructorProxyGenerator(
ModuleConstructorProxyGenerator generator, CompilerOptions compilerOptions) {
+ return hjarWrapper(generator, compilerOptions);
+ }
+
+ private static <T> SourceFileGenerator<T> hjarWrapper(
+ SourceFileGenerator<T> generator, CompilerOptions compilerOptions) {
return compilerOptions.headerCompilation()
? HjarSourceFileGenerator.wrap(generator)
: generator;
diff --git a/java/dagger/internal/codegen/SourceFiles.java b/java/dagger/internal/codegen/SourceFiles.java
deleted file mode 100644
index fd93d0d37..000000000
--- a/java/dagger/internal/codegen/SourceFiles.java
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.CaseFormat.LOWER_CAMEL;
-import static com.google.common.base.CaseFormat.UPPER_CAMEL;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.base.Verify.verify;
-import static dagger.internal.codegen.DaggerStreams.toImmutableList;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.Optionals.optionalComparator;
-import static dagger.internal.codegen.javapoet.TypeNames.DOUBLE_CHECK;
-import static dagger.internal.codegen.javapoet.TypeNames.MAP_FACTORY;
-import static dagger.internal.codegen.javapoet.TypeNames.MAP_OF_PRODUCED_PRODUCER;
-import static dagger.internal.codegen.javapoet.TypeNames.MAP_OF_PRODUCER_PRODUCER;
-import static dagger.internal.codegen.javapoet.TypeNames.MAP_PRODUCER;
-import static dagger.internal.codegen.javapoet.TypeNames.MAP_PROVIDER_FACTORY;
-import static dagger.internal.codegen.javapoet.TypeNames.PROVIDER_OF_LAZY;
-import static dagger.internal.codegen.javapoet.TypeNames.SET_FACTORY;
-import static dagger.internal.codegen.javapoet.TypeNames.SET_OF_PRODUCED_PRODUCER;
-import static dagger.internal.codegen.javapoet.TypeNames.SET_PRODUCER;
-import static dagger.model.BindingKind.INJECTION;
-import static dagger.model.BindingKind.MULTIBOUND_MAP;
-import static dagger.model.BindingKind.MULTIBOUND_SET;
-import static java.util.Comparator.comparing;
-import static javax.lang.model.SourceVersion.isName;
-
-import com.google.auto.common.MoreElements;
-import com.google.common.base.CaseFormat;
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Maps;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.FieldSpec;
-import com.squareup.javapoet.ParameterizedTypeName;
-import com.squareup.javapoet.TypeName;
-import com.squareup.javapoet.TypeVariableName;
-import dagger.internal.SetFactory;
-import dagger.model.DependencyRequest;
-import dagger.model.Key;
-import dagger.model.RequestKind;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
-import dagger.producers.internal.SetOfProducedProducer;
-import dagger.producers.internal.SetProducer;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import javax.inject.Provider;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.TypeParameterElement;
-
-/**
- * Utilities for generating files.
- */
-class SourceFiles {
-
- private static final Joiner CLASS_FILE_NAME_JOINER = Joiner.on('_');
-
- /**
- * Sorts {@link DependencyRequest} instances in an order likely to reflect their logical
- * importance.
- */
- static final Comparator<DependencyRequest> DEPENDENCY_ORDERING =
- // put fields before parameters
- comparing(
- (DependencyRequest request) -> request.requestElement().map(Element::getKind),
- optionalComparator())
- // order by dependency kind
- .thenComparing(DependencyRequest::kind)
- // then sort by name
- .thenComparing(
- request ->
- request.requestElement().map(element -> element.getSimpleName().toString()),
- optionalComparator());
-
- /**
- * Generates names and keys for the factory class fields needed to hold the framework classes for
- * all of the dependencies of {@code binding}. It is responsible for choosing a name that
- *
- * <ul>
- * <li>represents all of the dependency requests for this key
- * <li>is <i>probably</i> associated with the type being bound
- * <li>is unique within the class
- * </ul>
- *
- * @param binding must be an unresolved binding (type parameters must match its type element's)
- */
- static ImmutableMap<Key, FrameworkField> generateBindingFieldsForDependencies(
- Binding binding) {
- checkArgument(!binding.unresolved().isPresent(), "binding must be unresolved: %s", binding);
-
- ImmutableMap.Builder<Key, FrameworkField> bindingFields = ImmutableMap.builder();
- for (Binding.DependencyAssociation dependencyAssociation : binding.dependencyAssociations()) {
- FrameworkDependency frameworkDependency = dependencyAssociation.frameworkDependency();
- bindingFields.put(
- frameworkDependency.key(),
- FrameworkField.create(
- ClassName.get(frameworkDependency.frameworkClass()),
- TypeName.get(frameworkDependency.key().type()),
- fieldNameForDependency(dependencyAssociation.dependencyRequests())));
- }
- return bindingFields.build();
- }
-
- private static String fieldNameForDependency(ImmutableSet<DependencyRequest> dependencyRequests) {
- // collect together all of the names that we would want to call the provider
- ImmutableSet<String> dependencyNames =
- dependencyRequests.stream().map(DependencyVariableNamer::name).collect(toImmutableSet());
-
- if (dependencyNames.size() == 1) {
- // if there's only one name, great! use it!
- return Iterables.getOnlyElement(dependencyNames);
- } else {
- // in the event that a field is being used for a bunch of deps with different names,
- // add all the names together with "And"s in the middle. E.g.: stringAndS
- Iterator<String> namesIterator = dependencyNames.iterator();
- String first = namesIterator.next();
- StringBuilder compositeNameBuilder = new StringBuilder(first);
- while (namesIterator.hasNext()) {
- compositeNameBuilder
- .append("And")
- .append(CaseFormat.LOWER_CAMEL.to(UPPER_CAMEL, namesIterator.next()));
- }
- return compositeNameBuilder.toString();
- }
- }
-
- static CodeBlock frameworkTypeUsageStatement(
- CodeBlock frameworkTypeMemberSelect, RequestKind dependencyKind) {
- switch (dependencyKind) {
- case LAZY:
- return CodeBlock.of("$T.lazy($L)", DOUBLE_CHECK, frameworkTypeMemberSelect);
- case INSTANCE:
- case FUTURE:
- return CodeBlock.of("$L.get()", frameworkTypeMemberSelect);
- case PROVIDER:
- case PRODUCER:
- return frameworkTypeMemberSelect;
- case PROVIDER_OF_LAZY:
- return CodeBlock.of("$T.create($L)", PROVIDER_OF_LAZY, frameworkTypeMemberSelect);
- default: // including PRODUCED
- throw new AssertionError(dependencyKind);
- }
- }
-
- /**
- * Returns a mapping of {@link DependencyRequest}s to {@link CodeBlock}s that {@linkplain
- * #frameworkTypeUsageStatement(CodeBlock, RequestKind) use them}.
- */
- static ImmutableMap<DependencyRequest, CodeBlock> frameworkFieldUsages(
- ImmutableSet<DependencyRequest> dependencies, ImmutableMap<Key, FieldSpec> fields) {
- return Maps.toMap(
- dependencies,
- dep ->
- frameworkTypeUsageStatement(CodeBlock.of("$N", fields.get(dep.key())), dep.kind()));
- }
-
- /**
- * Returns the generated factory or members injector name for a binding.
- */
- static ClassName generatedClassNameForBinding(Binding binding) {
- switch (binding.bindingType()) {
- case PROVISION:
- case PRODUCTION:
- ContributionBinding contribution = (ContributionBinding) binding;
- switch (contribution.kind()) {
- case INJECTION:
- case PROVISION:
- case PRODUCTION:
- return elementBasedClassName(
- MoreElements.asExecutable(binding.bindingElement().get()), "Factory");
-
- default:
- throw new AssertionError();
- }
-
- case MEMBERS_INJECTION:
- return membersInjectorNameForType(
- ((MembersInjectionBinding) binding).membersInjectedType());
- }
- throw new AssertionError();
- }
-
- /**
- * Calculates an appropriate {@link ClassName} for a generated class that is based on {@code
- * element}, appending {@code suffix} at the end.
- *
- * <p>This will always return a {@linkplain ClassName#topLevelClassName() top level class name},
- * even if {@code element}'s enclosing class is a nested type.
- */
- static ClassName elementBasedClassName(ExecutableElement element, String suffix) {
- ClassName enclosingClassName =
- ClassName.get(MoreElements.asType(element.getEnclosingElement()));
- String methodName =
- element.getKind().equals(ElementKind.CONSTRUCTOR)
- ? ""
- : LOWER_CAMEL.to(UPPER_CAMEL, element.getSimpleName().toString());
- return ClassName.get(
- enclosingClassName.packageName(),
- classFileName(enclosingClassName) + "_" + methodName + suffix);
- }
-
- static TypeName parameterizedGeneratedTypeNameForBinding(Binding binding) {
- ClassName className = generatedClassNameForBinding(binding);
- ImmutableList<TypeVariableName> typeParameters = bindingTypeElementTypeVariableNames(binding);
- return typeParameters.isEmpty()
- ? className
- : ParameterizedTypeName.get(className, Iterables.toArray(typeParameters, TypeName.class));
- }
-
- static ClassName membersInjectorNameForType(TypeElement typeElement) {
- return siblingClassName(typeElement, "_MembersInjector");
- }
-
- static String classFileName(ClassName className) {
- return CLASS_FILE_NAME_JOINER.join(className.simpleNames());
- }
-
- static ClassName generatedMonitoringModuleName(
- TypeElement componentElement) {
- return siblingClassName(componentElement, "_MonitoringModule");
- }
-
- // TODO(ronshapiro): when JavaPoet migration is complete, replace the duplicated code
- // which could use this.
- private static ClassName siblingClassName(TypeElement typeElement, String suffix) {
- ClassName className = ClassName.get(typeElement);
- return className.topLevelClassName().peerClass(classFileName(className) + suffix);
- }
-
- /**
- * The {@link java.util.Set} factory class name appropriate for set bindings.
- *
- * <ul>
- * <li>{@link SetFactory} for provision bindings.
- * <li>{@link SetProducer} for production bindings for {@code Set<T>}.
- * <li>{@link SetOfProducedProducer} for production bindings for {@code Set<Produced<T>>}.
- * </ul>
- */
- static ClassName setFactoryClassName(ContributionBinding binding) {
- checkArgument(binding.kind().equals(MULTIBOUND_SET));
- if (binding.bindingType().equals(BindingType.PROVISION)) {
- return SET_FACTORY;
- } else {
- SetType setType = SetType.from(binding.key());
- return setType.elementsAreTypeOf(Produced.class) ? SET_OF_PRODUCED_PRODUCER : SET_PRODUCER;
- }
- }
-
- /** The {@link java.util.Map} factory class name appropriate for map bindings. */
- static ClassName mapFactoryClassName(ContributionBinding binding) {
- checkState(binding.kind().equals(MULTIBOUND_MAP), binding.kind());
- MapType mapType = MapType.from(binding.key());
- switch (binding.bindingType()) {
- case PROVISION:
- return mapType.valuesAreTypeOf(Provider.class) ? MAP_PROVIDER_FACTORY : MAP_FACTORY;
- case PRODUCTION:
- return mapType.valuesAreFrameworkType()
- ? mapType.valuesAreTypeOf(Producer.class)
- ? MAP_OF_PRODUCER_PRODUCER
- : MAP_OF_PRODUCED_PRODUCER
- : MAP_PRODUCER;
- default:
- throw new IllegalArgumentException(binding.bindingType().toString());
- }
- }
-
- static ImmutableList<TypeVariableName> bindingTypeElementTypeVariableNames(Binding binding) {
- if (binding instanceof ContributionBinding) {
- ContributionBinding contributionBinding = (ContributionBinding) binding;
- if (!contributionBinding.kind().equals(INJECTION)
- && !contributionBinding.requiresModuleInstance()) {
- return ImmutableList.of();
- }
- }
- List<? extends TypeParameterElement> typeParameters =
- binding.bindingTypeElement().get().getTypeParameters();
- return typeParameters.stream().map(TypeVariableName::get).collect(toImmutableList());
- }
-
- /**
- * Returns a name to be used for variables of the given {@linkplain TypeElement type}. Prefer
- * semantically meaningful variable names, but if none can be derived, this will produce something
- * readable.
- */
- // TODO(gak): maybe this should be a function of TypeMirrors instead of Elements?
- static String simpleVariableName(TypeElement typeElement) {
- String candidateName = UPPER_CAMEL.to(LOWER_CAMEL, typeElement.getSimpleName().toString());
- String variableName = protectAgainstKeywords(candidateName);
- verify(isName(variableName), "'%s' was expected to be a valid variable name");
- return variableName;
- }
-
- static String protectAgainstKeywords(String candidateName) {
- switch (candidateName) {
- case "package":
- return "pkg";
- case "boolean":
- return "b";
- case "double":
- return "d";
- case "byte":
- return "b";
- case "int":
- return "i";
- case "short":
- return "s";
- case "char":
- return "c";
- case "void":
- return "v";
- case "class":
- return "clazz";
- case "float":
- return "f";
- case "long":
- return "l";
- default:
- return SourceVersion.isKeyword(candidateName) ? candidateName + '_' : candidateName;
- }
- }
-
- private SourceFiles() {}
-}
diff --git a/java/dagger/internal/codegen/SpiModule.java b/java/dagger/internal/codegen/SpiModule.java
index a8f13e1a1..a4f537bff 100644
--- a/java/dagger/internal/codegen/SpiModule.java
+++ b/java/dagger/internal/codegen/SpiModule.java
@@ -24,6 +24,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.google.common.collect.ImmutableSet;
import dagger.Module;
import dagger.Provides;
+import dagger.internal.codegen.validation.BindingGraphValidator;
import dagger.spi.BindingGraphPlugin;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@@ -40,16 +41,21 @@ abstract class SpiModule {
@Provides
@Singleton
static ImmutableSet<BindingGraphPlugin> externalPlugins(
- @TestingPlugins Optional<ImmutableSet<BindingGraphPlugin>> testingPlugins) {
+ @TestingPlugins Optional<ImmutableSet<BindingGraphPlugin>> testingPlugins,
+ @ProcessorClassLoader ClassLoader processorClassLoader) {
return testingPlugins.orElseGet(
() ->
ImmutableSet.copyOf(
- ServiceLoader.load(
- BindingGraphPlugin.class, BindingGraphValidator.class.getClassLoader())));
+ ServiceLoader.load(BindingGraphPlugin.class, processorClassLoader)));
}
@Qualifier
@Retention(RUNTIME)
@Target({FIELD, PARAMETER, METHOD})
@interface TestingPlugins {}
+
+ @Qualifier
+ @Retention(RUNTIME)
+ @Target({PARAMETER, METHOD})
+ @interface ProcessorClassLoader {}
}
diff --git a/java/dagger/internal/codegen/SubcomponentCreatorBindingEdgeImpl.java b/java/dagger/internal/codegen/SubcomponentCreatorBindingEdgeImpl.java
deleted file mode 100644
index c97024ec5..000000000
--- a/java/dagger/internal/codegen/SubcomponentCreatorBindingEdgeImpl.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.DaggerStreams.presentValues;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static java.util.stream.Collectors.joining;
-
-import com.google.common.collect.ImmutableSet;
-import dagger.model.BindingGraph.SubcomponentCreatorBindingEdge;
-import javax.lang.model.element.TypeElement;
-
-/** An implementation of {@link SubcomponentCreatorBindingEdge}. */
-final class SubcomponentCreatorBindingEdgeImpl implements SubcomponentCreatorBindingEdge {
-
- private final ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations;
-
- SubcomponentCreatorBindingEdgeImpl(
- ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations) {
- this.subcomponentDeclarations = subcomponentDeclarations;
- }
-
- @Override
- public ImmutableSet<TypeElement> declaringModules() {
- return subcomponentDeclarations.stream()
- .map(SubcomponentDeclaration::contributingModule)
- .flatMap(presentValues())
- .collect(toImmutableSet());
- }
-
- @Override
- public String toString() {
- return "subcomponent declared by "
- + (subcomponentDeclarations.size() == 1
- ? getOnlyElement(declaringModules()).getQualifiedName()
- : declaringModules().stream()
- .map(TypeElement::getQualifiedName)
- .collect(joining(", ", "{", "}")));
- }
-}
diff --git a/java/dagger/internal/codegen/SubcomponentCreatorBindingExpression.java b/java/dagger/internal/codegen/SubcomponentCreatorBindingExpression.java
deleted file mode 100644
index b415d3fae..000000000
--- a/java/dagger/internal/codegen/SubcomponentCreatorBindingExpression.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import com.squareup.javapoet.ClassName;
-import dagger.internal.codegen.javapoet.Expression;
-import javax.lang.model.type.TypeMirror;
-
-/** A binding expression for a subcomponent creator that just invokes the constructor. */
-final class SubcomponentCreatorBindingExpression extends SimpleInvocationBindingExpression {
- private final TypeMirror creatorType;
- private final String creatorImplementationName;
-
- SubcomponentCreatorBindingExpression(
- ResolvedBindings resolvedBindings, String creatorImplementationName) {
- super(resolvedBindings);
- this.creatorType = resolvedBindings.key().type();
- this.creatorImplementationName = creatorImplementationName;
- }
-
- @Override
- Expression getDependencyExpression(ClassName requestingClass) {
- return Expression.create(creatorType, "new $L()", creatorImplementationName);
- }
-}
diff --git a/java/dagger/internal/codegen/SubcomponentDeclaration.java b/java/dagger/internal/codegen/SubcomponentDeclaration.java
deleted file mode 100644
index 567785700..000000000
--- a/java/dagger/internal/codegen/SubcomponentDeclaration.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.AnnotationMirrors.getAnnotationElementAndValue;
-import static dagger.internal.codegen.ConfigurationAnnotations.getSubcomponentCreator;
-
-import com.google.auto.value.AutoValue;
-import com.google.auto.value.extension.memoized.Memoized;
-import com.google.common.collect.ImmutableSet;
-import dagger.model.Key;
-import java.util.Optional;
-import javax.inject.Inject;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-
-/**
- * A declaration for a subcomponent that is included in a module via {@link
- * dagger.Module#subcomponents()}.
- */
-@AutoValue
-abstract class SubcomponentDeclaration extends BindingDeclaration {
- /**
- * Key for the {@link dagger.Subcomponent.Builder} or {@link
- * dagger.producers.ProductionSubcomponent.Builder} of {@link #subcomponentType()}.
- */
- @Override
- public abstract Key key();
-
- /**
- * The type element that defines the {@link dagger.Subcomponent} or {@link
- * dagger.producers.ProductionSubcomponent} for this declaration.
- */
- abstract TypeElement subcomponentType();
-
- /** The module annotation. */
- abstract ModuleAnnotation moduleAnnotation();
-
- @Memoized
- @Override
- public abstract int hashCode();
-
- @Override
- public abstract boolean equals(Object obj);
-
- static class Factory {
- private final KeyFactory keyFactory;
-
- @Inject
- Factory(KeyFactory keyFactory) {
- this.keyFactory = keyFactory;
- }
-
- ImmutableSet<SubcomponentDeclaration> forModule(TypeElement module) {
- ImmutableSet.Builder<SubcomponentDeclaration> declarations = ImmutableSet.builder();
- ModuleAnnotation moduleAnnotation = ModuleAnnotation.moduleAnnotation(module).get();
- Element subcomponentAttribute =
- getAnnotationElementAndValue(moduleAnnotation.annotation(), "subcomponents").getKey();
- for (TypeElement subcomponent : moduleAnnotation.subcomponents()) {
- declarations.add(
- new AutoValue_SubcomponentDeclaration(
- Optional.of(subcomponentAttribute),
- Optional.of(module),
- keyFactory.forSubcomponentCreator(
- getSubcomponentCreator(subcomponent).get().asType()),
- subcomponent,
- moduleAnnotation));
- }
- return declarations.build();
- }
- }
-}
diff --git a/java/dagger/internal/codegen/SubcomponentFactoryMethodValidator.java b/java/dagger/internal/codegen/SubcomponentFactoryMethodValidator.java
deleted file mode 100644
index 0c6a006dc..000000000
--- a/java/dagger/internal/codegen/SubcomponentFactoryMethodValidator.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.auto.common.MoreTypes.asDeclared;
-import static com.google.auto.common.MoreTypes.asExecutable;
-import static com.google.auto.common.MoreTypes.asTypeElements;
-import static com.google.common.collect.Sets.union;
-import static dagger.internal.codegen.DaggerStreams.instancesOf;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.Util.componentCanMakeNewInstances;
-import static javax.tools.Diagnostic.Kind.ERROR;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import com.google.common.collect.Sets.SetView;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.BindingGraph;
-import dagger.model.BindingGraph.ChildFactoryMethodEdge;
-import dagger.model.BindingGraph.ComponentNode;
-import dagger.spi.BindingGraphPlugin;
-import dagger.spi.DiagnosticReporter;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Function;
-import javax.inject.Inject;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ExecutableType;
-
-/** Reports an error if a subcomponent factory method is missing required modules. */
-final class SubcomponentFactoryMethodValidator implements BindingGraphPlugin {
-
- private final DaggerTypes types;
- private final Map<ComponentNode, Set<TypeElement>> inheritedModulesCache = new HashMap<>();
-
- @Inject
- SubcomponentFactoryMethodValidator(DaggerTypes types) {
- this.types = types;
- }
-
- @Override
- public String pluginName() {
- return "Dagger/SubcomponentFactoryMethodMissingModule";
- }
-
- @Override
- public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
- if (!bindingGraph.rootComponentNode().isRealComponent()
- || bindingGraph.rootComponentNode().isSubcomponent()) {
- // We don't know all the modules that might be owned by the child until we know the real root
- // component, which we don't if the root component node is really a module or a subcomponent.
- return;
- }
- bindingGraph.network().edges().stream()
- .flatMap(instancesOf(ChildFactoryMethodEdge.class))
- .forEach(
- edge -> {
- ImmutableSet<TypeElement> missingModules = findMissingModules(edge, bindingGraph);
- if (!missingModules.isEmpty()) {
- reportMissingModuleParameters(
- edge, missingModules, bindingGraph, diagnosticReporter);
- }
- });
- }
-
- private ImmutableSet<TypeElement> findMissingModules(
- ChildFactoryMethodEdge edge, BindingGraph graph) {
- ImmutableSet<TypeElement> factoryMethodParameters =
- subgraphFactoryMethodParameters(edge, graph);
- ComponentNode child = (ComponentNode) graph.network().incidentNodes(edge).target();
- SetView<TypeElement> modulesOwnedByChild = ownedModules(child, graph);
- return graph.bindings().stream()
- // bindings owned by child
- .filter(binding -> binding.componentPath().equals(child.componentPath()))
- // that require a module instance
- .filter(binding -> binding.requiresModuleInstance())
- .map(binding -> binding.contributingModule().get())
- .distinct()
- // module owned by child
- .filter(module -> modulesOwnedByChild.contains(module))
- // module not in the method parameters
- .filter(module -> !factoryMethodParameters.contains(module))
- // module doesn't have an accessible no-arg constructor
- .filter(moduleType -> !componentCanMakeNewInstances(moduleType))
- .collect(toImmutableSet());
- }
-
- private ImmutableSet<TypeElement> subgraphFactoryMethodParameters(
- ChildFactoryMethodEdge edge, BindingGraph bindingGraph) {
- ComponentNode parent = (ComponentNode) bindingGraph.network().incidentNodes(edge).source();
- DeclaredType parentType = asDeclared(parent.componentPath().currentComponent().asType());
- ExecutableType factoryMethodType =
- asExecutable(types.asMemberOf(parentType, edge.factoryMethod()));
- return asTypeElements(factoryMethodType.getParameterTypes());
- }
-
- private SetView<TypeElement> ownedModules(ComponentNode component, BindingGraph graph) {
- return Sets.difference(
- ((ComponentNodeImpl) component).componentDescriptor().moduleTypes(),
- inheritedModules(component, graph));
- }
-
- private Set<TypeElement> inheritedModules(ComponentNode component, BindingGraph graph) {
- return Util.reentrantComputeIfAbsent(
- inheritedModulesCache, component, uncachedInheritedModules(graph));
- }
-
- private Function<ComponentNode, Set<TypeElement>> uncachedInheritedModules(BindingGraph graph) {
- return componentNode ->
- componentNode.componentPath().atRoot()
- ? ImmutableSet.of()
- : graph
- .componentNode(componentNode.componentPath().parent())
- .map(parent -> union(ownedModules(parent, graph), inheritedModules(parent, graph)))
- .get();
- }
-
- private void reportMissingModuleParameters(
- ChildFactoryMethodEdge edge,
- ImmutableSet<TypeElement> missingModules,
- BindingGraph graph,
- DiagnosticReporter diagnosticReporter) {
- diagnosticReporter.reportSubcomponentFactoryMethod(
- ERROR,
- edge,
- "%s requires modules which have no visible default constructors. "
- + "Add the following modules as parameters to this method: %s",
- graph
- .network()
- .incidentNodes(edge)
- .target()
- .componentPath()
- .currentComponent()
- .getQualifiedName(),
- Joiner.on(", ").join(missingModules));
- }
-}
diff --git a/java/dagger/internal/codegen/SubcomponentNames.java b/java/dagger/internal/codegen/SubcomponentNames.java
deleted file mode 100644
index f2ffd8374..000000000
--- a/java/dagger/internal/codegen/SubcomponentNames.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static dagger.internal.codegen.DaggerStreams.toImmutableMap;
-import static java.lang.Character.isUpperCase;
-import static java.lang.String.format;
-
-import com.google.common.base.CharMatcher;
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableBiMap;
-import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimaps;
-import dagger.model.Key;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import javax.lang.model.element.Name;
-import javax.lang.model.element.TypeElement;
-
-/**
- * Holds the unique simple names for all subcomponents, keyed by their {@link ComponentDescriptor}
- * and {@link Key} of the subcomponent builder.
- */
-final class SubcomponentNames {
- private static final Splitter QUALIFIED_NAME_SPLITTER = Splitter.on('.');
-
- private final ImmutableMap<ComponentDescriptor, String> namesByDescriptor;
- private final ImmutableMap<Key, ComponentDescriptor> descriptorsByCreatorKey;
-
- SubcomponentNames(BindingGraph graph, KeyFactory keyFactory) {
- this.namesByDescriptor = namesByDescriptor(graph);
- this.descriptorsByCreatorKey = descriptorsByCreatorKey(keyFactory, namesByDescriptor.keySet());
- }
-
- /** Returns the simple component name for the given {@link ComponentDescriptor}. */
- String get(ComponentDescriptor componentDescriptor) {
- return namesByDescriptor.get(componentDescriptor);
- }
-
- /**
- * Returns the simple name for the subcomponent creator implementation with the given {@link Key}.
- */
- String getCreatorName(Key key) {
- return getCreatorName(descriptorsByCreatorKey.get(key));
- }
-
- /**
- * Returns the simple name for the subcomponent creator implementation for the given {@link
- * ComponentDescriptor}.
- */
- String getCreatorName(ComponentDescriptor componentDescriptor) {
- checkArgument(componentDescriptor.creatorDescriptor().isPresent());
- ComponentCreatorDescriptor creatorDescriptor = componentDescriptor.creatorDescriptor().get();
- return get(componentDescriptor) + creatorDescriptor.kind().typeName();
- }
-
- private static ImmutableMap<ComponentDescriptor, String> namesByDescriptor(BindingGraph graph) {
- ImmutableListMultimap<String, ComponentDescriptor> componentDescriptorsBySimpleName =
- Multimaps.index(
- graph.componentDescriptors(),
- componentDescriptor -> componentDescriptor.typeElement().getSimpleName().toString());
- ImmutableMap<ComponentDescriptor, Namer> componentNamers =
- qualifiedNames(graph.componentDescriptors());
- Map<ComponentDescriptor, String> subcomponentImplSimpleNames = new LinkedHashMap<>();
- componentDescriptorsBySimpleName
- .asMap()
- .values()
- .forEach(
- components ->
- subcomponentImplSimpleNames.putAll(
- disambiguateConflictingSimpleNames(components, componentNamers)));
- subcomponentImplSimpleNames.remove(graph.componentDescriptor());
- return ImmutableMap.copyOf(subcomponentImplSimpleNames);
- }
-
- private static ImmutableMap<Key, ComponentDescriptor> descriptorsByCreatorKey(
- KeyFactory keyFactory, ImmutableSet<ComponentDescriptor> subcomponents) {
- return subcomponents.stream()
- .filter(subcomponent -> subcomponent.creatorDescriptor().isPresent())
- .collect(
- toImmutableMap(
- subcomponent ->
- keyFactory.forSubcomponentCreator(
- subcomponent.creatorDescriptor().get().typeElement().asType()),
- subcomponent -> subcomponent));
- }
-
- private static ImmutableBiMap<ComponentDescriptor, String> disambiguateConflictingSimpleNames(
- Collection<ComponentDescriptor> components,
- ImmutableMap<ComponentDescriptor, Namer> componentNamers) {
- Map<String, ComponentDescriptor> generatedSimpleNames = new LinkedHashMap<>();
-
- // Let's see if we can get away with using simpleName() everywhere.
- for (ComponentDescriptor component : components) {
- Namer namer = componentNamers.get(component);
- if (generatedSimpleNames.containsKey(namer.simpleName())) {
- break;
- }
- generatedSimpleNames.put(namer.simpleName(), component);
- }
-
- if (generatedSimpleNames.size() != components.size()) {
- // Simple approach didn't work out, let's use more complicated names.
- // We keep them small to fix https://github.com/google/dagger/issues/421.
- generatedSimpleNames.clear();
- UniqueNameSet nameSet = new UniqueNameSet();
- for (ComponentDescriptor component : components) {
- Namer namer = componentNamers.get(component);
- String simpleName = namer.simpleName();
- String basePrefix = namer.uniquingPrefix();
- generatedSimpleNames.put(
- format("%s_%s", nameSet.getUniqueName(basePrefix), simpleName), component);
- }
- }
- return ImmutableBiMap.copyOf(generatedSimpleNames).inverse();
- }
-
- private static ImmutableMap<ComponentDescriptor, Namer> qualifiedNames(
- Iterable<ComponentDescriptor> componentDescriptors) {
- ImmutableMap.Builder<ComponentDescriptor, Namer> builder = ImmutableMap.builder();
- for (ComponentDescriptor component : componentDescriptors) {
- builder.put(component, new Namer(component.typeElement()));
- }
- return builder.build();
- }
-
- private static final class Namer {
- final TypeElement typeElement;
-
- Namer(TypeElement typeElement) {
- this.typeElement = typeElement;
- }
-
- String simpleName() {
- return typeElement.getSimpleName().toString();
- }
-
- /** Returns a prefix that could make {@link #simpleName()} more unique. */
- String uniquingPrefix() {
- String containerName = typeElement.getEnclosingElement().getSimpleName().toString();
-
- // If parent element looks like a class, use its initials as a prefix.
- if (!containerName.isEmpty() && isUpperCase(containerName.charAt(0))) {
- return CharMatcher.javaLowerCase().removeFrom(containerName);
- }
-
- // Not in a normally named class. Prefix with the initials of the elements leading here.
- Name qualifiedName = typeElement.getQualifiedName();
- Iterator<String> pieces = QUALIFIED_NAME_SPLITTER.split(qualifiedName).iterator();
- StringBuilder b = new StringBuilder();
-
- while (pieces.hasNext()) {
- String next = pieces.next();
- if (pieces.hasNext()) {
- b.append(next.charAt(0));
- }
- }
-
- // Note that a top level class in the root package will be prefixed "$_".
- return b.length() > 0 ? b.toString() : "$";
- }
- }
-}
diff --git a/java/dagger/internal/codegen/SwitchingProviders.java b/java/dagger/internal/codegen/SwitchingProviders.java
deleted file mode 100644
index 29633d910..000000000
--- a/java/dagger/internal/codegen/SwitchingProviders.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.collect.Iterables.getLast;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static com.squareup.javapoet.MethodSpec.methodBuilder;
-import static com.squareup.javapoet.TypeSpec.classBuilder;
-import static dagger.internal.codegen.DaggerStreams.toImmutableList;
-import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
-import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings;
-import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.PUBLIC;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.TypeSpec;
-import com.squareup.javapoet.TypeVariableName;
-import dagger.internal.codegen.javapoet.CodeBlocks;
-import dagger.internal.codegen.javapoet.Expression;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.Key;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.TreeMap;
-
-/**
- * Keeps track of all provider expression requests for a component.
- *
- * <p>The provider expression request will be satisfied by a single generated {@code Provider} inner
- * class that can provide instances for all types by switching on an id.
- */
-// TODO(ronshapiro): either merge this with InnerSwitchingProviders, or repurpose this for
-// SwitchingProducers
-abstract class SwitchingProviders {
- /**
- * Defines the {@linkplain Expression expressions} for a switch case in a {@code SwitchProvider}
- * for a particular binding.
- */
- // TODO(user): Consider handling SwitchingProviders with dependency arguments in this class,
- // then we wouldn't need the getProviderExpression method.
- // TODO(user): Consider making this an abstract class with equals/hashCode defined by the key
- // and then using this class directly in Map types instead of Key.
- interface SwitchCase {
- /** Returns the {@link Key} for this switch case. */
- Key key();
-
- /** Returns the {@link Expression} that returns the provided instance for this case. */
- Expression getReturnExpression(ClassName switchingProviderClass);
-
- /**
- * Returns the {@link Expression} that returns the {@code SwitchProvider} instance for this
- * case.
- */
- Expression getProviderExpression(ClassName switchingProviderClass, int switchId);
- }
-
- /**
- * Each switch size is fixed at 100 cases each and put in its own method. This is to limit the
- * size of the methods so that we don't reach the "huge" method size limit for Android that will
- * prevent it from being AOT compiled in some versions of Android (b/77652521). This generally
- * starts to happen around 1500 cases, but we are choosing 100 to be safe.
- */
- // TODO(user): Include a proguard_spec in the Dagger library to prevent inlining these methods?
- // TODO(ronshapiro): Consider making this configurable via a flag.
- private static final int MAX_CASES_PER_SWITCH = 100;
-
- private static final long MAX_CASES_PER_CLASS = MAX_CASES_PER_SWITCH * MAX_CASES_PER_SWITCH;
- private static final TypeVariableName T = TypeVariableName.get("T");
-
- /**
- * Maps a {@link Key} to an instance of a {@link SwitchingProviderBuilder}. Each group of {@code
- * MAX_CASES_PER_CLASS} keys will share the same instance.
- */
- private final Map<Key, SwitchingProviderBuilder> switchingProviderBuilders =
- new LinkedHashMap<>();
-
- private final ComponentImplementation componentImplementation;
- private final ClassName owningComponent;
- private final DaggerTypes types;
- private final UniqueNameSet switchingProviderNames = new UniqueNameSet();
-
- SwitchingProviders(ComponentImplementation componentImplementation, DaggerTypes types) {
- this.componentImplementation = checkNotNull(componentImplementation);
- this.types = checkNotNull(types);
- this.owningComponent = checkNotNull(componentImplementation).name();
- }
-
- /** Returns the {@link TypeSpec} for a {@code SwitchingProvider} based on the given builder. */
- protected abstract TypeSpec createSwitchingProviderType(TypeSpec.Builder builder);
-
- /**
- * Returns the {@link Expression} that returns the {@code SwitchProvider} instance for the case.
- */
- protected final Expression getProviderExpression(SwitchCase switchCase) {
- return switchingProviderBuilders
- .computeIfAbsent(switchCase.key(), key -> getSwitchingProviderBuilder())
- .getProviderExpression(switchCase);
- }
-
- private SwitchingProviderBuilder getSwitchingProviderBuilder() {
- if (switchingProviderBuilders.size() % MAX_CASES_PER_CLASS == 0) {
- String name = switchingProviderNames.getUniqueName("SwitchingProvider");
- SwitchingProviderBuilder switchingProviderBuilder =
- new SwitchingProviderBuilder(owningComponent.nestedClass(name));
- componentImplementation.addSwitchingProvider(switchingProviderBuilder::build);
- return switchingProviderBuilder;
- }
- return getLast(switchingProviderBuilders.values());
- }
-
- // TODO(user): Consider just merging this class with SwitchingProviders.
- private final class SwitchingProviderBuilder {
- // Keep the switch cases ordered by switch id. The switch Ids are assigned in pre-order
- // traversal, but the switch cases are assigned in post-order traversal of the binding graph.
- private final Map<Integer, CodeBlock> switchCases = new TreeMap<>();
- private final Map<Key, Integer> switchIds = new HashMap<>();
- private final ClassName switchingProviderType;
-
- SwitchingProviderBuilder(ClassName switchingProviderType) {
- this.switchingProviderType = checkNotNull(switchingProviderType);
- }
-
- Expression getProviderExpression(SwitchCase switchCase) {
- Key key = switchCase.key();
- if (!switchIds.containsKey(key)) {
- int switchId = switchIds.size();
- switchIds.put(key, switchId);
- switchCases.put(switchId, createSwitchCaseCodeBlock(switchCase));
- }
- return switchCase.getProviderExpression(switchingProviderType, switchIds.get(key));
- }
-
- private CodeBlock createSwitchCaseCodeBlock(SwitchCase switchCase) {
- CodeBlock instanceCodeBlock =
- switchCase.getReturnExpression(switchingProviderType).box(types).codeBlock();
-
- return CodeBlock.builder()
- // TODO(user): Is there something else more useful than the key?
- .add("case $L: // $L \n", switchIds.get(switchCase.key()), switchCase.key())
- .addStatement("return ($T) $L", T, instanceCodeBlock)
- .build();
- }
-
- private TypeSpec build() {
- return createSwitchingProviderType(
- classBuilder(switchingProviderType)
- .addTypeVariable(T)
- .addSuperinterface(providerOf(T))
- .addMethods(getMethods()));
- }
-
- private ImmutableList<MethodSpec> getMethods() {
- ImmutableList<CodeBlock> switchCodeBlockPartitions = switchCodeBlockPartitions();
- if (switchCodeBlockPartitions.size() == 1) {
- // There are less than MAX_CASES_PER_SWITCH cases, so no need for extra get methods.
- return ImmutableList.of(
- methodBuilder("get")
- .addModifiers(PUBLIC)
- .addAnnotation(suppressWarnings(UNCHECKED))
- .addAnnotation(Override.class)
- .returns(T)
- .addCode(getOnlyElement(switchCodeBlockPartitions))
- .build());
- }
-
- // This is the main public "get" method that will route to private getter methods.
- MethodSpec.Builder routerMethod =
- methodBuilder("get")
- .addModifiers(PUBLIC)
- .addAnnotation(Override.class)
- .returns(T)
- .beginControlFlow("switch (id / $L)", MAX_CASES_PER_SWITCH);
-
- ImmutableList.Builder<MethodSpec> getMethods = ImmutableList.builder();
- for (int i = 0; i < switchCodeBlockPartitions.size(); i++) {
- MethodSpec method =
- methodBuilder("get" + i)
- .addModifiers(PRIVATE)
- .addAnnotation(suppressWarnings(UNCHECKED))
- .returns(T)
- .addCode(switchCodeBlockPartitions.get(i))
- .build();
- getMethods.add(method);
- routerMethod.addStatement("case $L: return $N()", i, method);
- }
-
- routerMethod.addStatement("default: throw new $T(id)", AssertionError.class).endControlFlow();
-
- return getMethods.add(routerMethod.build()).build();
- }
-
- private ImmutableList<CodeBlock> switchCodeBlockPartitions() {
- return Lists.partition(ImmutableList.copyOf(switchCases.values()), MAX_CASES_PER_SWITCH)
- .stream()
- .map(
- partitionCases ->
- CodeBlock.builder()
- .beginControlFlow("switch (id)")
- .add(CodeBlocks.concat(partitionCases))
- .addStatement("default: throw new $T(id)", AssertionError.class)
- .endControlFlow()
- .build())
- .collect(toImmutableList());
- }
- }
-}
diff --git a/java/dagger/internal/codegen/SystemComponentsModule.java b/java/dagger/internal/codegen/SystemComponentsModule.java
deleted file mode 100644
index 3f59b240f..000000000
--- a/java/dagger/internal/codegen/SystemComponentsModule.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import com.google.common.base.Ticker;
-import dagger.Module;
-import dagger.Provides;
-
-/** Module to provide system-level dependencies (such as time-related objects). */
-@Module
-interface SystemComponentsModule {
-
- @Provides
- static Ticker ticker() {
- return Ticker.systemTicker();
- }
-}
diff --git a/java/dagger/internal/codegen/TopLevel.java b/java/dagger/internal/codegen/TopLevel.java
deleted file mode 100644
index 4f456f25a..000000000
--- a/java/dagger/internal/codegen/TopLevel.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import java.lang.annotation.Retention;
-import javax.inject.Qualifier;
-
-/**
- * A {@link Qualifier} for bindings that are associated with the top level component implementation.
- */
-@Retention(RUNTIME)
-@Qualifier
-@interface TopLevel {}
diff --git a/java/dagger/internal/codegen/TopLevelImplementationComponent.java b/java/dagger/internal/codegen/TopLevelImplementationComponent.java
deleted file mode 100644
index 306c05de2..000000000
--- a/java/dagger/internal/codegen/TopLevelImplementationComponent.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import dagger.BindsInstance;
-import dagger.Module;
-import dagger.Subcomponent;
-
-/**
- * A shared subcomponent for a top-level {@link ComponentImplementation} and any nested child
- * implementations.
- */
-@PerGeneratedFile
-@Subcomponent
-interface TopLevelImplementationComponent {
- CurrentImplementationSubcomponent.Builder currentImplementationSubcomponentBuilder();
-
- @Subcomponent.Builder
- interface Builder {
- @BindsInstance
- Builder topLevelComponent(@TopLevel ComponentImplementation topLevelImplementation);
- TopLevelImplementationComponent build();
- }
-
- @Module(subcomponents = TopLevelImplementationComponent.class)
- interface InstallationModule {}
-}
diff --git a/java/dagger/internal/codegen/TypeCheckingProcessingStep.java b/java/dagger/internal/codegen/TypeCheckingProcessingStep.java
deleted file mode 100644
index 00769b228..000000000
--- a/java/dagger/internal/codegen/TypeCheckingProcessingStep.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
-import com.google.common.collect.SetMultimap;
-import java.lang.annotation.Annotation;
-import java.util.function.Function;
-import javax.lang.model.element.Element;
-
-/**
- * A {@link ProcessingStep} that processes one element at a time and defers any for which {@link
- * TypeNotPresentException} is thrown.
- */
-// TODO(dpb): Contribute to auto-common.
-abstract class TypeCheckingProcessingStep<E extends Element> implements ProcessingStep {
- private final Function<Element, E> downcaster;
-
- TypeCheckingProcessingStep(Function<Element, E> downcaster) {
- this.downcaster = checkNotNull(downcaster);
- }
-
- @Override
- public ImmutableSet<Element> process(
- SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
- ImmutableSet.Builder<Element> deferredElements = ImmutableSet.builder();
- ImmutableSetMultimap.copyOf(elementsByAnnotation)
- .inverse()
- .asMap()
- .forEach(
- (element, annotations) -> {
- try {
- process(downcaster.apply(element), ImmutableSet.copyOf(annotations));
- } catch (TypeNotPresentException e) {
- deferredElements.add(element);
- }
- });
- return deferredElements.build();
- }
-
- /**
- * Processes one element. If this method throws {@link TypeNotPresentException}, the element will
- * be deferred until the next round of processing.
- *
- * @param annotations the subset of {@link ProcessingStep#annotations()} that annotate {@code
- * element}
- */
- protected abstract void process(E element, ImmutableSet<Class<? extends Annotation>> annotations);
-}
diff --git a/java/dagger/internal/codegen/TypeProtoConverter.java b/java/dagger/internal/codegen/TypeProtoConverter.java
deleted file mode 100644
index c703bd889..000000000
--- a/java/dagger/internal/codegen/TypeProtoConverter.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2019 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static javax.lang.model.util.ElementFilter.typesIn;
-
-import com.google.auto.common.MoreTypes;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.internal.codegen.serialization.TypeProto;
-import dagger.internal.codegen.serialization.TypeProto.PrimitiveKind;
-import javax.inject.Inject;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.type.WildcardType;
-
-/** Converts {@link TypeMirror}s to {@link TypeProto}s and vice-versa. */
-final class TypeProtoConverter {
- // TODO(ronshapiro): if DaggerTypes and DaggerElements become public, move this file to
- // dagger.internal.codegen.serialization
- private final DaggerTypes types;
- private final DaggerElements elements;
-
- @Inject
- TypeProtoConverter(DaggerTypes types, DaggerElements elements) {
- this.types = types;
- this.elements = elements;
- }
-
- /** Translates a {@link TypeMirror} to a proto representation. */
- static TypeProto toProto(TypeMirror type) {
- TypeProto.Builder builder = TypeProto.newBuilder();
- int arrayDimensions = 0;
- while (type.getKind().equals(TypeKind.ARRAY)) {
- type = MoreTypes.asArray(type).getComponentType();
- arrayDimensions++;
- }
- builder.setArrayDimensions(arrayDimensions);
- if (type.getKind().isPrimitive()) {
- builder.setPrimitiveKind(PrimitiveKind.valueOf(type.getKind().name()));
- } else if (type.getKind().equals(TypeKind.WILDCARD)) {
- WildcardType wildcardType = MoreTypes.asWildcard(type);
- TypeProto.Wildcard.Builder wildcardBuilder = TypeProto.Wildcard.newBuilder();
- if (wildcardType.getExtendsBound() != null) {
- wildcardBuilder.setExtendsBound(toProto(wildcardType.getExtendsBound()));
- } else if (wildcardType.getSuperBound() != null) {
- wildcardBuilder.setSuperBound(toProto(wildcardType.getSuperBound()));
- }
- builder.setWildcard(wildcardBuilder);
- } else {
- TypeElement typeElement = MoreTypes.asTypeElement(type);
- DeclaredType declaredType = MoreTypes.asDeclared(type);
- TypeMirror enclosingType = declaredType.getEnclosingType();
- if (enclosingType.getKind().equals(TypeKind.NONE)) {
- builder.setQualifiedName(typeElement.getQualifiedName().toString());
- } else {
- builder
- .setEnclosingType(toProto(enclosingType))
- .setSimpleName(typeElement.getSimpleName().toString());
- }
- declaredType.getTypeArguments().stream()
- .map(TypeProtoConverter::toProto)
- .forEachOrdered(builder::addTypeArguments);
- }
- return builder.build();
- }
-
- /** Creates an {@link TypeMirror} from its proto representation. */
- TypeMirror fromProto(TypeProto type) {
- if (type.hasWildcard()) {
- return wildcardType(type.getWildcard());
- }
-
- TypeMirror[] typeArguments =
- type.getTypeArgumentsList().stream().map(this::fromProto).toArray(TypeMirror[]::new);
- TypeMirror typeMirror;
- if (!type.getPrimitiveKind().equals(PrimitiveKind.UNKNOWN)) {
- typeMirror = types.getPrimitiveType(TypeKind.valueOf(type.getPrimitiveKind().name()));
- } else if (type.hasEnclosingType()) {
- DeclaredType enclosingType = MoreTypes.asDeclared(fromProto(type.getEnclosingType()));
- TypeElement typeElement =
- typesIn(enclosingType.asElement().getEnclosedElements()).stream()
- .filter(inner -> inner.getSimpleName().contentEquals(type.getSimpleName()))
- .findFirst()
- .get();
- typeMirror = types.getDeclaredType(enclosingType, typeElement, typeArguments);
- } else {
- typeMirror =
- types.getDeclaredType(elements.getTypeElement(type.getQualifiedName()), typeArguments);
- }
- for (int i = 0; i < type.getArrayDimensions(); i++) {
- typeMirror = types.getArrayType(typeMirror);
- }
- return typeMirror;
- }
-
- private TypeMirror wildcardType(TypeProto.Wildcard wildcard) {
- if (wildcard.hasExtendsBound()) {
- return types.getWildcardType(fromProto(wildcard.getExtendsBound()), null);
- } else if (wildcard.hasSuperBound()) {
- return types.getWildcardType(null, fromProto(wildcard.getSuperBound()));
- } else {
- return types.getWildcardType(null, null);
- }
- }
-}
diff --git a/java/dagger/internal/codegen/UniqueNameSet.java b/java/dagger/internal/codegen/UniqueNameSet.java
deleted file mode 100644
index 11c48b30a..000000000
--- a/java/dagger/internal/codegen/UniqueNameSet.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/** A collector for names to be used in the same namespace that should not conflict. */
-final class UniqueNameSet {
- private final Set<String> uniqueNames = new HashSet<>();
-
- /**
- * Generates a unique name using {@code base}. If {@code base} has not yet been added, it will be
- * returned as-is. If your {@code base} is healthy, this will always return {@code base}.
- */
- String getUniqueName(CharSequence base) {
- String name = base.toString();
- for (int differentiator = 2; !uniqueNames.add(name); differentiator++) {
- name = base.toString() + differentiator;
- }
- return name;
- }
-
- /**
- * Adds {@code name} without any modification to the name set. Has no effect if {@code name} is
- * already present in the set.
- */
- void claim(CharSequence name) {
- uniqueNames.add(name.toString());
- }
-}
diff --git a/java/dagger/internal/codegen/UnwrappedMapKeyGenerator.java b/java/dagger/internal/codegen/UnwrappedMapKeyGenerator.java
deleted file mode 100644
index 2b7b02c5b..000000000
--- a/java/dagger/internal/codegen/UnwrappedMapKeyGenerator.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2016 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import dagger.MapKey;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import java.util.Set;
-import javax.annotation.processing.Filer;
-import javax.inject.Inject;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.TypeElement;
-
-/**
- * Generates classes that create annotation instances for an unwrapped {@link MapKey} annotation
- * type whose nested value is an annotation. The generated class will have a private empty
- * constructor and a static method that creates each annotation type that is nested in the top-level
- * annotation type.
- *
- * <p>So for an example {@link MapKey} annotation:
- *
- * <pre>
- * {@literal @MapKey}(unwrapValue = true)
- * {@literal @interface} Foo {
- * Bar bar();
- * }
- *
- * {@literal @interface} Bar {
- * {@literal Class<?> baz();}
- * }
- * </pre>
- *
- * the generated class will look like:
- *
- * <pre>
- * public final class FooCreator {
- * private FooCreator() {}
- *
- * public static Bar createBar({@literal Class<?> baz}) { … }
- * }
- * </pre>
- */
-final class UnwrappedMapKeyGenerator extends AnnotationCreatorGenerator {
-
- @Inject
- UnwrappedMapKeyGenerator(Filer filer, DaggerElements elements, SourceVersion sourceVersion) {
- super(filer, elements, sourceVersion);
- }
-
- @Override
- protected Set<TypeElement> annotationsToCreate(TypeElement annotationElement) {
- Set<TypeElement> nestedAnnotationElements = super.annotationsToCreate(annotationElement);
- nestedAnnotationElements.remove(annotationElement);
- return nestedAnnotationElements;
- }
-}
diff --git a/java/dagger/internal/codegen/Util.java b/java/dagger/internal/codegen/Util.java
deleted file mode 100644
index 1869b7ced..000000000
--- a/java/dagger/internal/codegen/Util.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2013 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static javax.lang.model.element.ElementKind.CONSTRUCTOR;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.PRIVATE;
-import static javax.lang.model.element.Modifier.STATIC;
-
-import java.util.Map;
-import java.util.function.Function;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-
-/**
- * Utilities for handling types in annotation processors
- */
-final class Util {
- /**
- * Returns true if and only if a component can instantiate new instances (typically of a module)
- * rather than requiring that they be passed.
- */
- static boolean componentCanMakeNewInstances(TypeElement typeElement) {
- switch (typeElement.getKind()) {
- case CLASS:
- break;
- case ENUM:
- case ANNOTATION_TYPE:
- case INTERFACE:
- return false;
- default:
- throw new AssertionError("TypeElement cannot have kind: " + typeElement.getKind());
- }
-
- if (typeElement.getModifiers().contains(ABSTRACT)) {
- return false;
- }
-
- if (requiresEnclosingInstance(typeElement)) {
- return false;
- }
-
- for (Element enclosed : typeElement.getEnclosedElements()) {
- if (enclosed.getKind().equals(CONSTRUCTOR)
- && ((ExecutableElement) enclosed).getParameters().isEmpty()
- && !enclosed.getModifiers().contains(PRIVATE)) {
- return true;
- }
- }
-
- // TODO(gak): still need checks for visibility
-
- return false;
- }
-
- private static boolean requiresEnclosingInstance(TypeElement typeElement) {
- switch (typeElement.getNestingKind()) {
- case TOP_LEVEL:
- return false;
- case MEMBER:
- return !typeElement.getModifiers().contains(STATIC);
- case ANONYMOUS:
- case LOCAL:
- return true;
- }
- throw new AssertionError(
- "TypeElement cannot have nesting kind: " + typeElement.getNestingKind());
- }
-
- /**
- * A version of {@link Map#computeIfAbsent(Object, Function)} that allows {@code mappingFunction}
- * to update {@code map}.
- */
- static <K, V> V reentrantComputeIfAbsent(
- Map<K, V> map, K key, Function<? super K, ? extends V> mappingFunction) {
- V value = map.get(key);
- if (value == null) {
- value = mappingFunction.apply(key);
- if (value != null) {
- map.put(key, value);
- }
- }
- return value;
- }
-
- private Util() {}
-}
diff --git a/java/dagger/internal/codegen/Validation.java b/java/dagger/internal/codegen/Validation.java
deleted file mode 100644
index f6a4b3f4d..000000000
--- a/java/dagger/internal/codegen/Validation.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import javax.inject.Qualifier;
-
-/**
- * Qualifier annotation for the {@link dagger.spi.BindingGraphPlugin}s that are used to implement
- * core Dagger validation.
- */
-@Documented
-@Retention(RetentionPolicy.RUNTIME)
-@Qualifier
-@interface Validation {}
diff --git a/java/dagger/internal/codegen/ValidationReport.java b/java/dagger/internal/codegen/ValidationReport.java
deleted file mode 100644
index d7c3252e9..000000000
--- a/java/dagger/internal/codegen/ValidationReport.java
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (C) 2014 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.ElementFormatter.elementToString;
-import static javax.tools.Diagnostic.Kind.ERROR;
-import static javax.tools.Diagnostic.Kind.NOTE;
-import static javax.tools.Diagnostic.Kind.WARNING;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.graph.Traverser;
-import com.google.errorprone.annotations.CanIgnoreReturnValue;
-import com.google.errorprone.annotations.CheckReturnValue;
-import java.util.Optional;
-import javax.annotation.processing.Messager;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.Element;
-import javax.tools.Diagnostic;
-import javax.tools.Diagnostic.Kind;
-
-/** A collection of issues to report for source code. */
-@AutoValue
-abstract class ValidationReport<T extends Element> {
-
- /**
- * The subject of the report. Should be an element within a compilation unit being processed by
- * this compilation task.
- */
- abstract T subject();
-
- /** The items to report for the {@linkplain #subject() subject}. */
- abstract ImmutableSet<Item> items();
-
- /** Returns the {@link #items()} from this report and all transitive subreports. */
- ImmutableSet<Item> allItems() {
- return allReports()
- .stream()
- .flatMap(report -> report.items().stream())
- .collect(toImmutableSet());
- }
-
- /** Other reports associated with this one. */
- abstract ImmutableSet<ValidationReport<?>> subreports();
-
- private static final Traverser<ValidationReport<?>> SUBREPORTS =
- Traverser.forTree(ValidationReport::subreports);
-
- /** Returns this report and all transitive subreports. */
- ImmutableSet<ValidationReport<?>> allReports() {
- return ImmutableSet.copyOf(SUBREPORTS.depthFirstPreOrder(this));
- }
-
- /**
- * {@code true} if {@link #isClean()} should return {@code false} even if there are no error items
- * in this report.
- */
- abstract boolean markedDirty();
-
- /**
- * Returns {@code true} if there are no errors in this report or any subreports and {@link
- * #markedDirty()} is {@code false}.
- */
- boolean isClean() {
- if (markedDirty()) {
- return false;
- }
- for (Item item : items()) {
- switch (item.kind()) {
- case ERROR:
- return false;
- default:
- break;
- }
- }
- for (ValidationReport<?> subreport : subreports()) {
- if (!subreport.isClean()) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Prints all {@linkplain #items() messages} to {@code messager} (and recurs for subreports). If a
- * message's {@linkplain Item#element() element} is contained within the report's {@linkplain
- * #subject() subject}, associates the message with the message's element. Otherwise, since
- * {@link Diagnostic} reporting is expected to be associated with elements that are currently
- * being compiled, associates the message with the subject itself and prepends a reference to the
- * item's element.
- */
- void printMessagesTo(Messager messager) {
- for (Item item : items()) {
- if (isEnclosedIn(subject(), item.element())) {
- if (item.annotation().isPresent()) {
- if (item.annotationValue().isPresent()) {
- messager.printMessage(
- item.kind(),
- item.message(),
- item.element(),
- item.annotation().get(),
- item.annotationValue().get());
- } else {
- messager.printMessage(
- item.kind(), item.message(), item.element(), item.annotation().get());
- }
- } else {
- messager.printMessage(item.kind(), item.message(), item.element());
- }
- } else {
- String message = String.format("[%s] %s", elementToString(item.element()), item.message());
- messager.printMessage(item.kind(), message, subject());
- }
- }
- for (ValidationReport<?> subreport : subreports()) {
- subreport.printMessagesTo(messager);
- }
- }
-
- private static boolean isEnclosedIn(Element parent, Element child) {
- Element current = child;
- while (current != null) {
- if (current.equals(parent)) {
- return true;
- }
- current = current.getEnclosingElement();
- }
- return false;
- }
-
- @AutoValue
- static abstract class Item {
- abstract String message();
- abstract Kind kind();
- abstract Element element();
- abstract Optional<AnnotationMirror> annotation();
- abstract Optional<AnnotationValue> annotationValue();
- }
-
- static <T extends Element> Builder<T> about(T subject) {
- return new Builder<>(subject);
- }
-
- @CanIgnoreReturnValue
- static final class Builder<T extends Element> {
- private final T subject;
- private final ImmutableSet.Builder<Item> items = ImmutableSet.builder();
- private final ImmutableSet.Builder<ValidationReport<?>> subreports = ImmutableSet.builder();
- private boolean markedDirty;
-
- private Builder(T subject) {
- this.subject = subject;
- }
-
- @CheckReturnValue
- T getSubject() {
- return subject;
- }
-
- Builder<T> addItems(Iterable<Item> newItems) {
- items.addAll(newItems);
- return this;
- }
-
- Builder<T> addError(String message) {
- return addError(message, subject);
- }
-
- Builder<T> addError(String message, Element element) {
- return addItem(message, ERROR, element);
- }
-
- Builder<T> addError(String message, Element element, AnnotationMirror annotation) {
- return addItem(message, ERROR, element, annotation);
- }
-
- Builder<T> addError(
- String message,
- Element element,
- AnnotationMirror annotation,
- AnnotationValue annotationValue) {
- return addItem(message, ERROR, element, annotation, annotationValue);
- }
-
- Builder<T> addWarning(String message) {
- return addWarning(message, subject);
- }
-
- Builder<T> addWarning(String message, Element element) {
- return addItem(message, WARNING, element);
- }
-
- Builder<T> addWarning(String message, Element element, AnnotationMirror annotation) {
- return addItem(message, WARNING, element, annotation);
- }
-
- Builder<T> addWarning(
- String message,
- Element element,
- AnnotationMirror annotation,
- AnnotationValue annotationValue) {
- return addItem(message, WARNING, element, annotation, annotationValue);
- }
-
- Builder<T> addNote(String message) {
- return addNote(message, subject);
- }
-
- Builder<T> addNote(String message, Element element) {
- return addItem(message, NOTE, element);
- }
-
- Builder<T> addNote(String message, Element element, AnnotationMirror annotation) {
- return addItem(message, NOTE, element, annotation);
- }
-
- Builder<T> addNote(
- String message,
- Element element,
- AnnotationMirror annotation,
- AnnotationValue annotationValue) {
- return addItem(message, NOTE, element, annotation, annotationValue);
- }
-
- Builder<T> addItem(String message, Kind kind, Element element) {
- return addItem(message, kind, element, Optional.empty(), Optional.empty());
- }
-
- Builder<T> addItem(String message, Kind kind, Element element, AnnotationMirror annotation) {
- return addItem(message, kind, element, Optional.of(annotation), Optional.empty());
- }
-
- Builder<T> addItem(
- String message,
- Kind kind,
- Element element,
- AnnotationMirror annotation,
- AnnotationValue annotationValue) {
- return addItem(message, kind, element, Optional.of(annotation), Optional.of(annotationValue));
- }
-
- private Builder<T> addItem(
- String message,
- Kind kind,
- Element element,
- Optional<AnnotationMirror> annotation,
- Optional<AnnotationValue> annotationValue) {
- items.add(
- new AutoValue_ValidationReport_Item(message, kind, element, annotation, annotationValue));
- return this;
- }
-
- /**
- * If called, then {@link #isClean()} will return {@code false} even if there are no error items
- * in the report.
- */
- void markDirty() {
- this.markedDirty = true;
- }
-
- Builder<T> addSubreport(ValidationReport<?> subreport) {
- subreports.add(subreport);
- return this;
- }
-
- @CheckReturnValue
- ValidationReport<T> build() {
- return new AutoValue_ValidationReport<>(
- subject, items.build(), subreports.build(), markedDirty);
- }
- }
-}
diff --git a/java/dagger/internal/codegen/ValidationType.java b/java/dagger/internal/codegen/ValidationType.java
deleted file mode 100644
index 5d19dc17a..000000000
--- a/java/dagger/internal/codegen/ValidationType.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2015 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import java.util.Optional;
-import javax.tools.Diagnostic;
-
-/**
- * Allows options to control how component process validates things such as scope cycles
- * or nullability.
- */
-enum ValidationType {
- ERROR,
- WARNING,
- NONE;
-
- Optional<Diagnostic.Kind> diagnosticKind() {
- switch (this) {
- case ERROR:
- return Optional.of(Diagnostic.Kind.ERROR);
- case WARNING:
- return Optional.of(Diagnostic.Kind.WARNING);
- default:
- return Optional.empty();
- }
- }
-}
diff --git a/java/dagger/internal/codegen/base/BUILD b/java/dagger/internal/codegen/base/BUILD
new file mode 100644
index 000000000..0bcbb128e
--- /dev/null
+++ b/java/dagger/internal/codegen/base/BUILD
@@ -0,0 +1,66 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Sources related to compiler options.
+
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(default_visibility = ["//:src"])
+
+SHARED_SOURCES = [
+ "ClearableCache.java",
+ "MoreAnnotationMirrors.java",
+ "MoreAnnotationValues.java",
+]
+
+java_library(
+ name = "base",
+ srcs = glob(
+ ["*.java"],
+ exclude = SHARED_SOURCES,
+ ),
+ plugins = ["//java/dagger/internal/codegen/bootstrap"],
+ tags = ["maven:merged"],
+ exports = [":shared"],
+ deps = [
+ ":shared",
+ "//java/dagger:core",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/codegen/javapoet",
+ "//java/dagger/internal/codegen/langmodel",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:concurrent",
+ "//java/dagger/producers",
+ "//java/dagger/spi",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
+
+# TODO(bcorso): Remove this target but first remove spi and producers from :base
+java_library(
+ name = "shared",
+ srcs = SHARED_SOURCES,
+ tags = ["maven:merged"],
+ deps = [
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
diff --git a/java/dagger/internal/codegen/base/ClearableCache.java b/java/dagger/internal/codegen/base/ClearableCache.java
new file mode 100644
index 000000000..61e9482eb
--- /dev/null
+++ b/java/dagger/internal/codegen/base/ClearableCache.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+/** A cache of objects that can be cleared. */
+public interface ClearableCache {
+ /** Releases cached references. */
+ void clearCache();
+}
diff --git a/java/dagger/internal/codegen/base/ComponentAnnotation.java b/java/dagger/internal/codegen/base/ComponentAnnotation.java
new file mode 100644
index 000000000..c9b3580c1
--- /dev/null
+++ b/java/dagger/internal/codegen/base/ComponentAnnotation.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
+import static com.google.auto.common.MoreElements.asType;
+import static com.google.auto.common.MoreTypes.asTypeElements;
+import static com.google.auto.common.MoreTypes.isTypeOf;
+import static dagger.internal.codegen.base.MoreAnnotationValues.asAnnotationValues;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.javapoet.TypeNames.PRODUCER_MODULE;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnyAnnotation;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import dagger.Component;
+import dagger.Subcomponent;
+import dagger.producers.ProductionComponent;
+import dagger.producers.ProductionSubcomponent;
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A {@code @Component}, {@code @Subcomponent}, {@code @ProductionComponent}, or
+ * {@code @ProductionSubcomponent} annotation, or a {@code @Module} or {@code @ProducerModule}
+ * annotation that is being treated as a component annotation when validating full binding graphs
+ * for modules.
+ */
+public abstract class ComponentAnnotation {
+ /** The root component annotation types. */
+ private static final ImmutableSet<Class<? extends Annotation>> ROOT_COMPONENT_ANNOTATIONS =
+ ImmutableSet.of(Component.class, ProductionComponent.class);
+
+ /** The subcomponent annotation types. */
+ private static final ImmutableSet<Class<? extends Annotation>> SUBCOMPONENT_ANNOTATIONS =
+ ImmutableSet.of(Subcomponent.class, ProductionSubcomponent.class);
+
+ /** All component annotation types. */
+ private static final ImmutableSet<Class<? extends Annotation>> ALL_COMPONENT_ANNOTATIONS =
+ ImmutableSet.<Class<? extends Annotation>>builder()
+ .addAll(ROOT_COMPONENT_ANNOTATIONS)
+ .addAll(SUBCOMPONENT_ANNOTATIONS)
+ .build();
+
+ /** The annotation itself. */
+ public abstract AnnotationMirror annotation();
+
+ /** The simple name of the annotation type. */
+ public String simpleName() {
+ return MoreAnnotationMirrors.simpleName(annotation()).toString();
+ }
+
+ /**
+ * Returns {@code true} if the annotation is a {@code @Subcomponent} or
+ * {@code @ProductionSubcomponent}.
+ */
+ public abstract boolean isSubcomponent();
+
+ /**
+ * Returns {@code true} if the annotation is a {@code @ProductionComponent},
+ * {@code @ProductionSubcomponent}, or {@code @ProducerModule}.
+ */
+ public abstract boolean isProduction();
+
+ /**
+ * Returns {@code true} if the annotation is a real component annotation and not a module
+ * annotation.
+ */
+ public abstract boolean isRealComponent();
+
+ /** The values listed as {@code dependencies}. */
+ public abstract ImmutableList<AnnotationValue> dependencyValues();
+
+ /** The types listed as {@code dependencies}. */
+ public ImmutableList<TypeMirror> dependencyTypes() {
+ return dependencyValues().stream().map(MoreAnnotationValues::asType).collect(toImmutableList());
+ }
+
+ /**
+ * The types listed as {@code dependencies}.
+ *
+ * @throws IllegalArgumentException if any of {@link #dependencyTypes()} are error types
+ */
+ public ImmutableList<TypeElement> dependencies() {
+ return asTypeElements(dependencyTypes()).asList();
+ }
+
+ /** The values listed as {@code modules}. */
+ public abstract ImmutableList<AnnotationValue> moduleValues();
+
+ /** The types listed as {@code modules}. */
+ public ImmutableList<TypeMirror> moduleTypes() {
+ return moduleValues().stream().map(MoreAnnotationValues::asType).collect(toImmutableList());
+ }
+
+ /**
+ * The types listed as {@code modules}.
+ *
+ * @throws IllegalArgumentException if any of {@link #moduleTypes()} are error types
+ */
+ public ImmutableSet<TypeElement> modules() {
+ return asTypeElements(moduleTypes());
+ }
+
+ protected final ImmutableList<AnnotationValue> getAnnotationValues(String parameterName) {
+ return asAnnotationValues(getAnnotationValue(annotation(), parameterName));
+ }
+
+ /**
+ * Returns an object representing a root component annotation, not a subcomponent annotation, if
+ * one is present on {@code typeElement}.
+ */
+ public static Optional<ComponentAnnotation> rootComponentAnnotation(TypeElement typeElement) {
+ return anyComponentAnnotation(typeElement, ROOT_COMPONENT_ANNOTATIONS);
+ }
+
+ /**
+ * Returns an object representing a subcomponent annotation, if one is present on {@code
+ * typeElement}.
+ */
+ public static Optional<ComponentAnnotation> subcomponentAnnotation(TypeElement typeElement) {
+ return anyComponentAnnotation(typeElement, SUBCOMPONENT_ANNOTATIONS);
+ }
+
+ /**
+ * Returns an object representing a root component or subcomponent annotation, if one is present
+ * on {@code typeElement}.
+ */
+ public static Optional<ComponentAnnotation> anyComponentAnnotation(TypeElement typeElement) {
+ return anyComponentAnnotation(typeElement, ALL_COMPONENT_ANNOTATIONS);
+ }
+
+ private static Optional<ComponentAnnotation> anyComponentAnnotation(
+ TypeElement typeElement, Collection<Class<? extends Annotation>> annotations) {
+ return getAnyAnnotation(typeElement, annotations).map(ComponentAnnotation::componentAnnotation);
+ }
+
+ /** Returns {@code true} if the argument is a component annotation. */
+ public static boolean isComponentAnnotation(AnnotationMirror annotation) {
+ return ALL_COMPONENT_ANNOTATIONS.stream()
+ .anyMatch(annotationClass -> isTypeOf(annotationClass, annotation.getAnnotationType()));
+ }
+
+ /** Creates an object representing a component or subcomponent annotation. */
+ public static ComponentAnnotation componentAnnotation(AnnotationMirror annotation) {
+ RealComponentAnnotation.Builder annotationBuilder =
+ RealComponentAnnotation.builder().annotation(annotation);
+
+ if (isTypeOf(Component.class, annotation.getAnnotationType())) {
+ return annotationBuilder.isProduction(false).isSubcomponent(false).build();
+ }
+ if (isTypeOf(Subcomponent.class, annotation.getAnnotationType())) {
+ return annotationBuilder.isProduction(false).isSubcomponent(true).build();
+ }
+ if (isTypeOf(ProductionComponent.class, annotation.getAnnotationType())) {
+ return annotationBuilder.isProduction(true).isSubcomponent(false).build();
+ }
+ if (isTypeOf(ProductionSubcomponent.class, annotation.getAnnotationType())) {
+ return annotationBuilder.isProduction(true).isSubcomponent(true).build();
+ }
+ throw new IllegalArgumentException(
+ annotation
+ + " must be a Component, Subcomponent, ProductionComponent, "
+ + "or ProductionSubcomponent annotation");
+ }
+
+ /** Creates a fictional component annotation representing a module. */
+ public static ComponentAnnotation fromModuleAnnotation(ModuleAnnotation moduleAnnotation) {
+ return new AutoValue_ComponentAnnotation_FictionalComponentAnnotation(moduleAnnotation);
+ }
+
+ /** The root component annotation types. */
+ public static ImmutableSet<Class<? extends Annotation>> rootComponentAnnotations() {
+ return ROOT_COMPONENT_ANNOTATIONS;
+ }
+
+ /** The subcomponent annotation types. */
+ public static ImmutableSet<Class<? extends Annotation>> subcomponentAnnotations() {
+ return SUBCOMPONENT_ANNOTATIONS;
+ }
+
+ /** All component annotation types. */
+ public static ImmutableSet<Class<? extends Annotation>> allComponentAnnotations() {
+ return ALL_COMPONENT_ANNOTATIONS;
+ }
+
+ /**
+ * An actual component annotation.
+ *
+ * @see FictionalComponentAnnotation
+ */
+ @AutoValue
+ abstract static class RealComponentAnnotation extends ComponentAnnotation {
+
+ @Override
+ @Memoized
+ public ImmutableList<AnnotationValue> dependencyValues() {
+ return isSubcomponent() ? ImmutableList.of() : getAnnotationValues("dependencies");
+ }
+
+ @Override
+ @Memoized
+ public ImmutableList<TypeMirror> dependencyTypes() {
+ return super.dependencyTypes();
+ }
+
+ @Override
+ @Memoized
+ public ImmutableList<TypeElement> dependencies() {
+ return super.dependencies();
+ }
+
+ @Override
+ public boolean isRealComponent() {
+ return true;
+ }
+
+ @Override
+ @Memoized
+ public ImmutableList<AnnotationValue> moduleValues() {
+ return getAnnotationValues("modules");
+ }
+
+ @Override
+ @Memoized
+ public ImmutableList<TypeMirror> moduleTypes() {
+ return super.moduleTypes();
+ }
+
+ @Override
+ @Memoized
+ public ImmutableSet<TypeElement> modules() {
+ return super.modules();
+ }
+
+ static Builder builder() {
+ return new AutoValue_ComponentAnnotation_RealComponentAnnotation.Builder();
+ }
+
+ @AutoValue.Builder
+ interface Builder {
+ Builder annotation(AnnotationMirror annotation);
+
+ Builder isSubcomponent(boolean isSubcomponent);
+
+ Builder isProduction(boolean isProduction);
+
+ RealComponentAnnotation build();
+ }
+ }
+
+ /**
+ * A fictional component annotation used to represent modules or other collections of bindings as
+ * a component.
+ */
+ @AutoValue
+ abstract static class FictionalComponentAnnotation extends ComponentAnnotation {
+
+ @Override
+ public AnnotationMirror annotation() {
+ return moduleAnnotation().annotation();
+ }
+
+ @Override
+ public boolean isSubcomponent() {
+ return false;
+ }
+
+ @Override
+ public boolean isProduction() {
+ return ClassName.get(asType(moduleAnnotation().annotation().getAnnotationType().asElement()))
+ .equals(PRODUCER_MODULE);
+ }
+
+ @Override
+ public boolean isRealComponent() {
+ return false;
+ }
+
+ @Override
+ public ImmutableList<AnnotationValue> dependencyValues() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public ImmutableList<AnnotationValue> moduleValues() {
+ return moduleAnnotation().includesAsAnnotationValues();
+ }
+
+ @Override
+ @Memoized
+ public ImmutableList<TypeMirror> moduleTypes() {
+ return super.moduleTypes();
+ }
+
+ @Override
+ @Memoized
+ public ImmutableSet<TypeElement> modules() {
+ return super.modules();
+ }
+
+ public abstract ModuleAnnotation moduleAnnotation();
+ }
+}
diff --git a/java/dagger/internal/codegen/base/ContributionType.java b/java/dagger/internal/codegen/base/ContributionType.java
new file mode 100644
index 000000000..c046daaf2
--- /dev/null
+++ b/java/dagger/internal/codegen/base/ContributionType.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+
+import dagger.multibindings.ElementsIntoSet;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
+import javax.lang.model.element.Element;
+
+/** Whether a binding or declaration is for a unique contribution or a map or set multibinding. */
+public enum ContributionType {
+ /** Represents map bindings. */
+ MAP,
+ /** Represents set bindings. */
+ SET,
+ /** Represents set values bindings. */
+ SET_VALUES,
+ /** Represents a valid non-collection binding. */
+ UNIQUE,
+ ;
+
+ /** An object that is associated with a {@link ContributionType}. */
+ public interface HasContributionType {
+
+ /** The contribution type of this object. */
+ ContributionType contributionType();
+ }
+
+ /** {@code true} if this is for a multibinding. */
+ public boolean isMultibinding() {
+ return !this.equals(UNIQUE);
+ }
+
+ /**
+ * The contribution type from a binding element's annotations. Presumes a well-formed binding
+ * element (at most one of @IntoSet, @IntoMap, @ElementsIntoSet and @Provides.type). {@link
+ * dagger.internal.codegen.validation.BindingMethodValidator} and {@link
+ * dagger.internal.codegen.validation.BindsInstanceProcessingStep} validate correctness on their
+ * own.
+ */
+ public static ContributionType fromBindingElement(Element element) {
+ // TODO(bcorso): Replace these class references with ClassName.
+ if (isAnnotationPresent(element, IntoMap.class)) {
+ return ContributionType.MAP;
+ } else if (isAnnotationPresent(element, IntoSet.class)) {
+ return ContributionType.SET;
+ } else if (isAnnotationPresent(element, ElementsIntoSet.class)) {
+ return ContributionType.SET_VALUES;
+ }
+ return ContributionType.UNIQUE;
+ }
+}
diff --git a/java/dagger/internal/codegen/base/DiagnosticFormatting.java b/java/dagger/internal/codegen/base/DiagnosticFormatting.java
new file mode 100644
index 000000000..93e445c57
--- /dev/null
+++ b/java/dagger/internal/codegen/base/DiagnosticFormatting.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Utility methods for formatting diagnostics to the {@link javax.annotation.processing.Messager}.
+ */
+public final class DiagnosticFormatting {
+
+ /**
+ * A regular expression to match a small list of specific packages deemed to be unhelpful to
+ * display in fully qualified types in error messages.
+ *
+ * <p>Note: This should never be applied to messages themselves.
+ */
+ private static final Pattern COMMON_PACKAGE_PATTERN =
+ Pattern.compile(
+ "(?:^|[^.a-z_])" // What we want to match on but not capture.
+ + "((?:" // Start a group with a non-capturing or part
+ + "java[.]lang"
+ + "|java[.]util"
+ + "|javax[.]inject"
+ + "|dagger"
+ + "|dagger[.]multibindings"
+ + "|com[.]google[.]common[.]base"
+ + "|com[.]google[.]common[.]collect"
+ + ")[.])" // Always end with a literal .
+ + "[A-Z]"); // What we want to match on but not capture.
+
+ /**
+ * A method to strip out common packages and a few rare type prefixes from types' string
+ * representation before being used in error messages.
+ *
+ * <p>This type assumes a String value that is a valid fully qualified (and possibly
+ * parameterized) type, and should NOT be used with arbitrary text, especially prose error
+ * messages.
+ *
+ * <p>TODO(user): Tighten these to take type representations (mirrors and elements) to avoid
+ * accidental mis-use by running errors through this method.
+ */
+ public static String stripCommonTypePrefixes(String type) {
+ // Do regex magic to remove common packages we care to shorten.
+ Matcher matcher = COMMON_PACKAGE_PATTERN.matcher(type);
+ StringBuilder result = new StringBuilder();
+ int index = 0;
+ while (matcher.find()) {
+ result.append(type.subSequence(index, matcher.start(1)));
+ index = matcher.end(1); // Skip the matched pattern content.
+ }
+ result.append(type.subSequence(index, type.length()));
+ return result.toString();
+ }
+
+ private DiagnosticFormatting() {}
+}
diff --git a/java/dagger/internal/codegen/base/ElementFormatter.java b/java/dagger/internal/codegen/base/ElementFormatter.java
new file mode 100644
index 000000000..f85fbfde8
--- /dev/null
+++ b/java/dagger/internal/codegen/base/ElementFormatter.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2013 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import static com.google.auto.common.MoreElements.asExecutable;
+import static java.util.stream.Collectors.joining;
+
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.util.ElementKindVisitor8;
+
+/**
+ * Formats elements into a useful string representation.
+ *
+ * <p>Elements directly enclosed by a type are preceded by the enclosing type's qualified name.
+ *
+ * <p>Parameters are given with their enclosing executable, with other parameters elided.
+ */
+public final class ElementFormatter extends Formatter<Element> {
+ @Inject
+ ElementFormatter() {}
+
+ @Override
+ public String format(Element element) {
+ return elementToString(element);
+ }
+
+ /**
+ * Returns a useful string form for an element.
+ *
+ * <p>Elements directly enclosed by a type are preceded by the enclosing type's qualified name.
+ *
+ * <p>Parameters are given with their enclosing executable, with other parameters elided.
+ */
+ public static String elementToString(Element element) {
+ return element.accept(ELEMENT_TO_STRING, null);
+ }
+
+ private static final ElementVisitor<String, Void> ELEMENT_TO_STRING =
+ new ElementKindVisitor8<String, Void>() {
+ @Override
+ public String visitExecutable(ExecutableElement executableElement, Void aVoid) {
+ return enclosingTypeAndMemberName(executableElement)
+ .append(
+ executableElement.getParameters().stream()
+ .map(parameter -> parameter.asType().toString())
+ .collect(joining(", ", "(", ")")))
+ .toString();
+ }
+
+ @Override
+ public String visitVariableAsParameter(VariableElement parameter, Void aVoid) {
+ ExecutableElement methodOrConstructor = asExecutable(parameter.getEnclosingElement());
+ return enclosingTypeAndMemberName(methodOrConstructor)
+ .append('(')
+ .append(
+ formatArgumentInList(
+ methodOrConstructor.getParameters().indexOf(parameter),
+ methodOrConstructor.getParameters().size(),
+ parameter.getSimpleName()))
+ .append(')')
+ .toString();
+ }
+
+ @Override
+ public String visitVariableAsField(VariableElement field, Void aVoid) {
+ return enclosingTypeAndMemberName(field).toString();
+ }
+
+ @Override
+ public String visitType(TypeElement type, Void aVoid) {
+ return type.getQualifiedName().toString();
+ }
+
+ @Override
+ protected String defaultAction(Element element, Void aVoid) {
+ throw new UnsupportedOperationException(
+ "Can't determine string for " + element.getKind() + " element " + element);
+ }
+
+ private StringBuilder enclosingTypeAndMemberName(Element element) {
+ StringBuilder name = new StringBuilder(element.getEnclosingElement().accept(this, null));
+ if (!element.getSimpleName().contentEquals("<init>")) {
+ name.append('.').append(element.getSimpleName());
+ }
+ return name;
+ }
+ };
+}
diff --git a/java/dagger/internal/codegen/base/Formatter.java b/java/dagger/internal/codegen/base/Formatter.java
new file mode 100644
index 000000000..c5e235768
--- /dev/null
+++ b/java/dagger/internal/codegen/base/Formatter.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import static com.google.common.base.Preconditions.checkElementIndex;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+
+/**
+ * A formatter which transforms an instance of a particular type into a string
+ * representation.
+ *
+ * @param <T> the type of the object to be transformed.
+ */
+public abstract class Formatter<T> implements Function<T, String> {
+
+ public static final String INDENT = " ";
+ public static final String DOUBLE_INDENT = INDENT + INDENT;
+ private static final int LIST_LIMIT = 10;
+
+ /**
+ * Performs the transformation of an object into a string representation.
+ */
+ public abstract String format(T object);
+
+ /**
+ * Performs the transformation of an object into a string representation in conformity with the
+ * {@link Function}{@code <T, String>} contract, delegating to {@link #format(Object)}.
+ *
+ * @deprecated Call {@link #format(Object)} instead. This method exists to make formatters easy to
+ * use when functions are required, but shouldn't be called directly.
+ */
+ @SuppressWarnings("javadoc")
+ @Deprecated
+ @Override
+ public final String apply(T object) {
+ return format(object);
+ }
+
+ /** Formats {@code items}, one per line. Stops after {@value #LIST_LIMIT} items. */
+ public void formatIndentedList(
+ StringBuilder builder, Iterable<? extends T> items, int indentLevel) {
+ for (T item : Iterables.limit(items, LIST_LIMIT)) {
+ String formatted = format(item);
+ if (formatted.isEmpty()) {
+ continue;
+ }
+ builder.append('\n');
+ appendIndent(builder, indentLevel);
+ builder.append(formatted);
+ }
+ int numberOfOtherItems = Iterables.size(items) - LIST_LIMIT;
+ if (numberOfOtherItems > 0) {
+ builder.append('\n');
+ appendIndent(builder, indentLevel);
+ builder.append("and ").append(numberOfOtherItems).append(" other");
+ }
+ if (numberOfOtherItems > 1) {
+ builder.append('s');
+ }
+ }
+
+ private void appendIndent(StringBuilder builder, int indentLevel) {
+ for (int i = 0; i < indentLevel; i++) {
+ builder.append(INDENT);
+ }
+ }
+
+ public static String formatArgumentInList(int index, int size, CharSequence name) {
+ checkElementIndex(index, size);
+ StringBuilder builder = new StringBuilder();
+ if (index > 0) {
+ builder.append("…, ");
+ }
+ builder.append(name);
+ if (index < size - 1) {
+ builder.append(", …");
+ }
+ return builder.toString();
+ }
+}
diff --git a/java/dagger/internal/codegen/base/FrameworkTypes.java b/java/dagger/internal/codegen/base/FrameworkTypes.java
new file mode 100644
index 000000000..4cd54a33d
--- /dev/null
+++ b/java/dagger/internal/codegen/base/FrameworkTypes.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import static com.google.auto.common.MoreTypes.isType;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSet;
+import dagger.Lazy;
+import dagger.MembersInjector;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import java.util.Set;
+import javax.inject.Provider;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A collection of utility methods for dealing with Dagger framework types. A framework type is any
+ * type that the framework itself defines.
+ */
+public final class FrameworkTypes {
+ private static final ImmutableSet<Class<?>> PROVISION_TYPES =
+ ImmutableSet.of(Provider.class, Lazy.class, MembersInjector.class);
+
+ // NOTE(beder): ListenableFuture is not considered a producer framework type because it is not
+ // defined by the framework, so we can't treat it specially in ordinary Dagger.
+ private static final ImmutableSet<Class<?>> PRODUCTION_TYPES =
+ ImmutableSet.of(Produced.class, Producer.class);
+
+ /** Returns true if the type represents a producer-related framework type. */
+ public static boolean isProducerType(TypeMirror type) {
+ return isType(type) && typeIsOneOf(PRODUCTION_TYPES, type);
+ }
+
+ /** Returns true if the type represents a framework type. */
+ public static boolean isFrameworkType(TypeMirror type) {
+ return isType(type)
+ && (typeIsOneOf(PROVISION_TYPES, type)
+ || typeIsOneOf(PRODUCTION_TYPES, type));
+ }
+
+ private static boolean typeIsOneOf(Set<Class<?>> classes, TypeMirror type) {
+ for (Class<?> clazz : classes) {
+ if (MoreTypes.isTypeOf(clazz, type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private FrameworkTypes() {}
+}
diff --git a/java/dagger/internal/codegen/base/Keys.java b/java/dagger/internal/codegen/base/Keys.java
new file mode 100644
index 000000000..a25f9963f
--- /dev/null
+++ b/java/dagger/internal/codegen/base/Keys.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Key;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleTypeVisitor6;
+
+/** Utility methods related to {@link Key}s. */
+public final class Keys {
+ public static boolean isValidMembersInjectionKey(Key key) {
+ return !key.qualifier().isPresent()
+ && !key.multibindingContributionIdentifier().isPresent()
+ && key.type().getKind().equals(TypeKind.DECLARED);
+ }
+
+ /**
+ * Returns {@code true} if this is valid as an implicit key (that is, if it's valid for a
+ * just-in-time binding by discovering an {@code @Inject} constructor).
+ */
+ public static boolean isValidImplicitProvisionKey(Key key, DaggerTypes types) {
+ return isValidImplicitProvisionKey(key.qualifier(), key.type(), types);
+ }
+
+ /**
+ * Returns {@code true} if a key with {@code qualifier} and {@code type} is valid as an implicit
+ * key (that is, if it's valid for a just-in-time binding by discovering an {@code @Inject}
+ * constructor).
+ */
+ public static boolean isValidImplicitProvisionKey(
+ Optional<? extends AnnotationMirror> qualifier, TypeMirror type, final DaggerTypes types) {
+ // Qualifiers disqualify implicit provisioning.
+ if (qualifier.isPresent()) {
+ return false;
+ }
+
+ return type.accept(
+ new SimpleTypeVisitor6<Boolean, Void>(false) {
+ @Override
+ public Boolean visitDeclared(DeclaredType type, Void ignored) {
+ // Non-classes or abstract classes aren't allowed.
+ TypeElement element = MoreElements.asType(type.asElement());
+ if (!element.getKind().equals(ElementKind.CLASS)
+ || element.getModifiers().contains(Modifier.ABSTRACT)) {
+ return false;
+ }
+
+ // If the key has type arguments, validate that each type argument is declared.
+ // Otherwise the type argument may be a wildcard (or other type), and we can't
+ // resolve that to actual types.
+ for (TypeMirror arg : type.getTypeArguments()) {
+ if (arg.getKind() != TypeKind.DECLARED) {
+ return false;
+ }
+ }
+
+ // Also validate that the key is not the erasure of a generic type.
+ // If it is, that means the user referred to Foo<T> as just 'Foo',
+ // which we don't allow. (This is a judgement call -- we *could*
+ // allow it and instantiate the type bounds... but we don't.)
+ return MoreTypes.asDeclared(element.asType()).getTypeArguments().isEmpty()
+ || !types.isSameType(types.erasure(element.asType()), type);
+ }
+ },
+ null);
+ }
+}
diff --git a/java/dagger/internal/codegen/base/MapKeyAccessibility.java b/java/dagger/internal/codegen/base/MapKeyAccessibility.java
new file mode 100644
index 000000000..93c265aa6
--- /dev/null
+++ b/java/dagger/internal/codegen/base/MapKeyAccessibility.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+
+import dagger.internal.codegen.langmodel.Accessibility;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Predicate;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleAnnotationValueVisitor8;
+
+/** Utility class for checking the visibility of an annotation. */
+public final class MapKeyAccessibility extends SimpleAnnotationValueVisitor8<Boolean, Void> {
+ private final Predicate<TypeMirror> accessibilityChecker;
+
+ private MapKeyAccessibility(Predicate<TypeMirror> accessibilityChecker) {
+ this.accessibilityChecker = accessibilityChecker;
+ }
+
+ @Override
+ public Boolean visitAnnotation(AnnotationMirror annotation, Void aVoid) {
+ // The annotation type is not checked, as the generated code will refer to the @AutoAnnotation
+ // generated type which is always public
+ return visitValues(annotation.getElementValues().values());
+ }
+
+ @Override
+ public Boolean visitArray(List<? extends AnnotationValue> values, Void aVoid) {
+ return visitValues(values);
+ }
+
+ private boolean visitValues(Collection<? extends AnnotationValue> values) {
+ return values.stream().allMatch(value -> value.accept(this, null));
+ }
+
+ @Override
+ public Boolean visitEnumConstant(VariableElement enumConstant, Void aVoid) {
+ return accessibilityChecker.test(enumConstant.getEnclosingElement().asType());
+ }
+
+ @Override
+ public Boolean visitType(TypeMirror type, Void aVoid) {
+ return accessibilityChecker.test(type);
+ }
+
+ @Override
+ protected Boolean defaultAction(Object o, Void aVoid) {
+ return true;
+ }
+
+ public static boolean isMapKeyAccessibleFrom(
+ AnnotationMirror annotation, String accessingPackage) {
+ return new MapKeyAccessibility(type -> isTypeAccessibleFrom(type, accessingPackage))
+ .visitAnnotation(annotation, null);
+ }
+
+ public static boolean isMapKeyPubliclyAccessible(AnnotationMirror annotation) {
+ return new MapKeyAccessibility(Accessibility::isTypePubliclyAccessible)
+ .visitAnnotation(annotation, null);
+ }
+}
diff --git a/java/dagger/internal/codegen/base/MapType.java b/java/dagger/internal/codegen/base/MapType.java
new file mode 100644
index 000000000..4e2307a6c
--- /dev/null
+++ b/java/dagger/internal/codegen/base/MapType.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Equivalence;
+import dagger.model.Key;
+import java.util.Map;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Information about a {@link Map} {@link TypeMirror}.
+ */
+@AutoValue
+public abstract class MapType {
+ /**
+ * The map type itself, wrapped using {@link MoreTypes#equivalence()}. Use
+ * {@link #declaredMapType()} instead.
+ */
+ protected abstract Equivalence.Wrapper<DeclaredType> wrappedDeclaredMapType();
+
+ /**
+ * The map type itself.
+ */
+ private DeclaredType declaredMapType() {
+ return wrappedDeclaredMapType().get();
+ }
+
+ /**
+ * {@code true} if the map type is the raw {@link Map} type.
+ */
+ public boolean isRawType() {
+ return declaredMapType().getTypeArguments().isEmpty();
+ }
+
+ /**
+ * The map key type.
+ *
+ * @throws IllegalStateException if {@link #isRawType()} is true.
+ */
+ public TypeMirror keyType() {
+ checkState(!isRawType());
+ return declaredMapType().getTypeArguments().get(0);
+ }
+
+ /**
+ * The map value type.
+ *
+ * @throws IllegalStateException if {@link #isRawType()} is true.
+ */
+ public TypeMirror valueType() {
+ checkState(!isRawType());
+ return declaredMapType().getTypeArguments().get(1);
+ }
+
+ /**
+ * {@code true} if {@link #valueType()} is a {@code clazz}.
+ *
+ * @throws IllegalStateException if {@link #isRawType()} is true.
+ */
+ public boolean valuesAreTypeOf(Class<?> clazz) {
+ return MoreTypes.isType(valueType()) && MoreTypes.isTypeOf(clazz, valueType());
+ }
+
+ /**
+ * Returns {@code true} if the {@linkplain #valueType() value type} of the {@link Map} is a
+ * {@linkplain FrameworkTypes#isFrameworkType(TypeMirror) framework type}.
+ */
+ public boolean valuesAreFrameworkType() {
+ return FrameworkTypes.isFrameworkType(valueType());
+ }
+
+ /**
+ * {@code V} if {@link #valueType()} is a framework type like {@code Provider<V>} or {@code
+ * Producer<V>}.
+ *
+ * @throws IllegalStateException if {@link #isRawType()} is true or {@link #valueType()} is not a
+ * framework type
+ */
+ public TypeMirror unwrappedFrameworkValueType() {
+ checkState(
+ valuesAreFrameworkType(), "called unwrappedFrameworkValueType() on %s", declaredMapType());
+ return uncheckedUnwrappedValueType();
+ }
+
+ /**
+ * {@code V} if {@link #valueType()} is a {@code WrappingClass<V>}.
+ *
+ * @throws IllegalStateException if {@link #isRawType()} is true or {@link #valueType()} is not a
+ * {@code WrappingClass<V>}
+ * @throws IllegalArgumentException if {@code wrappingClass} does not have exactly one type
+ * parameter
+ */
+ public TypeMirror unwrappedValueType(Class<?> wrappingClass) {
+ checkArgument(
+ wrappingClass.getTypeParameters().length == 1,
+ "%s must have exactly one type parameter",
+ wrappingClass);
+ checkState(valuesAreTypeOf(wrappingClass), "expected values to be %s: %s", wrappingClass, this);
+ return uncheckedUnwrappedValueType();
+ }
+
+ private TypeMirror uncheckedUnwrappedValueType() {
+ return MoreTypes.asDeclared(valueType()).getTypeArguments().get(0);
+ }
+
+ /**
+ * {@code true} if {@code type} is a {@link Map} type.
+ */
+ public static boolean isMap(TypeMirror type) {
+ return MoreTypes.isType(type) && MoreTypes.isTypeOf(Map.class, type);
+ }
+
+ /**
+ * {@code true} if {@code key.type()} is a {@link Map} type.
+ */
+ public static boolean isMap(Key key) {
+ return isMap(key.type());
+ }
+
+ /**
+ * Returns a {@link MapType} for {@code type}.
+ *
+ * @throws IllegalArgumentException if {@code type} is not a {@link Map} type
+ */
+ public static MapType from(TypeMirror type) {
+ checkArgument(isMap(type), "%s is not a Map", type);
+ return new AutoValue_MapType(MoreTypes.equivalence().wrap(MoreTypes.asDeclared(type)));
+ }
+
+ /**
+ * Returns a {@link MapType} for {@code key}'s {@link Key#type() type}.
+ *
+ * @throws IllegalArgumentException if {@code key.type()} is not a {@link Map} type
+ */
+ public static MapType from(Key key) {
+ return from(key.type());
+ }
+}
diff --git a/java/dagger/internal/codegen/base/ModuleAnnotation.java b/java/dagger/internal/codegen/base/ModuleAnnotation.java
new file mode 100644
index 000000000..168873682
--- /dev/null
+++ b/java/dagger/internal/codegen/base/ModuleAnnotation.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
+import static com.google.auto.common.MoreTypes.asTypeElement;
+import static com.google.common.base.Preconditions.checkArgument;
+import static dagger.internal.codegen.base.MoreAnnotationValues.asAnnotationValues;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnyAnnotation;
+
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import dagger.Module;
+import dagger.producers.ProducerModule;
+import java.lang.annotation.Annotation;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.TypeElement;
+
+/** A {@code @Module} or {@code @ProducerModule} annotation. */
+@AutoValue
+public abstract class ModuleAnnotation {
+ private static final ImmutableSet<Class<? extends Annotation>> MODULE_ANNOTATIONS =
+ ImmutableSet.of(Module.class, ProducerModule.class);
+
+ /** The annotation itself. */
+ // This does not use AnnotationMirrors.equivalence() because we want the actual annotation
+ // instance.
+ public abstract AnnotationMirror annotation();
+
+ /** The simple name of the annotation. */
+ public String annotationName() {
+ return annotation().getAnnotationType().asElement().getSimpleName().toString();
+ }
+
+ /**
+ * The types specified in the {@code includes} attribute.
+ *
+ * @throws IllegalArgumentException if any of the values are error types
+ */
+ @Memoized
+ public ImmutableList<TypeElement> includes() {
+ return includesAsAnnotationValues().stream()
+ .map(MoreAnnotationValues::asType)
+ .map(MoreTypes::asTypeElement)
+ .collect(toImmutableList());
+ }
+
+ /** The values specified in the {@code includes} attribute. */
+ @Memoized
+ public ImmutableList<AnnotationValue> includesAsAnnotationValues() {
+ return asAnnotationValues(getAnnotationValue(annotation(), "includes"));
+ }
+
+ /**
+ * The types specified in the {@code subcomponents} attribute.
+ *
+ * @throws IllegalArgumentException if any of the values are error types
+ */
+ @Memoized
+ public ImmutableList<TypeElement> subcomponents() {
+ return subcomponentsAsAnnotationValues().stream()
+ .map(MoreAnnotationValues::asType)
+ .map(MoreTypes::asTypeElement)
+ .collect(toImmutableList());
+ }
+
+ /** The values specified in the {@code subcomponents} attribute. */
+ @Memoized
+ public ImmutableList<AnnotationValue> subcomponentsAsAnnotationValues() {
+ return asAnnotationValues(getAnnotationValue(annotation(), "subcomponents"));
+ }
+
+ /** Returns {@code true} if the argument is a {@code @Module} or {@code @ProducerModule}. */
+ public static boolean isModuleAnnotation(AnnotationMirror annotation) {
+ return MODULE_ANNOTATIONS.stream()
+ .map(Class::getCanonicalName)
+ .anyMatch(asTypeElement(annotation.getAnnotationType()).getQualifiedName()::contentEquals);
+ }
+
+ /** The module annotation types. */
+ public static ImmutableSet<Class<? extends Annotation>> moduleAnnotations() {
+ return MODULE_ANNOTATIONS;
+ }
+
+ /**
+ * Creates an object that represents a {@code @Module} or {@code @ProducerModule}.
+ *
+ * @throws IllegalArgumentException if {@link #isModuleAnnotation(AnnotationMirror)} returns
+ * {@code false}
+ */
+ public static ModuleAnnotation moduleAnnotation(AnnotationMirror annotation) {
+ checkArgument(
+ isModuleAnnotation(annotation),
+ "%s is not a Module or ProducerModule annotation",
+ annotation);
+ return new AutoValue_ModuleAnnotation(annotation);
+ }
+
+ /**
+ * Returns an object representing the {@code @Module} or {@code @ProducerModule} annotation if one
+ * annotates {@code typeElement}.
+ */
+ public static Optional<ModuleAnnotation> moduleAnnotation(TypeElement typeElement) {
+ return getAnyAnnotation(typeElement, Module.class, ProducerModule.class)
+ .map(ModuleAnnotation::moduleAnnotation);
+ }
+}
diff --git a/java/dagger/internal/codegen/base/MoreAnnotationMirrors.java b/java/dagger/internal/codegen/base/MoreAnnotationMirrors.java
new file mode 100644
index 000000000..234ecc10e
--- /dev/null
+++ b/java/dagger/internal/codegen/base/MoreAnnotationMirrors.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
+import static dagger.internal.codegen.base.MoreAnnotationValues.asAnnotationValues;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+
+import com.google.auto.common.AnnotationMirrors;
+import com.google.common.base.Equivalence;
+import com.google.common.collect.ImmutableList;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Name;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A utility class for working with {@link AnnotationMirror} instances, similar to {@link
+ * AnnotationMirrors}.
+ */
+public final class MoreAnnotationMirrors {
+
+ private MoreAnnotationMirrors() {}
+
+ /**
+ * Wraps an {@link Optional} of a type in an {@code Optional} of a {@link Equivalence.Wrapper} for
+ * that type.
+ */
+ public static Optional<Equivalence.Wrapper<AnnotationMirror>> wrapOptionalInEquivalence(
+ Optional<AnnotationMirror> optional) {
+ return optional.map(AnnotationMirrors.equivalence()::wrap);
+ }
+
+ /**
+ * Unwraps an {@link Optional} of a {@link Equivalence.Wrapper} into an {@code Optional} of the
+ * underlying type.
+ */
+ public static Optional<AnnotationMirror> unwrapOptionalEquivalence(
+ Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedOptional) {
+ return wrappedOptional.map(Equivalence.Wrapper::get);
+ }
+
+ public static Name simpleName(AnnotationMirror annotationMirror) {
+ return annotationMirror.getAnnotationType().asElement().getSimpleName();
+ }
+
+ /**
+ * Returns the list of types that is the value named {@code name} from {@code annotationMirror}.
+ *
+ * @throws IllegalArgumentException unless that member represents an array of types
+ */
+ public static ImmutableList<TypeMirror> getTypeListValue(
+ AnnotationMirror annotationMirror, String name) {
+ return asAnnotationValues(getAnnotationValue(annotationMirror, name))
+ .stream()
+ .map(MoreAnnotationValues::asType)
+ .collect(toImmutableList());
+ }
+}
diff --git a/java/dagger/internal/codegen/base/MoreAnnotationValues.java b/java/dagger/internal/codegen/base/MoreAnnotationValues.java
new file mode 100644
index 000000000..1ee50da11
--- /dev/null
+++ b/java/dagger/internal/codegen/base/MoreAnnotationValues.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2013 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValuesWithDefaults;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.AnnotationValueVisitor;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleAnnotationValueVisitor8;
+
+/** Utility methods for working with {@link AnnotationValue} instances. */
+public final class MoreAnnotationValues {
+ /**
+ * Returns the list of values represented by an array annotation value.
+ *
+ * @throws IllegalArgumentException unless {@code annotationValue} represents an array
+ */
+ public static ImmutableList<AnnotationValue> asAnnotationValues(AnnotationValue annotationValue) {
+ return annotationValue.accept(AS_ANNOTATION_VALUES, null);
+ }
+
+ private static final AnnotationValueVisitor<ImmutableList<AnnotationValue>, String>
+ AS_ANNOTATION_VALUES =
+ new SimpleAnnotationValueVisitor8<ImmutableList<AnnotationValue>, String>() {
+ @Override
+ public ImmutableList<AnnotationValue> visitArray(
+ List<? extends AnnotationValue> vals, String elementName) {
+ return ImmutableList.copyOf(vals);
+ }
+
+ @Override
+ protected ImmutableList<AnnotationValue> defaultAction(Object o, String elementName) {
+ throw new IllegalArgumentException(elementName + " is not an array: " + o);
+ }
+ };
+
+ /**
+ * Returns the type represented by an annotation value.
+ *
+ * @throws IllegalArgumentException unless {@code annotationValue} represents a single type
+ */
+ public static TypeMirror asType(AnnotationValue annotationValue) {
+ return AS_TYPE.visit(annotationValue);
+ }
+
+ private static final AnnotationValueVisitor<TypeMirror, Void> AS_TYPE =
+ new SimpleAnnotationValueVisitor8<TypeMirror, Void>() {
+ @Override
+ public TypeMirror visitType(TypeMirror t, Void p) {
+ return t;
+ }
+
+ @Override
+ protected TypeMirror defaultAction(Object o, Void p) {
+ throw new TypeNotPresentException(o.toString(), null);
+ }
+ };
+
+ /** Returns the int value of an annotation */
+ public static int getIntValue(AnnotationMirror annotation, String valueName) {
+ return (int) getAnnotationValue(annotation, valueName).getValue();
+ }
+
+ /** Returns an optional int value of an annotation if the value name is present */
+ public static Optional<Integer> getOptionalIntValue(
+ AnnotationMirror annotation, String valueName) {
+ return isValuePresent(annotation, valueName)
+ ? Optional.of(getIntValue(annotation, valueName))
+ : Optional.empty();
+ }
+
+ /** Returns the String value of an annotation */
+ public static String getStringValue(AnnotationMirror annotation, String valueName) {
+ return (String) getAnnotationValue(annotation, valueName).getValue();
+ }
+
+ /** Returns an optional String value of an annotation if the value name is present */
+ public static Optional<String> getOptionalStringValue(
+ AnnotationMirror annotation, String valueName) {
+ return isValuePresent(annotation, valueName)
+ ? Optional.of(getStringValue(annotation, valueName))
+ : Optional.empty();
+ }
+
+ /** Returns the int array value of an annotation */
+ public static int[] getIntArrayValue(AnnotationMirror annotation, String valueName) {
+ return asAnnotationValues(getAnnotationValue(annotation, valueName)).stream()
+ .mapToInt(it -> (int) it.getValue())
+ .toArray();
+ }
+
+ /** Returns the String array value of an annotation */
+ public static String[] getStringArrayValue(AnnotationMirror annotation, String valueName) {
+ return asAnnotationValues(getAnnotationValue(annotation, valueName)).stream()
+ .map(it -> (String) it.getValue())
+ .toArray(String[]::new);
+ }
+
+ private static boolean isValuePresent(AnnotationMirror annotation, String valueName) {
+ return getAnnotationValuesWithDefaults(annotation).keySet().stream()
+ .anyMatch(member -> member.getSimpleName().contentEquals(valueName));
+ }
+
+ private MoreAnnotationValues() {}
+}
diff --git a/java/dagger/internal/codegen/base/MultibindingAnnotations.java b/java/dagger/internal/codegen/base/MultibindingAnnotations.java
new file mode 100644
index 000000000..424f92a20
--- /dev/null
+++ b/java/dagger/internal/codegen/base/MultibindingAnnotations.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import static dagger.internal.codegen.langmodel.DaggerElements.getAllAnnotations;
+
+import com.google.common.collect.ImmutableSet;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+
+/**
+ * Utility methods related to processing {@link IntoSet}, {@link ElementsIntoSet}, and {@link
+ * IntoMap}.
+ */
+public final class MultibindingAnnotations {
+ public static ImmutableSet<AnnotationMirror> forElement(Element method) {
+ return getAllAnnotations(method, IntoSet.class, ElementsIntoSet.class, IntoMap.class);
+ }
+}
diff --git a/java/dagger/internal/codegen/base/OptionalType.java b/java/dagger/internal/codegen/base/OptionalType.java
new file mode 100644
index 000000000..15056828e
--- /dev/null
+++ b/java/dagger/internal/codegen/base/OptionalType.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Equivalence;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import dagger.model.Key;
+import java.util.Optional;
+import javax.lang.model.element.Name;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVisitor;
+import javax.lang.model.util.SimpleTypeVisitor8;
+
+/**
+ * Information about an {@code Optional} {@link TypeMirror}.
+ *
+ * <p>{@link com.google.common.base.Optional} and {@link java.util.Optional} are supported.
+ */
+@AutoValue
+public abstract class OptionalType {
+
+ /** A variant of {@code Optional}. */
+ public enum OptionalKind {
+ /** {@link com.google.common.base.Optional}. */
+ GUAVA_OPTIONAL(com.google.common.base.Optional.class, "absent"),
+
+ /** {@link java.util.Optional}. */
+ JDK_OPTIONAL(java.util.Optional.class, "empty"),
+ ;
+
+ private final Class<?> clazz;
+ private final String absentFactoryMethodName;
+
+ OptionalKind(Class<?> clazz, String absentFactoryMethodName) {
+ this.clazz = clazz;
+ this.absentFactoryMethodName = absentFactoryMethodName;
+ }
+
+ /** Returns {@code valueType} wrapped in the correct class. */
+ public ParameterizedTypeName of(TypeName valueType) {
+ return ParameterizedTypeName.get(ClassName.get(clazz), valueType);
+ }
+
+ /** Returns an expression for the absent/empty value. */
+ public CodeBlock absentValueExpression() {
+ return CodeBlock.of("$T.$L()", clazz, absentFactoryMethodName);
+ }
+
+ /**
+ * Returns an expression for the absent/empty value, parameterized with {@link #valueType()}.
+ */
+ public CodeBlock parameterizedAbsentValueExpression(OptionalType optionalType) {
+ return CodeBlock.of("$T.<$T>$L()", clazz, optionalType.valueType(), absentFactoryMethodName);
+ }
+
+ /** Returns an expression for the present {@code value}. */
+ public CodeBlock presentExpression(CodeBlock value) {
+ return CodeBlock.of("$T.of($L)", clazz, value);
+ }
+
+ /**
+ * Returns an expression for the present {@code value}, returning {@code Optional<Object>} no
+ * matter what type the value is.
+ */
+ public CodeBlock presentObjectExpression(CodeBlock value) {
+ return CodeBlock.of("$T.<$T>of($L)", clazz, Object.class, value);
+ }
+ }
+
+ private static final TypeVisitor<Optional<OptionalKind>, Void> OPTIONAL_KIND =
+ new SimpleTypeVisitor8<Optional<OptionalKind>, Void>(Optional.empty()) {
+ @Override
+ public Optional<OptionalKind> visitDeclared(DeclaredType t, Void p) {
+ for (OptionalKind optionalKind : OptionalKind.values()) {
+ Name qualifiedName = MoreElements.asType(t.asElement()).getQualifiedName();
+ if (qualifiedName.contentEquals(optionalKind.clazz.getCanonicalName())) {
+ return Optional.of(optionalKind);
+ }
+ }
+ return Optional.empty();
+ }
+ };
+
+ /**
+ * The optional type itself, wrapped using {@link MoreTypes#equivalence()}.
+ *
+ * @deprecated Use {@link #declaredOptionalType()} instead.
+ */
+ @Deprecated
+ protected abstract Equivalence.Wrapper<DeclaredType> wrappedDeclaredOptionalType();
+
+ /** The optional type itself. */
+ @SuppressWarnings("deprecation")
+ private DeclaredType declaredOptionalType() {
+ return wrappedDeclaredOptionalType().get();
+ }
+
+ /** Which {@code Optional} type is used. */
+ public OptionalKind kind() {
+ return declaredOptionalType().accept(OPTIONAL_KIND, null).get();
+ }
+
+ /** The value type. */
+ public TypeMirror valueType() {
+ return declaredOptionalType().getTypeArguments().get(0);
+ }
+
+ /** Returns {@code true} if {@code type} is an {@code Optional} type. */
+ private static boolean isOptional(TypeMirror type) {
+ return type.accept(OPTIONAL_KIND, null).isPresent();
+ }
+
+ /** Returns {@code true} if {@code key.type()} is an {@code Optional} type. */
+ public static boolean isOptional(Key key) {
+ return isOptional(key.type());
+ }
+
+ /**
+ * Returns a {@link OptionalType} for {@code type}.
+ *
+ * @throws IllegalArgumentException if {@code type} is not an {@code Optional} type
+ */
+ public static OptionalType from(TypeMirror type) {
+ checkArgument(isOptional(type), "%s must be an Optional", type);
+ return new AutoValue_OptionalType(MoreTypes.equivalence().wrap(MoreTypes.asDeclared(type)));
+ }
+
+ /**
+ * Returns a {@link OptionalType} for {@code key}'s {@link Key#type() type}.
+ *
+ * @throws IllegalArgumentException if {@code key.type()} is not an {@code Optional} type
+ */
+ public static OptionalType from(Key key) {
+ return from(key.type());
+ }
+}
diff --git a/java/dagger/internal/codegen/base/RequestKinds.java b/java/dagger/internal/codegen/base/RequestKinds.java
new file mode 100644
index 000000000..95d5ef4f9
--- /dev/null
+++ b/java/dagger/internal/codegen/base/RequestKinds.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import static com.google.auto.common.MoreTypes.asDeclared;
+import static com.google.auto.common.MoreTypes.isType;
+import static com.google.auto.common.MoreTypes.isTypeOf;
+import static com.google.common.base.Preconditions.checkArgument;
+import static dagger.internal.codegen.javapoet.TypeNames.lazyOf;
+import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf;
+import static dagger.internal.codegen.javapoet.TypeNames.producedOf;
+import static dagger.internal.codegen.javapoet.TypeNames.producerOf;
+import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
+import static dagger.internal.codegen.langmodel.DaggerTypes.checkTypePresent;
+import static dagger.model.RequestKind.LAZY;
+import static dagger.model.RequestKind.PRODUCED;
+import static dagger.model.RequestKind.PRODUCER;
+import static dagger.model.RequestKind.PROVIDER;
+import static dagger.model.RequestKind.PROVIDER_OF_LAZY;
+import static javax.lang.model.type.TypeKind.DECLARED;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.squareup.javapoet.TypeName;
+import dagger.Lazy;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.RequestKind;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import javax.inject.Provider;
+import javax.lang.model.type.TypeMirror;
+
+/** Utility methods for {@link RequestKind}s. */
+public final class RequestKinds {
+
+ /** Returns the type of a request of this kind for a key with a given type. */
+ public static TypeMirror requestType(
+ RequestKind requestKind, TypeMirror type, DaggerTypes types) {
+ switch (requestKind) {
+ case INSTANCE:
+ return type;
+
+ case PROVIDER_OF_LAZY:
+ return types.wrapType(requestType(LAZY, type, types), Provider.class);
+
+ case FUTURE:
+ return types.wrapType(type, ListenableFuture.class);
+
+ default:
+ return types.wrapType(type, frameworkClass(requestKind));
+ }
+ }
+
+ /** Returns the type of a request of this kind for a key with a given type. */
+ public static TypeName requestTypeName(RequestKind requestKind, TypeName keyType) {
+ switch (requestKind) {
+ case INSTANCE:
+ return keyType;
+
+ case PROVIDER:
+ return providerOf(keyType);
+
+ case LAZY:
+ return lazyOf(keyType);
+
+ case PROVIDER_OF_LAZY:
+ return providerOf(lazyOf(keyType));
+
+ case PRODUCER:
+ return producerOf(keyType);
+
+ case PRODUCED:
+ return producedOf(keyType);
+
+ case FUTURE:
+ return listenableFutureOf(keyType);
+
+ default:
+ throw new AssertionError(requestKind);
+ }
+ }
+
+ private static final ImmutableMap<RequestKind, Class<?>> FRAMEWORK_CLASSES =
+ ImmutableMap.of(
+ PROVIDER, Provider.class,
+ LAZY, Lazy.class,
+ PRODUCER, Producer.class,
+ PRODUCED, Produced.class);
+
+ /** Returns the {@link RequestKind} that matches the wrapping types (if any) of {@code type}. */
+ public static RequestKind getRequestKind(TypeMirror type) {
+ checkTypePresent(type);
+ if (!isType(type) // TODO(b/147320669): isType check can be removed once this bug is fixed.
+ || !type.getKind().equals(DECLARED)
+ || asDeclared(type).getTypeArguments().isEmpty()) {
+ // If the type is not a declared type (i.e. class or interface) with type arguments, then we
+ // know it can't be a parameterized type of one of the framework classes, so return INSTANCE.
+ return RequestKind.INSTANCE;
+ }
+ for (RequestKind kind : FRAMEWORK_CLASSES.keySet()) {
+ if (isTypeOf(frameworkClass(kind), type)) {
+ if (kind.equals(PROVIDER) && getRequestKind(DaggerTypes.unwrapType(type)).equals(LAZY)) {
+ return PROVIDER_OF_LAZY;
+ }
+ return kind;
+ }
+ }
+ return RequestKind.INSTANCE;
+ }
+
+ /**
+ * Unwraps the framework class(es) of {@code requestKind} from {@code type}. If {@code
+ * requestKind} is {@link RequestKind#INSTANCE}, this acts as an identity function.
+ *
+ * @throws TypeNotPresentException if {@code type} is an {@link javax.lang.model.type.ErrorType},
+ * which may mean that the type will be generated in a later round of processing
+ * @throws IllegalArgumentException if {@code type} is not wrapped with {@code requestKind}'s
+ * framework class(es).
+ */
+ public static TypeMirror extractKeyType(TypeMirror type) {
+ return extractKeyType(getRequestKind(type), type);
+ }
+
+ private static TypeMirror extractKeyType(RequestKind requestKind, TypeMirror type) {
+ switch (requestKind) {
+ case INSTANCE:
+ return type;
+ case PROVIDER_OF_LAZY:
+ return extractKeyType(LAZY, extractKeyType(PROVIDER, type));
+ default:
+ checkArgument(isType(type));
+ return DaggerTypes.unwrapType(type);
+ }
+ }
+
+ /**
+ * A dagger- or {@code javax.inject}-defined class for {@code requestKind} that that can wrap
+ * another type but share the same {@link dagger.model.Key}.
+ *
+ * <p>For example, {@code Provider<String>} and {@code Lazy<String>} can both be requested if a
+ * key exists for {@code String}; they all share the same key.
+ *
+ * <p>This concept is not well defined and should probably be removed and inlined into the cases
+ * that need it. For example, {@link RequestKind#PROVIDER_OF_LAZY} has <em>2</em> wrapping
+ * classes, and {@link RequestKind#FUTURE} is wrapped with a {@link ListenableFuture}, but for
+ * historical/implementation reasons has not had an associated framework class.
+ */
+ public static Class<?> frameworkClass(RequestKind requestKind) {
+ Class<?> result = FRAMEWORK_CLASSES.get(requestKind);
+ checkArgument(result != null, "no framework class for %s", requestKind);
+ return result;
+ }
+
+ /**
+ * Returns {@code true} if requests for {@code requestKind} can be satisfied by a production
+ * binding.
+ */
+ public static boolean canBeSatisfiedByProductionBinding(RequestKind requestKind) {
+ switch (requestKind) {
+ case INSTANCE:
+ case PROVIDER:
+ case LAZY:
+ case PROVIDER_OF_LAZY:
+ case MEMBERS_INJECTION:
+ return false;
+ case PRODUCER:
+ case PRODUCED:
+ case FUTURE:
+ return true;
+ }
+ throw new AssertionError();
+ }
+
+ private RequestKinds() {}
+}
diff --git a/java/dagger/internal/codegen/base/Scopes.java b/java/dagger/internal/codegen/base/Scopes.java
new file mode 100644
index 000000000..f2c39cea0
--- /dev/null
+++ b/java/dagger/internal/codegen/base/Scopes.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.base.DiagnosticFormatting.stripCommonTypePrefixes;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+
+import com.google.auto.common.AnnotationMirrors;
+import com.google.common.collect.ImmutableSet;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.model.Scope;
+import dagger.producers.ProductionScope;
+import java.lang.annotation.Annotation;
+import java.util.Optional;
+import javax.inject.Singleton;
+import javax.lang.model.element.Element;
+
+/** Common names and convenience methods for {@link Scope}s. */
+public final class Scopes {
+
+ /** Returns a representation for {@link ProductionScope @ProductionScope} scope. */
+ public static Scope productionScope(DaggerElements elements) {
+ return scope(elements, ProductionScope.class);
+ }
+
+ /** Returns a representation for {@link Singleton @Singleton} scope. */
+ public static Scope singletonScope(DaggerElements elements) {
+ return scope(elements, Singleton.class);
+ }
+
+ /**
+ * Creates a {@link Scope} object from the {@link javax.inject.Scope}-annotated annotation type.
+ */
+ private static Scope scope(
+ DaggerElements elements, Class<? extends Annotation> scopeAnnotationClass) {
+ return Scope.scope(SimpleAnnotationMirror.of(elements.getTypeElement(scopeAnnotationClass)));
+ }
+
+ /**
+ * Returns at most one associated scoped annotation from the source code element, throwing an
+ * exception if there are more than one.
+ */
+ public static Optional<Scope> uniqueScopeOf(Element element) {
+ // TODO(ronshapiro): Use MoreCollectors.toOptional() once we can use guava-jre
+ return Optional.ofNullable(getOnlyElement(scopesOf(element), null));
+ }
+
+ /**
+ * Returns the readable source representation (name with @ prefix) of the scope's annotation type.
+ *
+ * <p>It's readable source because it has had common package prefixes removed, e.g.
+ * {@code @javax.inject.Singleton} is returned as {@code @Singleton}.
+ */
+ public static String getReadableSource(Scope scope) {
+ return stripCommonTypePrefixes(scope.toString());
+ }
+
+ /** Returns all of the associated scopes for a source code element. */
+ public static ImmutableSet<Scope> scopesOf(Element element) {
+ return AnnotationMirrors.getAnnotatedAnnotations(element, javax.inject.Scope.class)
+ .stream()
+ .map(Scope::scope)
+ .collect(toImmutableSet());
+ }
+}
diff --git a/java/dagger/internal/codegen/base/SetType.java b/java/dagger/internal/codegen/base/SetType.java
new file mode 100644
index 000000000..a75a6d316
--- /dev/null
+++ b/java/dagger/internal/codegen/base/SetType.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Equivalence;
+import dagger.model.Key;
+import java.util.Set;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Information about a {@link Set} {@link TypeMirror}.
+ */
+@AutoValue
+public abstract class SetType {
+ /**
+ * The set type itself, wrapped using {@link MoreTypes#equivalence()}. Use
+ * {@link #declaredSetType()} instead.
+ */
+ protected abstract Equivalence.Wrapper<DeclaredType> wrappedDeclaredSetType();
+
+ /**
+ * The set type itself.
+ */
+ private DeclaredType declaredSetType() {
+ return wrappedDeclaredSetType().get();
+ }
+
+ /**
+ * {@code true} if the set type is the raw {@link Set} type.
+ */
+ public boolean isRawType() {
+ return declaredSetType().getTypeArguments().isEmpty();
+ }
+
+ /**
+ * The element type.
+ */
+ public TypeMirror elementType() {
+ return declaredSetType().getTypeArguments().get(0);
+ }
+
+ /**
+ * {@code true} if {@link #elementType()} is a {@code clazz}.
+ */
+ public boolean elementsAreTypeOf(Class<?> clazz) {
+ return MoreTypes.isType(elementType()) && MoreTypes.isTypeOf(clazz, elementType());
+ }
+
+ /**
+ * {@code T} if {@link #elementType()} is a {@code WrappingClass<T>}.
+ *
+ * @throws IllegalStateException if {@link #elementType()} is not a {@code WrappingClass<T>}
+ * @throws IllegalArgumentException if {@code wrappingClass} does not have exactly one type
+ * parameter
+ */
+ public TypeMirror unwrappedElementType(Class<?> wrappingClass) {
+ checkArgument(
+ wrappingClass.getTypeParameters().length == 1,
+ "%s must have exactly one type parameter",
+ wrappingClass);
+ checkArgument(
+ elementsAreTypeOf(wrappingClass),
+ "expected elements to be %s, but this type is %s",
+ wrappingClass,
+ declaredSetType());
+ return MoreTypes.asDeclared(elementType()).getTypeArguments().get(0);
+ }
+
+ /**
+ * {@code true} if {@code type} is a {@link Set} type.
+ */
+ public static boolean isSet(TypeMirror type) {
+ return MoreTypes.isType(type) && MoreTypes.isTypeOf(Set.class, type);
+ }
+
+ /**
+ * {@code true} if {@code key.type()} is a {@link Set} type.
+ */
+ public static boolean isSet(Key key) {
+ return isSet(key.type());
+ }
+
+ /**
+ * Returns a {@link SetType} for {@code type}.
+ *
+ * @throws IllegalArgumentException if {@code type} is not a {@link Set} type
+ */
+ public static SetType from(TypeMirror type) {
+ checkArgument(isSet(type), "%s must be a Set", type);
+ return new AutoValue_SetType(MoreTypes.equivalence().wrap(MoreTypes.asDeclared(type)));
+ }
+
+ /**
+ * Returns a {@link SetType} for {@code key}'s {@link Key#type() type}.
+ *
+ * @throws IllegalArgumentException if {@code key.type()} is not a {@link Set} type
+ */
+ public static SetType from(Key key) {
+ return from (key.type());
+ }
+}
diff --git a/java/dagger/internal/codegen/base/SimpleAnnotationMirror.java b/java/dagger/internal/codegen/base/SimpleAnnotationMirror.java
new file mode 100644
index 000000000..9690691eb
--- /dev/null
+++ b/java/dagger/internal/codegen/base/SimpleAnnotationMirror.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.base.Functions;
+import com.google.common.base.Joiner;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import java.util.Map;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+
+/** A representation of an annotation. */
+public final class SimpleAnnotationMirror implements AnnotationMirror {
+ private final TypeElement annotationType;
+ private final ImmutableMap<String, ? extends AnnotationValue> namedValues;
+ private final ImmutableMap<ExecutableElement, ? extends AnnotationValue> elementValues;
+
+ private SimpleAnnotationMirror(
+ TypeElement annotationType, Map<String, ? extends AnnotationValue> namedValues) {
+ checkArgument(
+ annotationType.getKind().equals(ElementKind.ANNOTATION_TYPE),
+ "annotationType must be an annotation: %s",
+ annotationType);
+ checkArgument(
+ FluentIterable.from(methodsIn(annotationType.getEnclosedElements()))
+ .transform(element -> element.getSimpleName().toString())
+ .toSet()
+ .equals(namedValues.keySet()),
+ "namedValues must have values for exactly the members in %s: %s",
+ annotationType,
+ namedValues);
+ this.annotationType = annotationType;
+ this.namedValues = ImmutableMap.copyOf(namedValues);
+ this.elementValues =
+ Maps.toMap(
+ methodsIn(annotationType.getEnclosedElements()),
+ Functions.compose(
+ Functions.forMap(namedValues), element -> element.getSimpleName().toString()));
+ }
+
+ @Override
+ public DeclaredType getAnnotationType() {
+ return MoreTypes.asDeclared(annotationType.asType());
+ }
+
+ @Override
+ public Map<ExecutableElement, ? extends AnnotationValue> getElementValues() {
+ return elementValues;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("@").append(annotationType.getQualifiedName());
+ if (!namedValues.isEmpty()) {
+ builder
+ .append('(')
+ .append(Joiner.on(", ").withKeyValueSeparator(" = ").join(namedValues))
+ .append(')');
+ }
+ return builder.toString();
+ }
+
+ /**
+ * An object representing an annotation instance.
+ *
+ * @param annotationType must be an annotation type with no members
+ */
+ public static AnnotationMirror of(TypeElement annotationType) {
+ return of(annotationType, ImmutableMap.<String, AnnotationValue>of());
+ }
+
+ /**
+ * An object representing an annotation instance.
+ *
+ * @param annotationType must be an annotation type
+ * @param namedValues a value for every annotation member, including those with defaults, indexed
+ * by simple name
+ */
+ private static AnnotationMirror of(
+ TypeElement annotationType, Map<String, ? extends AnnotationValue> namedValues) {
+ return new SimpleAnnotationMirror(annotationType, namedValues);
+ }
+}
diff --git a/java/dagger/internal/codegen/base/SimpleTypeAnnotationValue.java b/java/dagger/internal/codegen/base/SimpleTypeAnnotationValue.java
new file mode 100644
index 000000000..d595bcb70
--- /dev/null
+++ b/java/dagger/internal/codegen/base/SimpleTypeAnnotationValue.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.AnnotationValueVisitor;
+import javax.lang.model.type.TypeMirror;
+
+/** An {@link AnnotationValue} that contains a {@link TypeMirror}. */
+final class SimpleTypeAnnotationValue implements AnnotationValue {
+ private final TypeMirror value;
+
+ SimpleTypeAnnotationValue(TypeMirror value) {
+ this.value = value;
+ }
+
+ @Override
+ public TypeMirror getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return value + ".class";
+ }
+
+ @Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> visitor, P parameter) {
+ return visitor.visitType(getValue(), parameter);
+ }
+}
diff --git a/java/dagger/internal/codegen/base/SourceFileGenerationException.java b/java/dagger/internal/codegen/base/SourceFileGenerationException.java
new file mode 100644
index 000000000..5553dd85b
--- /dev/null
+++ b/java/dagger/internal/codegen/base/SourceFileGenerationException.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.squareup.javapoet.ClassName;
+import java.util.Optional;
+import javax.annotation.processing.Messager;
+import javax.lang.model.element.Element;
+
+/**
+ * An exception thrown to indicate that a source file could not be generated.
+ *
+ * <p>This exception <b>should not</b> be used to report detectable, logical errors as it may mask
+ * other errors that might have been caught upon further processing. Use a {@link ValidationReport}
+ * for that.
+ */
+public final class SourceFileGenerationException extends Exception {
+ private final Element associatedElement;
+
+ SourceFileGenerationException(
+ Optional<ClassName> generatedClassName, Throwable cause, Element associatedElement) {
+ super(createMessage(generatedClassName, cause.getMessage()), cause);
+ this.associatedElement = checkNotNull(associatedElement);
+ }
+
+ private static String createMessage(Optional<ClassName> generatedClassName, String message) {
+ return String.format("Could not generate %s: %s.",
+ generatedClassName.isPresent()
+ ? generatedClassName.get()
+ : "unknown file",
+ message);
+ }
+
+ public void printMessageTo(Messager messager) {
+ messager.printMessage(ERROR, getMessage(), associatedElement);
+ }
+}
diff --git a/java/dagger/internal/codegen/base/SourceFileGenerator.java b/java/dagger/internal/codegen/base/SourceFileGenerator.java
new file mode 100644
index 000000000..02348a4f4
--- /dev/null
+++ b/java/dagger/internal/codegen/base/SourceFileGenerator.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import static com.google.auto.common.GeneratedAnnotations.generatedAnnotation;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.javapoet.AnnotationSpecs;
+import dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import java.util.Optional;
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.Messager;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+
+/**
+ * A template class that provides a framework for properly handling IO while generating source files
+ * from an annotation processor. Particularly, it makes a best effort to ensure that files that fail
+ * to write successfully are deleted.
+ *
+ * @param <T> The input type from which source is to be generated.
+ */
+public abstract class SourceFileGenerator<T> {
+ private static final String GENERATED_COMMENTS = "https://dagger.dev";
+
+ private final Filer filer;
+ private final DaggerElements elements;
+ private final SourceVersion sourceVersion;
+
+ public SourceFileGenerator(Filer filer, DaggerElements elements, SourceVersion sourceVersion) {
+ this.filer = checkNotNull(filer);
+ this.elements = checkNotNull(elements);
+ this.sourceVersion = checkNotNull(sourceVersion);
+ }
+
+ public SourceFileGenerator(SourceFileGenerator<T> delegate) {
+ this(delegate.filer, delegate.elements, delegate.sourceVersion);
+ }
+
+ /**
+ * Generates a source file to be compiled for {@code T}. Writes any generation exception to {@code
+ * messager} and does not throw.
+ */
+ public void generate(T input, Messager messager) {
+ try {
+ generate(input);
+ } catch (SourceFileGenerationException e) {
+ e.printMessageTo(messager);
+ }
+ }
+
+ /** Generates a source file to be compiled for {@code T}. */
+ public void generate(T input) throws SourceFileGenerationException {
+ Optional<TypeSpec.Builder> type = write(input);
+ if (!type.isPresent()) {
+ return;
+ }
+ try {
+ buildJavaFile(input, type.get()).writeTo(filer);
+ } catch (Exception e) {
+ // if the code above threw a SFGE, use that
+ Throwables.propagateIfPossible(e, SourceFileGenerationException.class);
+ // otherwise, throw a new one
+ throw new SourceFileGenerationException(Optional.empty(), e, originatingElement(input));
+ }
+ }
+
+ private JavaFile buildJavaFile(T input, TypeSpec.Builder typeSpecBuilder) {
+ typeSpecBuilder.addOriginatingElement(originatingElement(input));
+ Optional<AnnotationSpec> generatedAnnotation =
+ generatedAnnotation(elements, sourceVersion)
+ .map(
+ annotation ->
+ AnnotationSpec.builder(ClassName.get(annotation))
+ .addMember("value", "$S", "dagger.internal.codegen.ComponentProcessor")
+ .addMember("comments", "$S", GENERATED_COMMENTS)
+ .build());
+ generatedAnnotation.ifPresent(typeSpecBuilder::addAnnotation);
+
+ // TODO(b/134590785): remove this and only suppress annotations locally, if necessary
+ typeSpecBuilder.addAnnotation(
+ AnnotationSpecs.suppressWarnings(
+ ImmutableSet.<Suppression>builder()
+ .addAll(warningSuppressions())
+ .add(UNCHECKED, RAWTYPES)
+ .build()));
+
+ JavaFile.Builder javaFileBuilder =
+ JavaFile.builder(nameGeneratedType(input).packageName(), typeSpecBuilder.build())
+ .skipJavaLangImports(true);
+ if (!generatedAnnotation.isPresent()) {
+ javaFileBuilder.addFileComment("Generated by Dagger ($L).", GENERATED_COMMENTS);
+ }
+ return javaFileBuilder.build();
+ }
+
+ /** Implementations should return the {@link ClassName} for the top-level type to be generated. */
+ public abstract ClassName nameGeneratedType(T input);
+
+ /** Returns the originating element of the generating type. */
+ public abstract Element originatingElement(T input);
+
+ /**
+ * Returns a {@link TypeSpec.Builder type} to be generated for {@code T}, or {@link
+ * Optional#empty()} if no file should be generated.
+ */
+ // TODO(ronshapiro): write() makes more sense in JavaWriter where all writers are mutable.
+ // consider renaming to something like typeBuilder() which conveys the mutability of the result
+ public abstract Optional<TypeSpec.Builder> write(T input);
+
+ /** Returns {@link Suppression}s that are applied to files generated by this generator. */
+ // TODO(b/134590785): When suppressions are removed locally, remove this and inline the usages
+ protected ImmutableSet<Suppression> warningSuppressions() {
+ return ImmutableSet.of();
+ }
+}
diff --git a/java/dagger/internal/codegen/base/UniqueNameSet.java b/java/dagger/internal/codegen/base/UniqueNameSet.java
new file mode 100644
index 000000000..c1ffe4731
--- /dev/null
+++ b/java/dagger/internal/codegen/base/UniqueNameSet.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/** A collector for names to be used in the same namespace that should not conflict. */
+public final class UniqueNameSet {
+ private final Set<String> uniqueNames = new HashSet<>();
+
+ /**
+ * Generates a unique name using {@code base}. If {@code base} has not yet been added, it will be
+ * returned as-is. If your {@code base} is healthy, this will always return {@code base}.
+ */
+ public String getUniqueName(CharSequence base) {
+ String name = base.toString();
+ for (int differentiator = 2; !uniqueNames.add(name); differentiator++) {
+ name = base.toString() + differentiator;
+ }
+ return name;
+ }
+
+ /**
+ * Adds {@code name} without any modification to the name set. Has no effect if {@code name} is
+ * already present in the set.
+ */
+ public void claim(CharSequence name) {
+ uniqueNames.add(name.toString());
+ }
+}
diff --git a/java/dagger/internal/codegen/base/Util.java b/java/dagger/internal/codegen/base/Util.java
new file mode 100644
index 000000000..e92b8ab5a
--- /dev/null
+++ b/java/dagger/internal/codegen/base/Util.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2013 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.base;
+
+import java.util.Map;
+import java.util.function.Function;
+
+/** General utilities for the annotation processor. */
+public final class Util {
+
+ /**
+ * A version of {@link Map#computeIfAbsent(Object, Function)} that allows {@code mappingFunction}
+ * to update {@code map}.
+ */
+ public static <K, V> V reentrantComputeIfAbsent(
+ Map<K, V> map, K key, Function<? super K, ? extends V> mappingFunction) {
+ V value = map.get(key);
+ if (value == null) {
+ value = mappingFunction.apply(key);
+ if (value != null) {
+ map.put(key, value);
+ }
+ }
+ return value;
+ }
+
+ private Util() {}
+}
diff --git a/java/dagger/internal/codegen/binding/AnnotationExpression.java b/java/dagger/internal/codegen/binding/AnnotationExpression.java
new file mode 100644
index 000000000..de0aea586
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/AnnotationExpression.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValuesWithDefaults;
+import static dagger.internal.codegen.binding.SourceFiles.classFileName;
+import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
+import static java.util.stream.Collectors.toList;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.TypeName;
+import java.util.List;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleAnnotationValueVisitor6;
+import javax.lang.model.util.SimpleTypeVisitor6;
+
+/**
+ * Returns an expression creating an instance of the visited annotation type. Its parameter must be
+ * a class as generated by {@link dagger.internal.codegen.writing.AnnotationCreatorGenerator}.
+ *
+ * <p>Note that {@link AnnotationValue#toString()} is the source-code representation of the value
+ * <em>when used in an annotation</em>, which is not always the same as the representation needed
+ * when creating the value in a method body.
+ *
+ * <p>For example, inside an annotation, a nested array of {@code int}s is simply {@code {1, 2, 3}},
+ * but in code it would have to be {@code new int[] {1, 2, 3}}.
+ */
+public class AnnotationExpression
+ extends SimpleAnnotationValueVisitor6<CodeBlock, AnnotationValue> {
+
+ private final AnnotationMirror annotation;
+ private final ClassName creatorClass;
+
+ AnnotationExpression(AnnotationMirror annotation) {
+ this.annotation = annotation;
+ this.creatorClass =
+ getAnnotationCreatorClassName(
+ MoreTypes.asTypeElement(annotation.getAnnotationType()));
+ }
+
+ /**
+ * Returns an expression that calls static methods on the annotation's creator class to create an
+ * annotation instance equivalent the annotation passed to the constructor.
+ */
+ CodeBlock getAnnotationInstanceExpression() {
+ return getAnnotationInstanceExpression(annotation);
+ }
+
+ private CodeBlock getAnnotationInstanceExpression(AnnotationMirror annotation) {
+ return CodeBlock.of(
+ "$T.$L($L)",
+ creatorClass,
+ createMethodName(
+ MoreElements.asType(annotation.getAnnotationType().asElement())),
+ makeParametersCodeBlock(
+ getAnnotationValuesWithDefaults(annotation)
+ .entrySet()
+ .stream()
+ .map(entry -> getValueExpression(entry.getKey().getReturnType(), entry.getValue()))
+ .collect(toList())));
+ }
+
+ /**
+ * Returns the name of the generated class that contains the static {@code create} methods for an
+ * annotation type.
+ */
+ public static ClassName getAnnotationCreatorClassName(TypeElement annotationType) {
+ ClassName annotationTypeName = ClassName.get(annotationType);
+ return annotationTypeName
+ .topLevelClassName()
+ .peerClass(classFileName(annotationTypeName) + "Creator");
+ }
+
+ public static String createMethodName(TypeElement annotationType) {
+ return "create" + annotationType.getSimpleName();
+ }
+
+ /**
+ * Returns an expression that evaluates to a {@code value} of a given type on an {@code
+ * annotation}.
+ */
+ CodeBlock getValueExpression(TypeMirror valueType, AnnotationValue value) {
+ return ARRAY_LITERAL_PREFIX.visit(valueType, this.visit(value, value));
+ }
+
+ @Override
+ public CodeBlock visitEnumConstant(VariableElement c, AnnotationValue p) {
+ return CodeBlock.of("$T.$L", c.getEnclosingElement(), c.getSimpleName());
+ }
+
+ @Override
+ public CodeBlock visitAnnotation(AnnotationMirror a, AnnotationValue p) {
+ return getAnnotationInstanceExpression(a);
+ }
+
+ @Override
+ public CodeBlock visitType(TypeMirror t, AnnotationValue p) {
+ return CodeBlock.of("$T.class", t);
+ }
+
+ @Override
+ public CodeBlock visitString(String s, AnnotationValue p) {
+ return CodeBlock.of("$S", s);
+ }
+
+ @Override
+ public CodeBlock visitByte(byte b, AnnotationValue p) {
+ return CodeBlock.of("(byte) $L", b);
+ }
+
+ @Override
+ public CodeBlock visitChar(char c, AnnotationValue p) {
+ return CodeBlock.of("$L", p);
+ }
+
+ @Override
+ public CodeBlock visitDouble(double d, AnnotationValue p) {
+ return CodeBlock.of("$LD", d);
+ }
+
+ @Override
+ public CodeBlock visitFloat(float f, AnnotationValue p) {
+ return CodeBlock.of("$LF", f);
+ }
+
+ @Override
+ public CodeBlock visitLong(long i, AnnotationValue p) {
+ return CodeBlock.of("$LL", i);
+ }
+
+ @Override
+ public CodeBlock visitShort(short s, AnnotationValue p) {
+ return CodeBlock.of("(short) $L", s);
+ }
+
+ @Override
+ protected CodeBlock defaultAction(Object o, AnnotationValue p) {
+ return CodeBlock.of("$L", o);
+ }
+
+ @Override
+ public CodeBlock visitArray(List<? extends AnnotationValue> values, AnnotationValue p) {
+ ImmutableList.Builder<CodeBlock> codeBlocks = ImmutableList.builder();
+ for (AnnotationValue value : values) {
+ codeBlocks.add(this.visit(value, p));
+ }
+ return CodeBlock.of("{$L}", makeParametersCodeBlock(codeBlocks.build()));
+ }
+
+ /**
+ * If the visited type is an array, prefixes the parameter code block with {@code new T[]}, where
+ * {@code T} is the raw array component type.
+ */
+ private static final SimpleTypeVisitor6<CodeBlock, CodeBlock> ARRAY_LITERAL_PREFIX =
+ new SimpleTypeVisitor6<CodeBlock, CodeBlock>() {
+
+ @Override
+ public CodeBlock visitArray(ArrayType t, CodeBlock p) {
+ return CodeBlock.of("new $T[] $L", RAW_TYPE_NAME.visit(t.getComponentType()), p);
+ }
+
+ @Override
+ protected CodeBlock defaultAction(TypeMirror e, CodeBlock p) {
+ return p;
+ }
+ };
+
+ /**
+ * If the visited type is an array, returns the name of its raw component type; otherwise returns
+ * the name of the type itself.
+ */
+ private static final SimpleTypeVisitor6<TypeName, Void> RAW_TYPE_NAME =
+ new SimpleTypeVisitor6<TypeName, Void>() {
+ @Override
+ public TypeName visitDeclared(DeclaredType t, Void p) {
+ return ClassName.get(MoreTypes.asTypeElement(t));
+ }
+
+ @Override
+ protected TypeName defaultAction(TypeMirror e, Void p) {
+ return TypeName.get(e);
+ }
+ };
+}
diff --git a/java/dagger/internal/codegen/binding/AssistedInjectionAnnotations.java b/java/dagger/internal/codegen/binding/AssistedInjectionAnnotations.java
new file mode 100644
index 000000000..8d6ee5df6
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/AssistedInjectionAnnotations.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.auto.common.MoreElements.asExecutable;
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.auto.common.MoreTypes.asDeclared;
+import static com.google.auto.common.MoreTypes.asExecutable;
+import static com.google.auto.common.MoreTypes.asTypeElement;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.base.MoreAnnotationValues.getStringValue;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.util.ElementFilter.constructorsIn;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.base.Equivalence;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.BindingKind;
+import java.util.List;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+
+/** Assisted injection utility methods. */
+public final class AssistedInjectionAnnotations {
+ /** Returns the factory method for the given factory {@link TypeElement}. */
+ public static ExecutableElement assistedFactoryMethod(
+ TypeElement factory, DaggerElements elements, DaggerTypes types) {
+ return getOnlyElement(assistedFactoryMethods(factory, elements, types));
+ }
+
+ /** Returns the list of abstract factory methods for the given factory {@link TypeElement}. */
+ public static ImmutableSet<ExecutableElement> assistedFactoryMethods(
+ TypeElement factory, DaggerElements elements, DaggerTypes types) {
+ return MoreElements.getLocalAndInheritedMethods(factory, types, elements).stream()
+ .filter(method -> method.getModifiers().contains(ABSTRACT))
+ .filter(method -> !method.isDefault())
+ .collect(toImmutableSet());
+ }
+
+ /** Returns {@code true} if the element uses assisted injection. */
+ public static boolean isAssistedInjectionType(TypeElement typeElement) {
+ ImmutableSet<ExecutableElement> injectConstructors = assistedInjectedConstructors(typeElement);
+ return !injectConstructors.isEmpty()
+ && isAnnotationPresent(getOnlyElement(injectConstructors), AssistedInject.class);
+ }
+
+ /** Returns {@code true} if this binding is an assisted factory. */
+ public static boolean isAssistedFactoryType(Element element) {
+ return isAnnotationPresent(element, AssistedFactory.class);
+ }
+
+ /**
+ * Returns the list of assisted parameters as {@link ParameterSpec}s.
+ *
+ * <p>The type of each parameter will be the resolved type given by the binding key, and the name
+ * of each parameter will be the name given in the {@link
+ * dagger.assisted.AssistedInject}-annotated constructor.
+ */
+ public static ImmutableList<ParameterSpec> assistedParameterSpecs(
+ Binding binding, DaggerTypes types) {
+ checkArgument(binding.kind() == BindingKind.ASSISTED_INJECTION);
+ ExecutableElement constructor = asExecutable(binding.bindingElement().get());
+ ExecutableType constructorType =
+ asExecutable(types.asMemberOf(asDeclared(binding.key().type()), constructor));
+ return assistedParameterSpecs(constructor.getParameters(), constructorType.getParameterTypes());
+ }
+
+ private static ImmutableList<ParameterSpec> assistedParameterSpecs(
+ List<? extends VariableElement> paramElements, List<? extends TypeMirror> paramTypes) {
+ ImmutableList.Builder<ParameterSpec> assistedParameterSpecs = ImmutableList.builder();
+ for (int i = 0; i < paramElements.size(); i++) {
+ VariableElement paramElement = paramElements.get(i);
+ TypeMirror paramType = paramTypes.get(i);
+ if (isAssistedParameter(paramElement)) {
+ assistedParameterSpecs.add(
+ ParameterSpec.builder(TypeName.get(paramType), paramElement.getSimpleName().toString())
+ .build());
+ }
+ }
+ return assistedParameterSpecs.build();
+ }
+
+ /**
+ * Returns the list of assisted factory parameters as {@link ParameterSpec}s.
+ *
+ * <p>The type of each parameter will be the resolved type given by the binding key, and the name
+ * of each parameter will be the name given in the {@link
+ * dagger.assisted.AssistedInject}-annotated constructor.
+ */
+ public static ImmutableList<ParameterSpec> assistedFactoryParameterSpecs(
+ Binding binding, DaggerElements elements, DaggerTypes types) {
+ checkArgument(binding.kind() == BindingKind.ASSISTED_FACTORY);
+
+ AssistedFactoryMetadata metadata =
+ AssistedFactoryMetadata.create(binding.bindingElement().get().asType(), elements, types);
+ ExecutableType factoryMethodType =
+ asExecutable(types.asMemberOf(asDeclared(binding.key().type()), metadata.factoryMethod()));
+ return assistedParameterSpecs(
+ // Use the order of the parameters from the @AssistedFactory method but use the parameter
+ // names of the @AssistedInject constructor.
+ metadata.assistedFactoryAssistedParameters().stream()
+ .map(metadata.assistedInjectAssistedParametersMap()::get)
+ .collect(toImmutableList()),
+ factoryMethodType.getParameterTypes());
+ }
+
+ /** Returns the constructors in {@code type} that are annotated with {@link AssistedInject}. */
+ public static ImmutableSet<ExecutableElement> assistedInjectedConstructors(TypeElement type) {
+ return constructorsIn(type.getEnclosedElements()).stream()
+ .filter(constructor -> isAnnotationPresent(constructor, AssistedInject.class))
+ .collect(toImmutableSet());
+ }
+
+ public static ImmutableList<VariableElement> assistedParameters(Binding binding) {
+ return binding.kind() == BindingKind.ASSISTED_INJECTION
+ ? assistedParameters(asExecutable(binding.bindingElement().get()))
+ : ImmutableList.of();
+ }
+
+ private static ImmutableList<VariableElement> assistedParameters(ExecutableElement constructor) {
+ return constructor.getParameters().stream()
+ .filter(AssistedInjectionAnnotations::isAssistedParameter)
+ .collect(toImmutableList());
+ }
+
+ /** Returns {@code true} if this binding is uses assisted injection. */
+ public static boolean isAssistedParameter(VariableElement param) {
+ return isAnnotationPresent(MoreElements.asVariable(param), Assisted.class);
+ }
+
+ /** Metadata about an {@link dagger.assisted.AssistedFactory} annotated type. */
+ @AutoValue
+ public abstract static class AssistedFactoryMetadata {
+ public static AssistedFactoryMetadata create(
+ TypeMirror factory, DaggerElements elements, DaggerTypes types) {
+ DeclaredType factoryType = asDeclared(factory);
+ TypeElement factoryElement = asTypeElement(factoryType);
+ ExecutableElement factoryMethod = assistedFactoryMethod(factoryElement, elements, types);
+ ExecutableType factoryMethodType = asExecutable(types.asMemberOf(factoryType, factoryMethod));
+ DeclaredType assistedInjectType = asDeclared(factoryMethodType.getReturnType());
+ return new AutoValue_AssistedInjectionAnnotations_AssistedFactoryMetadata(
+ factoryElement,
+ factoryType,
+ factoryMethod,
+ factoryMethodType,
+ asTypeElement(assistedInjectType),
+ assistedInjectType,
+ AssistedInjectionAnnotations.assistedInjectAssistedParameters(assistedInjectType, types),
+ AssistedInjectionAnnotations.assistedFactoryAssistedParameters(
+ factoryMethod, factoryMethodType));
+ }
+
+ public abstract TypeElement factory();
+
+ public abstract DeclaredType factoryType();
+
+ public abstract ExecutableElement factoryMethod();
+
+ public abstract ExecutableType factoryMethodType();
+
+ public abstract TypeElement assistedInjectElement();
+
+ public abstract DeclaredType assistedInjectType();
+
+ public abstract ImmutableList<AssistedParameter> assistedInjectAssistedParameters();
+
+ public abstract ImmutableList<AssistedParameter> assistedFactoryAssistedParameters();
+
+ @Memoized
+ public ImmutableMap<AssistedParameter, VariableElement> assistedInjectAssistedParametersMap() {
+ ImmutableMap.Builder<AssistedParameter, VariableElement> builder = ImmutableMap.builder();
+ for (AssistedParameter assistedParameter : assistedInjectAssistedParameters()) {
+ builder.put(assistedParameter, assistedParameter.variableElement);
+ }
+ return builder.build();
+ }
+
+ @Memoized
+ public ImmutableMap<AssistedParameter, VariableElement> assistedFactoryAssistedParametersMap() {
+ ImmutableMap.Builder<AssistedParameter, VariableElement> builder = ImmutableMap.builder();
+ for (AssistedParameter assistedParameter : assistedFactoryAssistedParameters()) {
+ builder.put(assistedParameter, assistedParameter.variableElement);
+ }
+ return builder.build();
+ }
+ }
+
+ /**
+ * Metadata about an {@link Assisted} annotated parameter.
+ *
+ * <p>This parameter can represent an {@link Assisted} annotated parameter from an {@link
+ * AssistedInject} constructor or an {@link AssistedFactory} method.
+ */
+ @AutoValue
+ public abstract static class AssistedParameter {
+ public static AssistedParameter create(VariableElement parameter, TypeMirror parameterType) {
+ AssistedParameter assistedParameter =
+ new AutoValue_AssistedInjectionAnnotations_AssistedParameter(
+ getAnnotationMirror(parameter, Assisted.class)
+ .map(assisted -> getStringValue(assisted, "value"))
+ .orElse(""),
+ MoreTypes.equivalence().wrap(parameterType));
+ assistedParameter.variableElement = parameter;
+ return assistedParameter;
+ }
+
+ private VariableElement variableElement;
+
+ /** Returns the string qualifier from the {@link Assisted#value()}. */
+ public abstract String qualifier();
+
+ /** Returns the wrapper for the type annotated with {@link Assisted}. */
+ public abstract Equivalence.Wrapper<TypeMirror> wrappedType();
+
+ /** Returns the type annotated with {@link Assisted}. */
+ public final TypeMirror type() {
+ return wrappedType().get();
+ }
+
+ public final VariableElement variableElement() {
+ return variableElement;
+ }
+
+ @Override
+ public final String toString() {
+ return qualifier().isEmpty()
+ ? String.format("@Assisted %s", type())
+ : String.format("@Assisted(\"%s\") %s", qualifier(), type());
+ }
+ }
+
+ public static ImmutableList<AssistedParameter> assistedInjectAssistedParameters(
+ DeclaredType assistedInjectType, DaggerTypes types) {
+ // We keep track of the constructor both as an ExecutableElement to access @Assisted
+ // parameters and as an ExecutableType to access the resolved parameter types.
+ ExecutableElement assistedInjectConstructor =
+ getOnlyElement(assistedInjectedConstructors(asTypeElement(assistedInjectType)));
+ ExecutableType assistedInjectConstructorType =
+ asExecutable(types.asMemberOf(assistedInjectType, assistedInjectConstructor));
+
+ ImmutableList.Builder<AssistedParameter> builder = ImmutableList.builder();
+ for (int i = 0; i < assistedInjectConstructor.getParameters().size(); i++) {
+ VariableElement parameter = assistedInjectConstructor.getParameters().get(i);
+ TypeMirror parameterType = assistedInjectConstructorType.getParameterTypes().get(i);
+ if (isAnnotationPresent(parameter, Assisted.class)) {
+ builder.add(AssistedParameter.create(parameter, parameterType));
+ }
+ }
+ return builder.build();
+ }
+
+ public static ImmutableList<AssistedParameter> assistedFactoryAssistedParameters(
+ ExecutableElement factoryMethod, ExecutableType factoryMethodType) {
+ ImmutableList.Builder<AssistedParameter> builder = ImmutableList.builder();
+ for (int i = 0; i < factoryMethod.getParameters().size(); i++) {
+ VariableElement parameter = factoryMethod.getParameters().get(i);
+ TypeMirror parameterType = factoryMethodType.getParameterTypes().get(i);
+ builder.add(AssistedParameter.create(parameter, parameterType));
+ }
+ return builder.build();
+ }
+
+ private AssistedInjectionAnnotations() {}
+}
diff --git a/java/dagger/internal/codegen/binding/BUILD b/java/dagger/internal/codegen/binding/BUILD
new file mode 100644
index 000000000..ff8db8f60
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/BUILD
@@ -0,0 +1,47 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# JavaPoet extensions for use in Dagger
+
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "binding",
+ srcs = glob(["*.java"]),
+ plugins = ["//java/dagger/internal/codegen/bootstrap"],
+ tags = ["maven:merged"],
+ deps = [
+ "//java/dagger:core",
+ "//java/dagger/internal/codegen/base",
+ "//java/dagger/internal/codegen/compileroption",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/codegen/javapoet",
+ "//java/dagger/internal/codegen/kotlin",
+ "//java/dagger/internal/codegen/langmodel",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:concurrent",
+ "//java/dagger/internal/guava:graph",
+ "//java/dagger/model:internal-proxies",
+ "//java/dagger/producers",
+ "//java/dagger/spi",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/error_prone:annotations",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
diff --git a/java/dagger/internal/codegen/binding/Binding.java b/java/dagger/internal/codegen/binding/Binding.java
new file mode 100644
index 000000000..0d4eef65e
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/Binding.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.common.base.Suppliers.memoize;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.BindingKind;
+import dagger.model.DependencyRequest;
+import dagger.model.Scope;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleTypeVisitor6;
+
+/**
+ * An abstract type for classes representing a Dagger binding. Particularly, contains the {@link
+ * Element} that generated the binding and the {@link DependencyRequest} instances that are required
+ * to satisfy the binding, but leaves the specifics of the <i>mechanism</i> of the binding to the
+ * subtypes.
+ */
+public abstract class Binding extends BindingDeclaration {
+
+ /**
+ * Returns {@code true} if using this binding requires an instance of the {@link
+ * #contributingModule()}.
+ */
+ public boolean requiresModuleInstance() {
+ if (!bindingElement().isPresent() || !contributingModule().isPresent()) {
+ return false;
+ }
+ Set<Modifier> modifiers = bindingElement().get().getModifiers();
+ return !modifiers.contains(ABSTRACT) && !modifiers.contains(STATIC);
+ }
+
+ /**
+ * Returns {@code true} if this binding may provide {@code null} instead of an instance of {@link
+ * #key()}. Nullable bindings cannot be requested from {@linkplain DependencyRequest#isNullable()
+ * non-nullable dependency requests}.
+ */
+ public abstract boolean isNullable();
+
+ /** The kind of binding this instance represents. */
+ public abstract BindingKind kind();
+
+ /** The {@link BindingType} of this binding. */
+ public abstract BindingType bindingType();
+
+ /** The {@link FrameworkType} of this binding. */
+ public final FrameworkType frameworkType() {
+ return FrameworkType.forBindingType(bindingType());
+ }
+
+ /**
+ * The explicit set of {@link DependencyRequest dependencies} required to satisfy this binding as
+ * defined by the user-defined injection sites.
+ */
+ public abstract ImmutableSet<DependencyRequest> explicitDependencies();
+
+ /**
+ * The set of {@link DependencyRequest dependencies} that are added by the framework rather than a
+ * user-defined injection site. This returns an unmodifiable set.
+ */
+ public ImmutableSet<DependencyRequest> implicitDependencies() {
+ return ImmutableSet.of();
+ }
+
+ private final Supplier<ImmutableSet<DependencyRequest>> dependencies =
+ memoize(
+ () -> {
+ ImmutableSet<DependencyRequest> implicitDependencies = implicitDependencies();
+ return ImmutableSet.copyOf(
+ implicitDependencies.isEmpty()
+ ? explicitDependencies()
+ : Sets.union(implicitDependencies, explicitDependencies()));
+ });
+
+ /**
+ * The set of {@link DependencyRequest dependencies} required to satisfy this binding. This is the
+ * union of {@link #explicitDependencies()} and {@link #implicitDependencies()}. This returns an
+ * unmodifiable set.
+ */
+ public final ImmutableSet<DependencyRequest> dependencies() {
+ return dependencies.get();
+ }
+
+ /**
+ * If this binding's key's type parameters are different from those of the {@link
+ * #bindingTypeElement()}, this is the binding for the {@link #bindingTypeElement()}'s unresolved
+ * type.
+ */
+ public abstract Optional<? extends Binding> unresolved();
+
+ public Optional<Scope> scope() {
+ return Optional.empty();
+ }
+
+ // TODO(sameb): Remove the TypeElement parameter and pull it from the TypeMirror.
+ static boolean hasNonDefaultTypeParameters(
+ TypeElement element, TypeMirror type, DaggerTypes types) {
+ // If the element has no type parameters, nothing can be wrong.
+ if (element.getTypeParameters().isEmpty()) {
+ return false;
+ }
+
+ List<TypeMirror> defaultTypes = Lists.newArrayList();
+ for (TypeParameterElement parameter : element.getTypeParameters()) {
+ defaultTypes.add(parameter.asType());
+ }
+
+ List<TypeMirror> actualTypes =
+ type.accept(
+ new SimpleTypeVisitor6<List<TypeMirror>, Void>() {
+ @Override
+ protected List<TypeMirror> defaultAction(TypeMirror e, Void p) {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public List<TypeMirror> visitDeclared(DeclaredType t, Void p) {
+ return ImmutableList.<TypeMirror>copyOf(t.getTypeArguments());
+ }
+ },
+ null);
+
+ // The actual type parameter size can be different if the user is using a raw type.
+ if (defaultTypes.size() != actualTypes.size()) {
+ return true;
+ }
+
+ for (int i = 0; i < defaultTypes.size(); i++) {
+ if (!types.isSameType(defaultTypes.get(i), actualTypes.get(i))) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/BindingDeclaration.java b/java/dagger/internal/codegen/binding/BindingDeclaration.java
new file mode 100644
index 000000000..712260fb7
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/BindingDeclaration.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static dagger.internal.codegen.extension.Optionals.emptiesLast;
+import static java.util.Comparator.comparing;
+
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.model.BindingKind;
+import dagger.model.Key;
+import java.util.Comparator;
+import java.util.Optional;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+/** An object that declares or specifies a binding. */
+public abstract class BindingDeclaration {
+ /**
+ * A comparator that compares binding declarations with elements.
+ *
+ * <p>Compares, in order:
+ *
+ * <ol>
+ * <li>Contributing module or enclosing type name
+ * <li>Binding element's simple name
+ * <li>Binding element's type
+ * </ol>
+ *
+ * Any binding declarations without elements are last.
+ */
+ public static final Comparator<BindingDeclaration> COMPARATOR =
+ comparing(
+ (BindingDeclaration declaration) ->
+ declaration.contributingModule().isPresent()
+ ? declaration.contributingModule()
+ : declaration.bindingTypeElement(),
+ emptiesLast(comparing((TypeElement type) -> type.getQualifiedName().toString())))
+ .thenComparing(
+ (BindingDeclaration declaration) -> declaration.bindingElement(),
+ emptiesLast(
+ comparing((Element element) -> element.getSimpleName().toString())
+ .thenComparing((Element element) -> element.asType().toString())));
+
+ /** The {@link Key} of this declaration. */
+ public abstract Key key();
+
+ /**
+ * The {@link Element} that declares this binding. Absent for {@linkplain BindingKind binding
+ * kinds} that are not always declared by exactly one element.
+ *
+ * <p>For example, consider {@link BindingKind#MULTIBOUND_SET}. A component with many
+ * {@code @IntoSet} bindings for the same key will have a synthetic binding that depends on all
+ * contributions, but with no identifiying binding element. A {@code @Multibinds} method will also
+ * contribute a synthetic binding, but since multiple {@code @Multibinds} methods can coexist in
+ * the same component (and contribute to one single binding), it has no binding element.
+ */
+ public abstract Optional<Element> bindingElement();
+
+ /**
+ * The type enclosing the {@link #bindingElement()}, or {@link Optional#empty()} if {@link
+ * #bindingElement()} is empty.
+ */
+ public final Optional<TypeElement> bindingTypeElement() {
+ return bindingElement().map(DaggerElements::closestEnclosingTypeElement);
+ }
+
+ /**
+ * The installed module class that contributed the {@link #bindingElement()}. May be a subclass of
+ * the class that contains {@link #bindingElement()}. Absent if {@link #bindingElement()} is
+ * empty.
+ */
+ public abstract Optional<TypeElement> contributingModule();
+}
diff --git a/java/dagger/internal/codegen/binding/BindingDeclarationFormatter.java b/java/dagger/internal/codegen/binding/BindingDeclarationFormatter.java
new file mode 100644
index 000000000..84764973b
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/BindingDeclarationFormatter.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.common.collect.Sets.immutableEnumSet;
+import static dagger.internal.codegen.base.DiagnosticFormatting.stripCommonTypePrefixes;
+import static dagger.internal.codegen.base.ElementFormatter.elementToString;
+import static javax.lang.model.element.ElementKind.PARAMETER;
+import static javax.lang.model.type.TypeKind.DECLARED;
+import static javax.lang.model.type.TypeKind.EXECUTABLE;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import dagger.internal.codegen.base.Formatter;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeKind;
+
+/**
+ * Formats a {@link BindingDeclaration} into a {@link String} suitable for use in error messages.
+ */
+public final class BindingDeclarationFormatter extends Formatter<BindingDeclaration> {
+ private static final ImmutableSet<TypeKind> FORMATTABLE_ELEMENT_TYPE_KINDS =
+ immutableEnumSet(EXECUTABLE, DECLARED);
+
+ private final MethodSignatureFormatter methodSignatureFormatter;
+
+ @Inject
+ BindingDeclarationFormatter(MethodSignatureFormatter methodSignatureFormatter) {
+ this.methodSignatureFormatter = methodSignatureFormatter;
+ }
+
+ /**
+ * Returns {@code true} for declarations that this formatter can format. Specifically bindings
+ * from subcomponent declarations or those with {@linkplain BindingDeclaration#bindingElement()
+ * binding elements} that are methods, constructors, or types.
+ */
+ public boolean canFormat(BindingDeclaration bindingDeclaration) {
+ if (bindingDeclaration instanceof SubcomponentDeclaration) {
+ return true;
+ }
+ if (bindingDeclaration.bindingElement().isPresent()) {
+ Element bindingElement = bindingDeclaration.bindingElement().get();
+ return bindingElement.getKind().equals(PARAMETER)
+ || FORMATTABLE_ELEMENT_TYPE_KINDS.contains(bindingElement.asType().getKind());
+ }
+ // TODO(dpb): validate whether what this is doing is correct
+ return false;
+ }
+
+ @Override
+ public String format(BindingDeclaration bindingDeclaration) {
+ if (bindingDeclaration instanceof SubcomponentDeclaration) {
+ return formatSubcomponentDeclaration((SubcomponentDeclaration) bindingDeclaration);
+ }
+
+ if (bindingDeclaration.bindingElement().isPresent()) {
+ Element bindingElement = bindingDeclaration.bindingElement().get();
+ if (bindingElement.getKind().equals(PARAMETER)) {
+ return elementToString(bindingElement);
+ }
+
+ switch (bindingElement.asType().getKind()) {
+ case EXECUTABLE:
+ return methodSignatureFormatter.format(
+ MoreElements.asExecutable(bindingElement),
+ bindingDeclaration
+ .contributingModule()
+ .map(module -> MoreTypes.asDeclared(module.asType())));
+
+ case DECLARED:
+ return stripCommonTypePrefixes(bindingElement.asType().toString());
+
+ default:
+ throw new IllegalArgumentException(
+ "Formatting unsupported for element: " + bindingElement);
+ }
+ }
+
+ return String.format(
+ "Dagger-generated binding for %s",
+ stripCommonTypePrefixes(bindingDeclaration.key().toString()));
+ }
+
+ private String formatSubcomponentDeclaration(SubcomponentDeclaration subcomponentDeclaration) {
+ ImmutableList<TypeElement> moduleSubcomponents =
+ subcomponentDeclaration.moduleAnnotation().subcomponents();
+ int index = moduleSubcomponents.indexOf(subcomponentDeclaration.subcomponentType());
+ StringBuilder annotationValue = new StringBuilder();
+ if (moduleSubcomponents.size() != 1) {
+ annotationValue.append("{");
+ }
+ annotationValue.append(
+ formatArgumentInList(
+ index,
+ moduleSubcomponents.size(),
+ subcomponentDeclaration.subcomponentType().getQualifiedName() + ".class"));
+ if (moduleSubcomponents.size() != 1) {
+ annotationValue.append("}");
+ }
+
+ return String.format(
+ "@%s(subcomponents = %s) for %s",
+ subcomponentDeclaration.moduleAnnotation().annotationName(),
+ annotationValue,
+ subcomponentDeclaration.contributingModule().get());
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/BindingFactory.java b/java/dagger/internal/codegen/binding/BindingFactory.java
new file mode 100644
index 000000000..6f2fc805e
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/BindingFactory.java
@@ -0,0 +1,578 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.auto.common.MoreTypes.asDeclared;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.base.MoreAnnotationMirrors.wrapOptionalInEquivalence;
+import static dagger.internal.codegen.base.Scopes.uniqueScopeOf;
+import static dagger.internal.codegen.binding.Binding.hasNonDefaultTypeParameters;
+import static dagger.internal.codegen.binding.ComponentDescriptor.isComponentProductionMethod;
+import static dagger.internal.codegen.binding.ConfigurationAnnotations.getNullableType;
+import static dagger.internal.codegen.binding.ContributionBinding.bindingKindForMultibindingKey;
+import static dagger.internal.codegen.binding.MapKeys.getMapKey;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.model.BindingKind.ASSISTED_FACTORY;
+import static dagger.model.BindingKind.ASSISTED_INJECTION;
+import static dagger.model.BindingKind.BOUND_INSTANCE;
+import static dagger.model.BindingKind.COMPONENT;
+import static dagger.model.BindingKind.COMPONENT_DEPENDENCY;
+import static dagger.model.BindingKind.COMPONENT_PRODUCTION;
+import static dagger.model.BindingKind.COMPONENT_PROVISION;
+import static dagger.model.BindingKind.DELEGATE;
+import static dagger.model.BindingKind.INJECTION;
+import static dagger.model.BindingKind.MEMBERS_INJECTOR;
+import static dagger.model.BindingKind.OPTIONAL;
+import static dagger.model.BindingKind.PRODUCTION;
+import static dagger.model.BindingKind.PROVISION;
+import static dagger.model.BindingKind.SUBCOMPONENT_CREATOR;
+import static javax.lang.model.element.ElementKind.CONSTRUCTOR;
+import static javax.lang.model.element.ElementKind.METHOD;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.Iterables;
+import dagger.Module;
+import dagger.assisted.AssistedInject;
+import dagger.internal.codegen.base.ContributionType;
+import dagger.internal.codegen.base.MapType;
+import dagger.internal.codegen.base.SetType;
+import dagger.internal.codegen.binding.MembersInjectionBinding.InjectionSite;
+import dagger.internal.codegen.binding.ProductionBinding.ProductionKind;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import java.util.Optional;
+import java.util.function.BiFunction;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+
+/** A factory for {@link Binding} objects. */
+public final class BindingFactory {
+ private final DaggerTypes types;
+ private final KeyFactory keyFactory;
+ private final DependencyRequestFactory dependencyRequestFactory;
+ private final InjectionSiteFactory injectionSiteFactory;
+ private final DaggerElements elements;
+ private final InjectionAnnotations injectionAnnotations;
+ private final KotlinMetadataUtil metadataUtil;
+
+ @Inject
+ BindingFactory(
+ DaggerTypes types,
+ DaggerElements elements,
+ KeyFactory keyFactory,
+ DependencyRequestFactory dependencyRequestFactory,
+ InjectionSiteFactory injectionSiteFactory,
+ InjectionAnnotations injectionAnnotations,
+ KotlinMetadataUtil metadataUtil) {
+ this.types = types;
+ this.elements = elements;
+ this.keyFactory = keyFactory;
+ this.dependencyRequestFactory = dependencyRequestFactory;
+ this.injectionSiteFactory = injectionSiteFactory;
+ this.injectionAnnotations = injectionAnnotations;
+ this.metadataUtil = metadataUtil;
+ }
+
+ /**
+ * Returns an {@link dagger.model.BindingKind#INJECTION} binding.
+ *
+ * @param constructorElement the {@code @Inject}-annotated constructor
+ * @param resolvedType the parameterized type if the constructor is for a generic class and the
+ * binding should be for the parameterized type
+ */
+ // TODO(dpb): See if we can just pass the parameterized type and not also the constructor.
+ public ProvisionBinding injectionBinding(
+ ExecutableElement constructorElement, Optional<TypeMirror> resolvedType) {
+ checkArgument(constructorElement.getKind().equals(CONSTRUCTOR));
+ checkArgument(
+ isAnnotationPresent(constructorElement, Inject.class)
+ || isAnnotationPresent(constructorElement, AssistedInject.class));
+ checkArgument(!injectionAnnotations.getQualifier(constructorElement).isPresent());
+
+ ExecutableType constructorType = MoreTypes.asExecutable(constructorElement.asType());
+ DeclaredType constructedType =
+ MoreTypes.asDeclared(constructorElement.getEnclosingElement().asType());
+ // If the class this is constructing has some type arguments, resolve everything.
+ if (!constructedType.getTypeArguments().isEmpty() && resolvedType.isPresent()) {
+ DeclaredType resolved = MoreTypes.asDeclared(resolvedType.get());
+ // Validate that we're resolving from the correct type.
+ checkState(
+ types.isSameType(types.erasure(resolved), types.erasure(constructedType)),
+ "erased expected type: %s, erased actual type: %s",
+ types.erasure(resolved),
+ types.erasure(constructedType));
+ constructorType = MoreTypes.asExecutable(types.asMemberOf(resolved, constructorElement));
+ constructedType = resolved;
+ }
+
+ // Collect all dependency requests within the provision method.
+ // Note: we filter out @Assisted parameters since these aren't considered dependency requests.
+ ImmutableSet.Builder<DependencyRequest> provisionDependencies = ImmutableSet.builder();
+ for (int i = 0; i < constructorElement.getParameters().size(); i++) {
+ VariableElement parameter = constructorElement.getParameters().get(i);
+ TypeMirror parameterType = constructorType.getParameterTypes().get(i);
+ if (!AssistedInjectionAnnotations.isAssistedParameter(parameter)) {
+ provisionDependencies.add(
+ dependencyRequestFactory.forRequiredResolvedVariable(parameter, parameterType));
+ }
+ }
+
+ Key key = keyFactory.forInjectConstructorWithResolvedType(constructedType);
+ ProvisionBinding.Builder builder =
+ ProvisionBinding.builder()
+ .contributionType(ContributionType.UNIQUE)
+ .bindingElement(constructorElement)
+ .key(key)
+ .provisionDependencies(provisionDependencies.build())
+ .injectionSites(injectionSiteFactory.getInjectionSites(constructedType))
+ .kind(
+ isAnnotationPresent(constructorElement, AssistedInject.class)
+ ? ASSISTED_INJECTION
+ : INJECTION)
+ .scope(uniqueScopeOf(constructorElement.getEnclosingElement()));
+
+ TypeElement bindingTypeElement = MoreElements.asType(constructorElement.getEnclosingElement());
+ if (hasNonDefaultTypeParameters(bindingTypeElement, key.type(), types)) {
+ builder.unresolved(injectionBinding(constructorElement, Optional.empty()));
+ }
+ return builder.build();
+ }
+
+ public ProvisionBinding assistedFactoryBinding(
+ TypeElement factory, Optional<TypeMirror> resolvedType) {
+
+ // If the class this is constructing has some type arguments, resolve everything.
+ DeclaredType factoryType = MoreTypes.asDeclared(factory.asType());
+ if (!factoryType.getTypeArguments().isEmpty() && resolvedType.isPresent()) {
+ DeclaredType resolved = MoreTypes.asDeclared(resolvedType.get());
+ // Validate that we're resolving from the correct type by checking that the erasure of the
+ // resolvedType is the same as the erasure of the factoryType.
+ checkState(
+ types.isSameType(types.erasure(resolved), types.erasure(factoryType)),
+ "erased expected type: %s, erased actual type: %s",
+ types.erasure(resolved),
+ types.erasure(factoryType));
+ factoryType = resolved;
+ }
+
+ ExecutableElement factoryMethod =
+ AssistedInjectionAnnotations.assistedFactoryMethod(factory, elements, types);
+ ExecutableType factoryMethodType =
+ MoreTypes.asExecutable(types.asMemberOf(factoryType, factoryMethod));
+ return ProvisionBinding.builder()
+ .contributionType(ContributionType.UNIQUE)
+ .key(Key.builder(factoryType).build())
+ .bindingElement(factory)
+ .provisionDependencies(
+ ImmutableSet.of(
+ DependencyRequest.builder()
+ .key(Key.builder(factoryMethodType.getReturnType()).build())
+ .kind(RequestKind.PROVIDER)
+ .build()))
+ .kind(ASSISTED_FACTORY)
+ .build();
+ }
+
+ /**
+ * Returns a {@link dagger.model.BindingKind#PROVISION} binding for a {@code @Provides}-annotated
+ * method.
+ *
+ * @param contributedBy the installed module that declares or inherits the method
+ */
+ public ProvisionBinding providesMethodBinding(
+ ExecutableElement providesMethod, TypeElement contributedBy) {
+ return setMethodBindingProperties(
+ ProvisionBinding.builder(),
+ providesMethod,
+ contributedBy,
+ keyFactory.forProvidesMethod(providesMethod, contributedBy),
+ this::providesMethodBinding)
+ .kind(PROVISION)
+ .scope(uniqueScopeOf(providesMethod))
+ .nullableType(getNullableType(providesMethod))
+ .build();
+ }
+
+ /**
+ * Returns a {@link dagger.model.BindingKind#PRODUCTION} binding for a {@code @Produces}-annotated
+ * method.
+ *
+ * @param contributedBy the installed module that declares or inherits the method
+ */
+ public ProductionBinding producesMethodBinding(
+ ExecutableElement producesMethod, TypeElement contributedBy) {
+ // TODO(beder): Add nullability checking with Java 8.
+ ProductionBinding.Builder builder =
+ setMethodBindingProperties(
+ ProductionBinding.builder(),
+ producesMethod,
+ contributedBy,
+ keyFactory.forProducesMethod(producesMethod, contributedBy),
+ this::producesMethodBinding)
+ .kind(PRODUCTION)
+ .productionKind(ProductionKind.fromProducesMethod(producesMethod))
+ .thrownTypes(producesMethod.getThrownTypes())
+ .executorRequest(dependencyRequestFactory.forProductionImplementationExecutor())
+ .monitorRequest(dependencyRequestFactory.forProductionComponentMonitor());
+ return builder.build();
+ }
+
+ private <C extends ContributionBinding, B extends ContributionBinding.Builder<C, B>>
+ B setMethodBindingProperties(
+ B builder,
+ ExecutableElement method,
+ TypeElement contributedBy,
+ Key key,
+ BiFunction<ExecutableElement, TypeElement, C> create) {
+ checkArgument(method.getKind().equals(METHOD));
+ ExecutableType methodType =
+ MoreTypes.asExecutable(
+ types.asMemberOf(MoreTypes.asDeclared(contributedBy.asType()), method));
+ if (!types.isSameType(methodType, method.asType())) {
+ builder.unresolved(create.apply(method, MoreElements.asType(method.getEnclosingElement())));
+ }
+ boolean isKotlinObject =
+ metadataUtil.isObjectClass(contributedBy)
+ || metadataUtil.isCompanionObjectClass(contributedBy);
+ return builder
+ .contributionType(ContributionType.fromBindingElement(method))
+ .bindingElement(method)
+ .contributingModule(contributedBy)
+ .isContributingModuleKotlinObject(isKotlinObject)
+ .key(key)
+ .dependencies(
+ dependencyRequestFactory.forRequiredResolvedVariables(
+ method.getParameters(), methodType.getParameterTypes()))
+ .wrappedMapKeyAnnotation(wrapOptionalInEquivalence(getMapKey(method)));
+ }
+
+ /**
+ * Returns a {@link dagger.model.BindingKind#MULTIBOUND_MAP} or {@link
+ * dagger.model.BindingKind#MULTIBOUND_SET} binding given a set of multibinding contribution
+ * bindings.
+ *
+ * @param key a key that may be satisfied by a multibinding
+ */
+ public ContributionBinding syntheticMultibinding(
+ Key key, Iterable<ContributionBinding> multibindingContributions) {
+ ContributionBinding.Builder<?, ?> builder =
+ multibindingRequiresProduction(key, multibindingContributions)
+ ? ProductionBinding.builder()
+ : ProvisionBinding.builder();
+ return builder
+ .contributionType(ContributionType.UNIQUE)
+ .key(key)
+ .dependencies(
+ dependencyRequestFactory.forMultibindingContributions(key, multibindingContributions))
+ .kind(bindingKindForMultibindingKey(key))
+ .build();
+ }
+
+ private boolean multibindingRequiresProduction(
+ Key key, Iterable<ContributionBinding> multibindingContributions) {
+ if (MapType.isMap(key)) {
+ MapType mapType = MapType.from(key);
+ if (mapType.valuesAreTypeOf(Producer.class) || mapType.valuesAreTypeOf(Produced.class)) {
+ return true;
+ }
+ } else if (SetType.isSet(key) && SetType.from(key).elementsAreTypeOf(Produced.class)) {
+ return true;
+ }
+ return Iterables.any(
+ multibindingContributions, binding -> binding.bindingType().equals(BindingType.PRODUCTION));
+ }
+
+ /** Returns a {@link dagger.model.BindingKind#COMPONENT} binding for the component. */
+ public ProvisionBinding componentBinding(TypeElement componentDefinitionType) {
+ checkNotNull(componentDefinitionType);
+ return ProvisionBinding.builder()
+ .contributionType(ContributionType.UNIQUE)
+ .bindingElement(componentDefinitionType)
+ .key(keyFactory.forType(componentDefinitionType.asType()))
+ .kind(COMPONENT)
+ .build();
+ }
+
+ /**
+ * Returns a {@link dagger.model.BindingKind#COMPONENT_DEPENDENCY} binding for a component's
+ * dependency.
+ */
+ public ProvisionBinding componentDependencyBinding(ComponentRequirement dependency) {
+ checkNotNull(dependency);
+ return ProvisionBinding.builder()
+ .contributionType(ContributionType.UNIQUE)
+ .bindingElement(dependency.typeElement())
+ .key(keyFactory.forType(dependency.type()))
+ .kind(COMPONENT_DEPENDENCY)
+ .build();
+ }
+
+ /**
+ * Returns a {@link dagger.model.BindingKind#COMPONENT_PROVISION} or {@link
+ * dagger.model.BindingKind#COMPONENT_PRODUCTION} binding for a method on a component's
+ * dependency.
+ *
+ * @param componentDescriptor the component with the dependency, not the dependency that has the
+ * method
+ */
+ public ContributionBinding componentDependencyMethodBinding(
+ ComponentDescriptor componentDescriptor, ExecutableElement dependencyMethod) {
+ checkArgument(dependencyMethod.getKind().equals(METHOD));
+ checkArgument(dependencyMethod.getParameters().isEmpty());
+ ContributionBinding.Builder<?, ?> builder;
+ if (componentDescriptor.isProduction()
+ && isComponentProductionMethod(elements, dependencyMethod)) {
+ builder =
+ ProductionBinding.builder()
+ .key(keyFactory.forProductionComponentMethod(dependencyMethod))
+ .kind(COMPONENT_PRODUCTION)
+ .thrownTypes(dependencyMethod.getThrownTypes());
+ } else {
+ builder =
+ ProvisionBinding.builder()
+ .key(keyFactory.forComponentMethod(dependencyMethod))
+ .nullableType(getNullableType(dependencyMethod))
+ .kind(COMPONENT_PROVISION)
+ .scope(uniqueScopeOf(dependencyMethod));
+ }
+ return builder
+ .contributionType(ContributionType.UNIQUE)
+ .bindingElement(dependencyMethod)
+ .build();
+ }
+
+ /**
+ * Returns a {@link dagger.model.BindingKind#BOUND_INSTANCE} binding for a
+ * {@code @BindsInstance}-annotated builder setter method or factory method parameter.
+ */
+ ProvisionBinding boundInstanceBinding(ComponentRequirement requirement, Element element) {
+ checkArgument(element instanceof VariableElement || element instanceof ExecutableElement);
+ VariableElement parameterElement =
+ element instanceof VariableElement
+ ? MoreElements.asVariable(element)
+ : getOnlyElement(MoreElements.asExecutable(element).getParameters());
+ return ProvisionBinding.builder()
+ .contributionType(ContributionType.UNIQUE)
+ .bindingElement(element)
+ .key(requirement.key().get())
+ .nullableType(getNullableType(parameterElement))
+ .kind(BOUND_INSTANCE)
+ .build();
+ }
+
+ /**
+ * Returns a {@link dagger.model.BindingKind#SUBCOMPONENT_CREATOR} binding declared by a component
+ * method that returns a subcomponent builder. Use {{@link
+ * #subcomponentCreatorBinding(ImmutableSet)}} for bindings declared using {@link
+ * Module#subcomponents()}.
+ *
+ * @param component the component that declares or inherits the method
+ */
+ ProvisionBinding subcomponentCreatorBinding(
+ ExecutableElement subcomponentCreatorMethod, TypeElement component) {
+ checkArgument(subcomponentCreatorMethod.getKind().equals(METHOD));
+ checkArgument(subcomponentCreatorMethod.getParameters().isEmpty());
+ Key key =
+ keyFactory.forSubcomponentCreatorMethod(
+ subcomponentCreatorMethod, asDeclared(component.asType()));
+ return ProvisionBinding.builder()
+ .contributionType(ContributionType.UNIQUE)
+ .bindingElement(subcomponentCreatorMethod)
+ .key(key)
+ .kind(SUBCOMPONENT_CREATOR)
+ .build();
+ }
+
+ /**
+ * Returns a {@link dagger.model.BindingKind#SUBCOMPONENT_CREATOR} binding declared using {@link
+ * Module#subcomponents()}.
+ */
+ ProvisionBinding subcomponentCreatorBinding(
+ ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations) {
+ SubcomponentDeclaration subcomponentDeclaration = subcomponentDeclarations.iterator().next();
+ return ProvisionBinding.builder()
+ .contributionType(ContributionType.UNIQUE)
+ .key(subcomponentDeclaration.key())
+ .kind(SUBCOMPONENT_CREATOR)
+ .build();
+ }
+
+ /**
+ * Returns a {@link dagger.model.BindingKind#DELEGATE} binding.
+ *
+ * @param delegateDeclaration the {@code @Binds}-annotated declaration
+ * @param actualBinding the binding that satisfies the {@code @Binds} declaration
+ */
+ ContributionBinding delegateBinding(
+ DelegateDeclaration delegateDeclaration, ContributionBinding actualBinding) {
+ switch (actualBinding.bindingType()) {
+ case PRODUCTION:
+ return buildDelegateBinding(
+ ProductionBinding.builder().nullableType(actualBinding.nullableType()),
+ delegateDeclaration,
+ Producer.class);
+
+ case PROVISION:
+ return buildDelegateBinding(
+ ProvisionBinding.builder()
+ .scope(uniqueScopeOf(delegateDeclaration.bindingElement().get()))
+ .nullableType(actualBinding.nullableType()),
+ delegateDeclaration,
+ Provider.class);
+
+ case MEMBERS_INJECTION: // fall-through to throw
+ }
+ throw new AssertionError("bindingType: " + actualBinding);
+ }
+
+ /**
+ * Returns a {@link dagger.model.BindingKind#DELEGATE} binding used when there is no binding that
+ * satisfies the {@code @Binds} declaration.
+ */
+ public ContributionBinding unresolvedDelegateBinding(DelegateDeclaration delegateDeclaration) {
+ return buildDelegateBinding(
+ ProvisionBinding.builder().scope(uniqueScopeOf(delegateDeclaration.bindingElement().get())),
+ delegateDeclaration,
+ Provider.class);
+ }
+
+ private ContributionBinding buildDelegateBinding(
+ ContributionBinding.Builder<?, ?> builder,
+ DelegateDeclaration delegateDeclaration,
+ Class<?> frameworkType) {
+ boolean isKotlinObject =
+ metadataUtil.isObjectClass(delegateDeclaration.contributingModule().get())
+ || metadataUtil.isCompanionObjectClass(delegateDeclaration.contributingModule().get());
+ return builder
+ .contributionType(delegateDeclaration.contributionType())
+ .bindingElement(delegateDeclaration.bindingElement().get())
+ .contributingModule(delegateDeclaration.contributingModule().get())
+ .isContributingModuleKotlinObject(isKotlinObject)
+ .key(keyFactory.forDelegateBinding(delegateDeclaration, frameworkType))
+ .dependencies(delegateDeclaration.delegateRequest())
+ .wrappedMapKeyAnnotation(delegateDeclaration.wrappedMapKey())
+ .kind(DELEGATE)
+ .build();
+ }
+
+ /**
+ * Returns an {@link dagger.model.BindingKind#OPTIONAL} binding for {@code key}.
+ *
+ * @param requestKind the kind of request for the optional binding
+ * @param underlyingKeyBindings the possibly empty set of bindings that exist in the component for
+ * the underlying (non-optional) key
+ */
+ ContributionBinding syntheticOptionalBinding(
+ Key key,
+ RequestKind requestKind,
+ ImmutableCollection<? extends Binding> underlyingKeyBindings) {
+ if (underlyingKeyBindings.isEmpty()) {
+ return ProvisionBinding.builder()
+ .contributionType(ContributionType.UNIQUE)
+ .key(key)
+ .kind(OPTIONAL)
+ .build();
+ }
+
+ boolean requiresProduction =
+ underlyingKeyBindings.stream()
+ .anyMatch(binding -> binding.bindingType() == BindingType.PRODUCTION)
+ || requestKind.equals(RequestKind.PRODUCER) // handles producerFromProvider cases
+ || requestKind.equals(RequestKind.PRODUCED); // handles producerFromProvider cases
+
+ return (requiresProduction ? ProductionBinding.builder() : ProvisionBinding.builder())
+ .contributionType(ContributionType.UNIQUE)
+ .key(key)
+ .kind(OPTIONAL)
+ .dependencies(dependencyRequestFactory.forSyntheticPresentOptionalBinding(key, requestKind))
+ .build();
+ }
+
+ /** Returns a {@link dagger.model.BindingKind#MEMBERS_INJECTOR} binding. */
+ public ProvisionBinding membersInjectorBinding(
+ Key key, MembersInjectionBinding membersInjectionBinding) {
+ return ProvisionBinding.builder()
+ .key(key)
+ .contributionType(ContributionType.UNIQUE)
+ .kind(MEMBERS_INJECTOR)
+ .bindingElement(MoreTypes.asTypeElement(membersInjectionBinding.key().type()))
+ .provisionDependencies(membersInjectionBinding.dependencies())
+ .injectionSites(membersInjectionBinding.injectionSites())
+ .build();
+ }
+
+ /**
+ * Returns a {@link dagger.model.BindingKind#MEMBERS_INJECTION} binding.
+ *
+ * @param resolvedType if {@code declaredType} is a generic class and {@code resolvedType} is a
+ * parameterization of that type, the returned binding will be for the resolved type
+ */
+ // TODO(dpb): See if we can just pass one nongeneric/parameterized type.
+ public MembersInjectionBinding membersInjectionBinding(
+ DeclaredType declaredType, Optional<TypeMirror> resolvedType) {
+ // If the class this is injecting has some type arguments, resolve everything.
+ if (!declaredType.getTypeArguments().isEmpty() && resolvedType.isPresent()) {
+ DeclaredType resolved = asDeclared(resolvedType.get());
+ // Validate that we're resolving from the correct type.
+ checkState(
+ types.isSameType(types.erasure(resolved), types.erasure(declaredType)),
+ "erased expected type: %s, erased actual type: %s",
+ types.erasure(resolved),
+ types.erasure(declaredType));
+ declaredType = resolved;
+ }
+ ImmutableSortedSet<InjectionSite> injectionSites =
+ injectionSiteFactory.getInjectionSites(declaredType);
+ ImmutableSet<DependencyRequest> dependencies =
+ injectionSites.stream()
+ .flatMap(injectionSite -> injectionSite.dependencies().stream())
+ .collect(toImmutableSet());
+
+ Key key = keyFactory.forMembersInjectedType(declaredType);
+ TypeElement typeElement = MoreElements.asType(declaredType.asElement());
+ return new AutoValue_MembersInjectionBinding(
+ key,
+ dependencies,
+ typeElement,
+ hasNonDefaultTypeParameters(typeElement, key.type(), types)
+ ? Optional.of(
+ membersInjectionBinding(asDeclared(typeElement.asType()), Optional.empty()))
+ : Optional.empty(),
+ injectionSites);
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/BindingGraph.java b/java/dagger/internal/codegen/binding/BindingGraph.java
new file mode 100644
index 000000000..4936a0526
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/BindingGraph.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static dagger.internal.codegen.extension.DaggerCollectors.toOptional;
+import static dagger.internal.codegen.extension.DaggerStreams.presentValues;
+import static dagger.internal.codegen.extension.DaggerStreams.stream;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Sets;
+import com.google.common.graph.Graphs;
+import com.google.common.graph.ImmutableNetwork;
+import com.google.common.graph.Traverser;
+import dagger.model.BindingGraph.ChildFactoryMethodEdge;
+import dagger.model.BindingGraph.ComponentNode;
+import dagger.model.BindingGraph.Edge;
+import dagger.model.BindingGraph.Node;
+import dagger.model.ComponentPath;
+import dagger.model.Key;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+
+/**
+ * A graph that represents a single component or subcomponent within a fully validated top-level
+ * binding graph.
+ */
+@AutoValue
+public abstract class BindingGraph {
+
+ @AutoValue
+ abstract static class TopLevelBindingGraph extends dagger.model.BindingGraph {
+ static TopLevelBindingGraph create(
+ ImmutableNetwork<Node, Edge> network, boolean isFullBindingGraph) {
+ TopLevelBindingGraph topLevelBindingGraph =
+ new AutoValue_BindingGraph_TopLevelBindingGraph(network, isFullBindingGraph);
+
+ ImmutableMap<ComponentPath, ComponentNode> componentNodes =
+ topLevelBindingGraph.componentNodes().stream()
+ .collect(
+ toImmutableMap(ComponentNode::componentPath, componentNode -> componentNode));
+
+ ImmutableSetMultimap.Builder<ComponentNode, ComponentNode> subcomponentNodesBuilder =
+ ImmutableSetMultimap.builder();
+ topLevelBindingGraph.componentNodes().stream()
+ .filter(componentNode -> !componentNode.componentPath().atRoot())
+ .forEach(
+ componentNode ->
+ subcomponentNodesBuilder.put(
+ componentNodes.get(componentNode.componentPath().parent()), componentNode));
+
+ // Set these fields directly on the instance rather than passing these in as input to the
+ // AutoValue to prevent exposing this data outside of the class.
+ topLevelBindingGraph.componentNodes = componentNodes;
+ topLevelBindingGraph.subcomponentNodes = subcomponentNodesBuilder.build();
+ return topLevelBindingGraph;
+ }
+
+ private ImmutableMap<ComponentPath, ComponentNode> componentNodes;
+ private ImmutableSetMultimap<ComponentNode, ComponentNode> subcomponentNodes;
+
+ TopLevelBindingGraph() {}
+
+ // This overrides dagger.model.BindingGraph with a more efficient implementation.
+ @Override
+ public Optional<ComponentNode> componentNode(ComponentPath componentPath) {
+ return componentNodes.containsKey(componentPath)
+ ? Optional.of(componentNodes.get(componentPath))
+ : Optional.empty();
+ }
+
+ /** Returns the set of subcomponent nodes of the given component node. */
+ ImmutableSet<ComponentNode> subcomponentNodes(ComponentNode componentNode) {
+ return subcomponentNodes.get(componentNode);
+ }
+
+ @Override
+ @Memoized
+ public ImmutableSetMultimap<Class<? extends Node>, ? extends Node> nodesByClass() {
+ return super.nodesByClass();
+ }
+ }
+
+ static BindingGraph create(
+ ComponentNode componentNode, TopLevelBindingGraph topLevelBindingGraph) {
+ return create(Optional.empty(), componentNode, topLevelBindingGraph);
+ }
+
+ private static BindingGraph create(
+ Optional<BindingGraph> parent,
+ ComponentNode componentNode,
+ TopLevelBindingGraph topLevelBindingGraph) {
+ ImmutableSet<BindingNode> reachableBindingNodes =
+ Graphs.reachableNodes(topLevelBindingGraph.network().asGraph(), componentNode).stream()
+ .filter(node -> isSubpath(componentNode.componentPath(), node.componentPath()))
+ .filter(node -> node instanceof BindingNode)
+ .map(node -> (BindingNode) node)
+ .collect(toImmutableSet());
+
+ // Construct the maps of the ContributionBindings and MembersInjectionBindings.
+ Map<Key, BindingNode> contributionBindings = new HashMap<>();
+ Map<Key, BindingNode> membersInjectionBindings = new HashMap<>();
+ for (BindingNode bindingNode : reachableBindingNodes) {
+ Map<Key, BindingNode> bindingsMap;
+ if (bindingNode.delegate() instanceof ContributionBinding) {
+ bindingsMap = contributionBindings;
+ } else if (bindingNode.delegate() instanceof MembersInjectionBinding) {
+ bindingsMap = membersInjectionBindings;
+ } else {
+ throw new AssertionError("Unexpected binding node type: " + bindingNode.delegate());
+ }
+
+ // TODO(bcorso): Mapping binding nodes by key is flawed since bindings that depend on local
+ // multibindings can have multiple nodes (one in each component). In this case, we choose the
+ // node in the child-most component since this is likely the node that users of this
+ // BindingGraph will want (and to remain consisted with LegacyBindingGraph). However, ideally
+ // we would avoid this ambiguity by getting dependencies directly from the top-level network.
+ // In particular, rather than using a Binding's list of DependencyRequests (which only
+ // contains the key) we would use the top-level network to find the DependencyEdges for a
+ // particular BindingNode.
+ Key key = bindingNode.key();
+ if (!bindingsMap.containsKey(key)
+ // Always choose the child-most binding node.
+ || bindingNode.componentPath().components().size()
+ > bindingsMap.get(key).componentPath().components().size()) {
+ bindingsMap.put(key, bindingNode);
+ }
+ }
+
+ BindingGraph bindingGraph = new AutoValue_BindingGraph(componentNode, topLevelBindingGraph);
+
+ ImmutableSet<ModuleDescriptor> modules =
+ ((ComponentNodeImpl) componentNode).componentDescriptor().modules();
+
+ ImmutableSet<ModuleDescriptor> inheritedModules =
+ parent.isPresent()
+ ? Sets.union(parent.get().ownedModules, parent.get().inheritedModules).immutableCopy()
+ : ImmutableSet.of();
+
+ // Set these fields directly on the instance rather than passing these in as input to the
+ // AutoValue to prevent exposing this data outside of the class.
+ bindingGraph.inheritedModules = inheritedModules;
+ bindingGraph.ownedModules = Sets.difference(modules, inheritedModules).immutableCopy();
+ bindingGraph.contributionBindings = ImmutableMap.copyOf(contributionBindings);
+ bindingGraph.membersInjectionBindings = ImmutableMap.copyOf(membersInjectionBindings);
+ bindingGraph.bindingModules =
+ contributionBindings.values().stream()
+ .map(BindingNode::contributingModule)
+ .flatMap(presentValues())
+ .collect(toImmutableSet());
+
+ return bindingGraph;
+ }
+
+ private ImmutableMap<Key, BindingNode> contributionBindings;
+ private ImmutableMap<Key, BindingNode> membersInjectionBindings;
+ private ImmutableSet<ModuleDescriptor> inheritedModules;
+ private ImmutableSet<ModuleDescriptor> ownedModules;
+ private ImmutableSet<TypeElement> bindingModules;
+
+ BindingGraph() {}
+
+ /** Returns the {@link ComponentNode} for this graph. */
+ public abstract ComponentNode componentNode();
+
+ /** Returns the {@link ComponentPath} for this graph. */
+ public final ComponentPath componentPath() {
+ return componentNode().componentPath();
+ }
+
+ /** Returns the {@link TopLevelBindingGraph} from which this graph is contained. */
+ public abstract TopLevelBindingGraph topLevelBindingGraph();
+
+ /** Returns the {@link ComponentDescriptor} for this graph */
+ public final ComponentDescriptor componentDescriptor() {
+ return ((ComponentNodeImpl) componentNode()).componentDescriptor();
+ }
+
+ /** Returns the {@link ContributionBinding} for the given {@link Key}. */
+ public final ContributionBinding contributionBinding(Key key) {
+ return (ContributionBinding) contributionBindings.get(key).delegate();
+ }
+
+ /**
+ * Returns the {@link MembersInjectionBinding} for the given {@link Key} or {@link
+ * Optional#empty()} if one does not exist.
+ */
+ public final Optional<MembersInjectionBinding> membersInjectionBinding(Key key) {
+ return membersInjectionBindings.containsKey(key)
+ ? Optional.of((MembersInjectionBinding) membersInjectionBindings.get(key).delegate())
+ : Optional.empty();
+ }
+
+ /** Returns the {@link TypeElement} for the component this graph represents. */
+ public final TypeElement componentTypeElement() {
+ return componentPath().currentComponent();
+ }
+
+ /**
+ * Returns the set of modules that are owned by this graph regardless of whether or not any of
+ * their bindings are used in this graph. For graphs representing top-level {@link
+ * dagger.Component components}, this set will be the same as {@linkplain
+ * ComponentDescriptor#modules() the component's transitive modules}. For {@linkplain Subcomponent
+ * subcomponents}, this set will be the transitive modules that are not owned by any of their
+ * ancestors.
+ */
+ public final ImmutableSet<TypeElement> ownedModuleTypes() {
+ return ownedModules.stream().map(ModuleDescriptor::moduleElement).collect(toImmutableSet());
+ }
+
+ /**
+ * Returns the factory method for this subcomponent, if it exists.
+ *
+ * <p>This factory method is the one defined in the parent component's interface.
+ *
+ * <p>In the example below, the {@link BindingGraph#factoryMethod} for {@code ChildComponent}
+ * would return the {@link ExecutableElement}: {@code childComponent(ChildModule1)} .
+ *
+ * <pre><code>
+ * {@literal @Component}
+ * interface ParentComponent {
+ * ChildComponent childComponent(ChildModule1 childModule);
+ * }
+ * </code></pre>
+ */
+ // TODO(b/73294201): Consider returning the resolved ExecutableType for the factory method.
+ public final Optional<ExecutableElement> factoryMethod() {
+ return topLevelBindingGraph().network().inEdges(componentNode()).stream()
+ .filter(edge -> edge instanceof ChildFactoryMethodEdge)
+ .map(edge -> ((ChildFactoryMethodEdge) edge).factoryMethod())
+ .collect(toOptional());
+ }
+
+ /**
+ * Returns a map between the {@linkplain ComponentRequirement component requirement} and the
+ * corresponding {@link VariableElement} for each module parameter in the {@linkplain
+ * BindingGraph#factoryMethod factory method}.
+ */
+ // TODO(dpb): Consider disallowing modules if none of their bindings are used.
+ public final ImmutableMap<ComponentRequirement, VariableElement> factoryMethodParameters() {
+ return factoryMethod().get().getParameters().stream()
+ .collect(
+ toImmutableMap(
+ parameter -> ComponentRequirement.forModule(parameter.asType()),
+ parameter -> parameter));
+ }
+
+ /**
+ * The types for which the component needs instances.
+ *
+ * <ul>
+ * <li>component dependencies
+ * <li>owned modules with concrete instance bindings that are used in the graph
+ * <li>bound instances
+ * </ul>
+ */
+ @Memoized
+ public ImmutableSet<ComponentRequirement> componentRequirements() {
+ ImmutableSet<TypeElement> requiredModules =
+ stream(Traverser.forTree(BindingGraph::subgraphs).depthFirstPostOrder(this))
+ .flatMap(graph -> graph.bindingModules.stream())
+ .filter(ownedModuleTypes()::contains)
+ .collect(toImmutableSet());
+ ImmutableSet.Builder<ComponentRequirement> requirements = ImmutableSet.builder();
+ componentDescriptor().requirements().stream()
+ .filter(
+ requirement ->
+ !requirement.kind().isModule()
+ || requiredModules.contains(requirement.typeElement()))
+ .forEach(requirements::add);
+ if (factoryMethod().isPresent()) {
+ requirements.addAll(factoryMethodParameters().keySet());
+ }
+ return requirements.build();
+ }
+
+ /** Returns all {@link ComponentDescriptor}s in the {@link TopLevelBindingGraph}. */
+ public final ImmutableSet<ComponentDescriptor> componentDescriptors() {
+ return topLevelBindingGraph().componentNodes().stream()
+ .map(componentNode -> ((ComponentNodeImpl) componentNode).componentDescriptor())
+ .collect(toImmutableSet());
+ }
+
+ @Memoized
+ public ImmutableList<BindingGraph> subgraphs() {
+ return topLevelBindingGraph().subcomponentNodes(componentNode()).stream()
+ .map(subcomponent -> create(Optional.of(this), subcomponent, topLevelBindingGraph()))
+ .collect(toImmutableList());
+ }
+
+ public final ImmutableSet<BindingNode> bindingNodes(Key key) {
+ ImmutableSet.Builder<BindingNode> builder = ImmutableSet.builder();
+ if (contributionBindings.containsKey(key)) {
+ builder.add(contributionBindings.get(key));
+ }
+ if (membersInjectionBindings.containsKey(key)) {
+ builder.add(membersInjectionBindings.get(key));
+ }
+ return builder.build();
+ }
+
+ @Memoized
+ public ImmutableSet<BindingNode> bindingNodes() {
+ return ImmutableSet.<BindingNode>builder()
+ .addAll(contributionBindings.values())
+ .addAll(membersInjectionBindings.values())
+ .build();
+ }
+
+ // TODO(bcorso): Move this to ComponentPath
+ private static boolean isSubpath(ComponentPath path, ComponentPath subpath) {
+ if (path.components().size() < subpath.components().size()) {
+ return false;
+ }
+ for (int i = 0; i < subpath.components().size(); i++) {
+ if (!path.components().get(i).equals(subpath.components().get(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/BindingGraphConverter.java b/java/dagger/internal/codegen/binding/BindingGraphConverter.java
new file mode 100644
index 000000000..b882b37a9
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/BindingGraphConverter.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.auto.common.MoreTypes.asTypeElement;
+import static com.google.common.base.Verify.verify;
+import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
+import static dagger.internal.codegen.extension.DaggerGraphs.unreachableNodes;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.model.BindingKind.SUBCOMPONENT_CREATOR;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
+import com.google.common.graph.ImmutableNetwork;
+import com.google.common.graph.MutableNetwork;
+import com.google.common.graph.Network;
+import com.google.common.graph.NetworkBuilder;
+import dagger.internal.codegen.binding.BindingGraph.TopLevelBindingGraph;
+import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.model.BindingGraph.ComponentNode;
+import dagger.model.BindingGraph.DependencyEdge;
+import dagger.model.BindingGraph.Edge;
+import dagger.model.BindingGraph.MissingBinding;
+import dagger.model.BindingGraph.Node;
+import dagger.model.ComponentPath;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+/** Converts {@link BindingGraph}s to {@link dagger.model.BindingGraph}s. */
+final class BindingGraphConverter {
+ private final BindingDeclarationFormatter bindingDeclarationFormatter;
+
+ @Inject
+ BindingGraphConverter(BindingDeclarationFormatter bindingDeclarationFormatter) {
+ this.bindingDeclarationFormatter = bindingDeclarationFormatter;
+ }
+
+ /**
+ * Creates the external {@link dagger.model.BindingGraph} representing the given internal {@link
+ * BindingGraph}.
+ */
+ BindingGraph convert(LegacyBindingGraph legacyBindingGraph, boolean isFullBindingGraph) {
+ MutableNetwork<Node, Edge> network = asNetwork(legacyBindingGraph);
+ ComponentNode rootNode = rootComponentNode(network);
+
+ // When bindings are copied down into child graphs because they transitively depend on local
+ // multibindings or optional bindings, the parent-owned binding is still there. If that
+ // parent-owned binding is not reachable from its component, it doesn't need to be in the graph
+ // because it will never be used. So remove all nodes that are not reachable from the root
+ // component—unless we're converting a full binding graph.
+ if (!isFullBindingGraph) {
+ unreachableNodes(network.asGraph(), rootNode).forEach(network::removeNode);
+ }
+
+ TopLevelBindingGraph topLevelBindingGraph =
+ TopLevelBindingGraph.create(ImmutableNetwork.copyOf(network), isFullBindingGraph);
+ return BindingGraph.create(rootNode, topLevelBindingGraph);
+ }
+
+ private MutableNetwork<Node, Edge> asNetwork(LegacyBindingGraph graph) {
+ Converter converter = new Converter(bindingDeclarationFormatter);
+ converter.visitRootComponent(graph);
+ return converter.network;
+ }
+
+ // TODO(dpb): Example of BindingGraph logic applied to derived networks.
+ private ComponentNode rootComponentNode(Network<Node, Edge> network) {
+ return (ComponentNode)
+ Iterables.find(
+ network.nodes(),
+ node -> node instanceof ComponentNode && node.componentPath().atRoot());
+ }
+
+ /**
+ * Used as a cache key to make sure resolved bindings are cached per component path.
+ * This is required so that binding nodes are not reused across different branches of the
+ * graph since the ResolvedBindings class only contains the component and not the path.
+ */
+ @AutoValue
+ abstract static class ResolvedBindingsWithPath {
+ abstract ResolvedBindings resolvedBindings();
+ abstract ComponentPath componentPath();
+
+ static ResolvedBindingsWithPath create(
+ ResolvedBindings resolvedBindings, ComponentPath componentPath) {
+ return new AutoValue_BindingGraphConverter_ResolvedBindingsWithPath(
+ resolvedBindings, componentPath);
+ }
+ }
+
+ private static final class Converter {
+ /** The path from the root graph to the currently visited graph. */
+ private final Deque<LegacyBindingGraph> bindingGraphPath = new ArrayDeque<>();
+
+ /** The {@link ComponentPath} for each component in {@link #bindingGraphPath}. */
+ private final Deque<ComponentPath> componentPaths = new ArrayDeque<>();
+
+ private final BindingDeclarationFormatter bindingDeclarationFormatter;
+ private final MutableNetwork<Node, Edge> network =
+ NetworkBuilder.directed().allowsParallelEdges(true).allowsSelfLoops(true).build();
+ private final Set<BindingNode> bindings = new HashSet<>();
+
+ private final Map<ResolvedBindingsWithPath, ImmutableSet<BindingNode>> resolvedBindingsMap =
+ new HashMap<>();
+
+ /** Constructs a converter for a root (component, not subcomponent) binding graph. */
+ private Converter(BindingDeclarationFormatter bindingDeclarationFormatter) {
+ this.bindingDeclarationFormatter = bindingDeclarationFormatter;
+ }
+
+ private void visitRootComponent(LegacyBindingGraph graph) {
+ visitComponent(graph, null);
+ }
+
+ /**
+ * Called once for each component in a component hierarchy.
+ *
+ * <p>This implementation does the following:
+ *
+ * <ol>
+ * <li>If this component is installed in its parent by a subcomponent factory method, calls
+ * {@link #visitSubcomponentFactoryMethod(ComponentNode, ComponentNode,
+ * ExecutableElement)}.
+ * <li>For each entry point in the component, calls {@link #visitEntryPoint(ComponentNode,
+ * DependencyRequest)}.
+ * <li>For each child component, calls {@link #visitComponent(LegacyBindingGraph,
+ * ComponentNode)}, updating the traversal state.
+ * </ol>
+ *
+ * @param graph the currently visited graph
+ */
+ private void visitComponent(LegacyBindingGraph graph, ComponentNode parentComponent) {
+ bindingGraphPath.addLast(graph);
+ ComponentPath graphPath =
+ ComponentPath.create(
+ bindingGraphPath.stream()
+ .map(LegacyBindingGraph::componentDescriptor)
+ .map(ComponentDescriptor::typeElement)
+ .collect(toImmutableList()));
+ componentPaths.addLast(graphPath);
+ ComponentNode currentComponent =
+ ComponentNodeImpl.create(componentPath(), graph.componentDescriptor());
+
+ network.addNode(currentComponent);
+
+ for (ComponentMethodDescriptor entryPointMethod :
+ graph.componentDescriptor().entryPointMethods()) {
+ visitEntryPoint(currentComponent, entryPointMethod.dependencyRequest().get());
+ }
+
+ for (ResolvedBindings resolvedBindings : graph.resolvedBindings()) {
+ for (BindingNode binding : bindingNodes(resolvedBindings)) {
+ if (bindings.add(binding)) {
+ network.addNode(binding);
+ for (DependencyRequest dependencyRequest : binding.dependencies()) {
+ addDependencyEdges(binding, dependencyRequest);
+ }
+ }
+ if (binding.kind().equals(SUBCOMPONENT_CREATOR)
+ && binding.componentPath().equals(currentComponent.componentPath())) {
+ network.addEdge(
+ binding,
+ subcomponentNode(binding.key().type(), graph),
+ new SubcomponentCreatorBindingEdgeImpl(
+ resolvedBindings.subcomponentDeclarations()));
+ }
+ }
+ }
+
+ if (bindingGraphPath.size() > 1) {
+ LegacyBindingGraph parent = Iterators.get(bindingGraphPath.descendingIterator(), 1);
+ parent
+ .componentDescriptor()
+ .getFactoryMethodForChildComponent(graph.componentDescriptor())
+ .ifPresent(
+ childFactoryMethod ->
+ visitSubcomponentFactoryMethod(
+ parentComponent, currentComponent, childFactoryMethod.methodElement()));
+ }
+
+ for (LegacyBindingGraph child : graph.subgraphs()) {
+ visitComponent(child, currentComponent);
+ }
+
+ verify(bindingGraphPath.removeLast().equals(graph));
+ verify(componentPaths.removeLast().equals(graphPath));
+ }
+
+ /**
+ * Called once for each entry point in a component.
+ *
+ * @param componentNode the component that contains the entry point
+ * @param entryPoint the entry point to visit
+ */
+ private void visitEntryPoint(ComponentNode componentNode, DependencyRequest entryPoint) {
+ addDependencyEdges(componentNode, entryPoint);
+ }
+
+ /**
+ * Called if this component was installed in its parent by a subcomponent factory method.
+ *
+ * @param parentComponent the parent graph
+ * @param currentComponent the currently visited graph
+ * @param factoryMethod the factory method in the parent component that declares that the
+ * current component is a child
+ */
+ private void visitSubcomponentFactoryMethod(
+ ComponentNode parentComponent,
+ ComponentNode currentComponent,
+ ExecutableElement factoryMethod) {
+ network.addEdge(
+ parentComponent,
+ currentComponent,
+ new ChildFactoryMethodEdgeImpl(factoryMethod));
+ }
+
+ /**
+ * Returns an immutable snapshot of the path from the root component to the currently visited
+ * component.
+ */
+ private ComponentPath componentPath() {
+ return componentPaths.getLast();
+ }
+
+ /**
+ * Returns the subpath from the root component to the matching {@code ancestor} of the current
+ * component.
+ */
+ private ComponentPath pathFromRootToAncestor(TypeElement ancestor) {
+ for (ComponentPath componentPath : componentPaths) {
+ if (componentPath.currentComponent().equals(ancestor)) {
+ return componentPath;
+ }
+ }
+ throw new IllegalArgumentException(
+ String.format(
+ "%s is not in the current path: %s", ancestor.getQualifiedName(), componentPath()));
+ }
+
+ /**
+ * Returns the LegacyBindingGraph for {@code ancestor}, where {@code ancestor} is in the
+ * component path of the current traversal.
+ */
+ private LegacyBindingGraph graphForAncestor(TypeElement ancestor) {
+ for (LegacyBindingGraph graph : bindingGraphPath) {
+ if (graph.componentDescriptor().typeElement().equals(ancestor)) {
+ return graph;
+ }
+ }
+ throw new IllegalArgumentException(
+ String.format(
+ "%s is not in the current path: %s", ancestor.getQualifiedName(), componentPath()));
+ }
+
+ /**
+ * Adds a {@link dagger.model.BindingGraph.DependencyEdge} from a node to the binding(s) that
+ * satisfy a dependency request.
+ */
+ private void addDependencyEdges(Node source, DependencyRequest dependencyRequest) {
+ ResolvedBindings dependencies = resolvedDependencies(source, dependencyRequest);
+ if (dependencies.isEmpty()) {
+ addDependencyEdge(source, dependencyRequest, missingBindingNode(dependencies));
+ } else {
+ for (BindingNode dependency : bindingNodes(dependencies)) {
+ addDependencyEdge(source, dependencyRequest, dependency);
+ }
+ }
+ }
+
+ private void addDependencyEdge(
+ Node source, DependencyRequest dependencyRequest, Node dependency) {
+ network.addNode(dependency);
+ if (!hasDependencyEdge(source, dependency, dependencyRequest)) {
+ network.addEdge(
+ source,
+ dependency,
+ new DependencyEdgeImpl(dependencyRequest, source instanceof ComponentNode));
+ }
+ }
+
+ private boolean hasDependencyEdge(
+ Node source, Node dependency, DependencyRequest dependencyRequest) {
+ // An iterative approach is used instead of a Stream because this method is called in a hot
+ // loop, and the Stream calculates the size of network.edgesConnecting(), which is slow. This
+ // seems to be because caculating the edges connecting two nodes in a Network that supports
+ // parallel edges is must check the equality of many nodes, and BindingNode's equality
+ // semantics drag in the equality of many other expensive objects
+ for (Edge edge : network.edgesConnecting(source, dependency)) {
+ if (edge instanceof DependencyEdge) {
+ if (((DependencyEdge) edge).dependencyRequest().equals(dependencyRequest)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private ResolvedBindings resolvedDependencies(
+ Node source, DependencyRequest dependencyRequest) {
+ return graphForAncestor(source.componentPath().currentComponent())
+ .resolvedBindings(bindingRequest(dependencyRequest));
+ }
+
+ private ImmutableSet<BindingNode> bindingNodes(ResolvedBindings resolvedBindings) {
+ ResolvedBindingsWithPath resolvedBindingsWithPath =
+ ResolvedBindingsWithPath.create(resolvedBindings, componentPath());
+ return resolvedBindingsMap.computeIfAbsent(
+ resolvedBindingsWithPath, this::uncachedBindingNodes);
+ }
+
+ private ImmutableSet<BindingNode> uncachedBindingNodes(
+ ResolvedBindingsWithPath resolvedBindingsWithPath) {
+ ImmutableSet.Builder<BindingNode> bindingNodes = ImmutableSet.builder();
+ resolvedBindingsWithPath.resolvedBindings()
+ .allBindings()
+ .asMap()
+ .forEach(
+ (component, bindings) -> {
+ for (Binding binding : bindings) {
+ bindingNodes.add(
+ bindingNode(resolvedBindingsWithPath.resolvedBindings(), binding, component));
+ }
+ });
+ return bindingNodes.build();
+ }
+
+ private BindingNode bindingNode(
+ ResolvedBindings resolvedBindings, Binding binding, TypeElement owningComponent) {
+ return BindingNode.create(
+ pathFromRootToAncestor(owningComponent),
+ binding,
+ resolvedBindings.multibindingDeclarations(),
+ resolvedBindings.optionalBindingDeclarations(),
+ resolvedBindings.subcomponentDeclarations(),
+ bindingDeclarationFormatter);
+ }
+
+ private MissingBinding missingBindingNode(ResolvedBindings dependencies) {
+ // Put all missing binding nodes in the root component. This simplifies the binding graph
+ // and produces better error messages for users since all dependents point to the same node.
+ return MissingBindingImpl.create(
+ ComponentPath.create(ImmutableList.of(componentPath().rootComponent())),
+ dependencies.key());
+ }
+
+ private ComponentNode subcomponentNode(
+ TypeMirror subcomponentBuilderType, LegacyBindingGraph graph) {
+ TypeElement subcomponentBuilderElement = asTypeElement(subcomponentBuilderType);
+ ComponentDescriptor subcomponent =
+ graph.componentDescriptor().getChildComponentWithBuilderType(subcomponentBuilderElement);
+ return ComponentNodeImpl.create(
+ componentPath().childPath(subcomponent.typeElement()), subcomponent);
+ }
+ }
+
+ @AutoValue
+ abstract static class MissingBindingImpl extends MissingBinding {
+ static MissingBinding create(ComponentPath component, Key key) {
+ return new AutoValue_BindingGraphConverter_MissingBindingImpl(component, key);
+ }
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object o);
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/BindingGraphFactory.java b/java/dagger/internal/codegen/binding/BindingGraphFactory.java
new file mode 100644
index 000000000..2c15e2625
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/BindingGraphFactory.java
@@ -0,0 +1,980 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.auto.common.MoreTypes.asTypeElement;
+import static com.google.auto.common.MoreTypes.isType;
+import static com.google.auto.common.MoreTypes.isTypeOf;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.base.RequestKinds.getRequestKind;
+import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.isAssistedFactoryType;
+import static dagger.internal.codegen.binding.ComponentDescriptor.isComponentContributionMethod;
+import static dagger.internal.codegen.binding.SourceFiles.generatedMonitoringModuleName;
+import static dagger.model.BindingKind.ASSISTED_INJECTION;
+import static dagger.model.BindingKind.DELEGATE;
+import static dagger.model.BindingKind.INJECTION;
+import static dagger.model.BindingKind.OPTIONAL;
+import static dagger.model.BindingKind.SUBCOMPONENT_CREATOR;
+import static dagger.model.RequestKind.MEMBERS_INJECTION;
+import static java.util.function.Predicate.isEqual;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimaps;
+import dagger.MembersInjector;
+import dagger.Reusable;
+import dagger.internal.codegen.base.ClearableCache;
+import dagger.internal.codegen.base.ContributionType;
+import dagger.internal.codegen.base.MapType;
+import dagger.internal.codegen.base.OptionalType;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.model.Scope;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import dagger.producers.internal.ProductionExecutorModule;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Queue;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeKind;
+
+/** A factory for {@link BindingGraph} objects. */
+@Singleton
+public final class BindingGraphFactory implements ClearableCache {
+
+ private final DaggerElements elements;
+ private final InjectBindingRegistry injectBindingRegistry;
+ private final KeyFactory keyFactory;
+ private final BindingFactory bindingFactory;
+ private final ModuleDescriptor.Factory moduleDescriptorFactory;
+ private final BindingGraphConverter bindingGraphConverter;
+ private final Map<Key, ImmutableSet<Key>> keysMatchingRequestCache = new HashMap<>();
+ private final CompilerOptions compilerOptions;
+
+ @Inject
+ BindingGraphFactory(
+ DaggerElements elements,
+ InjectBindingRegistry injectBindingRegistry,
+ KeyFactory keyFactory,
+ BindingFactory bindingFactory,
+ ModuleDescriptor.Factory moduleDescriptorFactory,
+ BindingGraphConverter bindingGraphConverter,
+ CompilerOptions compilerOptions) {
+ this.elements = elements;
+ this.injectBindingRegistry = injectBindingRegistry;
+ this.keyFactory = keyFactory;
+ this.bindingFactory = bindingFactory;
+ this.moduleDescriptorFactory = moduleDescriptorFactory;
+ this.bindingGraphConverter = bindingGraphConverter;
+ this.compilerOptions = compilerOptions;
+ }
+
+ /**
+ * Creates a binding graph for a component.
+ *
+ * @param createFullBindingGraph if {@code true}, the binding graph will include all bindings;
+ * otherwise it will include only bindings reachable from at least one entry point
+ */
+ public BindingGraph create(
+ ComponentDescriptor componentDescriptor, boolean createFullBindingGraph) {
+ return bindingGraphConverter.convert(
+ createLegacyBindingGraph(Optional.empty(), componentDescriptor, createFullBindingGraph),
+ createFullBindingGraph);
+ }
+
+ private LegacyBindingGraph createLegacyBindingGraph(
+ Optional<Resolver> parentResolver,
+ ComponentDescriptor componentDescriptor,
+ boolean createFullBindingGraph) {
+ ImmutableSet.Builder<ContributionBinding> explicitBindingsBuilder = ImmutableSet.builder();
+ ImmutableSet.Builder<DelegateDeclaration> delegatesBuilder = ImmutableSet.builder();
+ ImmutableSet.Builder<OptionalBindingDeclaration> optionalsBuilder = ImmutableSet.builder();
+
+ if (componentDescriptor.isRealComponent()) {
+ // binding for the component itself
+ explicitBindingsBuilder.add(
+ bindingFactory.componentBinding(componentDescriptor.typeElement()));
+ }
+
+ // Collect Component dependencies.
+ for (ComponentRequirement dependency : componentDescriptor.dependencies()) {
+ explicitBindingsBuilder.add(bindingFactory.componentDependencyBinding(dependency));
+ List<ExecutableElement> dependencyMethods =
+ methodsIn(elements.getAllMembers(dependency.typeElement()));
+
+ // Within a component dependency, we want to allow the same method to appear multiple
+ // times assuming it is the exact same method. We do this by tracking a set of bindings
+ // we've already added with the binding element removed since that is the only thing
+ // allowed to differ.
+ HashMultimap<String, ContributionBinding> dedupeBindings = HashMultimap.create();
+ for (ExecutableElement method : dependencyMethods) {
+ // MembersInjection methods aren't "provided" explicitly, so ignore them.
+ if (isComponentContributionMethod(elements, method)) {
+ ContributionBinding binding = bindingFactory.componentDependencyMethodBinding(
+ componentDescriptor, method);
+ if (dedupeBindings.put(
+ method.getSimpleName().toString(),
+ // Remove the binding element since we know that will be different, but everything
+ // else we want to be the same to consider it a duplicate.
+ binding.toBuilder().clearBindingElement().build())) {
+ explicitBindingsBuilder.add(binding);
+ }
+ }
+ }
+ }
+
+ // Collect bindings on the creator.
+ componentDescriptor
+ .creatorDescriptor()
+ .ifPresent(
+ creatorDescriptor ->
+ creatorDescriptor.boundInstanceRequirements().stream()
+ .map(
+ requirement ->
+ bindingFactory.boundInstanceBinding(
+ requirement, creatorDescriptor.elementForRequirement(requirement)))
+ .forEach(explicitBindingsBuilder::add));
+
+ componentDescriptor
+ .childComponentsDeclaredByBuilderEntryPoints()
+ .forEach(
+ (builderEntryPoint, childComponent) -> {
+ if (!componentDescriptor
+ .childComponentsDeclaredByModules()
+ .contains(childComponent)) {
+ explicitBindingsBuilder.add(
+ bindingFactory.subcomponentCreatorBinding(
+ builderEntryPoint.methodElement(), componentDescriptor.typeElement()));
+ }
+ });
+
+ ImmutableSet.Builder<MultibindingDeclaration> multibindingDeclarations = ImmutableSet.builder();
+ ImmutableSet.Builder<SubcomponentDeclaration> subcomponentDeclarations = ImmutableSet.builder();
+
+ // Collect transitive module bindings and multibinding declarations.
+ for (ModuleDescriptor moduleDescriptor : modules(componentDescriptor, parentResolver)) {
+ explicitBindingsBuilder.addAll(moduleDescriptor.bindings());
+ multibindingDeclarations.addAll(moduleDescriptor.multibindingDeclarations());
+ subcomponentDeclarations.addAll(moduleDescriptor.subcomponentDeclarations());
+ delegatesBuilder.addAll(moduleDescriptor.delegateDeclarations());
+ optionalsBuilder.addAll(moduleDescriptor.optionalDeclarations());
+ }
+
+ final Resolver requestResolver =
+ new Resolver(
+ parentResolver,
+ componentDescriptor,
+ indexBindingDeclarationsByKey(explicitBindingsBuilder.build()),
+ indexBindingDeclarationsByKey(multibindingDeclarations.build()),
+ indexBindingDeclarationsByKey(subcomponentDeclarations.build()),
+ indexBindingDeclarationsByKey(delegatesBuilder.build()),
+ indexBindingDeclarationsByKey(optionalsBuilder.build()));
+
+ componentDescriptor.entryPointMethods().stream()
+ .map(method -> method.dependencyRequest().get())
+ .forEach(
+ entryPoint -> {
+ if (entryPoint.kind().equals(MEMBERS_INJECTION)) {
+ requestResolver.resolveMembersInjection(entryPoint.key());
+ } else {
+ requestResolver.resolve(entryPoint.key());
+ }
+ });
+
+ if (createFullBindingGraph) {
+ // Resolve the keys for all bindings in all modules, stripping any multibinding contribution
+ // identifier so that the multibinding itself is resolved.
+ modules(componentDescriptor, parentResolver).stream()
+ .flatMap(module -> module.allBindingKeys().stream())
+ .map(key -> key.toBuilder().multibindingContributionIdentifier(Optional.empty()).build())
+ .forEach(requestResolver::resolve);
+ }
+
+ // Resolve all bindings for subcomponents, creating subgraphs for all subcomponents that have
+ // been detected during binding resolution. If a binding for a subcomponent is never resolved,
+ // no BindingGraph will be created for it and no implementation will be generated. This is
+ // done in a queue since resolving one subcomponent might resolve a key for a subcomponent
+ // from a parent graph. This is done until no more new subcomponents are resolved.
+ Set<ComponentDescriptor> resolvedSubcomponents = new HashSet<>();
+ ImmutableList.Builder<LegacyBindingGraph> subgraphs = ImmutableList.builder();
+ for (ComponentDescriptor subcomponent :
+ Iterables.consumingIterable(requestResolver.subcomponentsToResolve)) {
+ if (resolvedSubcomponents.add(subcomponent)) {
+ subgraphs.add(
+ createLegacyBindingGraph(
+ Optional.of(requestResolver), subcomponent, createFullBindingGraph));
+ }
+ }
+
+ return new LegacyBindingGraph(
+ componentDescriptor,
+ ImmutableMap.copyOf(requestResolver.getResolvedContributionBindings()),
+ ImmutableMap.copyOf(requestResolver.getResolvedMembersInjectionBindings()),
+ ImmutableList.copyOf(subgraphs.build()));
+ }
+
+ /**
+ * Returns all the modules that should be installed in the component. For production components
+ * and production subcomponents that have a parent that is not a production component or
+ * subcomponent, also includes the production monitoring module for the component and the
+ * production executor module.
+ */
+ private ImmutableSet<ModuleDescriptor> modules(
+ ComponentDescriptor componentDescriptor, Optional<Resolver> parentResolver) {
+ return shouldIncludeImplicitProductionModules(componentDescriptor, parentResolver)
+ ? new ImmutableSet.Builder<ModuleDescriptor>()
+ .addAll(componentDescriptor.modules())
+ .add(descriptorForMonitoringModule(componentDescriptor.typeElement()))
+ .add(descriptorForProductionExecutorModule())
+ .build()
+ : componentDescriptor.modules();
+ }
+
+ private boolean shouldIncludeImplicitProductionModules(
+ ComponentDescriptor component, Optional<Resolver> parentResolver) {
+ return component.isProduction()
+ && ((!component.isSubcomponent() && component.isRealComponent())
+ || (parentResolver.isPresent()
+ && !parentResolver.get().componentDescriptor.isProduction()));
+ }
+
+ /**
+ * Returns a descriptor for a generated module that handles monitoring for production components.
+ * This module is generated in the {@link
+ * dagger.internal.codegen.validation.MonitoringModuleProcessingStep}.
+ *
+ * @throws TypeNotPresentException if the module has not been generated yet. This will cause the
+ * processor to retry in a later processing round.
+ */
+ private ModuleDescriptor descriptorForMonitoringModule(TypeElement componentDefinitionType) {
+ return moduleDescriptorFactory.create(
+ elements.checkTypePresent(
+ generatedMonitoringModuleName(componentDefinitionType).toString()));
+ }
+
+ /** Returns a descriptor {@link ProductionExecutorModule}. */
+ private ModuleDescriptor descriptorForProductionExecutorModule() {
+ return moduleDescriptorFactory.create(elements.getTypeElement(ProductionExecutorModule.class));
+ }
+
+ /** Indexes {@code bindingDeclarations} by {@link BindingDeclaration#key()}. */
+ private static <T extends BindingDeclaration>
+ ImmutableSetMultimap<Key, T> indexBindingDeclarationsByKey(Iterable<T> declarations) {
+ return ImmutableSetMultimap.copyOf(Multimaps.index(declarations, BindingDeclaration::key));
+ }
+
+ @Override
+ public void clearCache() {
+ keysMatchingRequestCache.clear();
+ }
+
+ private final class Resolver {
+ final Optional<Resolver> parentResolver;
+ final ComponentDescriptor componentDescriptor;
+ final ImmutableSetMultimap<Key, ContributionBinding> explicitBindings;
+ final ImmutableSet<ContributionBinding> explicitBindingsSet;
+ final ImmutableSetMultimap<Key, ContributionBinding> explicitMultibindings;
+ final ImmutableSetMultimap<Key, MultibindingDeclaration> multibindingDeclarations;
+ final ImmutableSetMultimap<Key, SubcomponentDeclaration> subcomponentDeclarations;
+ final ImmutableSetMultimap<Key, DelegateDeclaration> delegateDeclarations;
+ final ImmutableSetMultimap<Key, OptionalBindingDeclaration> optionalBindingDeclarations;
+ final ImmutableSetMultimap<Key, DelegateDeclaration> delegateMultibindingDeclarations;
+ final Map<Key, ResolvedBindings> resolvedContributionBindings = new LinkedHashMap<>();
+ final Map<Key, ResolvedBindings> resolvedMembersInjectionBindings = new LinkedHashMap<>();
+ final Deque<Key> cycleStack = new ArrayDeque<>();
+ final Map<Key, Boolean> keyDependsOnLocalBindingsCache = new HashMap<>();
+ final Map<Binding, Boolean> bindingDependsOnLocalBindingsCache = new HashMap<>();
+ final Queue<ComponentDescriptor> subcomponentsToResolve = new ArrayDeque<>();
+
+ Resolver(
+ Optional<Resolver> parentResolver,
+ ComponentDescriptor componentDescriptor,
+ ImmutableSetMultimap<Key, ContributionBinding> explicitBindings,
+ ImmutableSetMultimap<Key, MultibindingDeclaration> multibindingDeclarations,
+ ImmutableSetMultimap<Key, SubcomponentDeclaration> subcomponentDeclarations,
+ ImmutableSetMultimap<Key, DelegateDeclaration> delegateDeclarations,
+ ImmutableSetMultimap<Key, OptionalBindingDeclaration> optionalBindingDeclarations) {
+ this.parentResolver = parentResolver;
+ this.componentDescriptor = checkNotNull(componentDescriptor);
+ this.explicitBindings = checkNotNull(explicitBindings);
+ this.explicitBindingsSet = ImmutableSet.copyOf(explicitBindings.values());
+ this.multibindingDeclarations = checkNotNull(multibindingDeclarations);
+ this.subcomponentDeclarations = checkNotNull(subcomponentDeclarations);
+ this.delegateDeclarations = checkNotNull(delegateDeclarations);
+ this.optionalBindingDeclarations = checkNotNull(optionalBindingDeclarations);
+ this.explicitMultibindings = multibindingContributionsByMultibindingKey(explicitBindingsSet);
+ this.delegateMultibindingDeclarations =
+ multibindingContributionsByMultibindingKey(delegateDeclarations.values());
+ subcomponentsToResolve.addAll(
+ componentDescriptor.childComponentsDeclaredByFactoryMethods().values());
+ subcomponentsToResolve.addAll(
+ componentDescriptor.childComponentsDeclaredByBuilderEntryPoints().values());
+ }
+
+ /**
+ * Returns the resolved contribution bindings for the given {@link Key}:
+ *
+ * <ul>
+ * <li>All explicit bindings for:
+ * <ul>
+ * <li>the requested key
+ * <li>{@code Set<T>} if the requested key's type is {@code Set<Produced<T>>}
+ * <li>{@code Map<K, Provider<V>>} if the requested key's type is {@code Map<K,
+ * Producer<V>>}.
+ * </ul>
+ * <li>An implicit {@link Inject @Inject}-annotated constructor binding if there is one and
+ * there are no explicit bindings or synthetic bindings.
+ * </ul>
+ */
+ ResolvedBindings lookUpBindings(Key requestKey) {
+ Set<ContributionBinding> bindings = new LinkedHashSet<>();
+ Set<ContributionBinding> multibindingContributions = new LinkedHashSet<>();
+ Set<MultibindingDeclaration> multibindingDeclarations = new LinkedHashSet<>();
+ Set<OptionalBindingDeclaration> optionalBindingDeclarations = new LinkedHashSet<>();
+ Set<SubcomponentDeclaration> subcomponentDeclarations = new LinkedHashSet<>();
+
+ // Gather all bindings, multibindings, optional, and subcomponent declarations/contributions.
+ ImmutableSet<Key> keysMatchingRequest = keysMatchingRequest(requestKey);
+ for (Resolver resolver : getResolverLineage()) {
+ bindings.addAll(resolver.getLocalExplicitBindings(requestKey));
+
+ for (Key key : keysMatchingRequest) {
+ multibindingContributions.addAll(resolver.getLocalExplicitMultibindings(key));
+ multibindingDeclarations.addAll(resolver.multibindingDeclarations.get(key));
+ subcomponentDeclarations.addAll(resolver.subcomponentDeclarations.get(key));
+ // The optional binding declarations are keyed by the unwrapped type.
+ keyFactory.unwrapOptional(key)
+ .map(resolver.optionalBindingDeclarations::get)
+ .ifPresent(optionalBindingDeclarations::addAll);
+ }
+ }
+
+ // Add synthetic multibinding
+ if (!multibindingContributions.isEmpty() || !multibindingDeclarations.isEmpty()) {
+ bindings.add(bindingFactory.syntheticMultibinding(requestKey, multibindingContributions));
+ }
+
+ // Add synthetic optional binding
+ if (!optionalBindingDeclarations.isEmpty()) {
+ bindings.add(
+ bindingFactory.syntheticOptionalBinding(
+ requestKey,
+ getRequestKind(OptionalType.from(requestKey).valueType()),
+ lookUpBindings(keyFactory.unwrapOptional(requestKey).get()).bindings()));
+ }
+
+ // Add subcomponent creator binding
+ if (!subcomponentDeclarations.isEmpty()) {
+ ProvisionBinding binding =
+ bindingFactory.subcomponentCreatorBinding(
+ ImmutableSet.copyOf(subcomponentDeclarations));
+ bindings.add(binding);
+ addSubcomponentToOwningResolver(binding);
+ }
+
+ // Add members injector binding
+ if (isType(requestKey.type()) && isTypeOf(MembersInjector.class, requestKey.type())) {
+ injectBindingRegistry
+ .getOrFindMembersInjectorProvisionBinding(requestKey)
+ .ifPresent(bindings::add);
+ }
+
+ // Add Assisted Factory binding
+ if (isType(requestKey.type())
+ && requestKey.type().getKind() == TypeKind.DECLARED
+ && isAssistedFactoryType(asTypeElement(requestKey.type()))) {
+ bindings.add(
+ bindingFactory.assistedFactoryBinding(
+ asTypeElement(requestKey.type()), Optional.of(requestKey.type())));
+ }
+
+ // If there are no bindings, add the implicit @Inject-constructed binding if there is one.
+ if (bindings.isEmpty()) {
+ injectBindingRegistry
+ .getOrFindProvisionBinding(requestKey)
+ .filter(this::isCorrectlyScopedInSubcomponent)
+ .ifPresent(bindings::add);
+ }
+
+ return ResolvedBindings.forContributionBindings(
+ requestKey,
+ Multimaps.index(bindings, binding -> getOwningComponent(requestKey, binding)),
+ multibindingDeclarations,
+ subcomponentDeclarations,
+ optionalBindingDeclarations);
+ }
+
+ /**
+ * Returns true if this binding graph resolution is for a subcomponent and the {@code @Inject}
+ * binding's scope correctly matches one of the components in the current component ancestry.
+ * If not, it means the binding is not owned by any of the currently known components, and will
+ * be owned by a future ancestor (or, if never owned, will result in an incompatibly scoped
+ * binding error at the root component).
+ */
+ private boolean isCorrectlyScopedInSubcomponent(ProvisionBinding binding) {
+ checkArgument(binding.kind() == INJECTION || binding.kind() == ASSISTED_INJECTION);
+ if (!rootComponent().isSubcomponent()
+ || !binding.scope().isPresent()
+ || binding.scope().get().isReusable()) {
+ return true;
+ }
+
+ Resolver owningResolver = getOwningResolver(binding).orElse(this);
+ ComponentDescriptor owningComponent = owningResolver.componentDescriptor;
+ return owningComponent.scopes().contains(binding.scope().get());
+ }
+
+ private ComponentDescriptor rootComponent() {
+ return parentResolver.map(Resolver::rootComponent).orElse(componentDescriptor);
+ }
+
+ /** Returns the resolved members injection bindings for the given {@link Key}. */
+ ResolvedBindings lookUpMembersInjectionBinding(Key requestKey) {
+ // no explicit deps for members injection, so just look it up
+ Optional<MembersInjectionBinding> binding =
+ injectBindingRegistry.getOrFindMembersInjectionBinding(requestKey);
+ return binding.isPresent()
+ ? ResolvedBindings.forMembersInjectionBinding(
+ requestKey, componentDescriptor, binding.get())
+ : ResolvedBindings.noBindings(requestKey);
+ }
+
+ /**
+ * When a binding is resolved for a {@link SubcomponentDeclaration}, adds corresponding {@link
+ * ComponentDescriptor subcomponent} to a queue in the owning component's resolver. The queue
+ * will be used to detect which subcomponents need to be resolved.
+ */
+ private void addSubcomponentToOwningResolver(ProvisionBinding subcomponentCreatorBinding) {
+ checkArgument(subcomponentCreatorBinding.kind().equals(SUBCOMPONENT_CREATOR));
+ Resolver owningResolver = getOwningResolver(subcomponentCreatorBinding).get();
+
+ TypeElement builderType = MoreTypes.asTypeElement(subcomponentCreatorBinding.key().type());
+ owningResolver.subcomponentsToResolve.add(
+ owningResolver.componentDescriptor.getChildComponentWithBuilderType(builderType));
+ }
+
+ /**
+ * Profiling has determined that computing the keys matching {@code requestKey} has measurable
+ * performance impact. It is called repeatedly (at least 3 times per key resolved per {@link
+ * BindingGraph}. {@code javac}'s name-checking performance seems suboptimal (converting byte
+ * strings to Strings repeatedly), and the matching keys creations relies on that. This also
+ * ensures that the resulting keys have their hash codes cached on successive calls to this
+ * method.
+ *
+ * <p>This caching may become obsolete if:
+ *
+ * <ul>
+ * <li>We decide to intern all {@link Key} instances
+ * <li>We fix javac's name-checking peformance (though we may want to keep this for older
+ * javac users)
+ * </ul>
+ */
+ private ImmutableSet<Key> keysMatchingRequest(Key requestKey) {
+ return keysMatchingRequestCache.computeIfAbsent(
+ requestKey, this::keysMatchingRequestUncached);
+ }
+
+ private ImmutableSet<Key> keysMatchingRequestUncached(Key requestKey) {
+ ImmutableSet.Builder<Key> keys = ImmutableSet.builder();
+ keys.add(requestKey);
+ keyFactory.unwrapSetKey(requestKey, Produced.class).ifPresent(keys::add);
+ keyFactory.rewrapMapKey(requestKey, Producer.class, Provider.class).ifPresent(keys::add);
+ keyFactory.rewrapMapKey(requestKey, Provider.class, Producer.class).ifPresent(keys::add);
+ keys.addAll(keyFactory.implicitFrameworkMapKeys(requestKey));
+ return keys.build();
+ }
+
+ private ImmutableSet<ContributionBinding> createDelegateBindings(
+ ImmutableSet<DelegateDeclaration> delegateDeclarations) {
+ ImmutableSet.Builder<ContributionBinding> builder = ImmutableSet.builder();
+ for (DelegateDeclaration delegateDeclaration : delegateDeclarations) {
+ builder.add(createDelegateBinding(delegateDeclaration));
+ }
+ return builder.build();
+ }
+
+ /**
+ * Creates one (and only one) delegate binding for a delegate declaration, based on the resolved
+ * bindings of the right-hand-side of a {@link dagger.Binds} method. If there are duplicate
+ * bindings for the dependency key, there should still be only one binding for the delegate key.
+ */
+ private ContributionBinding createDelegateBinding(DelegateDeclaration delegateDeclaration) {
+ Key delegateKey = delegateDeclaration.delegateRequest().key();
+ if (cycleStack.contains(delegateKey)) {
+ return bindingFactory.unresolvedDelegateBinding(delegateDeclaration);
+ }
+
+ ResolvedBindings resolvedDelegate;
+ try {
+ cycleStack.push(delegateKey);
+ resolvedDelegate = lookUpBindings(delegateKey);
+ } finally {
+ cycleStack.pop();
+ }
+ if (resolvedDelegate.contributionBindings().isEmpty()) {
+ // This is guaranteed to result in a missing binding error, so it doesn't matter if the
+ // binding is a Provision or Production, except if it is a @IntoMap method, in which
+ // case the key will be of type Map<K, Provider<V>>, which will be "upgraded" into a
+ // Map<K, Producer<V>> if it's requested in a ProductionComponent. This may result in a
+ // strange error, that the RHS needs to be provided with an @Inject or @Provides
+ // annotated method, but a user should be able to figure out if a @Produces annotation
+ // is needed.
+ // TODO(gak): revisit how we model missing delegates if/when we clean up how we model
+ // binding declarations
+ return bindingFactory.unresolvedDelegateBinding(delegateDeclaration);
+ }
+ // It doesn't matter which of these is selected, since they will later on produce a
+ // duplicate binding error.
+ ContributionBinding explicitDelegate =
+ resolvedDelegate.contributionBindings().iterator().next();
+ return bindingFactory.delegateBinding(delegateDeclaration, explicitDelegate);
+ }
+
+ /**
+ * Returns the component that should contain the framework field for {@code binding}.
+ *
+ * <p>If {@code binding} is either not bound in an ancestor component or depends transitively on
+ * bindings in this component, returns this component.
+ *
+ * <p>Otherwise, resolves {@code request} in this component's parent in order to resolve any
+ * multibinding contributions in the parent, and returns the parent-resolved {@link
+ * ResolvedBindings#owningComponent(ContributionBinding)}.
+ */
+ private TypeElement getOwningComponent(Key requestKey, ContributionBinding binding) {
+ if (isResolvedInParent(requestKey, binding)
+ && !new LocalDependencyChecker().dependsOnLocalBindings(binding)) {
+ ResolvedBindings parentResolvedBindings =
+ parentResolver.get().resolvedContributionBindings.get(requestKey);
+ return parentResolvedBindings.owningComponent(binding);
+ } else {
+ return componentDescriptor.typeElement();
+ }
+ }
+
+ /**
+ * Returns {@code true} if {@code binding} is owned by an ancestor. If so, {@linkplain #resolve
+ * resolves} the {@link Key} in this component's parent. Don't resolve directly in the owning
+ * component in case it depends on multibindings in any of its descendants.
+ */
+ private boolean isResolvedInParent(Key requestKey, ContributionBinding binding) {
+ Optional<Resolver> owningResolver = getOwningResolver(binding);
+ if (owningResolver.isPresent() && !owningResolver.get().equals(this)) {
+ parentResolver.get().resolve(requestKey);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private Optional<Resolver> getOwningResolver(ContributionBinding binding) {
+ // TODO(ronshapiro): extract the different pieces of this method into their own methods
+ if ((binding.scope().isPresent() && binding.scope().get().isProductionScope())
+ || binding.bindingType().equals(BindingType.PRODUCTION)) {
+ for (Resolver requestResolver : getResolverLineage()) {
+ // Resolve @Inject @ProductionScope bindings at the highest production component.
+ if (binding.kind().equals(INJECTION)
+ && requestResolver.componentDescriptor.isProduction()) {
+ return Optional.of(requestResolver);
+ }
+
+ // Resolve explicit @Produces and @ProductionScope bindings at the highest component that
+ // installs the binding.
+ if (requestResolver.containsExplicitBinding(binding)) {
+ return Optional.of(requestResolver);
+ }
+ }
+ }
+
+ if (binding.scope().isPresent() && binding.scope().get().isReusable()) {
+ for (Resolver requestResolver : getResolverLineage().reverse()) {
+ // If a @Reusable binding was resolved in an ancestor, use that component.
+ ResolvedBindings resolvedBindings =
+ requestResolver.resolvedContributionBindings.get(binding.key());
+ if (resolvedBindings != null
+ && resolvedBindings.contributionBindings().contains(binding)) {
+ return Optional.of(requestResolver);
+ }
+ }
+ // If a @Reusable binding was not resolved in any ancestor, resolve it here.
+ return Optional.empty();
+ }
+
+ for (Resolver requestResolver : getResolverLineage().reverse()) {
+ if (requestResolver.containsExplicitBinding(binding)) {
+ return Optional.of(requestResolver);
+ }
+ }
+
+ // look for scope separately. we do this for the case where @Singleton can appear twice
+ // in the † compatibility mode
+ Optional<Scope> bindingScope = binding.scope();
+ if (bindingScope.isPresent()) {
+ for (Resolver requestResolver : getResolverLineage().reverse()) {
+ if (requestResolver.componentDescriptor.scopes().contains(bindingScope.get())) {
+ return Optional.of(requestResolver);
+ }
+ }
+ }
+ return Optional.empty();
+ }
+
+ private boolean containsExplicitBinding(ContributionBinding binding) {
+ return explicitBindingsSet.contains(binding)
+ || resolverContainsDelegateDeclarationForBinding(binding)
+ || subcomponentDeclarations.containsKey(binding.key());
+ }
+
+ /** Returns true if {@code binding} was installed in a module in this resolver's component. */
+ private boolean resolverContainsDelegateDeclarationForBinding(ContributionBinding binding) {
+ if (!binding.kind().equals(DELEGATE)) {
+ return false;
+ }
+
+ // Map multibinding key values are wrapped with a framework type. This needs to be undone
+ // to look it up in the delegate declarations map.
+ // TODO(erichang): See if we can standardize the way map keys are used in these data
+ // structures, either always wrapped or unwrapped to be consistent and less errorprone.
+ Key bindingKey = binding.key();
+ if (compilerOptions.strictMultibindingValidation()
+ && binding.contributionType().equals(ContributionType.MAP)) {
+ bindingKey = keyFactory.unwrapMapValueType(bindingKey);
+ }
+
+ return delegateDeclarations.get(bindingKey).stream()
+ .anyMatch(
+ declaration ->
+ declaration.contributingModule().equals(binding.contributingModule())
+ && declaration.bindingElement().equals(binding.bindingElement()));
+ }
+
+ /** Returns the resolver lineage from parent to child. */
+ private ImmutableList<Resolver> getResolverLineage() {
+ ImmutableList.Builder<Resolver> resolverList = ImmutableList.builder();
+ for (Optional<Resolver> currentResolver = Optional.of(this);
+ currentResolver.isPresent();
+ currentResolver = currentResolver.get().parentResolver) {
+ resolverList.add(currentResolver.get());
+ }
+ return resolverList.build().reverse();
+ }
+
+ /**
+ * Returns the explicit {@link ContributionBinding}s that match the {@code key} from this
+ * resolver.
+ */
+ private ImmutableSet<ContributionBinding> getLocalExplicitBindings(Key key) {
+ return new ImmutableSet.Builder<ContributionBinding>()
+ .addAll(explicitBindings.get(key))
+ // @Binds @IntoMap declarations have key Map<K, V>, unlike @Provides @IntoMap or @Produces
+ // @IntoMap, which have Map<K, Provider/Producer<V>> keys. So unwrap the key's type's
+ // value type if it's a Map<K, Provider/Producer<V>> before looking in
+ // delegateDeclarations. createDelegateBindings() will create bindings with the properly
+ // wrapped key type.
+ .addAll(
+ createDelegateBindings(delegateDeclarations.get(keyFactory.unwrapMapValueType(key))))
+ .build();
+ }
+
+ /**
+ * Returns the explicit multibinding contributions that contribute to the map or set requested
+ * by {@code key} from this resolver.
+ */
+ private ImmutableSet<ContributionBinding> getLocalExplicitMultibindings(Key key) {
+ ImmutableSet.Builder<ContributionBinding> multibindings = ImmutableSet.builder();
+ multibindings.addAll(explicitMultibindings.get(key));
+ if (!MapType.isMap(key)
+ || MapType.from(key).isRawType()
+ || MapType.from(key).valuesAreFrameworkType()) {
+ // @Binds @IntoMap declarations have key Map<K, V>, unlike @Provides @IntoMap or @Produces
+ // @IntoMap, which have Map<K, Provider/Producer<V>> keys. So unwrap the key's type's
+ // value type if it's a Map<K, Provider/Producer<V>> before looking in
+ // delegateMultibindingDeclarations. createDelegateBindings() will create bindings with the
+ // properly wrapped key type.
+ multibindings.addAll(
+ createDelegateBindings(
+ delegateMultibindingDeclarations.get(keyFactory.unwrapMapValueType(key))));
+ }
+ return multibindings.build();
+ }
+
+ /**
+ * Returns the {@link OptionalBindingDeclaration}s that match the {@code key} from this and all
+ * ancestor resolvers.
+ */
+ private ImmutableSet<OptionalBindingDeclaration> getOptionalBindingDeclarations(Key key) {
+ Optional<Key> unwrapped = keyFactory.unwrapOptional(key);
+ if (!unwrapped.isPresent()) {
+ return ImmutableSet.of();
+ }
+ ImmutableSet.Builder<OptionalBindingDeclaration> declarations = ImmutableSet.builder();
+ for (Resolver resolver : getResolverLineage()) {
+ declarations.addAll(resolver.optionalBindingDeclarations.get(unwrapped.get()));
+ }
+ return declarations.build();
+ }
+
+ /**
+ * Returns the {@link ResolvedBindings} for {@code key} that was resolved in this resolver or an
+ * ancestor resolver. Only checks for {@link ContributionBinding}s as {@link
+ * MembersInjectionBinding}s are not inherited.
+ */
+ private Optional<ResolvedBindings> getPreviouslyResolvedBindings(Key key) {
+ Optional<ResolvedBindings> result =
+ Optional.ofNullable(resolvedContributionBindings.get(key));
+ if (result.isPresent()) {
+ return result;
+ } else if (parentResolver.isPresent()) {
+ return parentResolver.get().getPreviouslyResolvedBindings(key);
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ private void resolveMembersInjection(Key key) {
+ ResolvedBindings bindings = lookUpMembersInjectionBinding(key);
+ resolveDependencies(bindings);
+ resolvedMembersInjectionBindings.put(key, bindings);
+ }
+
+ void resolve(Key key) {
+ // If we find a cycle, stop resolving. The original request will add it with all of the
+ // other resolved deps.
+ if (cycleStack.contains(key)) {
+ return;
+ }
+
+ // If the binding was previously resolved in this (sub)component, don't resolve it again.
+ if (resolvedContributionBindings.containsKey(key)) {
+ return;
+ }
+
+ /*
+ * If the binding was previously resolved in an ancestor component, then we may be able to
+ * avoid resolving it here and just depend on the ancestor component resolution.
+ *
+ * 1. If it depends transitively on multibinding contributions or optional bindings with
+ * bindings from this subcomponent, then we have to resolve it in this subcomponent so
+ * that it sees the local bindings.
+ *
+ * 2. If there are any explicit bindings in this component, they may conflict with those in
+ * the ancestor component, so resolve them here so that conflicts can be caught.
+ */
+ if (getPreviouslyResolvedBindings(key).isPresent()) {
+ /* Resolve in the parent in case there are multibinding contributions or conflicts in some
+ * component between this one and the previously-resolved one. */
+ parentResolver.get().resolve(key);
+ if (!new LocalDependencyChecker().dependsOnLocalBindings(key)
+ && getLocalExplicitBindings(key).isEmpty()) {
+ /* Cache the inherited parent component's bindings in case resolving at the parent found
+ * bindings in some component between this one and the previously-resolved one. */
+ resolvedContributionBindings.put(key, getPreviouslyResolvedBindings(key).get());
+ return;
+ }
+ }
+
+ cycleStack.push(key);
+ try {
+ ResolvedBindings bindings = lookUpBindings(key);
+ resolvedContributionBindings.put(key, bindings);
+ resolveDependencies(bindings);
+ } finally {
+ cycleStack.pop();
+ }
+ }
+
+ /**
+ * {@link #resolve(Key) Resolves} each of the dependencies of the bindings owned by this
+ * component.
+ */
+ private void resolveDependencies(ResolvedBindings resolvedBindings) {
+ for (Binding binding : resolvedBindings.bindingsOwnedBy(componentDescriptor)) {
+ for (DependencyRequest dependency : binding.dependencies()) {
+ resolve(dependency.key());
+ }
+ }
+ }
+
+ /**
+ * Returns all of the {@link ResolvedBindings} for {@link ContributionBinding}s from this and
+ * all ancestor resolvers, indexed by {@link ResolvedBindings#key()}.
+ */
+ Map<Key, ResolvedBindings> getResolvedContributionBindings() {
+ Map<Key, ResolvedBindings> bindings = new LinkedHashMap<>();
+ parentResolver.ifPresent(parent -> bindings.putAll(parent.getResolvedContributionBindings()));
+ bindings.putAll(resolvedContributionBindings);
+ return bindings;
+ }
+
+ /**
+ * Returns all of the {@link ResolvedBindings} for {@link MembersInjectionBinding} from this
+ * resolvers, indexed by {@link ResolvedBindings#key()}.
+ */
+ ImmutableMap<Key, ResolvedBindings> getResolvedMembersInjectionBindings() {
+ return ImmutableMap.copyOf(resolvedMembersInjectionBindings);
+ }
+
+ private final class LocalDependencyChecker {
+ private final Set<Object> cycleChecker = new HashSet<>();
+
+ /**
+ * Returns {@code true} if any of the bindings resolved for {@code key} are multibindings with
+ * contributions declared within this component's modules or optional bindings with present
+ * values declared within this component's modules, or if any of its unscoped dependencies
+ * depend on such bindings.
+ *
+ * <p>We don't care about scoped dependencies because they will never depend on bindings from
+ * subcomponents.
+ *
+ * @throws IllegalArgumentException if {@link #getPreviouslyResolvedBindings(Key)} is empty
+ */
+ private boolean dependsOnLocalBindings(Key key) {
+ // Don't recur infinitely if there are valid cycles in the dependency graph.
+ // http://b/23032377
+ if (!cycleChecker.add(key)) {
+ return false;
+ }
+ return reentrantComputeIfAbsent(
+ keyDependsOnLocalBindingsCache, key, this::dependsOnLocalBindingsUncached);
+ }
+
+ /**
+ * Returns {@code true} if {@code binding} is unscoped (or has {@link Reusable @Reusable}
+ * scope) and depends on multibindings with contributions declared within this component's
+ * modules, or if any of its unscoped or {@link Reusable @Reusable} scoped dependencies depend
+ * on such local multibindings.
+ *
+ * <p>We don't care about non-reusable scoped dependencies because they will never depend on
+ * multibindings with contributions from subcomponents.
+ */
+ private boolean dependsOnLocalBindings(Binding binding) {
+ if (!cycleChecker.add(binding)) {
+ return false;
+ }
+ return reentrantComputeIfAbsent(
+ bindingDependsOnLocalBindingsCache, binding, this::dependsOnLocalBindingsUncached);
+ }
+
+ private boolean dependsOnLocalBindingsUncached(Key key) {
+ checkArgument(
+ getPreviouslyResolvedBindings(key).isPresent(),
+ "no previously resolved bindings in %s for %s",
+ Resolver.this,
+ key);
+ ResolvedBindings previouslyResolvedBindings = getPreviouslyResolvedBindings(key).get();
+ if (hasLocalMultibindingContributions(key)
+ || hasLocalOptionalBindingContribution(previouslyResolvedBindings)) {
+ return true;
+ }
+
+ for (Binding binding : previouslyResolvedBindings.bindings()) {
+ if (dependsOnLocalBindings(binding)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean dependsOnLocalBindingsUncached(Binding binding) {
+ if ((!binding.scope().isPresent() || binding.scope().get().isReusable())
+ // TODO(beder): Figure out what happens with production subcomponents.
+ && !binding.bindingType().equals(BindingType.PRODUCTION)) {
+ for (DependencyRequest dependency : binding.dependencies()) {
+ if (dependsOnLocalBindings(dependency.key())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns {@code true} if there is at least one multibinding contribution declared within
+ * this component's modules that matches the key.
+ */
+ private boolean hasLocalMultibindingContributions(Key requestKey) {
+ return keysMatchingRequest(requestKey)
+ .stream()
+ .anyMatch(key -> !getLocalExplicitMultibindings(key).isEmpty());
+ }
+
+ /**
+ * Returns {@code true} if there is a contribution in this component for an {@code
+ * Optional<Foo>} key that has not been contributed in a parent.
+ */
+ private boolean hasLocalOptionalBindingContribution(ResolvedBindings resolvedBindings) {
+ if (resolvedBindings
+ .contributionBindings()
+ .stream()
+ .map(ContributionBinding::kind)
+ .anyMatch(isEqual(OPTIONAL))) {
+ return !getLocalExplicitBindings(keyFactory.unwrapOptional(resolvedBindings.key()).get())
+ .isEmpty();
+ } else {
+ // If a parent contributes a @Provides Optional<Foo> binding and a child has a
+ // @BindsOptionalOf Foo method, the two should conflict, even if there is no binding for
+ // Foo on its own
+ return !getOptionalBindingDeclarations(resolvedBindings.key()).isEmpty();
+ }
+ }
+ }
+ }
+
+ /**
+ * A multimap of those {@code declarations} that are multibinding contribution declarations,
+ * indexed by the key of the set or map to which they contribute.
+ */
+ static <T extends BindingDeclaration>
+ ImmutableSetMultimap<Key, T> multibindingContributionsByMultibindingKey(
+ Iterable<T> declarations) {
+ ImmutableSetMultimap.Builder<Key, T> builder = ImmutableSetMultimap.builder();
+ for (T declaration : declarations) {
+ if (declaration.key().multibindingContributionIdentifier().isPresent()) {
+ builder.put(
+ declaration
+ .key()
+ .toBuilder()
+ .multibindingContributionIdentifier(Optional.empty())
+ .build(),
+ declaration);
+ }
+ }
+ return builder.build();
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/BindingNode.java b/java/dagger/internal/codegen/binding/BindingNode.java
new file mode 100644
index 000000000..78d440da6
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/BindingNode.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.binding.BindingType.PRODUCTION;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import dagger.BindsOptionalOf;
+import dagger.Module;
+import dagger.model.BindingKind;
+import dagger.model.ComponentPath;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.model.Scope;
+import dagger.multibindings.Multibinds;
+import java.util.Optional;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * An implementation of {@link dagger.model.Binding} that also exposes {@link BindingDeclaration}s
+ * associated with the binding.
+ */
+// TODO(dpb): Consider a supertype of dagger.model.Binding that
+// dagger.internal.codegen.binding.Binding
+// could also implement.
+@AutoValue
+public abstract class BindingNode implements dagger.model.Binding {
+ public static BindingNode create(
+ ComponentPath component,
+ Binding delegate,
+ ImmutableSet<MultibindingDeclaration> multibindingDeclarations,
+ ImmutableSet<OptionalBindingDeclaration> optionalBindingDeclarations,
+ ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations,
+ BindingDeclarationFormatter bindingDeclarationFormatter) {
+ BindingNode node =
+ new AutoValue_BindingNode(
+ component,
+ delegate,
+ multibindingDeclarations,
+ optionalBindingDeclarations,
+ subcomponentDeclarations);
+ node.bindingDeclarationFormatter = checkNotNull(bindingDeclarationFormatter);
+ return node;
+ }
+
+ private BindingDeclarationFormatter bindingDeclarationFormatter;
+
+ public abstract Binding delegate();
+
+ public abstract ImmutableSet<MultibindingDeclaration> multibindingDeclarations();
+
+ public abstract ImmutableSet<OptionalBindingDeclaration> optionalBindingDeclarations();
+
+ public abstract ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations();
+
+ /**
+ * The {@link Element}s (other than the binding's {@link #bindingElement()}) that are associated
+ * with the binding.
+ *
+ * <ul>
+ * <li>{@linkplain BindsOptionalOf optional binding} declarations
+ * <li>{@linkplain Module#subcomponents() module subcomponent} declarations
+ * <li>{@linkplain Multibinds multibinding} declarations
+ * </ul>
+ */
+ public final Iterable<BindingDeclaration> associatedDeclarations() {
+ return Iterables.concat(
+ multibindingDeclarations(), optionalBindingDeclarations(), subcomponentDeclarations());
+ }
+
+ @Override
+ public Key key() {
+ return delegate().key();
+ }
+
+ @Override
+ public ImmutableSet<DependencyRequest> dependencies() {
+ return delegate().dependencies();
+ }
+
+ @Override
+ public Optional<Element> bindingElement() {
+ return delegate().bindingElement();
+ }
+
+ @Override
+ public Optional<TypeElement> contributingModule() {
+ return delegate().contributingModule();
+ }
+
+ @Override
+ public boolean requiresModuleInstance() {
+ return delegate().requiresModuleInstance();
+ }
+
+ @Override
+ public Optional<Scope> scope() {
+ return delegate().scope();
+ }
+
+ @Override
+ public boolean isNullable() {
+ return delegate().isNullable();
+ }
+
+ @Override
+ public boolean isProduction() {
+ return delegate().bindingType().equals(PRODUCTION);
+ }
+
+ @Override
+ public BindingKind kind() {
+ return delegate().kind();
+ }
+
+ @Override
+ public final String toString() {
+ return bindingDeclarationFormatter.format(delegate());
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/BindingRequest.java b/java/dagger/internal/codegen/binding/BindingRequest.java
new file mode 100644
index 000000000..d61d9cfba
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/BindingRequest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static dagger.internal.codegen.base.RequestKinds.requestType;
+
+import com.google.auto.value.AutoValue;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import java.util.Optional;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A request for a binding, which may be in the form of a request for a dependency to pass to a
+ * constructor or module method ({@link RequestKind}) or an internal request for a framework
+ * instance ({@link FrameworkType}).
+ */
+@AutoValue
+public abstract class BindingRequest {
+ /** Creates a {@link BindingRequest} for the given {@link DependencyRequest}. */
+ public static BindingRequest bindingRequest(DependencyRequest dependencyRequest) {
+ return bindingRequest(dependencyRequest.key(), dependencyRequest.kind());
+ }
+
+ /**
+ * Creates a {@link BindingRequest} for a normal dependency request for the given {@link Key} and
+ * {@link RequestKind}.
+ */
+ public static BindingRequest bindingRequest(Key key, RequestKind requestKind) {
+ // When there's a request that has a 1:1 mapping to a FrameworkType, the request should be
+ // associated with that FrameworkType as well, because we want to ensure that if a request
+ // comes in for that as a dependency first and as a framework instance later, they resolve to
+ // the same binding expression.
+ // TODO(cgdecker): Instead of doing this, make ComponentBindingExpressions create a
+ // BindingExpression for the RequestKind that simply delegates to the BindingExpression for the
+ // FrameworkType. Then there are separate BindingExpressions, but we don't end up doing weird
+ // things like creating two fields when there should only be one.
+ return new AutoValue_BindingRequest(
+ key, Optional.of(requestKind), FrameworkType.forRequestKind(requestKind));
+ }
+
+ /**
+ * Creates a {@link BindingRequest} for a request for a framework instance for the given {@link
+ * Key} with the given {@link FrameworkType}.
+ */
+ public static BindingRequest bindingRequest(Key key, FrameworkType frameworkType) {
+ return new AutoValue_BindingRequest(
+ key, frameworkType.requestKind(), Optional.of(frameworkType));
+ }
+
+ /** Returns the {@link Key} for the requested binding. */
+ public abstract Key key();
+
+ /** Returns the request kind associated with this request, if any. */
+ public abstract Optional<RequestKind> requestKind();
+
+ /** Returns the framework type associated with this request, if any. */
+ public abstract Optional<FrameworkType> frameworkType();
+
+ /** Returns whether this request is of the given kind. */
+ public final boolean isRequestKind(RequestKind requestKind) {
+ return requestKind.equals(requestKind().orElse(null));
+ }
+
+ public final TypeMirror requestedType(TypeMirror contributedType, DaggerTypes types) {
+ if (requestKind().isPresent()) {
+ return requestType(requestKind().get(), contributedType, types);
+ }
+ return types.wrapType(contributedType, frameworkType().get().frameworkClass());
+ }
+
+ /** Returns a name that can be used for the kind of request this is. */
+ public final String kindName() {
+ Object requestKindObject =
+ requestKind().isPresent()
+ ? requestKind().get()
+ : frameworkType().get().frameworkClass().getSimpleName();
+ return requestKindObject.toString();
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/BindingType.java b/java/dagger/internal/codegen/binding/BindingType.java
new file mode 100644
index 000000000..7f5ea54df
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/BindingType.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import dagger.MembersInjector;
+
+/** Whether a binding or declaration is for provision, production, or a {@link MembersInjector}. */
+public enum BindingType {
+ /** A binding with this type is a {@link ProvisionBinding}. */
+ PROVISION,
+
+ /** A binding with this type is a {@link MembersInjectionBinding}. */
+ MEMBERS_INJECTION,
+
+ /** A binding with this type is a {@link ProductionBinding}. */
+ PRODUCTION,
+}
diff --git a/java/dagger/internal/codegen/binding/BindsTypeChecker.java b/java/dagger/internal/codegen/binding/BindsTypeChecker.java
new file mode 100644
index 000000000..f3e0a1b81
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/BindsTypeChecker.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import dagger.internal.codegen.base.ContributionType;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.util.Map;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Checks the assignability of one type to another, given a {@link ContributionType} context. This
+ * is used by {@link dagger.internal.codegen.validation.BindsMethodValidator} to validate that the
+ * right-hand- side of a {@link dagger.Binds} method is valid, as well as in {@link
+ * dagger.internal.codegen.writing.DelegateBindingExpression} when the right-hand-side in generated
+ * code might be an erased type due to accessibility.
+ */
+public final class BindsTypeChecker {
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+
+ // TODO(bcorso): Make this pkg-private. Used by DelegateBindingExpression.
+ @Inject
+ public BindsTypeChecker(DaggerTypes types, DaggerElements elements) {
+ this.types = types;
+ this.elements = elements;
+ }
+
+ /**
+ * Checks the assignability of {@code rightHandSide} to {@code leftHandSide} given a {@link
+ * ContributionType} context.
+ */
+ public boolean isAssignable(
+ TypeMirror rightHandSide, TypeMirror leftHandSide, ContributionType contributionType) {
+ return types.isAssignable(rightHandSide, desiredAssignableType(leftHandSide, contributionType));
+ }
+
+ private TypeMirror desiredAssignableType(
+ TypeMirror leftHandSide, ContributionType contributionType) {
+ switch (contributionType) {
+ case UNIQUE:
+ return leftHandSide;
+ case SET:
+ DeclaredType parameterizedSetType = types.getDeclaredType(setElement(), leftHandSide);
+ return methodParameterType(parameterizedSetType, "add");
+ case SET_VALUES:
+ return methodParameterType(MoreTypes.asDeclared(leftHandSide), "addAll");
+ case MAP:
+ DeclaredType parameterizedMapType =
+ types.getDeclaredType(mapElement(), unboundedWildcard(), leftHandSide);
+ return methodParameterTypes(parameterizedMapType, "put").get(1);
+ }
+ throw new AssertionError("Unknown contribution type: " + contributionType);
+ }
+
+ private ImmutableList<TypeMirror> methodParameterTypes(DeclaredType type, String methodName) {
+ ImmutableList.Builder<ExecutableElement> methodsForName = ImmutableList.builder();
+ for (ExecutableElement method :
+ // type.asElement().getEnclosedElements() is not used because some non-standard JDKs (e.g.
+ // J2CL) don't redefine Set.add() (whose only purpose of being redefined in the standard JDK
+ // is documentation, and J2CL's implementation doesn't declare docs for JDK types).
+ // MoreElements.getLocalAndInheritedMethods ensures that the method will always be present.
+ MoreElements.getLocalAndInheritedMethods(MoreTypes.asTypeElement(type), types, elements)) {
+ if (method.getSimpleName().contentEquals(methodName)) {
+ methodsForName.add(method);
+ }
+ }
+ ExecutableElement method = getOnlyElement(methodsForName.build());
+ return ImmutableList.copyOf(
+ MoreTypes.asExecutable(types.asMemberOf(type, method)).getParameterTypes());
+ }
+
+ private TypeMirror methodParameterType(DeclaredType type, String methodName) {
+ return getOnlyElement(methodParameterTypes(type, methodName));
+ }
+
+ private TypeElement setElement() {
+ return elements.getTypeElement(Set.class);
+ }
+
+ private TypeElement mapElement() {
+ return elements.getTypeElement(Map.class);
+ }
+
+ private TypeMirror unboundedWildcard() {
+ return types.getWildcardType(null, null);
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/ChildFactoryMethodEdgeImpl.java b/java/dagger/internal/codegen/binding/ChildFactoryMethodEdgeImpl.java
new file mode 100644
index 000000000..c0565881a
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/ChildFactoryMethodEdgeImpl.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static dagger.internal.codegen.base.ElementFormatter.elementToString;
+
+import dagger.model.BindingGraph.ChildFactoryMethodEdge;
+import javax.lang.model.element.ExecutableElement;
+
+/** An implementation of {@link ChildFactoryMethodEdge}. */
+public final class ChildFactoryMethodEdgeImpl implements ChildFactoryMethodEdge {
+
+ private final ExecutableElement factoryMethod;
+
+ ChildFactoryMethodEdgeImpl(ExecutableElement factoryMethod) {
+ this.factoryMethod = factoryMethod;
+ }
+
+ @Override
+ public ExecutableElement factoryMethod() {
+ return factoryMethod;
+ }
+
+ @Override
+ public String toString() {
+ return elementToString(factoryMethod);
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/ComponentCreatorAnnotation.java b/java/dagger/internal/codegen/binding/ComponentCreatorAnnotation.java
new file mode 100644
index 000000000..6297188d0
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/ComponentCreatorAnnotation.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.common.base.Ascii.toUpperCase;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.extension.DaggerStreams.valuesOf;
+import static java.util.stream.Collectors.mapping;
+
+import com.google.common.collect.ImmutableSet;
+import dagger.Component;
+import dagger.Subcomponent;
+import dagger.internal.codegen.base.ComponentAnnotation;
+import dagger.producers.ProductionComponent;
+import dagger.producers.ProductionSubcomponent;
+import java.lang.annotation.Annotation;
+import java.util.stream.Collector;
+import java.util.stream.Stream;
+import javax.lang.model.element.TypeElement;
+
+/** Simple representation of a component creator annotation type. */
+public enum ComponentCreatorAnnotation {
+ COMPONENT_BUILDER(Component.Builder.class),
+ COMPONENT_FACTORY(Component.Factory.class),
+ SUBCOMPONENT_BUILDER(Subcomponent.Builder.class),
+ SUBCOMPONENT_FACTORY(Subcomponent.Factory.class),
+ PRODUCTION_COMPONENT_BUILDER(ProductionComponent.Builder.class),
+ PRODUCTION_COMPONENT_FACTORY(ProductionComponent.Factory.class),
+ PRODUCTION_SUBCOMPONENT_BUILDER(ProductionSubcomponent.Builder.class),
+ PRODUCTION_SUBCOMPONENT_FACTORY(ProductionSubcomponent.Factory.class),
+ ;
+
+ private final Class<? extends Annotation> annotation;
+ private final ComponentCreatorKind creatorKind;
+ private final Class<? extends Annotation> componentAnnotation;
+
+ @SuppressWarnings("unchecked") // Builder/factory annotations live within their parent annotation.
+ ComponentCreatorAnnotation(Class<? extends Annotation> annotation) {
+ this.annotation = annotation;
+ this.creatorKind = ComponentCreatorKind.valueOf(toUpperCase(annotation.getSimpleName()));
+ this.componentAnnotation = (Class<? extends Annotation>) annotation.getEnclosingClass();
+ }
+
+ /** The actual annotation type. */
+ public Class<? extends Annotation> annotation() {
+ return annotation;
+ }
+
+ /** The component annotation type that encloses this creator annotation type. */
+ public final Class<? extends Annotation> componentAnnotation() {
+ return componentAnnotation;
+ }
+
+ /** Returns {@code true} if the creator annotation is for a subcomponent. */
+ public final boolean isSubcomponentCreatorAnnotation() {
+ return componentAnnotation().getSimpleName().endsWith("Subcomponent");
+ }
+
+ /**
+ * Returns {@code true} if the creator annotation is for a production component or subcomponent.
+ */
+ public final boolean isProductionCreatorAnnotation() {
+ return componentAnnotation().getSimpleName().startsWith("Production");
+ }
+
+ /** The creator kind the annotation is associated with. */
+ // TODO(dpb): Remove ComponentCreatorKind.
+ public ComponentCreatorKind creatorKind() {
+ return creatorKind;
+ }
+
+ @Override
+ public final String toString() {
+ return annotation().getName();
+ }
+
+ /** Returns all component creator annotations. */
+ public static ImmutableSet<Class<? extends Annotation>> allCreatorAnnotations() {
+ return stream().collect(toAnnotationClasses());
+ }
+
+ /** Returns all root component creator annotations. */
+ public static ImmutableSet<Class<? extends Annotation>> rootComponentCreatorAnnotations() {
+ return stream()
+ .filter(
+ componentCreatorAnnotation ->
+ !componentCreatorAnnotation.isSubcomponentCreatorAnnotation())
+ .collect(toAnnotationClasses());
+ }
+
+ /** Returns all subcomponent creator annotations. */
+ public static ImmutableSet<Class<? extends Annotation>> subcomponentCreatorAnnotations() {
+ return stream()
+ .filter(
+ componentCreatorAnnotation ->
+ componentCreatorAnnotation.isSubcomponentCreatorAnnotation())
+ .collect(toAnnotationClasses());
+ }
+
+ /** Returns all production component creator annotations. */
+ public static ImmutableSet<Class<? extends Annotation>> productionCreatorAnnotations() {
+ return stream()
+ .filter(
+ componentCreatorAnnotation ->
+ componentCreatorAnnotation.isProductionCreatorAnnotation())
+ .collect(toAnnotationClasses());
+ }
+
+ /** Returns the legal creator annotations for the given {@code componentAnnotation}. */
+ public static ImmutableSet<Class<? extends Annotation>> creatorAnnotationsFor(
+ ComponentAnnotation componentAnnotation) {
+ return stream()
+ .filter(
+ creatorAnnotation ->
+ creatorAnnotation
+ .componentAnnotation()
+ .getSimpleName()
+ .equals(componentAnnotation.simpleName()))
+ .collect(toAnnotationClasses());
+ }
+
+ /** Returns all creator annotations present on the given {@code type}. */
+ public static ImmutableSet<ComponentCreatorAnnotation> getCreatorAnnotations(TypeElement type) {
+ return stream()
+ .filter(cca -> isAnnotationPresent(type, cca.annotation()))
+ .collect(toImmutableSet());
+ }
+
+ private static Stream<ComponentCreatorAnnotation> stream() {
+ return valuesOf(ComponentCreatorAnnotation.class);
+ }
+
+ private static Collector<ComponentCreatorAnnotation, ?, ImmutableSet<Class<? extends Annotation>>>
+ toAnnotationClasses() {
+ return mapping(ComponentCreatorAnnotation::annotation, toImmutableSet());
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/ComponentCreatorDescriptor.java b/java/dagger/internal/codegen/binding/ComponentCreatorDescriptor.java
new file mode 100644
index 000000000..5ea30ed42
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/ComponentCreatorDescriptor.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.auto.common.MoreTypes.asTypeElement;
+import static com.google.common.base.Verify.verify;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.base.ModuleAnnotation.moduleAnnotation;
+import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.getCreatorAnnotations;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import dagger.BindsInstance;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import java.util.List;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A descriptor for a component <i>creator</i> type: that is, a type annotated with
+ * {@code @Component.Builder} (or one of the corresponding production or subcomponent versions).
+ */
+@AutoValue
+public abstract class ComponentCreatorDescriptor {
+
+ /** Returns the annotation marking this creator. */
+ public abstract ComponentCreatorAnnotation annotation();
+
+ /** The kind of this creator. */
+ public final ComponentCreatorKind kind() {
+ return annotation().creatorKind();
+ }
+
+ /** The annotated creator type. */
+ public abstract TypeElement typeElement();
+
+ /** The method that creates and returns a component instance. */
+ public abstract ExecutableElement factoryMethod();
+
+ /**
+ * Multimap of component requirements to setter methods that set that requirement.
+ *
+ * <p>In a valid creator, there will be exactly one element per component requirement, so this
+ * method should only be called when validating the descriptor.
+ */
+ abstract ImmutableSetMultimap<ComponentRequirement, ExecutableElement> unvalidatedSetterMethods();
+
+ /**
+ * Multimap of component requirements to factory method parameters that set that requirement.
+ *
+ * <p>In a valid creator, there will be exactly one element per component requirement, so this
+ * method should only be called when validating the descriptor.
+ */
+ abstract ImmutableSetMultimap<ComponentRequirement, VariableElement>
+ unvalidatedFactoryParameters();
+
+ /**
+ * Multimap of component requirements to elements (methods or parameters) that set that
+ * requirement.
+ *
+ * <p>In a valid creator, there will be exactly one element per component requirement, so this
+ * method should only be called when validating the descriptor.
+ */
+ public final ImmutableSetMultimap<ComponentRequirement, Element>
+ unvalidatedRequirementElements() {
+ // ComponentCreatorValidator ensures that there are either setter methods or factory method
+ // parameters, but not both, so we can cheat a little here since we know that only one of
+ // the two multimaps will be non-empty.
+ return ImmutableSetMultimap.copyOf( // no actual copy
+ unvalidatedSetterMethods().isEmpty()
+ ? unvalidatedFactoryParameters()
+ : unvalidatedSetterMethods());
+ }
+
+ /**
+ * Map of component requirements to elements (setter methods or factory method parameters) that
+ * set them.
+ */
+ @Memoized
+ ImmutableMap<ComponentRequirement, Element> requirementElements() {
+ return flatten(unvalidatedRequirementElements());
+ }
+
+ /** Map of component requirements to setter methods for those requirements. */
+ @Memoized
+ public ImmutableMap<ComponentRequirement, ExecutableElement> setterMethods() {
+ return flatten(unvalidatedSetterMethods());
+ }
+
+ /** Map of component requirements to factory method parameters for those requirements. */
+ @Memoized
+ public ImmutableMap<ComponentRequirement, VariableElement> factoryParameters() {
+ return flatten(unvalidatedFactoryParameters());
+ }
+
+ private static <K, V> ImmutableMap<K, V> flatten(Multimap<K, V> multimap) {
+ return ImmutableMap.copyOf(
+ Maps.transformValues(multimap.asMap(), values -> getOnlyElement(values)));
+ }
+
+ /** Returns the set of component requirements this creator allows the user to set. */
+ public final ImmutableSet<ComponentRequirement> userSettableRequirements() {
+ // Note: they should have been validated at the point this is used, so this set is valid.
+ return unvalidatedRequirementElements().keySet();
+ }
+
+ /** Returns the set of requirements for modules and component dependencies for this creator. */
+ public final ImmutableSet<ComponentRequirement> moduleAndDependencyRequirements() {
+ return userSettableRequirements().stream()
+ .filter(requirement -> !requirement.isBoundInstance())
+ .collect(toImmutableSet());
+ }
+
+ /** Returns the set of bound instance requirements for this creator. */
+ final ImmutableSet<ComponentRequirement> boundInstanceRequirements() {
+ return userSettableRequirements().stream()
+ .filter(ComponentRequirement::isBoundInstance)
+ .collect(toImmutableSet());
+ }
+
+ /** Returns the element in this creator that sets the given {@code requirement}. */
+ final Element elementForRequirement(ComponentRequirement requirement) {
+ return requirementElements().get(requirement);
+ }
+
+ /** Creates a new {@link ComponentCreatorDescriptor} for the given creator {@code type}. */
+ public static ComponentCreatorDescriptor create(
+ DeclaredType type,
+ DaggerElements elements,
+ DaggerTypes types,
+ DependencyRequestFactory dependencyRequestFactory) {
+ TypeElement typeElement = asTypeElement(type);
+ TypeMirror componentType = typeElement.getEnclosingElement().asType();
+
+ ImmutableSetMultimap.Builder<ComponentRequirement, ExecutableElement> setterMethods =
+ ImmutableSetMultimap.builder();
+
+ ExecutableElement factoryMethod = null;
+ for (ExecutableElement method : elements.getUnimplementedMethods(typeElement)) {
+ ExecutableType resolvedMethodType = MoreTypes.asExecutable(types.asMemberOf(type, method));
+
+ if (types.isSubtype(componentType, resolvedMethodType.getReturnType())) {
+ factoryMethod = method;
+ } else {
+ VariableElement parameter = getOnlyElement(method.getParameters());
+ TypeMirror parameterType = getOnlyElement(resolvedMethodType.getParameterTypes());
+ setterMethods.put(
+ requirement(method, parameter, parameterType, dependencyRequestFactory, method),
+ method);
+ }
+ }
+ verify(factoryMethod != null); // validation should have ensured this.
+
+ ImmutableSetMultimap.Builder<ComponentRequirement, VariableElement> factoryParameters =
+ ImmutableSetMultimap.builder();
+
+ ExecutableType resolvedFactoryMethodType =
+ MoreTypes.asExecutable(types.asMemberOf(type, factoryMethod));
+ List<? extends VariableElement> parameters = factoryMethod.getParameters();
+ List<? extends TypeMirror> parameterTypes = resolvedFactoryMethodType.getParameterTypes();
+ for (int i = 0; i < parameters.size(); i++) {
+ VariableElement parameter = parameters.get(i);
+ TypeMirror parameterType = parameterTypes.get(i);
+ factoryParameters.put(
+ requirement(factoryMethod, parameter, parameterType, dependencyRequestFactory, parameter),
+ parameter);
+ }
+
+ // Validation should have ensured exactly one creator annotation is present on the type.
+ ComponentCreatorAnnotation annotation = getOnlyElement(getCreatorAnnotations(typeElement));
+ return new AutoValue_ComponentCreatorDescriptor(
+ annotation, typeElement, factoryMethod, setterMethods.build(), factoryParameters.build());
+ }
+
+ private static ComponentRequirement requirement(
+ ExecutableElement method,
+ VariableElement parameter,
+ TypeMirror type,
+ DependencyRequestFactory dependencyRequestFactory,
+ Element elementForVariableName) {
+ if (isAnnotationPresent(method, BindsInstance.class)
+ || isAnnotationPresent(parameter, BindsInstance.class)) {
+ DependencyRequest request =
+ dependencyRequestFactory.forRequiredResolvedVariable(parameter, type);
+ String variableName = elementForVariableName.getSimpleName().toString();
+ return ComponentRequirement.forBoundInstance(
+ request.key(), request.isNullable(), variableName);
+ }
+
+ return moduleAnnotation(asTypeElement(type)).isPresent()
+ ? ComponentRequirement.forModule(type)
+ : ComponentRequirement.forDependency(type);
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/ComponentCreatorKind.java b/java/dagger/internal/codegen/binding/ComponentCreatorKind.java
new file mode 100644
index 000000000..b2581d685
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/ComponentCreatorKind.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
+
+import com.google.common.base.Ascii;
+
+/** Enumeration of the different kinds of component creators. */
+public enum ComponentCreatorKind {
+ /** {@code @Component.Builder} or one of its subcomponent/production variants. */
+ BUILDER,
+
+ /** {@code @Component.Factory} or one of its subcomponent/production variants. */
+ FACTORY,
+ ;
+
+ /** Name to use as (or as part of) a type name for a creator of this kind. */
+ public String typeName() {
+ return UPPER_UNDERSCORE.to(UPPER_CAMEL, name());
+ }
+
+ /** Name to use for a component's static method returning a creator of this kind. */
+ public String methodName() {
+ return Ascii.toLowerCase(name());
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/ComponentDescriptor.java b/java/dagger/internal/codegen/binding/ComponentDescriptor.java
new file mode 100644
index 000000000..a7e4cc4f3
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/ComponentDescriptor.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.langmodel.DaggerTypes.isFutureType;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.type.TypeKind.VOID;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.google.errorprone.annotations.CheckReturnValue;
+import dagger.Component;
+import dagger.Module;
+import dagger.Subcomponent;
+import dagger.internal.codegen.base.ComponentAnnotation;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import dagger.model.Scope;
+import dagger.producers.CancellationPolicy;
+import dagger.producers.ProductionComponent;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Stream;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A component declaration.
+ *
+ * <p>Represents one type annotated with {@code @Component}, {@code Subcomponent},
+ * {@code @ProductionComponent}, or {@code @ProductionSubcomponent}.
+ *
+ * <p>When validating bindings installed in modules, a {@link ComponentDescriptor} can also
+ * represent a synthetic component for the module, where there is an entry point for each binding in
+ * the module.
+ */
+@AutoValue
+public abstract class ComponentDescriptor {
+ /** The annotation that specifies that {@link #typeElement()} is a component. */
+ public abstract ComponentAnnotation annotation();
+
+ /** Returns {@code true} if this is a subcomponent. */
+ public final boolean isSubcomponent() {
+ return annotation().isSubcomponent();
+ }
+
+ /**
+ * Returns {@code true} if this is a production component or subcomponent, or a
+ * {@code @ProducerModule} when doing module binding validation.
+ */
+ public final boolean isProduction() {
+ return annotation().isProduction();
+ }
+
+ /**
+ * Returns {@code true} if this is a real component, and not a fictional one used to validate
+ * module bindings.
+ */
+ public final boolean isRealComponent() {
+ return annotation().isRealComponent();
+ }
+
+ /**
+ * The element that defines the component. This is the element to which the {@link #annotation()}
+ * was applied.
+ */
+ public abstract TypeElement typeElement();
+
+ /**
+ * The set of component dependencies listed in {@link Component#dependencies} or {@link
+ * ProductionComponent#dependencies()}.
+ */
+ public abstract ImmutableSet<ComponentRequirement> dependencies();
+
+ /** The non-abstract {@link #modules()} and the {@link #dependencies()}. */
+ public final ImmutableSet<ComponentRequirement> dependenciesAndConcreteModules() {
+ return Stream.concat(
+ moduleTypes().stream()
+ .filter(dep -> !dep.getModifiers().contains(ABSTRACT))
+ .map(module -> ComponentRequirement.forModule(module.asType())),
+ dependencies().stream())
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * The {@link ModuleDescriptor modules} declared in {@link Component#modules()} and reachable by
+ * traversing {@link Module#includes()}.
+ */
+ public abstract ImmutableSet<ModuleDescriptor> modules();
+
+ /** The types of the {@link #modules()}. */
+ public final ImmutableSet<TypeElement> moduleTypes() {
+ return modules().stream().map(ModuleDescriptor::moduleElement).collect(toImmutableSet());
+ }
+
+ /**
+ * The types for which the component will need instances if all of its bindings are used. For the
+ * types the component will need in a given binding graph, use {@link
+ * BindingGraph#componentRequirements()}.
+ *
+ * <ul>
+ * <li>{@linkplain #modules()} modules} with concrete instance bindings
+ * <li>Bound instances
+ * <li>{@linkplain #dependencies() dependencies}
+ * </ul>
+ */
+ @Memoized
+ ImmutableSet<ComponentRequirement> requirements() {
+ ImmutableSet.Builder<ComponentRequirement> requirements = ImmutableSet.builder();
+ modules().stream()
+ .filter(
+ module ->
+ module.bindings().stream().anyMatch(ContributionBinding::requiresModuleInstance))
+ .map(module -> ComponentRequirement.forModule(module.moduleElement().asType()))
+ .forEach(requirements::add);
+ requirements.addAll(dependencies());
+ requirements.addAll(
+ creatorDescriptor()
+ .map(ComponentCreatorDescriptor::boundInstanceRequirements)
+ .orElse(ImmutableSet.of()));
+ return requirements.build();
+ }
+
+ /**
+ * This component's {@linkplain #dependencies() dependencies} keyed by each provision or
+ * production method defined by that dependency. Note that the dependencies' types are not simply
+ * the enclosing type of the method; a method may be declared by a supertype of the actual
+ * dependency.
+ */
+ public abstract ImmutableMap<ExecutableElement, ComponentRequirement>
+ dependenciesByDependencyMethod();
+
+ /** The {@linkplain #dependencies() component dependency} that defines a method. */
+ public final ComponentRequirement getDependencyThatDefinesMethod(Element method) {
+ checkArgument(
+ method instanceof ExecutableElement, "method must be an executable element: %s", method);
+ return checkNotNull(
+ dependenciesByDependencyMethod().get(method), "no dependency implements %s", method);
+ }
+
+ /** The scopes of the component. */
+ public abstract ImmutableSet<Scope> scopes();
+
+ /**
+ * All {@link Subcomponent}s which are direct children of this component. This includes
+ * subcomponents installed from {@link Module#subcomponents()} as well as subcomponent {@linkplain
+ * #childComponentsDeclaredByFactoryMethods() factory methods} and {@linkplain
+ * #childComponentsDeclaredByBuilderEntryPoints() builder methods}.
+ */
+ public final ImmutableSet<ComponentDescriptor> childComponents() {
+ return ImmutableSet.<ComponentDescriptor>builder()
+ .addAll(childComponentsDeclaredByFactoryMethods().values())
+ .addAll(childComponentsDeclaredByBuilderEntryPoints().values())
+ .addAll(childComponentsDeclaredByModules())
+ .build();
+ }
+
+ /**
+ * All {@linkplain Subcomponent direct child} components that are declared by a {@linkplain
+ * Module#subcomponents() module's subcomponents}.
+ */
+ abstract ImmutableSet<ComponentDescriptor> childComponentsDeclaredByModules();
+
+ /**
+ * All {@linkplain Subcomponent direct child} components that are declared by a subcomponent
+ * factory method.
+ */
+ public abstract ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor>
+ childComponentsDeclaredByFactoryMethods();
+
+ /** Returns a map of {@link #childComponents()} indexed by {@link #typeElement()}. */
+ @Memoized
+ public ImmutableMap<TypeElement, ComponentDescriptor> childComponentsByElement() {
+ return Maps.uniqueIndex(childComponents(), ComponentDescriptor::typeElement);
+ }
+
+ /** Returns the factory method that declares a child component. */
+ final Optional<ComponentMethodDescriptor> getFactoryMethodForChildComponent(
+ ComponentDescriptor childComponent) {
+ return Optional.ofNullable(
+ childComponentsDeclaredByFactoryMethods().inverse().get(childComponent));
+ }
+
+ /**
+ * All {@linkplain Subcomponent direct child} components that are declared by a subcomponent
+ * builder method.
+ */
+ abstract ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor>
+ childComponentsDeclaredByBuilderEntryPoints();
+
+ private final Supplier<ImmutableMap<TypeElement, ComponentDescriptor>>
+ childComponentsByBuilderType =
+ Suppliers.memoize(
+ () ->
+ childComponents().stream()
+ .filter(child -> child.creatorDescriptor().isPresent())
+ .collect(
+ toImmutableMap(
+ child -> child.creatorDescriptor().get().typeElement(),
+ child -> child)));
+
+ /** Returns the child component with the given builder type. */
+ final ComponentDescriptor getChildComponentWithBuilderType(TypeElement builderType) {
+ return checkNotNull(
+ childComponentsByBuilderType.get().get(builderType),
+ "no child component found for builder type %s",
+ builderType.getQualifiedName());
+ }
+
+ public abstract ImmutableSet<ComponentMethodDescriptor> componentMethods();
+
+ /** Returns the first component method associated with this binding request, if one exists. */
+ public Optional<ComponentMethodDescriptor> firstMatchingComponentMethod(BindingRequest request) {
+ return componentMethods().stream()
+ .filter(method -> doesComponentMethodMatch(method, request))
+ .findFirst();
+ }
+
+ /** Returns true if the component method matches the binding request. */
+ private static boolean doesComponentMethodMatch(
+ ComponentMethodDescriptor componentMethod, BindingRequest request) {
+ return componentMethod
+ .dependencyRequest()
+ .map(BindingRequest::bindingRequest)
+ .filter(request::equals)
+ .isPresent();
+ }
+
+ /** The entry point methods on the component type. Each has a {@link DependencyRequest}. */
+ public final ImmutableSet<ComponentMethodDescriptor> entryPointMethods() {
+ return componentMethods()
+ .stream()
+ .filter(method -> method.dependencyRequest().isPresent())
+ .collect(toImmutableSet());
+ }
+
+ // TODO(gak): Consider making this non-optional and revising the
+ // interaction between the spec & generation
+ /** Returns a descriptor for the creator type for this component type, if the user defined one. */
+ public abstract Optional<ComponentCreatorDescriptor> creatorDescriptor();
+
+ /**
+ * Returns {@code true} for components that have a creator, either because the user {@linkplain
+ * #creatorDescriptor() specified one} or because it's a top-level component with an implicit
+ * builder.
+ */
+ public final boolean hasCreator() {
+ return !isSubcomponent() || creatorDescriptor().isPresent();
+ }
+
+ /**
+ * Returns the {@link CancellationPolicy} for this component, or an empty optional if either the
+ * component is not a production component or no {@code CancellationPolicy} annotation is present.
+ */
+ public final Optional<CancellationPolicy> cancellationPolicy() {
+ return isProduction()
+ ? Optional.ofNullable(typeElement().getAnnotation(CancellationPolicy.class))
+ : Optional.empty();
+ }
+
+ @Memoized
+ @Override
+ public int hashCode() {
+ // TODO(b/122962745): Only use typeElement().hashCode()
+ return Objects.hash(typeElement(), annotation());
+ }
+
+ // TODO(ronshapiro): simplify the equality semantics
+ @Override
+ public abstract boolean equals(Object obj);
+
+ /** A component method. */
+ @AutoValue
+ public abstract static class ComponentMethodDescriptor {
+ /** The method itself. Note that this may be declared on a supertype of the component. */
+ public abstract ExecutableElement methodElement();
+
+ /**
+ * The dependency request for production, provision, and subcomponent creator methods. Absent
+ * for subcomponent factory methods.
+ */
+ public abstract Optional<DependencyRequest> dependencyRequest();
+
+ /** The subcomponent for subcomponent factory methods and subcomponent creator methods. */
+ public abstract Optional<ComponentDescriptor> subcomponent();
+
+ /**
+ * Returns the return type of {@link #methodElement()} as resolved in the {@link
+ * ComponentDescriptor#typeElement() component type}. If there are no type variables in the
+ * return type, this is the equivalent of {@code methodElement().getReturnType()}.
+ */
+ public TypeMirror resolvedReturnType(DaggerTypes types) {
+ checkState(dependencyRequest().isPresent());
+
+ TypeMirror returnType = methodElement().getReturnType();
+ if (returnType.getKind().isPrimitive() || returnType.getKind().equals(VOID)) {
+ return returnType;
+ }
+ return BindingRequest.bindingRequest(dependencyRequest().get())
+ .requestedType(dependencyRequest().get().key().type(), types);
+ }
+
+ /** A {@link ComponentMethodDescriptor}builder for a method. */
+ public static Builder builder(ExecutableElement method) {
+ return new AutoValue_ComponentDescriptor_ComponentMethodDescriptor.Builder()
+ .methodElement(method);
+ }
+
+ /** A builder of {@link ComponentMethodDescriptor}s. */
+ @AutoValue.Builder
+ @CanIgnoreReturnValue
+ public interface Builder {
+ /** @see ComponentMethodDescriptor#methodElement() */
+ Builder methodElement(ExecutableElement methodElement);
+
+ /** @see ComponentMethodDescriptor#dependencyRequest() */
+ Builder dependencyRequest(DependencyRequest dependencyRequest);
+
+ /** @see ComponentMethodDescriptor#subcomponent() */
+ Builder subcomponent(ComponentDescriptor subcomponent);
+
+ /** Builds the descriptor. */
+ @CheckReturnValue
+ ComponentMethodDescriptor build();
+ }
+ }
+
+ /** No-argument methods defined on {@link Object} that are ignored for contribution. */
+ private static final ImmutableSet<String> NON_CONTRIBUTING_OBJECT_METHOD_NAMES =
+ ImmutableSet.of("toString", "hashCode", "clone", "getClass");
+
+ /**
+ * Returns {@code true} if a method could be a component entry point but not a members-injection
+ * method.
+ */
+ static boolean isComponentContributionMethod(DaggerElements elements, ExecutableElement method) {
+ return method.getParameters().isEmpty()
+ && !method.getReturnType().getKind().equals(VOID)
+ && !elements.getTypeElement(Object.class).equals(method.getEnclosingElement())
+ && !NON_CONTRIBUTING_OBJECT_METHOD_NAMES.contains(method.getSimpleName().toString());
+ }
+
+ /** Returns {@code true} if a method could be a component production entry point. */
+ static boolean isComponentProductionMethod(DaggerElements elements, ExecutableElement method) {
+ return isComponentContributionMethod(elements, method) && isFutureType(method.getReturnType());
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/ComponentDescriptorFactory.java b/java/dagger/internal/codegen/binding/ComponentDescriptorFactory.java
new file mode 100644
index 000000000..f13aa50c0
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/ComponentDescriptorFactory.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.auto.common.MoreElements.asType;
+import static com.google.auto.common.MoreTypes.asTypeElement;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.base.ComponentAnnotation.subcomponentAnnotation;
+import static dagger.internal.codegen.base.Scopes.productionScope;
+import static dagger.internal.codegen.base.Scopes.scopesOf;
+import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.creatorAnnotationsFor;
+import static dagger.internal.codegen.binding.ComponentDescriptor.isComponentContributionMethod;
+import static dagger.internal.codegen.binding.ConfigurationAnnotations.enclosedAnnotatedTypes;
+import static dagger.internal.codegen.binding.ConfigurationAnnotations.isSubcomponentCreator;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static javax.lang.model.type.TypeKind.DECLARED;
+import static javax.lang.model.type.TypeKind.VOID;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import dagger.internal.codegen.base.ComponentAnnotation;
+import dagger.internal.codegen.base.ModuleAnnotation;
+import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Scope;
+import java.util.Optional;
+import java.util.function.Function;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+
+/** A factory for {@link ComponentDescriptor}s. */
+public final class ComponentDescriptorFactory {
+ private final DaggerElements elements;
+ private final DaggerTypes types;
+ private final DependencyRequestFactory dependencyRequestFactory;
+ private final ModuleDescriptor.Factory moduleDescriptorFactory;
+ private final InjectionAnnotations injectionAnnotations;
+
+ @Inject
+ ComponentDescriptorFactory(
+ DaggerElements elements,
+ DaggerTypes types,
+ DependencyRequestFactory dependencyRequestFactory,
+ ModuleDescriptor.Factory moduleDescriptorFactory,
+ InjectionAnnotations injectionAnnotations) {
+ this.elements = elements;
+ this.types = types;
+ this.dependencyRequestFactory = dependencyRequestFactory;
+ this.moduleDescriptorFactory = moduleDescriptorFactory;
+ this.injectionAnnotations = injectionAnnotations;
+ }
+
+ /** Returns a descriptor for a root component type. */
+ public ComponentDescriptor rootComponentDescriptor(TypeElement typeElement) {
+ return create(
+ typeElement,
+ checkAnnotation(
+ typeElement,
+ ComponentAnnotation::rootComponentAnnotation,
+ "must have a component annotation"));
+ }
+
+ /** Returns a descriptor for a subcomponent type. */
+ public ComponentDescriptor subcomponentDescriptor(TypeElement typeElement) {
+ return create(
+ typeElement,
+ checkAnnotation(
+ typeElement,
+ ComponentAnnotation::subcomponentAnnotation,
+ "must have a subcomponent annotation"));
+ }
+
+ /**
+ * Returns a descriptor for a fictional component based on a module type in order to validate its
+ * bindings.
+ */
+ public ComponentDescriptor moduleComponentDescriptor(TypeElement typeElement) {
+ return create(
+ typeElement,
+ ComponentAnnotation.fromModuleAnnotation(
+ checkAnnotation(
+ typeElement, ModuleAnnotation::moduleAnnotation, "must have a module annotation")));
+ }
+
+ private static <A> A checkAnnotation(
+ TypeElement typeElement,
+ Function<TypeElement, Optional<A>> annotationFunction,
+ String message) {
+ return annotationFunction
+ .apply(typeElement)
+ .orElseThrow(() -> new IllegalArgumentException(typeElement + " " + message));
+ }
+
+ private ComponentDescriptor create(
+ TypeElement typeElement, ComponentAnnotation componentAnnotation) {
+ ImmutableSet<ComponentRequirement> componentDependencies =
+ componentAnnotation.dependencyTypes().stream()
+ .map(ComponentRequirement::forDependency)
+ .collect(toImmutableSet());
+
+ ImmutableMap.Builder<ExecutableElement, ComponentRequirement> dependenciesByDependencyMethod =
+ ImmutableMap.builder();
+
+ for (ComponentRequirement componentDependency : componentDependencies) {
+ for (ExecutableElement dependencyMethod :
+ methodsIn(elements.getAllMembers(componentDependency.typeElement()))) {
+ if (isComponentContributionMethod(elements, dependencyMethod)) {
+ dependenciesByDependencyMethod.put(dependencyMethod, componentDependency);
+ }
+ }
+ }
+
+ // Start with the component's modules. For fictional components built from a module, start with
+ // that module.
+ ImmutableSet<TypeElement> modules =
+ componentAnnotation.isRealComponent()
+ ? componentAnnotation.modules()
+ : ImmutableSet.of(typeElement);
+
+ ImmutableSet<ModuleDescriptor> transitiveModules =
+ moduleDescriptorFactory.transitiveModules(modules);
+
+ ImmutableSet.Builder<ComponentDescriptor> subcomponentsFromModules = ImmutableSet.builder();
+ for (ModuleDescriptor module : transitiveModules) {
+ for (SubcomponentDeclaration subcomponentDeclaration : module.subcomponentDeclarations()) {
+ TypeElement subcomponent = subcomponentDeclaration.subcomponentType();
+ subcomponentsFromModules.add(subcomponentDescriptor(subcomponent));
+ }
+ }
+
+ ImmutableSet.Builder<ComponentMethodDescriptor> componentMethodsBuilder =
+ ImmutableSet.builder();
+ ImmutableBiMap.Builder<ComponentMethodDescriptor, ComponentDescriptor>
+ subcomponentsByFactoryMethod = ImmutableBiMap.builder();
+ ImmutableBiMap.Builder<ComponentMethodDescriptor, ComponentDescriptor>
+ subcomponentsByBuilderMethod = ImmutableBiMap.builder();
+ if (componentAnnotation.isRealComponent()) {
+ ImmutableSet<ExecutableElement> unimplementedMethods =
+ elements.getUnimplementedMethods(typeElement);
+ for (ExecutableElement componentMethod : unimplementedMethods) {
+ ComponentMethodDescriptor componentMethodDescriptor =
+ getDescriptorForComponentMethod(typeElement, componentAnnotation, componentMethod);
+ componentMethodsBuilder.add(componentMethodDescriptor);
+ componentMethodDescriptor
+ .subcomponent()
+ .ifPresent(
+ subcomponent -> {
+ // If the dependency request is present, that means the method returns the
+ // subcomponent factory.
+ if (componentMethodDescriptor.dependencyRequest().isPresent()) {
+ subcomponentsByBuilderMethod.put(componentMethodDescriptor, subcomponent);
+ } else {
+ subcomponentsByFactoryMethod.put(componentMethodDescriptor, subcomponent);
+ }
+ });
+ }
+ }
+
+ // Validation should have ensured that this set will have at most one element.
+ ImmutableSet<DeclaredType> enclosedCreators =
+ creatorAnnotationsFor(componentAnnotation).stream()
+ .flatMap(
+ creatorAnnotation ->
+ enclosedAnnotatedTypes(typeElement, creatorAnnotation).stream())
+ .collect(toImmutableSet());
+ Optional<ComponentCreatorDescriptor> creatorDescriptor =
+ enclosedCreators.isEmpty()
+ ? Optional.empty()
+ : Optional.of(
+ ComponentCreatorDescriptor.create(
+ getOnlyElement(enclosedCreators), elements, types, dependencyRequestFactory));
+
+ ImmutableSet<Scope> scopes = scopesOf(typeElement);
+ if (componentAnnotation.isProduction()) {
+ scopes = ImmutableSet.<Scope>builder().addAll(scopes).add(productionScope(elements)).build();
+ }
+
+ return new AutoValue_ComponentDescriptor(
+ componentAnnotation,
+ typeElement,
+ componentDependencies,
+ transitiveModules,
+ dependenciesByDependencyMethod.build(),
+ scopes,
+ subcomponentsFromModules.build(),
+ subcomponentsByFactoryMethod.build(),
+ subcomponentsByBuilderMethod.build(),
+ componentMethodsBuilder.build(),
+ creatorDescriptor);
+ }
+
+ private ComponentMethodDescriptor getDescriptorForComponentMethod(
+ TypeElement componentElement,
+ ComponentAnnotation componentAnnotation,
+ ExecutableElement componentMethod) {
+ ComponentMethodDescriptor.Builder descriptor =
+ ComponentMethodDescriptor.builder(componentMethod);
+
+ ExecutableType resolvedComponentMethod =
+ MoreTypes.asExecutable(
+ types.asMemberOf(MoreTypes.asDeclared(componentElement.asType()), componentMethod));
+ TypeMirror returnType = resolvedComponentMethod.getReturnType();
+ if (returnType.getKind().equals(DECLARED)
+ && !injectionAnnotations.getQualifier(componentMethod).isPresent()) {
+ TypeElement returnTypeElement = asTypeElement(returnType);
+ if (subcomponentAnnotation(returnTypeElement).isPresent()) {
+ // It's a subcomponent factory method. There is no dependency request, and there could be
+ // any number of parameters. Just return the descriptor.
+ return descriptor.subcomponent(subcomponentDescriptor(returnTypeElement)).build();
+ }
+ if (isSubcomponentCreator(returnTypeElement)) {
+ descriptor.subcomponent(
+ subcomponentDescriptor(asType(returnTypeElement.getEnclosingElement())));
+ }
+ }
+
+ switch (componentMethod.getParameters().size()) {
+ case 0:
+ checkArgument(
+ !returnType.getKind().equals(VOID),
+ "component method cannot be void: %s",
+ componentMethod);
+ descriptor.dependencyRequest(
+ componentAnnotation.isProduction()
+ ? dependencyRequestFactory.forComponentProductionMethod(
+ componentMethod, resolvedComponentMethod)
+ : dependencyRequestFactory.forComponentProvisionMethod(
+ componentMethod, resolvedComponentMethod));
+ break;
+
+ case 1:
+ checkArgument(
+ returnType.getKind().equals(VOID)
+ || MoreTypes.equivalence()
+ .equivalent(returnType, resolvedComponentMethod.getParameterTypes().get(0)),
+ "members injection method must return void or parameter type: %s",
+ componentMethod);
+ descriptor.dependencyRequest(
+ dependencyRequestFactory.forComponentMembersInjectionMethod(
+ componentMethod, resolvedComponentMethod));
+ break;
+
+ default:
+ throw new IllegalArgumentException(
+ "component method has too many parameters: " + componentMethod);
+ }
+
+ return descriptor.build();
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/ComponentKind.java b/java/dagger/internal/codegen/binding/ComponentKind.java
new file mode 100644
index 000000000..1cb3d7c1c
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/ComponentKind.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.common.collect.Sets.immutableEnumSet;
+import static dagger.internal.codegen.extension.DaggerStreams.stream;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.extension.DaggerStreams.valuesOf;
+import static java.util.EnumSet.allOf;
+
+import com.google.common.collect.ImmutableSet;
+import dagger.Component;
+import dagger.Module;
+import dagger.Subcomponent;
+import dagger.producers.ProducerModule;
+import dagger.producers.ProductionComponent;
+import dagger.producers.ProductionSubcomponent;
+import java.lang.annotation.Annotation;
+import java.util.Optional;
+import javax.lang.model.element.TypeElement;
+
+/** Enumeration of the different kinds of components. */
+public enum ComponentKind {
+ /** {@code @Component} */
+ COMPONENT(Component.class, true, false),
+
+ /** {@code @Subcomponent} */
+ SUBCOMPONENT(Subcomponent.class, false, false),
+
+ /** {@code @ProductionComponent} */
+ PRODUCTION_COMPONENT(ProductionComponent.class, true, true),
+
+ /** {@code @ProductionSubcomponent} */
+ PRODUCTION_SUBCOMPONENT(ProductionSubcomponent.class, false, true),
+
+ /**
+ * Kind for a descriptor that was generated from a {@link Module} instead of a component type in
+ * order to validate the module's bindings.
+ */
+ MODULE(Module.class, true, false),
+
+ /**
+ * Kind for a descriptor was generated from a {@link ProducerModule} instead of a component type
+ * in order to validate the module's bindings.
+ */
+ PRODUCER_MODULE(ProducerModule.class, true, true),
+ ;
+
+ private static final ImmutableSet<ComponentKind> ROOT_COMPONENT_KINDS =
+ valuesOf(ComponentKind.class)
+ .filter(kind -> !kind.isForModuleValidation())
+ .filter(kind -> kind.isRoot())
+ .collect(toImmutableSet());
+
+ private static final ImmutableSet<ComponentKind> SUBCOMPONENT_KINDS =
+ valuesOf(ComponentKind.class)
+ .filter(kind -> !kind.isForModuleValidation())
+ .filter(kind -> !kind.isRoot())
+ .collect(toImmutableSet());
+
+ /** Returns the set of kinds for root components. */
+ public static ImmutableSet<ComponentKind> rootComponentKinds() {
+ return ROOT_COMPONENT_KINDS;
+ }
+
+ /** Returns the set of kinds for subcomponents. */
+ public static ImmutableSet<ComponentKind> subcomponentKinds() {
+ return SUBCOMPONENT_KINDS;
+ }
+
+ /** Returns the annotations for components of the given kinds. */
+ public static ImmutableSet<Class<? extends Annotation>> annotationsFor(
+ Iterable<ComponentKind> kinds) {
+ return stream(kinds).map(ComponentKind::annotation).collect(toImmutableSet());
+ }
+
+ /** Returns the set of component kinds the given {@code element} has annotations for. */
+ public static ImmutableSet<ComponentKind> getComponentKinds(TypeElement element) {
+ return valuesOf(ComponentKind.class)
+ .filter(kind -> isAnnotationPresent(element, kind.annotation()))
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * Returns the kind of an annotated element if it is annotated with one of the {@linkplain
+ * #annotation() annotations}.
+ *
+ * @throws IllegalArgumentException if the element is annotated with more than one of the
+ * annotations
+ */
+ public static Optional<ComponentKind> forAnnotatedElement(TypeElement element) {
+ ImmutableSet<ComponentKind> kinds = getComponentKinds(element);
+ if (kinds.size() > 1) {
+ throw new IllegalArgumentException(
+ element + " cannot be annotated with more than one of " + annotationsFor(kinds));
+ }
+ return kinds.stream().findAny();
+ }
+
+ private final Class<? extends Annotation> annotation;
+ private final boolean isRoot;
+ private final boolean production;
+
+ ComponentKind(
+ Class<? extends Annotation> annotation,
+ boolean isRoot,
+ boolean production) {
+ this.annotation = annotation;
+ this.isRoot = isRoot;
+ this.production = production;
+ }
+
+ /** Returns the annotation that marks a component of this kind. */
+ public Class<? extends Annotation> annotation() {
+ return annotation;
+ }
+
+ /** Returns the kinds of modules that can be used with a component of this kind. */
+ public ImmutableSet<ModuleKind> legalModuleKinds() {
+ return isProducer()
+ ? immutableEnumSet(allOf(ModuleKind.class))
+ : immutableEnumSet(ModuleKind.MODULE);
+ }
+
+ /** Returns the kinds of subcomponents a component of this kind can have. */
+ public ImmutableSet<ComponentKind> legalSubcomponentKinds() {
+ return isProducer()
+ ? immutableEnumSet(PRODUCTION_SUBCOMPONENT)
+ : immutableEnumSet(SUBCOMPONENT, PRODUCTION_SUBCOMPONENT);
+ }
+
+ /**
+ * Returns {@code true} if the descriptor is for a root component (not a subcomponent) or is for
+ * {@linkplain #isForModuleValidation() module-validation}.
+ */
+ public boolean isRoot() {
+ return isRoot;
+ }
+
+ /** Returns true if this is a production component. */
+ public boolean isProducer() {
+ return production;
+ }
+
+ /** Returns {@code true} if the descriptor is for a module in order to validate its bindings. */
+ public boolean isForModuleValidation() {
+ switch (this) {
+ case MODULE:
+ case PRODUCER_MODULE:
+ return true;
+ default:
+ // fall through
+ }
+ return false;
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/ComponentNodeImpl.java b/java/dagger/internal/codegen/binding/ComponentNodeImpl.java
new file mode 100644
index 000000000..0947c252c
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/ComponentNodeImpl.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableSet;
+import dagger.model.BindingGraph.ComponentNode;
+import dagger.model.ComponentPath;
+import dagger.model.DependencyRequest;
+import dagger.model.Scope;
+
+/** An implementation of {@link ComponentNode} that also exposes the {@link ComponentDescriptor}. */
+@AutoValue
+public abstract class ComponentNodeImpl implements ComponentNode {
+ public static ComponentNode create(
+ ComponentPath componentPath, ComponentDescriptor componentDescriptor) {
+ return new AutoValue_ComponentNodeImpl(componentPath, componentDescriptor);
+ }
+
+ @Override
+ public final boolean isSubcomponent() {
+ return componentDescriptor().isSubcomponent();
+ }
+
+ @Override
+ public boolean isRealComponent() {
+ return componentDescriptor().isRealComponent();
+ }
+
+ @Override
+ public final ImmutableSet<DependencyRequest> entryPoints() {
+ return componentDescriptor().entryPointMethods().stream()
+ .map(method -> method.dependencyRequest().get())
+ .collect(toImmutableSet());
+ }
+
+ @Override
+ public ImmutableSet<Scope> scopes() {
+ return componentDescriptor().scopes();
+ }
+
+ public abstract ComponentDescriptor componentDescriptor();
+
+ @Override
+ public final String toString() {
+ return componentPath().toString();
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/ComponentRequirement.java b/java/dagger/internal/codegen/binding/ComponentRequirement.java
new file mode 100644
index 000000000..fa24b56f3
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/ComponentRequirement.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.binding.SourceFiles.simpleVariableName;
+import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
+import static javax.lang.model.element.ElementKind.CONSTRUCTOR;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Equivalence;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.Binds;
+import dagger.BindsOptionalOf;
+import dagger.Provides;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.BindingKind;
+import dagger.model.Key;
+import dagger.multibindings.Multibinds;
+import dagger.producers.Produces;
+import java.util.Optional;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+/** A type that a component needs an instance of. */
+@AutoValue
+public abstract class ComponentRequirement {
+ /** The kind of the {@link ComponentRequirement}. */
+ public enum Kind {
+ /** A type listed in the component's {@code dependencies} attribute. */
+ DEPENDENCY,
+
+ /** A type listed in the component or subcomponent's {@code modules} attribute. */
+ MODULE,
+
+ /**
+ * An object that is passed to a builder's {@link dagger.BindsInstance @BindsInstance} method.
+ */
+ BOUND_INSTANCE,
+ ;
+
+ public boolean isBoundInstance() {
+ return equals(BOUND_INSTANCE);
+ }
+
+ public boolean isModule() {
+ return equals(MODULE);
+ }
+ }
+
+ /** The kind of requirement. */
+ public abstract Kind kind();
+
+ /** Returns true if this is a {@link Kind#BOUND_INSTANCE} requirement. */
+ // TODO(ronshapiro): consider removing this and inlining the usages
+ final boolean isBoundInstance() {
+ return kind().isBoundInstance();
+ }
+
+ /**
+ * The type of the instance the component must have, wrapped so that requirements can be used as
+ * value types.
+ */
+ public abstract Equivalence.Wrapper<TypeMirror> wrappedType();
+
+ /** The type of the instance the component must have. */
+ public TypeMirror type() {
+ return wrappedType().get();
+ }
+
+ /** The element associated with the type of this requirement. */
+ public TypeElement typeElement() {
+ return MoreTypes.asTypeElement(type());
+ }
+
+ /** The action a component builder should take if it {@code null} is passed. */
+ public enum NullPolicy {
+ /** Make a new instance. */
+ NEW,
+ /** Throw an exception. */
+ THROW,
+ /** Allow use of null values. */
+ ALLOW,
+ }
+
+ /**
+ * An override for the requirement's null policy. If set, this is used as the null policy instead
+ * of the default behavior in {@link #nullPolicy}.
+ *
+ * <p>Some implementations' null policy can be determined upon construction (e.g., for binding
+ * instances), but others' require Elements and Types, which must wait until {@link #nullPolicy}
+ * is called.
+ */
+ abstract Optional<NullPolicy> overrideNullPolicy();
+
+ /** The requirement's null policy. */
+ public NullPolicy nullPolicy(
+ DaggerElements elements, DaggerTypes types, KotlinMetadataUtil metadataUtil) {
+ if (overrideNullPolicy().isPresent()) {
+ return overrideNullPolicy().get();
+ }
+ switch (kind()) {
+ case MODULE:
+ return componentCanMakeNewInstances(typeElement(), metadataUtil)
+ ? NullPolicy.NEW
+ : requiresAPassedInstance(elements, types, metadataUtil)
+ ? NullPolicy.THROW
+ : NullPolicy.ALLOW;
+ case DEPENDENCY:
+ case BOUND_INSTANCE:
+ return NullPolicy.THROW;
+ }
+ throw new AssertionError();
+ }
+
+ /**
+ * Returns true if the passed {@link ComponentRequirement} requires a passed instance in order to
+ * be used within a component.
+ */
+ public boolean requiresAPassedInstance(
+ DaggerElements elements, DaggerTypes types, KotlinMetadataUtil metadataUtil) {
+ if (!kind().isModule()) {
+ // Bound instances and dependencies always require the user to provide an instance.
+ return true;
+ }
+ return requiresModuleInstance(elements, types, metadataUtil)
+ && !componentCanMakeNewInstances(typeElement(), metadataUtil);
+ }
+
+ /**
+ * Returns {@code true} if an instance is needed for this (module) requirement.
+ *
+ * <p>An instance is only needed if there is a binding method on the module that is neither {@code
+ * abstract} nor {@code static}; if all bindings are one of those, then there should be no
+ * possible dependency on instance state in the module's bindings.
+ *
+ * <p>Alternatively, if the module is a Kotlin Object then the binding methods are considered
+ * {@code static}, requiring no module instance.
+ */
+ private boolean requiresModuleInstance(
+ DaggerElements elements, DaggerTypes types, KotlinMetadataUtil metadataUtil) {
+ boolean isKotlinObject =
+ metadataUtil.isObjectClass(typeElement())
+ || metadataUtil.isCompanionObjectClass(typeElement());
+ if (isKotlinObject) {
+ return false;
+ }
+
+ ImmutableSet<ExecutableElement> methods =
+ getLocalAndInheritedMethods(typeElement(), types, elements);
+ return methods.stream()
+ .filter(this::isBindingMethod)
+ .map(ExecutableElement::getModifiers)
+ .anyMatch(modifiers -> !modifiers.contains(ABSTRACT) && !modifiers.contains(STATIC));
+ }
+
+ private boolean isBindingMethod(ExecutableElement method) {
+ // TODO(cgdecker): At the very least, we should have utility methods to consolidate this stuff
+ // in one place; listing individual annotations all over the place is brittle.
+ return isAnyAnnotationPresent(
+ method,
+ Provides.class,
+ Produces.class,
+ // TODO(ronshapiro): it would be cool to have internal meta-annotations that could describe
+ // these, like @AbstractBindingMethod
+ Binds.class,
+ Multibinds.class,
+ BindsOptionalOf.class);
+ }
+
+ /** The key for this requirement, if one is available. */
+ public abstract Optional<Key> key();
+
+ /** Returns the name for this requirement that could be used as a variable. */
+ public abstract String variableName();
+
+ /** Returns a parameter spec for this requirement. */
+ public ParameterSpec toParameterSpec() {
+ return ParameterSpec.builder(TypeName.get(type()), variableName()).build();
+ }
+
+ public static ComponentRequirement forDependency(TypeMirror type) {
+ return new AutoValue_ComponentRequirement(
+ Kind.DEPENDENCY,
+ MoreTypes.equivalence().wrap(checkNotNull(type)),
+ Optional.empty(),
+ Optional.empty(),
+ simpleVariableName(MoreTypes.asTypeElement(type)));
+ }
+
+ public static ComponentRequirement forModule(TypeMirror type) {
+ return new AutoValue_ComponentRequirement(
+ Kind.MODULE,
+ MoreTypes.equivalence().wrap(checkNotNull(type)),
+ Optional.empty(),
+ Optional.empty(),
+ simpleVariableName(MoreTypes.asTypeElement(type)));
+ }
+
+ static ComponentRequirement forBoundInstance(Key key, boolean nullable, String variableName) {
+ return new AutoValue_ComponentRequirement(
+ Kind.BOUND_INSTANCE,
+ MoreTypes.equivalence().wrap(key.type()),
+ nullable ? Optional.of(NullPolicy.ALLOW) : Optional.empty(),
+ Optional.of(key),
+ variableName);
+ }
+
+ public static ComponentRequirement forBoundInstance(ContributionBinding binding) {
+ checkArgument(binding.kind().equals(BindingKind.BOUND_INSTANCE));
+ return forBoundInstance(
+ binding.key(),
+ binding.nullableType().isPresent(),
+ binding.bindingElement().get().getSimpleName().toString());
+ }
+
+ /**
+ * Returns true if and only if a component can instantiate new instances (typically of a module)
+ * rather than requiring that they be passed.
+ */
+ // TODO(bcorso): Should this method throw if its called knowing that an instance is not needed?
+ public static boolean componentCanMakeNewInstances(
+ TypeElement typeElement, KotlinMetadataUtil metadataUtil) {
+ switch (typeElement.getKind()) {
+ case CLASS:
+ break;
+ case ENUM:
+ case ANNOTATION_TYPE:
+ case INTERFACE:
+ return false;
+ default:
+ throw new AssertionError("TypeElement cannot have kind: " + typeElement.getKind());
+ }
+
+ if (typeElement.getModifiers().contains(ABSTRACT)) {
+ return false;
+ }
+
+ if (requiresEnclosingInstance(typeElement)) {
+ return false;
+ }
+
+ if (metadataUtil.isObjectClass(typeElement)
+ || metadataUtil.isCompanionObjectClass(typeElement)) {
+ return false;
+ }
+
+ for (Element enclosed : typeElement.getEnclosedElements()) {
+ if (enclosed.getKind().equals(CONSTRUCTOR)
+ && MoreElements.asExecutable(enclosed).getParameters().isEmpty()
+ && !enclosed.getModifiers().contains(PRIVATE)) {
+ return true;
+ }
+ }
+
+ // TODO(gak): still need checks for visibility
+
+ return false;
+ }
+
+ private static boolean requiresEnclosingInstance(TypeElement typeElement) {
+ switch (typeElement.getNestingKind()) {
+ case TOP_LEVEL:
+ return false;
+ case MEMBER:
+ return !typeElement.getModifiers().contains(STATIC);
+ case ANONYMOUS:
+ case LOCAL:
+ return true;
+ }
+ throw new AssertionError(
+ "TypeElement cannot have nesting kind: " + typeElement.getNestingKind());
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/ConfigurationAnnotations.java b/java/dagger/internal/codegen/binding/ConfigurationAnnotations.java
new file mode 100644
index 000000000..539a66ac6
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/ConfigurationAnnotations.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.consumingIterable;
+import static dagger.internal.codegen.base.ComponentAnnotation.subcomponentAnnotation;
+import static dagger.internal.codegen.base.ModuleAnnotation.moduleAnnotation;
+import static dagger.internal.codegen.base.MoreAnnotationMirrors.getTypeListValue;
+import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.subcomponentCreatorAnnotations;
+import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
+import static javax.lang.model.util.ElementFilter.typesIn;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import dagger.Component;
+import dagger.Module;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.lang.annotation.Annotation;
+import java.util.ArrayDeque;
+import java.util.List;
+import java.util.Optional;
+import java.util.Queue;
+import java.util.Set;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Utility methods related to dagger configuration annotations (e.g.: {@link Component} and {@link
+ * Module}).
+ */
+public final class ConfigurationAnnotations {
+
+ public static Optional<TypeElement> getSubcomponentCreator(TypeElement subcomponent) {
+ checkArgument(subcomponentAnnotation(subcomponent).isPresent());
+ for (TypeElement nestedType : typesIn(subcomponent.getEnclosedElements())) {
+ if (isSubcomponentCreator(nestedType)) {
+ return Optional.of(nestedType);
+ }
+ }
+ return Optional.empty();
+ }
+
+ static boolean isSubcomponentCreator(Element element) {
+ return isAnyAnnotationPresent(element, subcomponentCreatorAnnotations());
+ }
+
+ // Dagger 1 support.
+ public static ImmutableList<TypeMirror> getModuleInjects(AnnotationMirror moduleAnnotation) {
+ checkNotNull(moduleAnnotation);
+ return getTypeListValue(moduleAnnotation, "injects");
+ }
+
+ /** Returns the first type that specifies this' nullability, or empty if none. */
+ public static Optional<DeclaredType> getNullableType(Element element) {
+ List<? extends AnnotationMirror> mirrors = element.getAnnotationMirrors();
+ for (AnnotationMirror mirror : mirrors) {
+ if (mirror.getAnnotationType().asElement().getSimpleName().contentEquals("Nullable")) {
+ return Optional.of(mirror.getAnnotationType());
+ }
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Returns the full set of modules transitively {@linkplain Module#includes included} from the
+ * given seed modules. If a module is malformed and a type listed in {@link Module#includes} is
+ * not annotated with {@link Module}, it is ignored.
+ *
+ * @deprecated Use {@link ComponentDescriptor#modules()}.
+ */
+ @Deprecated
+ public static ImmutableSet<TypeElement> getTransitiveModules(
+ DaggerTypes types, DaggerElements elements, Iterable<TypeElement> seedModules) {
+ TypeMirror objectType = elements.getTypeElement(Object.class).asType();
+ Queue<TypeElement> moduleQueue = new ArrayDeque<>();
+ Iterables.addAll(moduleQueue, seedModules);
+ Set<TypeElement> moduleElements = Sets.newLinkedHashSet();
+ for (TypeElement moduleElement : consumingIterable(moduleQueue)) {
+ moduleAnnotation(moduleElement)
+ .ifPresent(
+ moduleAnnotation -> {
+ ImmutableSet.Builder<TypeElement> moduleDependenciesBuilder =
+ ImmutableSet.builder();
+ moduleDependenciesBuilder.addAll(moduleAnnotation.includes());
+ // We don't recur on the parent class because we don't want the parent class as a
+ // root that the component depends on, and also because we want the dependencies
+ // rooted against this element, not the parent.
+ addIncludesFromSuperclasses(
+ types, moduleElement, moduleDependenciesBuilder, objectType);
+ ImmutableSet<TypeElement> moduleDependencies = moduleDependenciesBuilder.build();
+ moduleElements.add(moduleElement);
+ for (TypeElement dependencyType : moduleDependencies) {
+ if (!moduleElements.contains(dependencyType)) {
+ moduleQueue.add(dependencyType);
+ }
+ }
+ });
+ }
+ return ImmutableSet.copyOf(moduleElements);
+ }
+
+ /** Returns the enclosed types annotated with the given annotation. */
+ public static ImmutableList<DeclaredType> enclosedAnnotatedTypes(
+ TypeElement typeElement, Class<? extends Annotation> annotation) {
+ final ImmutableList.Builder<DeclaredType> builders = ImmutableList.builder();
+ for (TypeElement element : typesIn(typeElement.getEnclosedElements())) {
+ if (MoreElements.isAnnotationPresent(element, annotation)) {
+ builders.add(MoreTypes.asDeclared(element.asType()));
+ }
+ }
+ return builders.build();
+ }
+
+ /** Traverses includes from superclasses and adds them into the builder. */
+ private static void addIncludesFromSuperclasses(
+ DaggerTypes types,
+ TypeElement element,
+ ImmutableSet.Builder<TypeElement> builder,
+ TypeMirror objectType) {
+ // Also add the superclass to the queue, in case any @Module definitions were on that.
+ TypeMirror superclass = element.getSuperclass();
+ while (!types.isSameType(objectType, superclass)
+ && superclass.getKind().equals(TypeKind.DECLARED)) {
+ element = MoreElements.asType(types.asElement(superclass));
+ moduleAnnotation(element)
+ .ifPresent(moduleAnnotation -> builder.addAll(moduleAnnotation.includes()));
+ superclass = element.getSuperclass();
+ }
+ }
+
+ private ConfigurationAnnotations() {}
+}
diff --git a/java/dagger/internal/codegen/binding/ContributionBinding.java b/java/dagger/internal/codegen/binding/ContributionBinding.java
new file mode 100644
index 000000000..1942e8c1f
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/ContributionBinding.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static dagger.internal.codegen.base.MoreAnnotationMirrors.unwrapOptionalEquivalence;
+import static dagger.internal.codegen.binding.ContributionBinding.FactoryCreationStrategy.CLASS_CONSTRUCTOR;
+import static dagger.internal.codegen.binding.ContributionBinding.FactoryCreationStrategy.DELEGATE;
+import static dagger.internal.codegen.binding.ContributionBinding.FactoryCreationStrategy.SINGLETON_INSTANCE;
+import static java.util.Arrays.asList;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.base.Equivalence;
+import com.google.common.base.Preconditions;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.google.errorprone.annotations.CheckReturnValue;
+import dagger.internal.codegen.base.ContributionType;
+import dagger.internal.codegen.base.ContributionType.HasContributionType;
+import dagger.internal.codegen.base.MapType;
+import dagger.internal.codegen.base.SetType;
+import dagger.model.BindingKind;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * An abstract class for a value object representing the mechanism by which a {@link Key} can be
+ * contributed to a dependency graph.
+ */
+public abstract class ContributionBinding extends Binding implements HasContributionType {
+
+ /** Returns the type that specifies this' nullability, absent if not nullable. */
+ public abstract Optional<DeclaredType> nullableType();
+
+ public abstract Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedMapKeyAnnotation();
+
+ public final Optional<AnnotationMirror> mapKeyAnnotation() {
+ return unwrapOptionalEquivalence(wrappedMapKeyAnnotation());
+ }
+
+ /** If {@link #bindingElement()} is a method that returns a primitive type, returns that type. */
+ public final Optional<TypeMirror> contributedPrimitiveType() {
+ return bindingElement()
+ .filter(bindingElement -> bindingElement instanceof ExecutableElement)
+ .map(bindingElement -> MoreElements.asExecutable(bindingElement).getReturnType())
+ .filter(type -> type.getKind().isPrimitive());
+ }
+
+ @Override
+ public boolean requiresModuleInstance() {
+ return !isContributingModuleKotlinObject().orElse(false) && super.requiresModuleInstance();
+ }
+
+ @Override
+ public final boolean isNullable() {
+ return nullableType().isPresent();
+ }
+
+ /**
+ * Returns {@code true} if the contributing module is a Kotlin object. Note that a companion
+ * object is also considered a Kotlin object.
+ */
+ abstract Optional<Boolean> isContributingModuleKotlinObject();
+
+ /** The strategy for getting an instance of a factory for a {@link ContributionBinding}. */
+ public enum FactoryCreationStrategy {
+ /** The factory class is a single instance. */
+ SINGLETON_INSTANCE,
+ /** The factory must be created by calling the constructor. */
+ CLASS_CONSTRUCTOR,
+ /** The factory is simply delegated to another. */
+ DELEGATE,
+ }
+
+ /**
+ * Returns the {@link FactoryCreationStrategy} appropriate for a binding.
+ *
+ * <p>Delegate bindings use the {@link FactoryCreationStrategy#DELEGATE} strategy.
+ *
+ * <p>Bindings without dependencies that don't require a module instance use the {@link
+ * FactoryCreationStrategy#SINGLETON_INSTANCE} strategy.
+ *
+ * <p>All other bindings use the {@link FactoryCreationStrategy#CLASS_CONSTRUCTOR} strategy.
+ */
+ public final FactoryCreationStrategy factoryCreationStrategy() {
+ switch (kind()) {
+ case DELEGATE:
+ return DELEGATE;
+ case PROVISION:
+ return dependencies().isEmpty() && !requiresModuleInstance()
+ ? SINGLETON_INSTANCE
+ : CLASS_CONSTRUCTOR;
+ case INJECTION:
+ case MULTIBOUND_SET:
+ case MULTIBOUND_MAP:
+ return dependencies().isEmpty() ? SINGLETON_INSTANCE : CLASS_CONSTRUCTOR;
+ default:
+ return CLASS_CONSTRUCTOR;
+ }
+ }
+
+ /**
+ * The {@link TypeMirror type} for the {@code Factory<T>} or {@code Producer<T>} which is created
+ * for this binding. Uses the binding's key, V in the case of {@code Map<K, FrameworkClass<V>>>},
+ * and E {@code Set<E>} for {@link dagger.multibindings.IntoSet @IntoSet} methods.
+ */
+ public final TypeMirror contributedType() {
+ switch (contributionType()) {
+ case MAP:
+ return MapType.from(key()).unwrappedFrameworkValueType();
+ case SET:
+ return SetType.from(key()).elementType();
+ case SET_VALUES:
+ case UNIQUE:
+ return key().type();
+ }
+ throw new AssertionError();
+ }
+
+ /**
+ * Returns {@link BindingKind#MULTIBOUND_SET} or {@link
+ * BindingKind#MULTIBOUND_MAP} if the key is a set or map.
+ *
+ * @throws IllegalArgumentException if {@code key} is neither a set nor a map
+ */
+ static BindingKind bindingKindForMultibindingKey(Key key) {
+ if (SetType.isSet(key)) {
+ return BindingKind.MULTIBOUND_SET;
+ } else if (MapType.isMap(key)) {
+ return BindingKind.MULTIBOUND_MAP;
+ } else {
+ throw new IllegalArgumentException(String.format("key is not for a set or map: %s", key));
+ }
+ }
+
+ public abstract Builder<?, ?> toBuilder();
+
+ /**
+ * Base builder for {@link com.google.auto.value.AutoValue @AutoValue} subclasses of {@link
+ * ContributionBinding}.
+ */
+ @CanIgnoreReturnValue
+ public abstract static class Builder<C extends ContributionBinding, B extends Builder<C, B>> {
+ public abstract B dependencies(Iterable<DependencyRequest> dependencies);
+
+ public B dependencies(DependencyRequest... dependencies) {
+ return dependencies(asList(dependencies));
+ }
+
+ public abstract B unresolved(C unresolved);
+
+ public abstract B contributionType(ContributionType contributionType);
+
+ public abstract B bindingElement(Element bindingElement);
+
+ abstract B bindingElement(Optional<Element> bindingElement);
+
+ public final B clearBindingElement() {
+ return bindingElement(Optional.empty());
+ };
+
+ abstract B contributingModule(TypeElement contributingModule);
+
+ abstract B isContributingModuleKotlinObject(boolean isModuleKotlinObject);
+
+ public abstract B key(Key key);
+
+ public abstract B nullableType(Optional<DeclaredType> nullableType);
+
+ abstract B wrappedMapKeyAnnotation(
+ Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedMapKeyAnnotation);
+
+ public abstract B kind(BindingKind kind);
+
+ @CheckReturnValue
+ abstract C autoBuild();
+
+ @CheckReturnValue
+ public C build() {
+ C binding = autoBuild();
+ Preconditions.checkState(
+ binding.contributingModule().isPresent()
+ == binding.isContributingModuleKotlinObject().isPresent(),
+ "The contributionModule and isModuleKotlinObject must both be set together.");
+ return binding;
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/DelegateDeclaration.java b/java/dagger/internal/codegen/binding/DelegateDeclaration.java
new file mode 100644
index 000000000..b6c3c3830
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/DelegateDeclaration.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static dagger.internal.codegen.base.MoreAnnotationMirrors.wrapOptionalInEquivalence;
+import static dagger.internal.codegen.binding.MapKeys.getMapKey;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.base.Equivalence;
+import com.google.common.collect.Iterables;
+import dagger.Binds;
+import dagger.internal.codegen.base.ContributionType;
+import dagger.internal.codegen.base.ContributionType.HasContributionType;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.ExecutableType;
+
+/** The declaration for a delegate binding established by a {@link Binds} method. */
+@AutoValue
+public abstract class DelegateDeclaration extends BindingDeclaration
+ implements HasContributionType {
+ abstract DependencyRequest delegateRequest();
+
+ abstract Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedMapKey();
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object obj);
+
+ /** A {@link DelegateDeclaration} factory. */
+ public static final class Factory {
+ private final DaggerTypes types;
+ private final KeyFactory keyFactory;
+ private final DependencyRequestFactory dependencyRequestFactory;
+
+ @Inject
+ Factory(
+ DaggerTypes types,
+ KeyFactory keyFactory,
+ DependencyRequestFactory dependencyRequestFactory) {
+ this.types = types;
+ this.keyFactory = keyFactory;
+ this.dependencyRequestFactory = dependencyRequestFactory;
+ }
+
+ public DelegateDeclaration create(
+ ExecutableElement bindsMethod, TypeElement contributingModule) {
+ checkArgument(MoreElements.isAnnotationPresent(bindsMethod, Binds.class));
+ ExecutableType resolvedMethod =
+ MoreTypes.asExecutable(
+ types.asMemberOf(MoreTypes.asDeclared(contributingModule.asType()), bindsMethod));
+ DependencyRequest delegateRequest =
+ dependencyRequestFactory.forRequiredResolvedVariable(
+ Iterables.getOnlyElement(bindsMethod.getParameters()),
+ Iterables.getOnlyElement(resolvedMethod.getParameterTypes()));
+ return new AutoValue_DelegateDeclaration(
+ ContributionType.fromBindingElement(bindsMethod),
+ keyFactory.forBindsMethod(bindsMethod, contributingModule),
+ Optional.<Element>of(bindsMethod),
+ Optional.of(contributingModule),
+ delegateRequest,
+ wrapOptionalInEquivalence(getMapKey(bindsMethod)));
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/DependencyEdgeImpl.java b/java/dagger/internal/codegen/binding/DependencyEdgeImpl.java
new file mode 100644
index 000000000..f11517e6d
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/DependencyEdgeImpl.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import dagger.internal.codegen.base.ElementFormatter;
+import dagger.model.BindingGraph.DependencyEdge;
+import dagger.model.DependencyRequest;
+
+/** An implementation of {@link DependencyEdge}. */
+final class DependencyEdgeImpl implements DependencyEdge {
+
+ private final DependencyRequest dependencyRequest;
+ private final boolean entryPoint;
+
+ DependencyEdgeImpl(DependencyRequest dependencyRequest, boolean entryPoint) {
+ this.dependencyRequest = dependencyRequest;
+ this.entryPoint = entryPoint;
+ }
+
+ @Override
+ public DependencyRequest dependencyRequest() {
+ return dependencyRequest;
+ }
+
+ @Override
+ public boolean isEntryPoint() {
+ return entryPoint;
+ }
+
+ @Override
+ public String toString() {
+ String string =
+ dependencyRequest
+ .requestElement()
+ .map(ElementFormatter::elementToString)
+ .orElseGet(
+ () ->
+ "synthetic request for "
+ + dependencyRequest.kind().format(dependencyRequest.key()));
+ return entryPoint ? string + " (entry point)" : string;
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/DependencyRequestFactory.java b/java/dagger/internal/codegen/binding/DependencyRequestFactory.java
new file mode 100644
index 000000000..707de4ce6
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/DependencyRequestFactory.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.auto.common.MoreTypes.isTypeOf;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.base.RequestKinds.extractKeyType;
+import static dagger.internal.codegen.base.RequestKinds.frameworkClass;
+import static dagger.internal.codegen.base.RequestKinds.getRequestKind;
+import static dagger.internal.codegen.binding.ConfigurationAnnotations.getNullableType;
+import static dagger.internal.codegen.langmodel.DaggerTypes.unwrapType;
+import static dagger.model.RequestKind.FUTURE;
+import static dagger.model.RequestKind.INSTANCE;
+import static dagger.model.RequestKind.MEMBERS_INJECTION;
+import static dagger.model.RequestKind.PRODUCER;
+import static dagger.model.RequestKind.PROVIDER;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.Lazy;
+import dagger.internal.codegen.base.MapType;
+import dagger.internal.codegen.base.OptionalType;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import java.util.List;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Factory for {@link DependencyRequest}s.
+ *
+ * <p>Any factory method may throw {@link TypeNotPresentException} if a type is not available, which
+ * may mean that the type will be generated in a later round of processing.
+ */
+public final class DependencyRequestFactory {
+ private final KeyFactory keyFactory;
+ private final InjectionAnnotations injectionAnnotations;
+
+ @Inject
+ DependencyRequestFactory(KeyFactory keyFactory, InjectionAnnotations injectionAnnotations) {
+ this.keyFactory = keyFactory;
+ this.injectionAnnotations = injectionAnnotations;
+ }
+
+ ImmutableSet<DependencyRequest> forRequiredResolvedVariables(
+ List<? extends VariableElement> variables, List<? extends TypeMirror> resolvedTypes) {
+ checkState(resolvedTypes.size() == variables.size());
+ ImmutableSet.Builder<DependencyRequest> builder = ImmutableSet.builder();
+ for (int i = 0; i < variables.size(); i++) {
+ builder.add(forRequiredResolvedVariable(variables.get(i), resolvedTypes.get(i)));
+ }
+ return builder.build();
+ }
+
+ /**
+ * Creates synthetic dependency requests for each individual multibinding contribution in {@code
+ * multibindingContributions}.
+ */
+ ImmutableSet<DependencyRequest> forMultibindingContributions(
+ Key multibindingKey, Iterable<ContributionBinding> multibindingContributions) {
+ ImmutableSet.Builder<DependencyRequest> requests = ImmutableSet.builder();
+ for (ContributionBinding multibindingContribution : multibindingContributions) {
+ requests.add(forMultibindingContribution(multibindingKey, multibindingContribution));
+ }
+ return requests.build();
+ }
+
+ /** Creates a synthetic dependency request for one individual {@code multibindingContribution}. */
+ private DependencyRequest forMultibindingContribution(
+ Key multibindingKey, ContributionBinding multibindingContribution) {
+ checkArgument(
+ multibindingContribution.key().multibindingContributionIdentifier().isPresent(),
+ "multibindingContribution's key must have a multibinding contribution identifier: %s",
+ multibindingContribution);
+ return DependencyRequest.builder()
+ .kind(multibindingContributionRequestKind(multibindingKey, multibindingContribution))
+ .key(multibindingContribution.key())
+ .build();
+ }
+
+ // TODO(b/28555349): support PROVIDER_OF_LAZY here too
+ private static final ImmutableSet<RequestKind> WRAPPING_MAP_VALUE_FRAMEWORK_TYPES =
+ ImmutableSet.of(PROVIDER, PRODUCER);
+
+ private RequestKind multibindingContributionRequestKind(
+ Key multibindingKey, ContributionBinding multibindingContribution) {
+ switch (multibindingContribution.contributionType()) {
+ case MAP:
+ MapType mapType = MapType.from(multibindingKey);
+ for (RequestKind kind : WRAPPING_MAP_VALUE_FRAMEWORK_TYPES) {
+ if (mapType.valuesAreTypeOf(frameworkClass(kind))) {
+ return kind;
+ }
+ }
+ // fall through
+ case SET:
+ case SET_VALUES:
+ return INSTANCE;
+ case UNIQUE:
+ throw new IllegalArgumentException(
+ "multibindingContribution must be a multibinding: " + multibindingContribution);
+ }
+ throw new AssertionError(multibindingContribution.toString());
+ }
+
+ DependencyRequest forRequiredResolvedVariable(
+ VariableElement variableElement, TypeMirror resolvedType) {
+ checkNotNull(variableElement);
+ checkNotNull(resolvedType);
+ // Ban @Assisted parameters, they are not considered dependency requests.
+ checkArgument(!AssistedInjectionAnnotations.isAssistedParameter(variableElement));
+ Optional<AnnotationMirror> qualifier = injectionAnnotations.getQualifier(variableElement);
+ return newDependencyRequest(variableElement, resolvedType, qualifier);
+ }
+
+ public DependencyRequest forComponentProvisionMethod(
+ ExecutableElement provisionMethod, ExecutableType provisionMethodType) {
+ checkNotNull(provisionMethod);
+ checkNotNull(provisionMethodType);
+ checkArgument(
+ provisionMethod.getParameters().isEmpty(),
+ "Component provision methods must be empty: %s",
+ provisionMethod);
+ Optional<AnnotationMirror> qualifier = injectionAnnotations.getQualifier(provisionMethod);
+ return newDependencyRequest(provisionMethod, provisionMethodType.getReturnType(), qualifier);
+ }
+
+ public DependencyRequest forComponentProductionMethod(
+ ExecutableElement productionMethod, ExecutableType productionMethodType) {
+ checkNotNull(productionMethod);
+ checkNotNull(productionMethodType);
+ checkArgument(
+ productionMethod.getParameters().isEmpty(),
+ "Component production methods must be empty: %s",
+ productionMethod);
+ TypeMirror type = productionMethodType.getReturnType();
+ Optional<AnnotationMirror> qualifier = injectionAnnotations.getQualifier(productionMethod);
+ // Only a component production method can be a request for a ListenableFuture, so we
+ // special-case it here.
+ if (isTypeOf(ListenableFuture.class, type)) {
+ return DependencyRequest.builder()
+ .kind(FUTURE)
+ .key(keyFactory.forQualifiedType(qualifier, unwrapType(type)))
+ .requestElement(productionMethod)
+ .build();
+ } else {
+ return newDependencyRequest(productionMethod, type, qualifier);
+ }
+ }
+
+ DependencyRequest forComponentMembersInjectionMethod(
+ ExecutableElement membersInjectionMethod, ExecutableType membersInjectionMethodType) {
+ checkNotNull(membersInjectionMethod);
+ checkNotNull(membersInjectionMethodType);
+ Optional<AnnotationMirror> qualifier =
+ injectionAnnotations.getQualifier(membersInjectionMethod);
+ checkArgument(!qualifier.isPresent());
+ TypeMirror membersInjectedType = getOnlyElement(membersInjectionMethodType.getParameterTypes());
+ return DependencyRequest.builder()
+ .kind(MEMBERS_INJECTION)
+ .key(keyFactory.forMembersInjectedType(membersInjectedType))
+ .requestElement(membersInjectionMethod)
+ .build();
+ }
+
+ DependencyRequest forProductionImplementationExecutor() {
+ return DependencyRequest.builder()
+ .kind(PROVIDER)
+ .key(keyFactory.forProductionImplementationExecutor())
+ .build();
+ }
+
+ DependencyRequest forProductionComponentMonitor() {
+ return DependencyRequest.builder()
+ .kind(PROVIDER)
+ .key(keyFactory.forProductionComponentMonitor())
+ .build();
+ }
+
+ /**
+ * Returns a synthetic request for the present value of an optional binding generated from a
+ * {@link dagger.BindsOptionalOf} declaration.
+ */
+ DependencyRequest forSyntheticPresentOptionalBinding(Key requestKey, RequestKind kind) {
+ Optional<Key> key = keyFactory.unwrapOptional(requestKey);
+ checkArgument(key.isPresent(), "not a request for optional: %s", requestKey);
+ return DependencyRequest.builder()
+ .kind(kind)
+ .key(key.get())
+ .isNullable(
+ allowsNull(getRequestKind(OptionalType.from(requestKey).valueType()), Optional.empty()))
+ .build();
+ }
+
+ private DependencyRequest newDependencyRequest(
+ Element requestElement, TypeMirror type, Optional<AnnotationMirror> qualifier) {
+ RequestKind requestKind = getRequestKind(type);
+ return DependencyRequest.builder()
+ .kind(requestKind)
+ .key(keyFactory.forQualifiedType(qualifier, extractKeyType(type)))
+ .requestElement(requestElement)
+ .isNullable(allowsNull(requestKind, getNullableType(requestElement)))
+ .build();
+ }
+
+ /**
+ * Returns {@code true} if a given request element allows null values. {@link
+ * RequestKind#INSTANCE} requests must be annotated with {@code @Nullable} in order to allow null
+ * values. All other request kinds implicitly allow null values because they are are wrapped
+ * inside {@link Provider}, {@link Lazy}, etc.
+ */
+ private boolean allowsNull(RequestKind kind, Optional<DeclaredType> nullableType) {
+ return nullableType.isPresent() || !kind.equals(INSTANCE);
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/DependencyRequestFormatter.java b/java/dagger/internal/codegen/binding/DependencyRequestFormatter.java
new file mode 100644
index 000000000..888dec2d8
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/DependencyRequestFormatter.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static dagger.internal.codegen.base.ElementFormatter.elementToString;
+import static dagger.internal.codegen.base.RequestKinds.requestType;
+
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import dagger.Provides;
+import dagger.internal.codegen.base.Formatter;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import dagger.producers.Produces;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementKindVisitor8;
+
+/**
+ * Formats a {@link DependencyRequest} into a {@link String} suitable for an error message listing a
+ * chain of dependencies.
+ *
+ * <dl>
+ * <dt>For component provision methods
+ * <dd>{@code @Qualifier SomeType is provided at\n ComponentType.method()}
+ * <dt>For component injection methods
+ * <dd>{@code SomeType is injected at\n ComponentType.method(foo)}
+ * <dt>For parameters to {@link Provides @Provides}, {@link Produces @Produces}, or {@link
+ * Inject @Inject} methods:
+ * <dd>{@code @Qualified ResolvedType is injected at\n EnclosingType.method([…, ]param[, …])}
+ * <dt>For parameters to {@link Inject @Inject} constructors:
+ * <dd>{@code @Qualified ResolvedType is injected at\n EnclosingType([…, ]param[, …])}
+ * <dt>For {@link Inject @Inject} fields:
+ * <dd>{@code @Qualified ResolvedType is injected at\n EnclosingType.field}
+ * </dl>
+ */
+public final class DependencyRequestFormatter extends Formatter<DependencyRequest> {
+
+ private final DaggerTypes types;
+
+ @Inject
+ DependencyRequestFormatter(DaggerTypes types) {
+ this.types = types;
+ }
+
+ @Override
+ public String format(DependencyRequest request) {
+ return request
+ .requestElement()
+ .map(element -> element.accept(formatVisitor, request))
+ .orElse("");
+ }
+
+ /**
+ * Appends a newline and the formatted dependency request unless {@link
+ * #format(DependencyRequest)} returns the empty string.
+ */
+ @CanIgnoreReturnValue
+ public StringBuilder appendFormatLine(
+ StringBuilder builder, DependencyRequest dependencyRequest) {
+ String formatted = format(dependencyRequest);
+ if (!formatted.isEmpty()) {
+ builder.append('\n').append(formatted);
+ }
+ return builder;
+ }
+
+ private final ElementVisitor<String, DependencyRequest> formatVisitor =
+ new ElementKindVisitor8<String, DependencyRequest>() {
+
+ @Override
+ public String visitExecutableAsMethod(ExecutableElement method, DependencyRequest request) {
+ return INDENT
+ + request.key()
+ + " is "
+ + componentMethodRequestVerb(request)
+ + " at\n"
+ + DOUBLE_INDENT
+ + elementToString(method);
+ }
+
+ @Override
+ public String visitVariable(VariableElement variable, DependencyRequest request) {
+ TypeMirror requestedType = requestType(request.kind(), request.key().type(), types);
+ return INDENT
+ + formatQualifier(request.key().qualifier())
+ + requestedType
+ + " is injected at\n"
+ + DOUBLE_INDENT
+ + elementToString(variable);
+ }
+
+ @Override
+ public String visitType(TypeElement e, DependencyRequest request) {
+ return ""; // types by themselves provide no useful information.
+ }
+
+ @Override
+ protected String defaultAction(Element element, DependencyRequest request) {
+ throw new IllegalStateException(
+ "Invalid request " + element.getKind() + " element " + element);
+ }
+ };
+
+ private String formatQualifier(Optional<AnnotationMirror> maybeQualifier) {
+ return maybeQualifier.map(qualifier -> qualifier + " ").orElse("");
+ }
+
+ /**
+ * Returns the verb for a component method dependency request. Returns "produced", "provided", or
+ * "injected", depending on the kind of request.
+ */
+ private String componentMethodRequestVerb(DependencyRequest request) {
+ switch (request.kind()) {
+ case FUTURE:
+ case PRODUCER:
+ case INSTANCE:
+ case LAZY:
+ case PROVIDER:
+ case PROVIDER_OF_LAZY:
+ return "requested";
+
+ case MEMBERS_INJECTION:
+ return "injected";
+
+ case PRODUCED:
+ break;
+ }
+ throw new AssertionError("illegal request kind for method: " + request);
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/DependencyVariableNamer.java b/java/dagger/internal/codegen/binding/DependencyVariableNamer.java
new file mode 100644
index 000000000..e01d22ef2
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/DependencyVariableNamer.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static dagger.internal.codegen.binding.SourceFiles.simpleVariableName;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.base.Ascii;
+import com.google.common.base.CaseFormat;
+import dagger.Lazy;
+import dagger.model.DependencyRequest;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.inject.Provider;
+
+/**
+ * Picks a reasonable name for what we think is being provided from the variable name associated
+ * with the {@link DependencyRequest}. I.e. strips out words like "lazy" and "provider" if we
+ * believe that those refer to {@link Lazy} and {@link Provider} rather than the type being
+ * provided.
+ */
+//TODO(gak): develop the heuristics to get better names
+final class DependencyVariableNamer {
+ private static final Pattern LAZY_PROVIDER_PATTERN = Pattern.compile("lazy(\\w+)Provider");
+
+ static String name(DependencyRequest dependency) {
+ if (!dependency.requestElement().isPresent()) {
+ return simpleVariableName(MoreTypes.asTypeElement(dependency.key().type()));
+ }
+
+ String variableName = dependency.requestElement().get().getSimpleName().toString();
+ if (Ascii.isUpperCase(variableName.charAt(0))) {
+ variableName = toLowerCamel(variableName);
+ }
+ switch (dependency.kind()) {
+ case INSTANCE:
+ return variableName;
+ case LAZY:
+ return variableName.startsWith("lazy") && !variableName.equals("lazy")
+ ? toLowerCamel(variableName.substring(4))
+ : variableName;
+ case PROVIDER_OF_LAZY:
+ Matcher matcher = LAZY_PROVIDER_PATTERN.matcher(variableName);
+ if (matcher.matches()) {
+ return toLowerCamel(matcher.group(1));
+ }
+ // fall through
+ case PROVIDER:
+ return variableName.endsWith("Provider") && !variableName.equals("Provider")
+ ? variableName.substring(0, variableName.length() - 8)
+ : variableName;
+ case PRODUCED:
+ return variableName.startsWith("produced") && !variableName.equals("produced")
+ ? toLowerCamel(variableName.substring(8))
+ : variableName;
+ case PRODUCER:
+ return variableName.endsWith("Producer") && !variableName.equals("Producer")
+ ? variableName.substring(0, variableName.length() - 8)
+ : variableName;
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ private static String toLowerCamel(String name) {
+ return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, name);
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/ErrorMessages.java b/java/dagger/internal/codegen/binding/ErrorMessages.java
new file mode 100644
index 000000000..8962ade81
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/ErrorMessages.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableMap;
+import dagger.internal.codegen.base.ComponentAnnotation;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.UnaryOperator;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+/** The collection of error messages to be reported back to users. */
+public final class ErrorMessages {
+
+ private static final UnaryOperator<String> PRODUCTION =
+ s ->
+ s.replace("component", "production component")
+ .replace("Component", "ProductionComponent");
+
+ private static final UnaryOperator<String> SUBCOMPONENT =
+ s -> s.replace("component", "subcomponent").replace("Component", "Subcomponent");
+
+ private static final UnaryOperator<String> FACTORY = s -> s.replace("Builder", "Factory");
+
+ private static final ImmutableMap<ComponentKind, Function<String, String>>
+ COMPONENT_TRANSFORMATIONS =
+ ImmutableMap.of(
+ ComponentKind.COMPONENT, UnaryOperator.identity(),
+ ComponentKind.SUBCOMPONENT, SUBCOMPONENT,
+ ComponentKind.PRODUCTION_COMPONENT, PRODUCTION,
+ ComponentKind.PRODUCTION_SUBCOMPONENT, PRODUCTION.andThen(SUBCOMPONENT));
+
+ public static ComponentMessages componentMessagesFor(ComponentKind componentKind) {
+ return new ComponentMessages(COMPONENT_TRANSFORMATIONS.get(componentKind));
+ }
+
+ public static ComponentMessages componentMessagesFor(ComponentAnnotation componentAnnotation) {
+ return new ComponentMessages(
+ transformation(componentAnnotation.isProduction(), componentAnnotation.isSubcomponent()));
+ }
+
+ public static ComponentCreatorMessages creatorMessagesFor(
+ ComponentCreatorAnnotation creatorAnnotation) {
+ Function<String, String> transformation =
+ transformation(
+ creatorAnnotation.isProductionCreatorAnnotation(),
+ creatorAnnotation.isSubcomponentCreatorAnnotation());
+ switch (creatorAnnotation.creatorKind()) {
+ case BUILDER:
+ return new BuilderMessages(transformation);
+ case FACTORY:
+ return new FactoryMessages(transformation);
+ }
+ throw new AssertionError(creatorAnnotation);
+ }
+
+ private static Function<String, String> transformation(
+ boolean isProduction, boolean isSubcomponent) {
+ Function<String, String> transformation = isProduction ? PRODUCTION : UnaryOperator.identity();
+ return isSubcomponent ? transformation.andThen(SUBCOMPONENT) : transformation;
+ }
+
+ private abstract static class Messages {
+ private final Function<String, String> transformation;
+
+ Messages(Function<String, String> transformation) {
+ this.transformation = transformation;
+ }
+
+ protected final String process(String s) {
+ return transformation.apply(s);
+ }
+ }
+
+ /** Errors for components. */
+ public static final class ComponentMessages extends Messages {
+ ComponentMessages(Function<String, String> transformation) {
+ super(transformation);
+ }
+
+ public final String moreThanOne() {
+ return process("@Component has more than one @Component.Builder or @Component.Factory: %s");
+ }
+ }
+
+ /** Errors for component creators. */
+ public abstract static class ComponentCreatorMessages extends Messages {
+ ComponentCreatorMessages(Function<String, String> transformation) {
+ super(transformation);
+ }
+
+ public static String builderMethodRequiresNoArgs() {
+ return "Methods returning a @Component.Builder must have no arguments";
+ }
+
+ public static String moreThanOneRefToSubcomponent() {
+ return "Only one method can create a given subcomponent. %s is created by: %s";
+ }
+
+ public final String invalidConstructor() {
+ return process("@Component.Builder classes must have exactly one constructor,"
+ + " and it must not be private or have any parameters");
+ }
+
+ public final String generics() {
+ return process("@Component.Builder types must not have any generic types");
+ }
+
+ public final String mustBeInComponent() {
+ return process("@Component.Builder types must be nested within a @Component");
+ }
+
+ public final String mustBeClassOrInterface() {
+ return process("@Component.Builder types must be abstract classes or interfaces");
+ }
+
+ public final String isPrivate() {
+ return process("@Component.Builder types must not be private");
+ }
+
+ public final String mustBeStatic() {
+ return process("@Component.Builder types must be static");
+ }
+
+ public final String mustBeAbstract() {
+ return process("@Component.Builder types must be abstract");
+ }
+
+ public abstract String missingFactoryMethod();
+
+ public abstract String multipleSettersForModuleOrDependencyType();
+
+ public abstract String extraSetters();
+
+ public abstract String missingSetters();
+
+ public abstract String twoFactoryMethods();
+
+ public abstract String inheritedTwoFactoryMethods();
+
+ public abstract String factoryMethodMustReturnComponentType();
+
+ public final String inheritedFactoryMethodMustReturnComponentType() {
+ return factoryMethodMustReturnComponentType() + ". Inherited method: %s";
+ }
+
+ public abstract String factoryMethodMayNotBeAnnotatedWithBindsInstance();
+
+ public final String inheritedFactoryMethodMayNotBeAnnotatedWithBindsInstance() {
+ return factoryMethodMayNotBeAnnotatedWithBindsInstance() + ". Inherited method: %s";
+ }
+
+ public final String setterMethodsMustTakeOneArg() {
+ return process("@Component.Builder methods must not have more than one argument");
+ }
+
+ public final String inheritedSetterMethodsMustTakeOneArg() {
+ return setterMethodsMustTakeOneArg() + ". Inherited method: %s";
+ }
+
+ public final String setterMethodsMustReturnVoidOrBuilder() {
+ return process("@Component.Builder setter methods must return void, the builder,"
+ + " or a supertype of the builder");
+ }
+
+ public final String inheritedSetterMethodsMustReturnVoidOrBuilder() {
+ return setterMethodsMustReturnVoidOrBuilder() + ". Inherited method: %s";
+ }
+
+ public final String methodsMayNotHaveTypeParameters() {
+ return process("@Component.Builder methods must not have type parameters");
+ }
+
+ public final String inheritedMethodsMayNotHaveTypeParameters() {
+ return methodsMayNotHaveTypeParameters() + ". Inherited method: %s";
+ }
+
+ public abstract String nonBindsInstanceParametersMayNotBePrimitives();
+
+ public final String inheritedNonBindsInstanceParametersMayNotBePrimitives() {
+ return nonBindsInstanceParametersMayNotBePrimitives() + ". Inherited method: %s";
+ }
+
+ public final String factoryMethodReturnsSupertypeWithMissingMethods(
+ TypeElement component,
+ TypeElement componentBuilder,
+ TypeMirror returnType,
+ ExecutableElement buildMethod,
+ Set<ExecutableElement> additionalMethods) {
+ return String.format(
+ "%1$s.%2$s() returns %3$s, but %4$s declares additional component method(s): %5$s. In "
+ + "order to provide type-safe access to these methods, override %2$s() to return "
+ + "%4$s",
+ componentBuilder.getQualifiedName(),
+ buildMethod.getSimpleName(),
+ returnType,
+ component.getQualifiedName(),
+ Joiner.on(", ").join(additionalMethods));
+ }
+
+ public final String bindsInstanceNotAllowedOnBothSetterMethodAndParameter() {
+ return process("@Component.Builder setter methods may not have @BindsInstance on both the "
+ + "method and its parameter; choose one or the other");
+ }
+
+ public final String inheritedBindsInstanceNotAllowedOnBothSetterMethodAndParameter() {
+ return bindsInstanceNotAllowedOnBothSetterMethodAndParameter() + ". Inherited method: %s";
+ }
+ }
+
+ private static final class BuilderMessages extends ComponentCreatorMessages {
+ BuilderMessages(Function<String, String> transformation) {
+ super(transformation);
+ }
+
+ @Override
+ public String missingFactoryMethod() {
+ return process(
+ "@Component.Builder types must have exactly one no-args method that "
+ + " returns the @Component type");
+ }
+
+ @Override
+ public String multipleSettersForModuleOrDependencyType() {
+ return process(
+ "@Component.Builder types must not have more than one setter method per module or "
+ + "dependency, but %s is set by %s");
+ }
+
+ @Override
+ public String extraSetters() {
+ return process(
+ "@Component.Builder has setters for modules or components that aren't required: %s");
+ }
+
+ @Override
+ public String missingSetters() {
+ return process(
+ "@Component.Builder is missing setters for required modules or components: %s");
+ }
+
+ @Override
+ public String twoFactoryMethods() {
+ return process(
+ "@Component.Builder types must have exactly one zero-arg method, and that"
+ + " method must return the @Component type. Already found: %s");
+ }
+
+ @Override
+ public String inheritedTwoFactoryMethods() {
+ return process(
+ "@Component.Builder types must have exactly one zero-arg method, and that"
+ + " method must return the @Component type. Found %s and %s");
+ }
+
+ @Override
+ public String factoryMethodMustReturnComponentType() {
+ return process(
+ "@Component.Builder methods that have no arguments must return the @Component type or a "
+ + "supertype of the @Component");
+ }
+
+ @Override
+ public String factoryMethodMayNotBeAnnotatedWithBindsInstance() {
+ return process(
+ "@Component.Builder no-arg build methods may not be annotated with @BindsInstance");
+ }
+
+ @Override
+ public String nonBindsInstanceParametersMayNotBePrimitives() {
+ return process(
+ "@Component.Builder methods that are not annotated with @BindsInstance "
+ + "must take either a module or a component dependency, not a primitive");
+ }
+ }
+
+ private static final class FactoryMessages extends ComponentCreatorMessages {
+ FactoryMessages(Function<String, String> transformation) {
+ super(transformation.andThen(FACTORY));
+ }
+
+ @Override
+ public String missingFactoryMethod() {
+ return process(
+ "@Component.Factory types must have exactly one method that "
+ + "returns the @Component type");
+ }
+
+ @Override
+ public String multipleSettersForModuleOrDependencyType() {
+ return process(
+ "@Component.Factory methods must not have more than one parameter per module or "
+ + "dependency, but %s is set by %s");
+ }
+
+ @Override
+ public String extraSetters() {
+ return process(
+ "@Component.Factory method has parameters for modules or components that aren't "
+ + "required: %s");
+ }
+
+ @Override
+ public String missingSetters() {
+ return process(
+ "@Component.Factory method is missing parameters for required modules or components: %s");
+ }
+
+ @Override
+ public String twoFactoryMethods() {
+ return process(
+ "@Component.Factory types must have exactly one abstract method. Already found: %s");
+ }
+
+ @Override
+ public String inheritedTwoFactoryMethods() {
+ return twoFactoryMethods();
+ }
+
+ @Override
+ public String factoryMethodMustReturnComponentType() {
+ return process(
+ "@Component.Factory abstract methods must return the @Component type or a "
+ + "supertype of the @Component");
+ }
+
+ @Override
+ public String factoryMethodMayNotBeAnnotatedWithBindsInstance() {
+ return process("@Component.Factory method may not be annotated with @BindsInstance");
+ }
+
+ @Override
+ public String nonBindsInstanceParametersMayNotBePrimitives() {
+ return process(
+ "@Component.Factory method parameters that are not annotated with @BindsInstance "
+ + "must be either a module or a component dependency, not a primitive");
+ }
+ }
+
+ private ErrorMessages() {}
+}
diff --git a/java/dagger/internal/codegen/binding/FrameworkField.java b/java/dagger/internal/codegen/binding/FrameworkField.java
new file mode 100644
index 000000000..3b0b73fbe
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/FrameworkField.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static dagger.model.BindingKind.MEMBERS_INJECTOR;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.CaseFormat;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import java.util.Optional;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementKindVisitor8;
+
+/**
+ * A value object that represents a field in the generated Component class.
+ *
+ * <p>Examples:
+ *
+ * <ul>
+ * <li>{@code Provider<String>}
+ * <li>{@code Producer<Widget>}
+ * <li>{@code Provider<Map<SomeMapKey, MapValue>>}.
+ * </ul>
+ */
+@AutoValue
+public abstract class FrameworkField {
+
+ /**
+ * Creates a framework field.
+ *
+ * @param frameworkClassName the name of the framework class (e.g., {@link javax.inject.Provider})
+ * @param valueTypeName the name of the type parameter of the framework class (e.g., {@code Foo}
+ * for {@code Provider<Foo>}
+ * @param fieldName the name of the field
+ */
+ public static FrameworkField create(
+ ClassName frameworkClassName, TypeName valueTypeName, String fieldName) {
+ String suffix = frameworkClassName.simpleName();
+ return new AutoValue_FrameworkField(
+ ParameterizedTypeName.get(frameworkClassName, valueTypeName),
+ fieldName.endsWith(suffix) ? fieldName : fieldName + suffix);
+ }
+
+ /**
+ * A framework field for a {@link ContributionBinding}.
+ *
+ * @param frameworkClass if present, the field will use this framework class instead of the normal
+ * one for the binding's type.
+ */
+ public static FrameworkField forBinding(
+ ContributionBinding binding, Optional<ClassName> frameworkClass) {
+ return create(
+ frameworkClass.orElse(
+ ClassName.get(
+ FrameworkType.forBindingType(binding.bindingType()).frameworkClass())),
+ TypeName.get(fieldValueType(binding)),
+ frameworkFieldName(binding));
+ }
+
+ private static TypeMirror fieldValueType(ContributionBinding binding) {
+ return binding.contributionType().isMultibinding()
+ ? binding.contributedType()
+ : binding.key().type();
+ }
+
+ private static String frameworkFieldName(ContributionBinding binding) {
+ if (binding.bindingElement().isPresent()) {
+ String name = BINDING_ELEMENT_NAME.visit(binding.bindingElement().get(), binding);
+ return binding.kind().equals(MEMBERS_INJECTOR) ? name + "MembersInjector" : name;
+ }
+ return KeyVariableNamer.name(binding.key());
+ }
+
+ private static final ElementVisitor<String, Binding> BINDING_ELEMENT_NAME =
+ new ElementKindVisitor8<String, Binding>() {
+
+ @Override
+ protected String defaultAction(Element e, Binding p) {
+ throw new IllegalArgumentException("Unexpected binding " + p);
+ }
+
+ @Override
+ public String visitExecutableAsConstructor(ExecutableElement e, Binding p) {
+ return visit(e.getEnclosingElement(), p);
+ }
+
+ @Override
+ public String visitExecutableAsMethod(ExecutableElement e, Binding p) {
+ return e.getSimpleName().toString();
+ }
+
+ @Override
+ public String visitType(TypeElement e, Binding p) {
+ return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, e.getSimpleName().toString());
+ }
+
+ @Override
+ public String visitVariableAsParameter(VariableElement e, Binding p) {
+ return e.getSimpleName().toString();
+ }
+ };
+
+ public abstract ParameterizedTypeName type();
+
+ public abstract String name();
+}
diff --git a/java/dagger/internal/codegen/binding/FrameworkType.java b/java/dagger/internal/codegen/binding/FrameworkType.java
new file mode 100644
index 000000000..6b160b613
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/FrameworkType.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
+import static dagger.model.RequestKind.INSTANCE;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import dagger.Lazy;
+import dagger.internal.DoubleCheck;
+import dagger.internal.ProviderOfLazy;
+import dagger.internal.codegen.base.RequestKinds;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import dagger.model.RequestKind;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import dagger.producers.internal.Producers;
+import java.util.Optional;
+import javax.inject.Provider;
+import javax.lang.model.type.TypeMirror;
+
+/** One of the core types initialized as fields in a generated component. */
+public enum FrameworkType {
+ /** A {@link Provider}. */
+ PROVIDER {
+ @Override
+ public Class<?> frameworkClass() {
+ return Provider.class;
+ }
+
+ @Override
+ public Optional<RequestKind> requestKind() {
+ return Optional.of(RequestKind.PROVIDER);
+ }
+
+ @Override
+ public CodeBlock to(RequestKind requestKind, CodeBlock from) {
+ switch (requestKind) {
+ case INSTANCE:
+ return CodeBlock.of("$L.get()", from);
+
+ case LAZY:
+ return CodeBlock.of("$T.lazy($L)", DoubleCheck.class, from);
+
+ case PROVIDER:
+ return from;
+
+ case PROVIDER_OF_LAZY:
+ return CodeBlock.of("$T.create($L)", ProviderOfLazy.class, from);
+
+ case PRODUCER:
+ return CodeBlock.of("$T.producerFromProvider($L)", Producers.class, from);
+
+ case FUTURE:
+ return CodeBlock.of("$T.immediateFuture($L)", Futures.class, to(INSTANCE, from));
+
+ case PRODUCED:
+ return CodeBlock.of("$T.successful($L)", Produced.class, to(INSTANCE, from));
+
+ default:
+ throw new IllegalArgumentException(
+ String.format("Cannot request a %s from a %s", requestKind, this));
+ }
+ }
+
+ @Override
+ public Expression to(RequestKind requestKind, Expression from, DaggerTypes types) {
+ CodeBlock codeBlock = to(requestKind, from.codeBlock());
+ switch (requestKind) {
+ case INSTANCE:
+ return Expression.create(types.unwrapTypeOrObject(from.type()), codeBlock);
+
+ case PROVIDER:
+ return from;
+
+ case PROVIDER_OF_LAZY:
+ TypeMirror lazyType = types.rewrapType(from.type(), Lazy.class);
+ return Expression.create(types.wrapType(lazyType, Provider.class), codeBlock);
+
+ case FUTURE:
+ return Expression.create(
+ types.rewrapType(from.type(), ListenableFuture.class), codeBlock);
+
+ default:
+ return Expression.create(
+ types.rewrapType(from.type(), RequestKinds.frameworkClass(requestKind)), codeBlock);
+ }
+ }
+ },
+
+ /** A {@link Producer}. */
+ PRODUCER_NODE {
+ @Override
+ public Class<?> frameworkClass() {
+ // TODO(cgdecker): Replace this with new class for representing internal producer nodes.
+ // Currently the new class is CancellableProducer, but it may be changed to ProducerNode and
+ // made to not implement Producer.
+ return Producer.class;
+ }
+
+ @Override
+ public Optional<RequestKind> requestKind() {
+ return Optional.empty();
+ }
+
+ @Override
+ public CodeBlock to(RequestKind requestKind, CodeBlock from) {
+ switch (requestKind) {
+ case FUTURE:
+ return CodeBlock.of("$L.get()", from);
+
+ case PRODUCER:
+ return from;
+
+ default:
+ throw new IllegalArgumentException(
+ String.format("Cannot request a %s from a %s", requestKind, this));
+ }
+ }
+
+ @Override
+ public Expression to(RequestKind requestKind, Expression from, DaggerTypes types) {
+ switch (requestKind) {
+ case FUTURE:
+ return Expression.create(
+ types.rewrapType(from.type(), ListenableFuture.class),
+ to(requestKind, from.codeBlock()));
+
+ case PRODUCER:
+ return Expression.create(from.type(), to(requestKind, from.codeBlock()));
+
+ default:
+ throw new IllegalArgumentException(
+ String.format("Cannot request a %s from a %s", requestKind, this));
+ }
+ }
+ },
+ ;
+
+ /** Returns the framework type appropriate for fields for a given binding type. */
+ public static FrameworkType forBindingType(BindingType bindingType) {
+ switch (bindingType) {
+ case PROVISION:
+ return PROVIDER;
+ case PRODUCTION:
+ return PRODUCER_NODE;
+ case MEMBERS_INJECTION:
+ }
+ throw new AssertionError(bindingType);
+ }
+
+ /** Returns the framework type that exactly matches the given request kind, if one exists. */
+ public static Optional<FrameworkType> forRequestKind(RequestKind requestKind) {
+ switch (requestKind) {
+ case PROVIDER:
+ return Optional.of(FrameworkType.PROVIDER);
+ default:
+ return Optional.empty();
+ }
+ }
+
+ /** The class of fields of this type. */
+ public abstract Class<?> frameworkClass();
+
+ /** Returns the {@link #frameworkClass()} parameterized with a type. */
+ public ParameterizedTypeName frameworkClassOf(TypeName valueType) {
+ return ParameterizedTypeName.get(ClassName.get(frameworkClass()), valueType);
+ }
+
+ /** The request kind that an instance of this framework type can satisfy directly, if any. */
+ public abstract Optional<RequestKind> requestKind();
+
+ /**
+ * Returns a {@link CodeBlock} that evaluates to a requested object given an expression that
+ * evaluates to an instance of this framework type.
+ *
+ * @param requestKind the kind of {@link DependencyRequest} that the returned expression can
+ * satisfy
+ * @param from a {@link CodeBlock} that evaluates to an instance of this framework type
+ * @throws IllegalArgumentException if a valid expression cannot be generated for {@code
+ * requestKind}
+ */
+ public abstract CodeBlock to(RequestKind requestKind, CodeBlock from);
+
+ /**
+ * Returns an {@link Expression} that evaluates to a requested object given an expression that
+ * evaluates to an instance of this framework type.
+ *
+ * @param requestKind the kind of {@link DependencyRequest} that the returned expression can
+ * satisfy
+ * @param from an expression that evaluates to an instance of this framework type
+ * @throws IllegalArgumentException if a valid expression cannot be generated for {@code
+ * requestKind}
+ */
+ public abstract Expression to(RequestKind requestKind, Expression from, DaggerTypes types);
+
+ @Override
+ public String toString() {
+ return UPPER_UNDERSCORE.to(UPPER_CAMEL, super.toString());
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/FrameworkTypeMapper.java b/java/dagger/internal/codegen/binding/FrameworkTypeMapper.java
new file mode 100644
index 000000000..85463aff8
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/FrameworkTypeMapper.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static dagger.internal.codegen.binding.BindingType.PRODUCTION;
+
+import dagger.model.RequestKind;
+import dagger.producers.Producer;
+import javax.inject.Provider;
+
+/**
+ * A mapper for associating a {@link RequestKind} to a {@link FrameworkType}, dependent on the type
+ * of code to be generated (e.g., for {@link Provider} or {@link Producer}).
+ */
+public enum FrameworkTypeMapper {
+ FOR_PROVIDER() {
+ @Override
+ public FrameworkType getFrameworkType(RequestKind requestKind) {
+ switch (requestKind) {
+ case INSTANCE:
+ case PROVIDER:
+ case PROVIDER_OF_LAZY:
+ case LAZY:
+ return FrameworkType.PROVIDER;
+ case PRODUCED:
+ case PRODUCER:
+ throw new IllegalArgumentException(requestKind.toString());
+ default:
+ throw new AssertionError(requestKind);
+ }
+ }
+ },
+ FOR_PRODUCER() {
+ @Override
+ public FrameworkType getFrameworkType(RequestKind requestKind) {
+ switch (requestKind) {
+ case INSTANCE:
+ case PRODUCED:
+ case PRODUCER:
+ return FrameworkType.PRODUCER_NODE;
+ case PROVIDER:
+ case PROVIDER_OF_LAZY:
+ case LAZY:
+ return FrameworkType.PROVIDER;
+ default:
+ throw new AssertionError(requestKind);
+ }
+ }
+ };
+
+ public static FrameworkTypeMapper forBindingType(BindingType bindingType) {
+ return bindingType.equals(PRODUCTION) ? FOR_PRODUCER : FOR_PROVIDER;
+ }
+
+ public abstract FrameworkType getFrameworkType(RequestKind requestKind);
+}
diff --git a/java/dagger/internal/codegen/binding/InjectBindingRegistry.java b/java/dagger/internal/codegen/binding/InjectBindingRegistry.java
new file mode 100644
index 000000000..5203130f4
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/InjectBindingRegistry.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import dagger.Component;
+import dagger.Provides;
+import dagger.internal.codegen.base.SourceFileGenerationException;
+import dagger.internal.codegen.base.SourceFileGenerator;
+import dagger.model.Key;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * Maintains the collection of provision bindings from {@link Inject} constructors and members
+ * injection bindings from {@link Inject} fields and methods known to the annotation processor. Note
+ * that this registry <b>does not</b> handle any explicit bindings (those from {@link Provides}
+ * methods, {@link Component} dependencies, etc.).
+ */
+public interface InjectBindingRegistry {
+ /**
+ * Returns a {@link ProvisionBinding} for {@code key}. If none has been registered yet, registers
+ * one.
+ */
+ Optional<ProvisionBinding> getOrFindProvisionBinding(Key key);
+
+ /**
+ * Returns a {@link MembersInjectionBinding} for {@code key}. If none has been registered yet,
+ * registers one, along with all necessary members injection bindings for superclasses.
+ */
+ Optional<MembersInjectionBinding> getOrFindMembersInjectionBinding(Key key);
+
+ /**
+ * Returns a {@link ProvisionBinding} for a {@link dagger.MembersInjector} of {@code key}. If none
+ * has been registered yet, registers one.
+ */
+ Optional<ProvisionBinding> getOrFindMembersInjectorProvisionBinding(Key key);
+
+ @CanIgnoreReturnValue
+ Optional<ProvisionBinding> tryRegisterConstructor(ExecutableElement constructorElement);
+
+ @CanIgnoreReturnValue
+ Optional<MembersInjectionBinding> tryRegisterMembersInjectedType(TypeElement typeElement);
+
+ /**
+ * This method ensures that sources for all registered {@link Binding bindings} (either explicitly
+ * or implicitly via {@link #getOrFindMembersInjectionBinding} or {@link
+ * #getOrFindProvisionBinding}) are generated.
+ */
+ void generateSourcesForRequiredBindings(
+ SourceFileGenerator<ProvisionBinding> factoryGenerator,
+ SourceFileGenerator<MembersInjectionBinding> membersInjectorGenerator)
+ throws SourceFileGenerationException;
+}
diff --git a/java/dagger/internal/codegen/binding/InjectionAnnotations.java b/java/dagger/internal/codegen/binding/InjectionAnnotations.java
new file mode 100644
index 000000000..748755e08
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/InjectionAnnotations.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.auto.common.MoreElements.asType;
+import static com.google.auto.common.MoreElements.asVariable;
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.base.MoreAnnotationValues.getStringValue;
+import static dagger.internal.codegen.binding.SourceFiles.memberInjectedFieldSignatureForVariable;
+import static dagger.internal.codegen.binding.SourceFiles.membersInjectorNameForType;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
+import static javax.lang.model.element.Modifier.STATIC;
+import static javax.lang.model.util.ElementFilter.constructorsIn;
+
+import com.google.auto.common.AnnotationMirrors;
+import com.google.auto.common.SuperficialValidation;
+import com.google.common.base.Equivalence;
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableSet;
+import dagger.internal.InjectedFieldSignature;
+import dagger.internal.codegen.extension.DaggerCollectors;
+import dagger.internal.codegen.extension.DaggerStreams;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import java.util.Optional;
+import java.util.stream.Stream;
+import javax.inject.Inject;
+import javax.inject.Qualifier;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.util.ElementFilter;
+
+/** Utilities relating to annotations defined in the {@code javax.inject} package. */
+public final class InjectionAnnotations {
+
+ private static final Equivalence<AnnotationMirror> EQUIVALENCE = AnnotationMirrors.equivalence();
+
+ private final DaggerElements elements;
+ private final KotlinMetadataUtil kotlinMetadataUtil;
+
+ @Inject
+ InjectionAnnotations(DaggerElements elements, KotlinMetadataUtil kotlinMetadataUtil) {
+ this.elements = elements;
+ this.kotlinMetadataUtil = kotlinMetadataUtil;
+ }
+
+ public Optional<AnnotationMirror> getQualifier(Element e) {
+ if (!SuperficialValidation.validateElement(e)) {
+ throw new TypeNotPresentException(e.toString(), null);
+ }
+ checkNotNull(e);
+ ImmutableCollection<? extends AnnotationMirror> qualifierAnnotations = getQualifiers(e);
+ switch (qualifierAnnotations.size()) {
+ case 0:
+ return Optional.empty();
+ case 1:
+ return Optional.<AnnotationMirror>of(qualifierAnnotations.iterator().next());
+ default:
+ throw new IllegalArgumentException(
+ e + " was annotated with more than one @Qualifier annotation");
+ }
+ }
+
+ public ImmutableCollection<? extends AnnotationMirror> getQualifiers(Element element) {
+ ImmutableSet<? extends AnnotationMirror> qualifiers =
+ AnnotationMirrors.getAnnotatedAnnotations(element, Qualifier.class);
+ if (element.getKind() == ElementKind.FIELD
+ // static injected fields are not supported, no need to get qualifier from kotlin metadata
+ && !element.getModifiers().contains(STATIC)
+ && isAnnotationPresent(element, Inject.class)
+ && kotlinMetadataUtil.hasMetadata(element)) {
+ return Stream.concat(
+ qualifiers.stream(), getQualifiersForKotlinProperty(asVariable(element)).stream())
+ .map(EQUIVALENCE::wrap) // Wrap in equivalence to deduplicate
+ .distinct()
+ .map(Wrapper::get)
+ .collect(DaggerStreams.toImmutableList());
+ } else {
+ return qualifiers.asList();
+ }
+ }
+
+ /** Returns the constructors in {@code type} that are annotated with {@link Inject}. */
+ public static ImmutableSet<ExecutableElement> injectedConstructors(TypeElement type) {
+ return FluentIterable.from(constructorsIn(type.getEnclosedElements()))
+ .filter(constructor -> isAnnotationPresent(constructor, Inject.class))
+ .toSet();
+ }
+
+ /**
+ * Gets the qualifiers annotation of a Kotlin Property. Finding these annotations involve finding
+ * the synthetic method for annotations as described by the Kotlin metadata or finding the
+ * corresponding MembersInjector method for the field, which also contains the qualifier
+ * annotation.
+ */
+ private ImmutableCollection<? extends AnnotationMirror> getQualifiersForKotlinProperty(
+ VariableElement fieldElement) {
+ // TODO(bcorso): Consider moving this to KotlinMetadataUtil
+ if (kotlinMetadataUtil.isMissingSyntheticPropertyForAnnotations(fieldElement)) {
+ // If we detect that the synthetic method for annotations is missing, possibly due to the
+ // element being from a compiled class, then find the MembersInjector that was generated
+ // for the enclosing class and extract the qualifier information from it.
+ TypeElement membersInjector =
+ elements.getTypeElement(
+ membersInjectorNameForType(asType(fieldElement.getEnclosingElement())));
+ if (membersInjector != null) {
+ String memberInjectedFieldSignature = memberInjectedFieldSignatureForVariable(fieldElement);
+ // TODO(danysantiago): We have to iterate over all the injection methods for every qualifier
+ // look up. Making this N^2 when looking through all the injected fields. :(
+ return ElementFilter.methodsIn(membersInjector.getEnclosedElements()).stream()
+ .filter(
+ method ->
+ getAnnotationMirror(method, InjectedFieldSignature.class)
+ .map(annotation -> getStringValue(annotation, "value"))
+ .map(memberInjectedFieldSignature::equals)
+ // If a method is not an @InjectedFieldSignature method then filter it out
+ .orElse(false))
+ .collect(DaggerCollectors.toOptional())
+ .map(this::getQualifiers)
+ .orElseThrow(
+ () ->
+ new IllegalStateException(
+ String.format(
+ "No matching InjectedFieldSignature for %1$s. This likely means that "
+ + "%1$s was compiled with an older, incompatible version of "
+ + "Dagger. Please update all Dagger dependencies to the same "
+ + "version.",
+ memberInjectedFieldSignature)));
+ } else {
+ throw new IllegalStateException(
+ "No MembersInjector found for " + fieldElement.getEnclosingElement());
+ }
+ } else {
+ return kotlinMetadataUtil.getSyntheticPropertyAnnotations(fieldElement, Qualifier.class);
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/InjectionSiteFactory.java b/java/dagger/internal/codegen/binding/InjectionSiteFactory.java
new file mode 100644
index 000000000..7a6f8d2f5
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/InjectionSiteFactory.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static dagger.internal.codegen.langmodel.DaggerElements.DECLARATION_ORDER;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.SetMultimap;
+import dagger.internal.codegen.binding.MembersInjectionBinding.InjectionSite;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementKindVisitor8;
+
+/** A factory for {@link Binding} objects. */
+final class InjectionSiteFactory {
+
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+ private final DependencyRequestFactory dependencyRequestFactory;
+
+ @Inject
+ InjectionSiteFactory(
+ DaggerTypes types,
+ DaggerElements elements,
+ DependencyRequestFactory dependencyRequestFactory) {
+ this.types = types;
+ this.elements = elements;
+ this.dependencyRequestFactory = dependencyRequestFactory;
+ }
+
+ /** Returns the injection sites for a type. */
+ ImmutableSortedSet<InjectionSite> getInjectionSites(DeclaredType declaredType) {
+ Set<InjectionSite> injectionSites = new HashSet<>();
+ List<TypeElement> ancestors = new ArrayList<>();
+ InjectionSiteVisitor injectionSiteVisitor = new InjectionSiteVisitor();
+ for (Optional<DeclaredType> currentType = Optional.of(declaredType);
+ currentType.isPresent();
+ currentType = types.nonObjectSuperclass(currentType.get())) {
+ DeclaredType type = currentType.get();
+ ancestors.add(MoreElements.asType(type.asElement()));
+ for (Element enclosedElement : type.asElement().getEnclosedElements()) {
+ injectionSiteVisitor.visit(enclosedElement, type).ifPresent(injectionSites::add);
+ }
+ }
+ return ImmutableSortedSet.copyOf(
+ // supertypes before subtypes
+ Comparator.comparing(
+ (InjectionSite injectionSite) ->
+ ancestors.indexOf(injectionSite.element().getEnclosingElement()))
+ .reversed()
+ // fields before methods
+ .thenComparing(injectionSite -> injectionSite.element().getKind())
+ // then sort by whichever element comes first in the parent
+ // this isn't necessary, but makes the processor nice and predictable
+ .thenComparing(InjectionSite::element, DECLARATION_ORDER),
+ injectionSites);
+ }
+
+ private final class InjectionSiteVisitor
+ extends ElementKindVisitor8<Optional<InjectionSite>, DeclaredType> {
+ private final SetMultimap<String, ExecutableElement> subclassMethodMap =
+ LinkedHashMultimap.create();
+
+ InjectionSiteVisitor() {
+ super(Optional.empty());
+ }
+
+ @Override
+ public Optional<InjectionSite> visitExecutableAsMethod(
+ ExecutableElement method, DeclaredType type) {
+ subclassMethodMap.put(method.getSimpleName().toString(), method);
+ if (!shouldBeInjected(method)) {
+ return Optional.empty();
+ }
+ // This visitor assumes that subclass methods are visited before superclass methods, so we can
+ // skip any overridden method that has already been visited. To decrease the number of methods
+ // that are checked, we store the already injected methods in a SetMultimap and only check the
+ // methods with the same name.
+ String methodName = method.getSimpleName().toString();
+ TypeElement enclosingType = MoreElements.asType(method.getEnclosingElement());
+ for (ExecutableElement subclassMethod : subclassMethodMap.get(methodName)) {
+ if (method != subclassMethod && elements.overrides(subclassMethod, method, enclosingType)) {
+ return Optional.empty();
+ }
+ }
+ ExecutableType resolved = MoreTypes.asExecutable(types.asMemberOf(type, method));
+ return Optional.of(
+ InjectionSite.method(
+ method,
+ dependencyRequestFactory.forRequiredResolvedVariables(
+ method.getParameters(), resolved.getParameterTypes())));
+ }
+
+ @Override
+ public Optional<InjectionSite> visitVariableAsField(
+ VariableElement field, DeclaredType type) {
+ if (!shouldBeInjected(field)) {
+ return Optional.empty();
+ }
+ TypeMirror resolved = types.asMemberOf(type, field);
+ return Optional.of(
+ InjectionSite.field(
+ field, dependencyRequestFactory.forRequiredResolvedVariable(field, resolved)));
+ }
+
+ private boolean shouldBeInjected(Element injectionSite) {
+ return isAnnotationPresent(injectionSite, Inject.class)
+ && !injectionSite.getModifiers().contains(PRIVATE)
+ && !injectionSite.getModifiers().contains(STATIC);
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/KeyFactory.java b/java/dagger/internal/codegen/binding/KeyFactory.java
new file mode 100644
index 000000000..d9236665d
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/KeyFactory.java
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.auto.common.MoreTypes.asExecutable;
+import static com.google.auto.common.MoreTypes.isType;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.base.RequestKinds.extractKeyType;
+import static dagger.internal.codegen.binding.MapKeys.getMapKey;
+import static dagger.internal.codegen.binding.MapKeys.mapKeyType;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.extension.Optionals.firstPresent;
+import static dagger.internal.codegen.langmodel.DaggerTypes.isFutureType;
+import static dagger.internal.codegen.langmodel.DaggerTypes.unwrapType;
+import static java.util.Arrays.asList;
+import static javax.lang.model.element.ElementKind.METHOD;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSet;
+import dagger.Binds;
+import dagger.BindsOptionalOf;
+import dagger.internal.codegen.base.ContributionType;
+import dagger.internal.codegen.base.FrameworkTypes;
+import dagger.internal.codegen.base.MapType;
+import dagger.internal.codegen.base.OptionalType;
+import dagger.internal.codegen.base.RequestKinds;
+import dagger.internal.codegen.base.SetType;
+import dagger.internal.codegen.base.SimpleAnnotationMirror;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Key;
+import dagger.model.Key.MultibindingContributionIdentifier;
+import dagger.model.RequestKind;
+import dagger.multibindings.Multibinds;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import dagger.producers.Production;
+import dagger.producers.internal.ProductionImplementation;
+import dagger.producers.monitoring.ProductionComponentMonitor;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.stream.Stream;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeMirror;
+
+/** A factory for {@link Key}s. */
+public final class KeyFactory {
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+ private final InjectionAnnotations injectionAnnotations;
+
+ @Inject
+ KeyFactory(
+ DaggerTypes types, DaggerElements elements, InjectionAnnotations injectionAnnotations) {
+ this.types = checkNotNull(types);
+ this.elements = checkNotNull(elements);
+ this.injectionAnnotations = injectionAnnotations;
+ }
+
+ private TypeMirror boxPrimitives(TypeMirror type) {
+ return type.getKind().isPrimitive() ? types.boxedClass((PrimitiveType) type).asType() : type;
+ }
+
+ private DeclaredType setOf(TypeMirror elementType) {
+ return types.getDeclaredType(elements.getTypeElement(Set.class), boxPrimitives(elementType));
+ }
+
+ private DeclaredType mapOf(TypeMirror keyType, TypeMirror valueType) {
+ return types.getDeclaredType(
+ elements.getTypeElement(Map.class), boxPrimitives(keyType), boxPrimitives(valueType));
+ }
+
+ /** Returns {@code Map<KeyType, FrameworkType<ValueType>>}. */
+ private TypeMirror mapOfFrameworkType(
+ TypeMirror keyType, TypeElement frameworkType, TypeMirror valueType) {
+ return mapOf(keyType, types.getDeclaredType(frameworkType, boxPrimitives(valueType)));
+ }
+
+ Key forComponentMethod(ExecutableElement componentMethod) {
+ checkArgument(componentMethod.getKind().equals(METHOD));
+ return forMethod(componentMethod, componentMethod.getReturnType());
+ }
+
+ Key forProductionComponentMethod(ExecutableElement componentMethod) {
+ checkArgument(componentMethod.getKind().equals(METHOD));
+ TypeMirror returnType = componentMethod.getReturnType();
+ TypeMirror keyType =
+ isFutureType(returnType)
+ ? getOnlyElement(MoreTypes.asDeclared(returnType).getTypeArguments())
+ : returnType;
+ return forMethod(componentMethod, keyType);
+ }
+
+ Key forSubcomponentCreatorMethod(
+ ExecutableElement subcomponentCreatorMethod, DeclaredType declaredContainer) {
+ checkArgument(subcomponentCreatorMethod.getKind().equals(METHOD));
+ ExecutableType resolvedMethod =
+ asExecutable(types.asMemberOf(declaredContainer, subcomponentCreatorMethod));
+ return Key.builder(resolvedMethod.getReturnType()).build();
+ }
+
+ public Key forSubcomponentCreator(TypeMirror creatorType) {
+ return Key.builder(creatorType).build();
+ }
+
+ public Key forProvidesMethod(ExecutableElement method, TypeElement contributingModule) {
+ return forBindingMethod(
+ method, contributingModule, Optional.of(elements.getTypeElement(Provider.class)));
+ }
+
+ public Key forProducesMethod(ExecutableElement method, TypeElement contributingModule) {
+ return forBindingMethod(
+ method, contributingModule, Optional.of(elements.getTypeElement(Producer.class)));
+ }
+
+ /** Returns the key bound by a {@link Binds} method. */
+ Key forBindsMethod(ExecutableElement method, TypeElement contributingModule) {
+ checkArgument(isAnnotationPresent(method, Binds.class));
+ return forBindingMethod(method, contributingModule, Optional.empty());
+ }
+
+ /** Returns the base key bound by a {@link BindsOptionalOf} method. */
+ Key forBindsOptionalOfMethod(ExecutableElement method, TypeElement contributingModule) {
+ checkArgument(isAnnotationPresent(method, BindsOptionalOf.class));
+ return forBindingMethod(method, contributingModule, Optional.empty());
+ }
+
+ private Key forBindingMethod(
+ ExecutableElement method,
+ TypeElement contributingModule,
+ Optional<TypeElement> frameworkType) {
+ checkArgument(method.getKind().equals(METHOD));
+ ExecutableType methodType =
+ MoreTypes.asExecutable(
+ types.asMemberOf(MoreTypes.asDeclared(contributingModule.asType()), method));
+ ContributionType contributionType = ContributionType.fromBindingElement(method);
+ TypeMirror returnType = methodType.getReturnType();
+ if (frameworkType.isPresent()
+ && frameworkType.get().equals(elements.getTypeElement(Producer.class))
+ && isType(returnType)) {
+ if (isFutureType(methodType.getReturnType())) {
+ returnType = getOnlyElement(MoreTypes.asDeclared(returnType).getTypeArguments());
+ } else if (contributionType.equals(ContributionType.SET_VALUES)
+ && SetType.isSet(returnType)) {
+ SetType setType = SetType.from(returnType);
+ if (isFutureType(setType.elementType())) {
+ returnType =
+ types.getDeclaredType(
+ elements.getTypeElement(Set.class), unwrapType(setType.elementType()));
+ }
+ }
+ }
+ TypeMirror keyType = bindingMethodKeyType(returnType, method, contributionType, frameworkType);
+ Key key = forMethod(method, keyType);
+ return contributionType.equals(ContributionType.UNIQUE)
+ ? key
+ : key.toBuilder()
+ .multibindingContributionIdentifier(
+ new MultibindingContributionIdentifier(method, contributingModule))
+ .build();
+ }
+
+ /**
+ * Returns the key for a {@link Multibinds @Multibinds} method.
+ *
+ * <p>The key's type is either {@code Set<T>} or {@code Map<K, Provider<V>>}. The latter works
+ * even for maps used by {@code Producer}s.
+ */
+ Key forMultibindsMethod(ExecutableType executableType, ExecutableElement method) {
+ checkArgument(method.getKind().equals(METHOD), "%s must be a method", method);
+ TypeMirror returnType = executableType.getReturnType();
+ TypeMirror keyType =
+ MapType.isMap(returnType)
+ ? mapOfFrameworkType(
+ MapType.from(returnType).keyType(),
+ elements.getTypeElement(Provider.class),
+ MapType.from(returnType).valueType())
+ : returnType;
+ return forMethod(method, keyType);
+ }
+
+ private TypeMirror bindingMethodKeyType(
+ TypeMirror returnType,
+ ExecutableElement method,
+ ContributionType contributionType,
+ Optional<TypeElement> frameworkType) {
+ switch (contributionType) {
+ case UNIQUE:
+ return returnType;
+ case SET:
+ return setOf(returnType);
+ case MAP:
+ TypeMirror mapKeyType = mapKeyType(getMapKey(method).get(), types);
+ return frameworkType.isPresent()
+ ? mapOfFrameworkType(mapKeyType, frameworkType.get(), returnType)
+ : mapOf(mapKeyType, returnType);
+ case SET_VALUES:
+ // TODO(gak): do we want to allow people to use "covariant return" here?
+ checkArgument(SetType.isSet(returnType));
+ return returnType;
+ }
+ throw new AssertionError();
+ }
+
+ /**
+ * Returns the key for a binding associated with a {@link DelegateDeclaration}.
+ *
+ * <p>If {@code delegateDeclaration} is {@code @IntoMap}, transforms the {@code Map<K, V>} key
+ * from {@link DelegateDeclaration#key()} to {@code Map<K, FrameworkType<V>>}. If {@code
+ * delegateDeclaration} is not a map contribution, its key is returned.
+ */
+ Key forDelegateBinding(DelegateDeclaration delegateDeclaration, Class<?> frameworkType) {
+ return delegateDeclaration.contributionType().equals(ContributionType.MAP)
+ ? wrapMapValue(delegateDeclaration.key(), frameworkType)
+ : delegateDeclaration.key();
+ }
+
+ private Key forMethod(ExecutableElement method, TypeMirror keyType) {
+ return forQualifiedType(injectionAnnotations.getQualifier(method), keyType);
+ }
+
+ public Key forInjectConstructorWithResolvedType(TypeMirror type) {
+ return Key.builder(type).build();
+ }
+
+ // TODO(ronshapiro): Remove these conveniences which are simple wrappers around Key.Builder
+ Key forType(TypeMirror type) {
+ return Key.builder(type).build();
+ }
+
+ public Key forMembersInjectedType(TypeMirror type) {
+ return Key.builder(type).build();
+ }
+
+ Key forQualifiedType(Optional<AnnotationMirror> qualifier, TypeMirror type) {
+ return Key.builder(boxPrimitives(type)).qualifier(qualifier).build();
+ }
+
+ public Key forProductionExecutor() {
+ return Key.builder(elements.getTypeElement(Executor.class).asType())
+ .qualifier(SimpleAnnotationMirror.of(elements.getTypeElement(Production.class)))
+ .build();
+ }
+
+ public Key forProductionImplementationExecutor() {
+ return Key.builder(elements.getTypeElement(Executor.class).asType())
+ .qualifier(SimpleAnnotationMirror.of(elements.getTypeElement(ProductionImplementation.class)))
+ .build();
+ }
+
+ public Key forProductionComponentMonitor() {
+ return Key.builder(elements.getTypeElement(ProductionComponentMonitor.class).asType()).build();
+ }
+
+ /**
+ * If {@code requestKey} is for a {@code Map<K, V>} or {@code Map<K, Produced<V>>}, returns keys
+ * for {@code Map<K, Provider<V>>} and {@code Map<K, Producer<V>>} (if Dagger-Producers is on
+ * the classpath).
+ */
+ ImmutableSet<Key> implicitFrameworkMapKeys(Key requestKey) {
+ return Stream.of(implicitMapProviderKeyFrom(requestKey), implicitMapProducerKeyFrom(requestKey))
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * Optionally extract a {@link Key} for the underlying provision binding(s) if such a valid key
+ * can be inferred from the given key. Specifically, if the key represents a {@link Map}{@code
+ * <K, V>} or {@code Map<K, Producer<V>>}, a key of {@code Map<K, Provider<V>>} will be
+ * returned.
+ */
+ Optional<Key> implicitMapProviderKeyFrom(Key possibleMapKey) {
+ return firstPresent(
+ rewrapMapKey(possibleMapKey, Produced.class, Provider.class),
+ wrapMapKey(possibleMapKey, Provider.class));
+ }
+
+ /**
+ * Optionally extract a {@link Key} for the underlying production binding(s) if such a
+ * valid key can be inferred from the given key. Specifically, if the key represents a
+ * {@link Map}{@code <K, V>} or {@code Map<K, Produced<V>>}, a key of
+ * {@code Map<K, Producer<V>>} will be returned.
+ */
+ Optional<Key> implicitMapProducerKeyFrom(Key possibleMapKey) {
+ return firstPresent(
+ rewrapMapKey(possibleMapKey, Produced.class, Producer.class),
+ wrapMapKey(possibleMapKey, Producer.class));
+ }
+
+ /**
+ * If {@code key}'s type is {@code Map<K, Provider<V>>}, {@code Map<K, Producer<V>>}, or {@code
+ * Map<K, Produced<V>>}, returns a key with the same qualifier and {@link
+ * Key#multibindingContributionIdentifier()} whose type is simply {@code Map<K, V>}.
+ *
+ * <p>Otherwise, returns {@code key}.
+ */
+ public Key unwrapMapValueType(Key key) {
+ if (MapType.isMap(key)) {
+ MapType mapType = MapType.from(key);
+ if (!mapType.isRawType()) {
+ for (Class<?> frameworkClass : asList(Provider.class, Producer.class, Produced.class)) {
+ if (mapType.valuesAreTypeOf(frameworkClass)) {
+ return key.toBuilder()
+ .type(mapOf(mapType.keyType(), mapType.unwrappedValueType(frameworkClass)))
+ .build();
+ }
+ }
+ }
+ }
+ return key;
+ }
+
+ /**
+ * Converts a {@link Key} of type {@code Map<K, V>} to {@code Map<K, Provider<V>>}.
+ */
+ private Key wrapMapValue(Key key, Class<?> newWrappingClass) {
+ checkArgument(
+ FrameworkTypes.isFrameworkType(elements.getTypeElement(newWrappingClass).asType()));
+ return wrapMapKey(key, newWrappingClass).get();
+ }
+
+ /**
+ * If {@code key}'s type is {@code Map<K, CurrentWrappingClass<Bar>>}, returns a key with type
+ * {@code Map<K, NewWrappingClass<Bar>>} with the same qualifier. Otherwise returns {@link
+ * Optional#empty()}.
+ *
+ * <p>Returns {@link Optional#empty()} if {@code newWrappingClass} is not in the classpath.
+ *
+ * @throws IllegalArgumentException if {@code newWrappingClass} is the same as {@code
+ * currentWrappingClass}
+ */
+ public Optional<Key> rewrapMapKey(
+ Key possibleMapKey, Class<?> currentWrappingClass, Class<?> newWrappingClass) {
+ checkArgument(!currentWrappingClass.equals(newWrappingClass));
+ if (MapType.isMap(possibleMapKey)) {
+ MapType mapType = MapType.from(possibleMapKey);
+ if (!mapType.isRawType() && mapType.valuesAreTypeOf(currentWrappingClass)) {
+ TypeElement wrappingElement = elements.getTypeElement(newWrappingClass);
+ if (wrappingElement == null) {
+ // This target might not be compiled with Producers, so wrappingClass might not have an
+ // associated element.
+ return Optional.empty();
+ }
+ DeclaredType wrappedValueType =
+ types.getDeclaredType(
+ wrappingElement, mapType.unwrappedValueType(currentWrappingClass));
+ return Optional.of(
+ possibleMapKey.toBuilder().type(mapOf(mapType.keyType(), wrappedValueType)).build());
+ }
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * If {@code key}'s type is {@code Map<K, Foo>} and {@code Foo} is not {@code WrappingClass
+ * <Bar>}, returns a key with type {@code Map<K, WrappingClass<Foo>>} with the same qualifier.
+ * Otherwise returns {@link Optional#empty()}.
+ *
+ * <p>Returns {@link Optional#empty()} if {@code WrappingClass} is not in the classpath.
+ */
+ private Optional<Key> wrapMapKey(Key possibleMapKey, Class<?> wrappingClass) {
+ if (MapType.isMap(possibleMapKey)) {
+ MapType mapType = MapType.from(possibleMapKey);
+ if (!mapType.isRawType() && !mapType.valuesAreTypeOf(wrappingClass)) {
+ TypeElement wrappingElement = elements.getTypeElement(wrappingClass);
+ if (wrappingElement == null) {
+ // This target might not be compiled with Producers, so wrappingClass might not have an
+ // associated element.
+ return Optional.empty();
+ }
+ DeclaredType wrappedValueType = types.getDeclaredType(wrappingElement, mapType.valueType());
+ return Optional.of(
+ possibleMapKey.toBuilder().type(mapOf(mapType.keyType(), wrappedValueType)).build());
+ }
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * If {@code key}'s type is {@code Set<WrappingClass<Bar>>}, returns a key with type {@code Set
+ * <Bar>} with the same qualifier. Otherwise returns {@link Optional#empty()}.
+ */
+ Optional<Key> unwrapSetKey(Key key, Class<?> wrappingClass) {
+ if (SetType.isSet(key)) {
+ SetType setType = SetType.from(key);
+ if (!setType.isRawType() && setType.elementsAreTypeOf(wrappingClass)) {
+ return Optional.of(
+ key.toBuilder().type(setOf(setType.unwrappedElementType(wrappingClass))).build());
+ }
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * If {@code key}'s type is {@code Optional<T>} for some {@code T}, returns a key with the same
+ * qualifier whose type is {@linkplain RequestKinds#extractKeyType(RequestKind, TypeMirror)}
+ * extracted} from {@code T}.
+ */
+ Optional<Key> unwrapOptional(Key key) {
+ if (!OptionalType.isOptional(key)) {
+ return Optional.empty();
+ }
+
+ TypeMirror optionalValueType = OptionalType.from(key).valueType();
+ return Optional.of(key.toBuilder().type(extractKeyType(optionalValueType)).build());
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/KeyVariableNamer.java b/java/dagger/internal/codegen/binding/KeyVariableNamer.java
new file mode 100644
index 000000000..9ac0efa83
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/KeyVariableNamer.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static dagger.internal.codegen.binding.SourceFiles.protectAgainstKeywords;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSet;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import java.util.Iterator;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVisitor;
+import javax.lang.model.util.SimpleTypeVisitor8;
+
+/**
+ * Suggests a variable name for a type based on a {@link Key}. Prefer {@link
+ * DependencyVariableNamer} for cases where a specific {@link DependencyRequest} is present.
+ */
+public final class KeyVariableNamer {
+ /** Simple names that are very common. Inspired by https://errorprone.info/bugpattern/BadImport */
+ private static final ImmutableSet<String> VERY_SIMPLE_NAMES =
+ ImmutableSet.of(
+ "Builder",
+ "Factory",
+ "Component",
+ "Subcomponent",
+ "Injector");
+
+ private static final TypeVisitor<Void, StringBuilder> TYPE_NAMER =
+ new SimpleTypeVisitor8<Void, StringBuilder>() {
+ @Override
+ public Void visitDeclared(DeclaredType declaredType, StringBuilder builder) {
+ TypeElement element = MoreTypes.asTypeElement(declaredType);
+ if (element.getNestingKind().isNested()
+ && VERY_SIMPLE_NAMES.contains(element.getSimpleName().toString())) {
+ builder.append(element.getEnclosingElement().getSimpleName());
+ }
+
+ builder.append(element.getSimpleName());
+ Iterator<? extends TypeMirror> argumentIterator =
+ declaredType.getTypeArguments().iterator();
+ if (argumentIterator.hasNext()) {
+ builder.append("Of");
+ TypeMirror first = argumentIterator.next();
+ first.accept(this, builder);
+ while (argumentIterator.hasNext()) {
+ builder.append("And");
+ argumentIterator.next().accept(this, builder);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitPrimitive(PrimitiveType type, StringBuilder builder) {
+ builder.append(LOWER_CAMEL.to(UPPER_CAMEL, type.toString()));
+ return null;
+ }
+
+ @Override
+ public Void visitArray(ArrayType type, StringBuilder builder) {
+ type.getComponentType().accept(this, builder);
+ builder.append("Array");
+ return null;
+ }
+ };
+
+ private KeyVariableNamer() {}
+
+ public static String name(Key key) {
+ if (key.multibindingContributionIdentifier().isPresent()) {
+ return key.multibindingContributionIdentifier().get().bindingElement();
+ }
+
+ StringBuilder builder = new StringBuilder();
+
+ if (key.qualifier().isPresent()) {
+ // TODO(gak): Use a better name for fields with qualifiers with members.
+ builder.append(key.qualifier().get().getAnnotationType().asElement().getSimpleName());
+ }
+
+ key.type().accept(TYPE_NAMER, builder);
+
+ return protectAgainstKeywords(UPPER_CAMEL.to(LOWER_CAMEL, builder.toString()));
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/LegacyBindingGraph.java b/java/dagger/internal/codegen/binding/LegacyBindingGraph.java
new file mode 100644
index 000000000..7c0040167
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/LegacyBindingGraph.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimaps;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import java.util.Collection;
+import java.util.Map;
+import javax.lang.model.element.TypeElement;
+
+// TODO(bcorso): Remove the LegacyBindingGraph after we've migrated to the new BindingGraph.
+/** The canonical representation of a full-resolved graph. */
+final class LegacyBindingGraph {
+ private final ComponentDescriptor componentDescriptor;
+ private final ImmutableMap<Key, ResolvedBindings> contributionBindings;
+ private final ImmutableMap<Key, ResolvedBindings> membersInjectionBindings;
+ private final ImmutableList<LegacyBindingGraph> subgraphs;
+
+ LegacyBindingGraph(
+ ComponentDescriptor componentDescriptor,
+ ImmutableMap<Key, ResolvedBindings> contributionBindings,
+ ImmutableMap<Key, ResolvedBindings> membersInjectionBindings,
+ ImmutableList<LegacyBindingGraph> subgraphs) {
+ this.componentDescriptor = componentDescriptor;
+ this.contributionBindings = contributionBindings;
+ this.membersInjectionBindings = membersInjectionBindings;
+ this.subgraphs = checkForDuplicates(subgraphs);
+ }
+
+ ComponentDescriptor componentDescriptor() {
+ return componentDescriptor;
+ }
+
+ ResolvedBindings resolvedBindings(BindingRequest request) {
+ return request.isRequestKind(RequestKind.MEMBERS_INJECTION)
+ ? membersInjectionBindings.get(request.key())
+ : contributionBindings.get(request.key());
+ }
+
+ Iterable<ResolvedBindings> resolvedBindings() {
+ // Don't return an immutable collection - this is only ever used for looping over all bindings
+ // in the graph. Copying is wasteful, especially if is a hashing collection, since the values
+ // should all, by definition, be distinct.
+ return Iterables.concat(membersInjectionBindings.values(), contributionBindings.values());
+ }
+
+ ImmutableList<LegacyBindingGraph> subgraphs() {
+ return subgraphs;
+ }
+
+ private static ImmutableList<LegacyBindingGraph> checkForDuplicates(
+ ImmutableList<LegacyBindingGraph> graphs) {
+ Map<TypeElement, Collection<LegacyBindingGraph>> duplicateGraphs =
+ Maps.filterValues(
+ Multimaps.index(graphs, graph -> graph.componentDescriptor().typeElement()).asMap(),
+ overlapping -> overlapping.size() > 1);
+ if (!duplicateGraphs.isEmpty()) {
+ throw new IllegalArgumentException("Expected no duplicates: " + duplicateGraphs);
+ }
+ return graphs;
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/MapKeys.java b/java/dagger/internal/codegen/binding/MapKeys.java
new file mode 100644
index 000000000..ec7d79df1
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/MapKeys.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValuesWithDefaults;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static dagger.internal.codegen.base.MapKeyAccessibility.isMapKeyPubliclyAccessible;
+import static dagger.internal.codegen.binding.SourceFiles.elementBasedClassName;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.MapKey;
+import dagger.internal.codegen.base.MapKeyAccessibility;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleTypeVisitor6;
+
+/** Methods for extracting {@link MapKey} annotations and key code blocks from binding elements. */
+public final class MapKeys {
+
+ /**
+ * If {@code bindingElement} is annotated with a {@link MapKey} annotation, returns it.
+ *
+ * @throws IllegalArgumentException if the element is annotated with more than one {@code MapKey}
+ * annotation
+ */
+ static Optional<AnnotationMirror> getMapKey(Element bindingElement) {
+ ImmutableSet<? extends AnnotationMirror> mapKeys = getMapKeys(bindingElement);
+ return mapKeys.isEmpty()
+ ? Optional.empty()
+ : Optional.<AnnotationMirror>of(getOnlyElement(mapKeys));
+ }
+
+ /** Returns all of the {@link MapKey} annotations that annotate {@code bindingElement}. */
+ public static ImmutableSet<? extends AnnotationMirror> getMapKeys(Element bindingElement) {
+ return getAnnotatedAnnotations(bindingElement, MapKey.class);
+ }
+
+ /**
+ * Returns the annotation value if {@code mapKey}'s type is annotated with
+ * {@link MapKey @MapKey(unwrapValue = true)}.
+ *
+ * @throws IllegalArgumentException if {@code mapKey}'s type is not annotated with
+ * {@link MapKey @MapKey} at all.
+ */
+ static Optional<? extends AnnotationValue> unwrapValue(AnnotationMirror mapKey) {
+ MapKey mapKeyAnnotation = mapKey.getAnnotationType().asElement().getAnnotation(MapKey.class);
+ checkArgument(
+ mapKeyAnnotation != null, "%s is not annotated with @MapKey", mapKey.getAnnotationType());
+ return mapKeyAnnotation.unwrapValue()
+ ? Optional.of(getOnlyElement(getAnnotationValuesWithDefaults(mapKey).values()))
+ : Optional.empty();
+ }
+
+ static TypeMirror mapKeyType(AnnotationMirror mapKeyAnnotation, DaggerTypes types) {
+ return unwrapValue(mapKeyAnnotation).isPresent()
+ ? getUnwrappedMapKeyType(mapKeyAnnotation.getAnnotationType(), types)
+ : mapKeyAnnotation.getAnnotationType();
+ }
+
+ /**
+ * Returns the map key type for an unwrapped {@link MapKey} annotation type. If the single member
+ * type is primitive, returns the boxed type.
+ *
+ * @throws IllegalArgumentException if {@code mapKeyAnnotationType} is not an annotation type or
+ * has more than one member, or if its single member is an array
+ * @throws NoSuchElementException if the annotation has no members
+ */
+ public static DeclaredType getUnwrappedMapKeyType(
+ final DeclaredType mapKeyAnnotationType, final DaggerTypes types) {
+ checkArgument(
+ MoreTypes.asTypeElement(mapKeyAnnotationType).getKind() == ElementKind.ANNOTATION_TYPE,
+ "%s is not an annotation type",
+ mapKeyAnnotationType);
+
+ final ExecutableElement onlyElement =
+ getOnlyElement(methodsIn(mapKeyAnnotationType.asElement().getEnclosedElements()));
+
+ SimpleTypeVisitor6<DeclaredType, Void> keyTypeElementVisitor =
+ new SimpleTypeVisitor6<DeclaredType, Void>() {
+
+ @Override
+ public DeclaredType visitArray(ArrayType t, Void p) {
+ throw new IllegalArgumentException(
+ mapKeyAnnotationType + "." + onlyElement.getSimpleName() + " cannot be an array");
+ }
+
+ @Override
+ public DeclaredType visitPrimitive(PrimitiveType t, Void p) {
+ return MoreTypes.asDeclared(types.boxedClass(t).asType());
+ }
+
+ @Override
+ public DeclaredType visitDeclared(DeclaredType t, Void p) {
+ return t;
+ }
+ };
+ return keyTypeElementVisitor.visit(onlyElement.getReturnType());
+ }
+
+ /**
+ * Returns a code block for {@code binding}'s {@link ContributionBinding#mapKeyAnnotation() map
+ * key}. If for whatever reason the map key is not accessible from within {@code requestingClass}
+ * (i.e. it has a package-private {@code enum} from a different package), this will return an
+ * invocation of a proxy-method giving it access.
+ *
+ * @throws IllegalStateException if {@code binding} is not a {@link dagger.multibindings.IntoMap
+ * map} contribution.
+ */
+ public static CodeBlock getMapKeyExpression(
+ ContributionBinding binding, ClassName requestingClass, DaggerElements elements) {
+ AnnotationMirror mapKeyAnnotation = binding.mapKeyAnnotation().get();
+ return MapKeyAccessibility.isMapKeyAccessibleFrom(
+ mapKeyAnnotation, requestingClass.packageName())
+ ? directMapKeyExpression(mapKeyAnnotation, elements)
+ : CodeBlock.of("$T.create()", mapKeyProxyClassName(binding));
+ }
+
+ /**
+ * Returns a code block for the map key annotation {@code mapKey}.
+ *
+ * <p>This method assumes the map key will be accessible in the context that the returned {@link
+ * CodeBlock} is used. Use {@link #getMapKeyExpression(ContributionBinding, ClassName,
+ * DaggerElements)} when that assumption is not guaranteed.
+ *
+ * @throws IllegalArgumentException if the element is annotated with more than one {@code MapKey}
+ * annotation
+ * @throws IllegalStateException if {@code bindingElement} is not annotated with a {@code MapKey}
+ * annotation
+ */
+ private static CodeBlock directMapKeyExpression(
+ AnnotationMirror mapKey, DaggerElements elements) {
+ Optional<? extends AnnotationValue> unwrappedValue = unwrapValue(mapKey);
+ AnnotationExpression annotationExpression = new AnnotationExpression(mapKey);
+
+ if (MoreTypes.asTypeElement(mapKey.getAnnotationType())
+ .getQualifiedName()
+ .contentEquals("dagger.android.AndroidInjectionKey")) {
+ TypeElement unwrappedType =
+ elements.checkTypePresent((String) unwrappedValue.get().getValue());
+ return CodeBlock.of(
+ "$T.of($S)",
+ ClassName.get("dagger.android.internal", "AndroidInjectionKeys"),
+ ClassName.get(unwrappedType).reflectionName());
+ }
+
+ if (unwrappedValue.isPresent()) {
+ TypeMirror unwrappedValueType =
+ getOnlyElement(getAnnotationValuesWithDefaults(mapKey).keySet()).getReturnType();
+ return annotationExpression.getValueExpression(unwrappedValueType, unwrappedValue.get());
+ } else {
+ return annotationExpression.getAnnotationInstanceExpression();
+ }
+ }
+
+ /**
+ * Returns the {@link ClassName} in which {@link #mapKeyFactoryMethod(ContributionBinding,
+ * DaggerTypes, DaggerElements)} is generated.
+ */
+ public static ClassName mapKeyProxyClassName(ContributionBinding binding) {
+ return elementBasedClassName(
+ MoreElements.asExecutable(binding.bindingElement().get()), "MapKey");
+ }
+
+ /**
+ * A {@code static create()} method to be added to {@link
+ * #mapKeyProxyClassName(ContributionBinding)} when the {@code @MapKey} annotation is not publicly
+ * accessible.
+ */
+ public static Optional<MethodSpec> mapKeyFactoryMethod(
+ ContributionBinding binding, DaggerTypes types, DaggerElements elements) {
+ return binding
+ .mapKeyAnnotation()
+ .filter(mapKey -> !isMapKeyPubliclyAccessible(mapKey))
+ .map(
+ mapKey ->
+ methodBuilder("create")
+ .addModifiers(PUBLIC, STATIC)
+ .returns(TypeName.get(mapKeyType(mapKey, types)))
+ .addStatement("return $L", directMapKeyExpression(mapKey, elements))
+ .build());
+ }
+
+ private MapKeys() {}
+}
diff --git a/java/dagger/internal/codegen/binding/MembersInjectionBinding.java b/java/dagger/internal/codegen/binding/MembersInjectionBinding.java
new file mode 100644
index 000000000..3dd101657
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/MembersInjectionBinding.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static java.util.stream.Collectors.toList;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSortedSet;
+import dagger.model.BindingKind;
+import dagger.model.DependencyRequest;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+
+/** Represents the full members injection of a particular type. */
+@AutoValue
+public abstract class MembersInjectionBinding extends Binding {
+ @Override
+ public final Optional<Element> bindingElement() {
+ return Optional.of(membersInjectedType());
+ }
+
+ public abstract TypeElement membersInjectedType();
+
+ @Override
+ public abstract Optional<MembersInjectionBinding> unresolved();
+
+ @Override
+ public Optional<TypeElement> contributingModule() {
+ return Optional.empty();
+ }
+
+ /** The set of individual sites where {@link Inject} is applied. */
+ public abstract ImmutableSortedSet<InjectionSite> injectionSites();
+
+ @Override
+ public BindingType bindingType() {
+ return BindingType.MEMBERS_INJECTION;
+ }
+
+ @Override
+ public BindingKind kind() {
+ return BindingKind.MEMBERS_INJECTION;
+ }
+
+ @Override
+ public boolean isNullable() {
+ return false;
+ }
+
+ /**
+ * Returns {@code true} if any of this binding's injection sites are directly on the bound type.
+ */
+ public boolean hasLocalInjectionSites() {
+ return injectionSites()
+ .stream()
+ .anyMatch(
+ injectionSite ->
+ injectionSite.element().getEnclosingElement().equals(membersInjectedType()));
+ }
+
+ @Override
+ public boolean requiresModuleInstance() {
+ return false;
+ }
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ // TODO(ronshapiro,dpb): simplify the equality semantics
+ @Override
+ public abstract boolean equals(Object obj);
+
+ /** Metadata about a field or method injection site. */
+ @AutoValue
+ public abstract static class InjectionSite {
+ /** The type of injection site. */
+ public enum Kind {
+ FIELD,
+ METHOD,
+ }
+
+ public abstract Kind kind();
+
+ public abstract Element element();
+
+ public abstract ImmutableSet<DependencyRequest> dependencies();
+
+ /**
+ * Returns the index of {@link #element()} in its parents {@code @Inject} members that have the
+ * same simple name. This method filters out private elements so that the results will be
+ * consistent independent of whether the build system uses header jars or not.
+ */
+ @Memoized
+ public int indexAmongAtInjectMembersWithSameSimpleName() {
+ return element()
+ .getEnclosingElement()
+ .getEnclosedElements()
+ .stream()
+ .filter(element -> isAnnotationPresent(element, Inject.class))
+ .filter(element -> !element.getModifiers().contains(Modifier.PRIVATE))
+ .filter(element -> element.getSimpleName().equals(this.element().getSimpleName()))
+ .collect(toList())
+ .indexOf(element());
+ }
+
+ public static InjectionSite field(VariableElement element, DependencyRequest dependency) {
+ return new AutoValue_MembersInjectionBinding_InjectionSite(
+ Kind.FIELD, element, ImmutableSet.of(dependency));
+ }
+
+ public static InjectionSite method(
+ ExecutableElement element, Iterable<DependencyRequest> dependencies) {
+ return new AutoValue_MembersInjectionBinding_InjectionSite(
+ Kind.METHOD, element, ImmutableSet.copyOf(dependencies));
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/MethodSignatureFormatter.java b/java/dagger/internal/codegen/binding/MethodSignatureFormatter.java
new file mode 100644
index 000000000..97c680f4c
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/MethodSignatureFormatter.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.common.base.Preconditions.checkState;
+import static dagger.internal.codegen.base.DiagnosticFormatting.stripCommonTypePrefixes;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import dagger.internal.codegen.base.Formatter;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+
+/** Formats the signature of an {@link ExecutableElement} suitable for use in error messages. */
+public final class MethodSignatureFormatter extends Formatter<ExecutableElement> {
+ private final DaggerTypes types;
+ private final InjectionAnnotations injectionAnnotations;
+
+ @Inject
+ public MethodSignatureFormatter(DaggerTypes types, InjectionAnnotations injectionAnnotations) {
+ this.types = types;
+ this.injectionAnnotations = injectionAnnotations;
+ }
+
+ /**
+ * A formatter that uses the type where the method is declared for the annotations and name of the
+ * method, but the method's resolved type as a member of {@code declaredType} for the key.
+ */
+ public Formatter<ExecutableElement> typedFormatter(DeclaredType declaredType) {
+ return new Formatter<ExecutableElement>() {
+ @Override
+ public String format(ExecutableElement method) {
+ return MethodSignatureFormatter.this.format(
+ method,
+ MoreTypes.asExecutable(types.asMemberOf(declaredType, method)),
+ MoreElements.asType(method.getEnclosingElement()));
+ }
+ };
+ }
+
+ @Override
+ public String format(ExecutableElement method) {
+ return format(method, Optional.empty());
+ }
+
+ /**
+ * Formats an ExecutableElement as if it were contained within the container, if the container is
+ * present.
+ */
+ public String format(ExecutableElement method, Optional<DeclaredType> container) {
+ TypeElement type = MoreElements.asType(method.getEnclosingElement());
+ ExecutableType executableType = MoreTypes.asExecutable(method.asType());
+ if (container.isPresent()) {
+ executableType = MoreTypes.asExecutable(types.asMemberOf(container.get(), method));
+ type = MoreElements.asType(container.get().asElement());
+ }
+ return format(method, executableType, type);
+ }
+
+ private String format(
+ ExecutableElement method, ExecutableType methodType, TypeElement declaringType) {
+ StringBuilder builder = new StringBuilder();
+ // TODO(user): AnnotationMirror formatter.
+ List<? extends AnnotationMirror> annotations = method.getAnnotationMirrors();
+ if (!annotations.isEmpty()) {
+ Iterator<? extends AnnotationMirror> annotationIterator = annotations.iterator();
+ for (int i = 0; annotationIterator.hasNext(); i++) {
+ if (i > 0) {
+ builder.append(' ');
+ }
+ builder.append(formatAnnotation(annotationIterator.next()));
+ }
+ builder.append(' ');
+ }
+ if (method.getSimpleName().contentEquals("<init>")) {
+ builder.append(declaringType.getQualifiedName());
+ } else {
+ builder
+ .append(nameOfType(methodType.getReturnType()))
+ .append(' ')
+ .append(declaringType.getQualifiedName())
+ .append('.')
+ .append(method.getSimpleName());
+ }
+ builder.append('(');
+ checkState(method.getParameters().size() == methodType.getParameterTypes().size());
+ Iterator<? extends VariableElement> parameters = method.getParameters().iterator();
+ Iterator<? extends TypeMirror> parameterTypes = methodType.getParameterTypes().iterator();
+ for (int i = 0; parameters.hasNext(); i++) {
+ if (i > 0) {
+ builder.append(", ");
+ }
+ appendParameter(builder, parameters.next(), parameterTypes.next());
+ }
+ builder.append(')');
+ return builder.toString();
+ }
+
+ private void appendParameter(StringBuilder builder, VariableElement parameter, TypeMirror type) {
+ injectionAnnotations
+ .getQualifier(parameter)
+ .ifPresent(
+ qualifier -> {
+ builder.append(formatAnnotation(qualifier)).append(' ');
+ });
+ builder.append(nameOfType(type));
+ }
+
+ private static String nameOfType(TypeMirror type) {
+ return stripCommonTypePrefixes(type.toString());
+ }
+
+ private static String formatAnnotation(AnnotationMirror annotation) {
+ return stripCommonTypePrefixes(annotation.toString());
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/ModuleDescriptor.java b/java/dagger/internal/codegen/binding/ModuleDescriptor.java
new file mode 100644
index 000000000..c471f94b6
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/ModuleDescriptor.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.auto.common.MoreElements.asExecutable;
+import static com.google.auto.common.MoreElements.getPackage;
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Verify.verify;
+import static com.google.common.collect.Iterables.transform;
+import static dagger.internal.codegen.base.ModuleAnnotation.moduleAnnotation;
+import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.binding.SourceFiles.classFileName;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.langmodel.DaggerElements.getMethodDescriptor;
+import static dagger.internal.codegen.langmodel.DaggerElements.isAnnotationPresent;
+import static javax.lang.model.type.TypeKind.DECLARED;
+import static javax.lang.model.type.TypeKind.NONE;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.graph.Traverser;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.squareup.javapoet.ClassName;
+import dagger.Binds;
+import dagger.BindsOptionalOf;
+import dagger.Module;
+import dagger.Provides;
+import dagger.internal.codegen.base.ClearableCache;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.model.Key;
+import dagger.multibindings.Multibinds;
+import dagger.producers.Produces;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+/** Contains metadata that describes a module. */
+@AutoValue
+public abstract class ModuleDescriptor {
+
+ public abstract TypeElement moduleElement();
+
+ abstract ImmutableSet<TypeElement> includedModules();
+
+ public abstract ImmutableSet<ContributionBinding> bindings();
+
+ /** The multibinding declarations contained in this module. */
+ abstract ImmutableSet<MultibindingDeclaration> multibindingDeclarations();
+
+ /** The {@link Module#subcomponents() subcomponent declarations} contained in this module. */
+ abstract ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations();
+
+ /** The {@link Binds} method declarations that define delegate bindings. */
+ abstract ImmutableSet<DelegateDeclaration> delegateDeclarations();
+
+ /** The {@link BindsOptionalOf} method declarations that define optional bindings. */
+ abstract ImmutableSet<OptionalBindingDeclaration> optionalDeclarations();
+
+ /** The kind of the module. */
+ public abstract ModuleKind kind();
+
+ /** Returns all of the bindings declared in this module. */
+ @Memoized
+ public ImmutableSet<BindingDeclaration> allBindingDeclarations() {
+ return ImmutableSet.<BindingDeclaration>builder()
+ .addAll(bindings())
+ .addAll(delegateDeclarations())
+ .addAll(multibindingDeclarations())
+ .addAll(optionalDeclarations())
+ .addAll(subcomponentDeclarations())
+ .build();
+ }
+
+ /** Returns the keys of all bindings declared by this module. */
+ ImmutableSet<Key> allBindingKeys() {
+ return allBindingDeclarations().stream().map(BindingDeclaration::key).collect(toImmutableSet());
+ }
+
+ /** A {@link ModuleDescriptor} factory. */
+ @Singleton
+ public static final class Factory implements ClearableCache {
+ private final DaggerElements elements;
+ private final KotlinMetadataUtil metadataUtil;
+ private final BindingFactory bindingFactory;
+ private final MultibindingDeclaration.Factory multibindingDeclarationFactory;
+ private final DelegateDeclaration.Factory bindingDelegateDeclarationFactory;
+ private final SubcomponentDeclaration.Factory subcomponentDeclarationFactory;
+ private final OptionalBindingDeclaration.Factory optionalBindingDeclarationFactory;
+ private final Map<TypeElement, ModuleDescriptor> cache = new HashMap<>();
+
+ @Inject
+ Factory(
+ DaggerElements elements,
+ KotlinMetadataUtil metadataUtil,
+ BindingFactory bindingFactory,
+ MultibindingDeclaration.Factory multibindingDeclarationFactory,
+ DelegateDeclaration.Factory bindingDelegateDeclarationFactory,
+ SubcomponentDeclaration.Factory subcomponentDeclarationFactory,
+ OptionalBindingDeclaration.Factory optionalBindingDeclarationFactory) {
+ this.elements = elements;
+ this.metadataUtil = metadataUtil;
+ this.bindingFactory = bindingFactory;
+ this.multibindingDeclarationFactory = multibindingDeclarationFactory;
+ this.bindingDelegateDeclarationFactory = bindingDelegateDeclarationFactory;
+ this.subcomponentDeclarationFactory = subcomponentDeclarationFactory;
+ this.optionalBindingDeclarationFactory = optionalBindingDeclarationFactory;
+ }
+
+ public ModuleDescriptor create(TypeElement moduleElement) {
+ return reentrantComputeIfAbsent(cache, moduleElement, this::createUncached);
+ }
+
+ public ModuleDescriptor createUncached(TypeElement moduleElement) {
+ ImmutableSet.Builder<ContributionBinding> bindings = ImmutableSet.builder();
+ ImmutableSet.Builder<DelegateDeclaration> delegates = ImmutableSet.builder();
+ ImmutableSet.Builder<MultibindingDeclaration> multibindingDeclarations =
+ ImmutableSet.builder();
+ ImmutableSet.Builder<OptionalBindingDeclaration> optionalDeclarations =
+ ImmutableSet.builder();
+
+ for (ExecutableElement moduleMethod : methodsIn(elements.getAllMembers(moduleElement))) {
+ if (isAnnotationPresent(moduleMethod, Provides.class)) {
+ bindings.add(bindingFactory.providesMethodBinding(moduleMethod, moduleElement));
+ }
+ if (isAnnotationPresent(moduleMethod, Produces.class)) {
+ bindings.add(bindingFactory.producesMethodBinding(moduleMethod, moduleElement));
+ }
+ if (isAnnotationPresent(moduleMethod, Binds.class)) {
+ delegates.add(bindingDelegateDeclarationFactory.create(moduleMethod, moduleElement));
+ }
+ if (isAnnotationPresent(moduleMethod, Multibinds.class)) {
+ multibindingDeclarations.add(
+ multibindingDeclarationFactory.forMultibindsMethod(moduleMethod, moduleElement));
+ }
+ if (isAnnotationPresent(moduleMethod, BindsOptionalOf.class)) {
+ optionalDeclarations.add(
+ optionalBindingDeclarationFactory.forMethod(moduleMethod, moduleElement));
+ }
+ }
+
+ if (metadataUtil.hasEnclosedCompanionObject(moduleElement)) {
+ collectCompanionModuleBindings(moduleElement, bindings);
+ }
+
+ return new AutoValue_ModuleDescriptor(
+ moduleElement,
+ ImmutableSet.copyOf(collectIncludedModules(new LinkedHashSet<>(), moduleElement)),
+ bindings.build(),
+ multibindingDeclarations.build(),
+ subcomponentDeclarationFactory.forModule(moduleElement),
+ delegates.build(),
+ optionalDeclarations.build(),
+ ModuleKind.forAnnotatedElement(moduleElement).get());
+ }
+
+ private void collectCompanionModuleBindings(
+ TypeElement moduleElement, ImmutableSet.Builder<ContributionBinding> bindings) {
+ checkArgument(metadataUtil.hasEnclosedCompanionObject(moduleElement));
+ TypeElement companionModule = metadataUtil.getEnclosedCompanionObject(moduleElement);
+ ImmutableSet<String> bindingElementDescriptors =
+ bindings.build().stream()
+ .map(binding -> getMethodDescriptor(asExecutable(binding.bindingElement().get())))
+ .collect(toImmutableSet());
+ methodsIn(elements.getAllMembers(companionModule)).stream()
+ // Binding methods in companion objects with @JvmStatic are mirrored in the enclosing
+ // class, therefore we should ignore it or else it'll be a duplicate binding.
+ .filter(method -> !KotlinMetadataUtil.isJvmStaticPresent(method))
+ // Fallback strategy for de-duping contributing bindings in the companion module with
+ // @JvmStatic by comparing descriptors. Contributing bindings are the only valid bindings
+ // a companion module can declare. See: https://youtrack.jetbrains.com/issue/KT-35104
+ // TODO(danysantiago): Checks qualifiers too.
+ .filter(method -> !bindingElementDescriptors.contains(getMethodDescriptor(method)))
+ .forEach(
+ method -> {
+ if (isAnnotationPresent(method, Provides.class)) {
+ bindings.add(bindingFactory.providesMethodBinding(method, companionModule));
+ }
+ if (isAnnotationPresent(method, Produces.class)) {
+ bindings.add(bindingFactory.producesMethodBinding(method, companionModule));
+ }
+ });
+ }
+
+ /** Returns all the modules transitively included by given modules, including the arguments. */
+ ImmutableSet<ModuleDescriptor> transitiveModules(Iterable<TypeElement> modules) {
+ return ImmutableSet.copyOf(
+ Traverser.forGraph(
+ (ModuleDescriptor module) -> transform(module.includedModules(), this::create))
+ .depthFirstPreOrder(transform(modules, this::create)));
+ }
+
+ @CanIgnoreReturnValue
+ private Set<TypeElement> collectIncludedModules(
+ Set<TypeElement> includedModules, TypeElement moduleElement) {
+ TypeMirror superclass = moduleElement.getSuperclass();
+ if (!superclass.getKind().equals(NONE)) {
+ verify(superclass.getKind().equals(DECLARED));
+ TypeElement superclassElement = MoreTypes.asTypeElement(superclass);
+ if (!superclassElement.getQualifiedName().contentEquals(Object.class.getCanonicalName())) {
+ collectIncludedModules(includedModules, superclassElement);
+ }
+ }
+ moduleAnnotation(moduleElement)
+ .ifPresent(
+ moduleAnnotation -> {
+ includedModules.addAll(moduleAnnotation.includes());
+ includedModules.addAll(implicitlyIncludedModules(moduleElement));
+ });
+ return includedModules;
+ }
+
+ // @ContributesAndroidInjector generates a module that is implicitly included in the enclosing
+ // module
+ private ImmutableSet<TypeElement> implicitlyIncludedModules(TypeElement moduleElement) {
+ TypeElement contributesAndroidInjector =
+ elements.getTypeElement("dagger.android.ContributesAndroidInjector");
+ if (contributesAndroidInjector == null) {
+ return ImmutableSet.of();
+ }
+ return methodsIn(moduleElement.getEnclosedElements()).stream()
+ .filter(method -> isAnnotationPresent(method, contributesAndroidInjector.asType()))
+ .map(method -> elements.checkTypePresent(implicitlyIncludedModuleName(method)))
+ .collect(toImmutableSet());
+ }
+
+ private String implicitlyIncludedModuleName(ExecutableElement method) {
+ return getPackage(method).getQualifiedName()
+ + "."
+ + classFileName(ClassName.get(MoreElements.asType(method.getEnclosingElement())))
+ + "_"
+ + LOWER_CAMEL.to(UPPER_CAMEL, method.getSimpleName().toString());
+ }
+
+ @Override
+ public void clearCache() {
+ cache.clear();
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/ModuleKind.java b/java/dagger/internal/codegen/binding/ModuleKind.java
new file mode 100644
index 000000000..6b52f049e
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/ModuleKind.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.auto.common.MoreElements.asType;
+import static com.google.common.base.Preconditions.checkArgument;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import dagger.Module;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.producers.ProducerModule;
+import java.lang.annotation.Annotation;
+import java.util.EnumSet;
+import java.util.Optional;
+import java.util.Set;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.TypeElement;
+
+/** Enumeration of the kinds of modules. */
+public enum ModuleKind {
+ /** {@code @Module} */
+ MODULE(Module.class),
+
+ /** {@code @ProducerModule} */
+ PRODUCER_MODULE(ProducerModule.class);
+
+ /** Returns the annotations for modules of the given kinds. */
+ public static ImmutableSet<Class<? extends Annotation>> annotationsFor(Set<ModuleKind> kinds) {
+ return kinds.stream().map(ModuleKind::annotation).collect(toImmutableSet());
+ }
+
+ /**
+ * Returns the kind of an annotated element if it is annotated with one of the module {@linkplain
+ * #annotation() annotations}.
+ *
+ * @throws IllegalArgumentException if the element is annotated with more than one of the module
+ * annotations
+ */
+ public static Optional<ModuleKind> forAnnotatedElement(TypeElement element) {
+ Set<ModuleKind> kinds = EnumSet.noneOf(ModuleKind.class);
+ for (ModuleKind kind : values()) {
+ if (MoreElements.isAnnotationPresent(element, kind.annotation())) {
+ kinds.add(kind);
+ }
+ }
+
+ if (kinds.size() > 1) {
+ throw new IllegalArgumentException(
+ element + " cannot be annotated with more than one of " + annotationsFor(kinds));
+ }
+ return kinds.stream().findAny();
+ }
+
+ public static void checkIsModule(TypeElement moduleElement, KotlinMetadataUtil metadataUtil) {
+ // If the type element is a Kotlin companion object, then assert it is a module if its enclosing
+ // type is a module.
+ if (metadataUtil.isCompanionObjectClass(moduleElement)) {
+ checkArgument(forAnnotatedElement(asType(moduleElement.getEnclosingElement())).isPresent());
+ } else {
+ checkArgument(forAnnotatedElement(moduleElement).isPresent());
+ }
+ }
+
+ private final Class<? extends Annotation> moduleAnnotation;
+
+ ModuleKind(Class<? extends Annotation> moduleAnnotation) {
+ this.moduleAnnotation = moduleAnnotation;
+ }
+
+ /**
+ * Returns the annotation mirror for this module kind on the given type.
+ *
+ * @throws IllegalArgumentException if the annotation is not present on the type
+ */
+ public AnnotationMirror getModuleAnnotation(TypeElement element) {
+ Optional<AnnotationMirror> result = getAnnotationMirror(element, moduleAnnotation);
+ checkArgument(
+ result.isPresent(), "annotation %s is not present on type %s", moduleAnnotation, element);
+ return result.get();
+ }
+
+ /** Returns the annotation that marks a module of this kind. */
+ public Class<? extends Annotation> annotation() {
+ return moduleAnnotation;
+ }
+
+ /** Returns the kinds of modules that a module of this kind is allowed to include. */
+ public ImmutableSet<ModuleKind> legalIncludedModuleKinds() {
+ switch (this) {
+ case MODULE:
+ return Sets.immutableEnumSet(MODULE);
+ case PRODUCER_MODULE:
+ return Sets.immutableEnumSet(MODULE, PRODUCER_MODULE);
+ }
+ throw new AssertionError(this);
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/MultibindingDeclaration.java b/java/dagger/internal/codegen/binding/MultibindingDeclaration.java
new file mode 100644
index 000000000..f1d3f8d75
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/MultibindingDeclaration.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import dagger.internal.codegen.base.ContributionType;
+import dagger.internal.codegen.base.ContributionType.HasContributionType;
+import dagger.internal.codegen.base.MapType;
+import dagger.internal.codegen.base.SetType;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Key;
+import dagger.multibindings.Multibinds;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A declaration that a multibinding with a certain key is available to be injected in a component
+ * even if the component has no multibindings for that key. Identified by a map- or set-returning
+ * method annotated with {@link Multibinds @Multibinds}.
+ */
+@AutoValue
+public abstract class MultibindingDeclaration extends BindingDeclaration
+ implements HasContributionType {
+
+ /**
+ * The map or set key whose availability is declared. For maps, this will be {@code Map<K,
+ * Provider<V>>}. For sets, this will be {@code Set<T>}.
+ */
+ @Override
+ public abstract Key key();
+
+ /**
+ * {@link ContributionType#SET} if the declared type is a {@link Set}, or
+ * {@link ContributionType#MAP} if it is a {@link Map}.
+ */
+ @Override
+ public abstract ContributionType contributionType();
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object obj);
+
+ /** A factory for {@link MultibindingDeclaration}s. */
+ public static final class Factory {
+ private final DaggerTypes types;
+ private final KeyFactory keyFactory;
+
+ @Inject
+ Factory(DaggerTypes types, KeyFactory keyFactory) {
+ this.types = types;
+ this.keyFactory = keyFactory;
+ }
+
+ /** A multibinding declaration for a {@link Multibinds @Multibinds} method. */
+ MultibindingDeclaration forMultibindsMethod(
+ ExecutableElement moduleMethod, TypeElement moduleElement) {
+ checkArgument(isAnnotationPresent(moduleMethod, Multibinds.class));
+ return forDeclaredMethod(
+ moduleMethod,
+ MoreTypes.asExecutable(
+ types.asMemberOf(MoreTypes.asDeclared(moduleElement.asType()), moduleMethod)),
+ moduleElement);
+ }
+
+ private MultibindingDeclaration forDeclaredMethod(
+ ExecutableElement method,
+ ExecutableType methodType,
+ TypeElement contributingType) {
+ TypeMirror returnType = methodType.getReturnType();
+ checkArgument(
+ SetType.isSet(returnType) || MapType.isMap(returnType),
+ "%s must return a set or map",
+ method);
+ return new AutoValue_MultibindingDeclaration(
+ Optional.<Element>of(method),
+ Optional.of(contributingType),
+ keyFactory.forMultibindsMethod(methodType, method),
+ contributionType(returnType));
+ }
+
+ private ContributionType contributionType(TypeMirror returnType) {
+ if (MapType.isMap(returnType)) {
+ return ContributionType.MAP;
+ } else if (SetType.isSet(returnType)) {
+ return ContributionType.SET;
+ } else {
+ throw new IllegalArgumentException("Must be Map or Set: " + returnType);
+ }
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/OptionalBindingDeclaration.java b/java/dagger/internal/codegen/binding/OptionalBindingDeclaration.java
new file mode 100644
index 000000000..d7ba7bcfc
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/OptionalBindingDeclaration.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import dagger.BindsOptionalOf;
+import dagger.model.Key;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+
+/** A {@link BindsOptionalOf} declaration. */
+@AutoValue
+abstract class OptionalBindingDeclaration extends BindingDeclaration {
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The key's type is the method's return type, even though the synthetic bindings will be for
+ * {@code Optional} of derived types.
+ */
+ @Override
+ public abstract Key key();
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object obj);
+
+ static class Factory {
+ private final KeyFactory keyFactory;
+
+ @Inject
+ Factory(KeyFactory keyFactory) {
+ this.keyFactory = keyFactory;
+ }
+
+ OptionalBindingDeclaration forMethod(ExecutableElement method, TypeElement contributingModule) {
+ checkArgument(isAnnotationPresent(method, BindsOptionalOf.class));
+ return new AutoValue_OptionalBindingDeclaration(
+ Optional.<Element>of(method),
+ Optional.of(contributingModule),
+ keyFactory.forBindsOptionalOfMethod(method, contributingModule));
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/ProductionBinding.java b/java/dagger/internal/codegen/binding/ProductionBinding.java
new file mode 100644
index 000000000..ad85a68d0
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/ProductionBinding.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.langmodel.DaggerTypes.isFutureType;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import dagger.internal.codegen.base.ContributionType;
+import dagger.internal.codegen.base.SetType;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import java.util.Optional;
+import java.util.stream.Stream;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.TypeMirror;
+
+/** A value object representing the mechanism by which a {@link Key} can be produced. */
+@AutoValue
+public abstract class ProductionBinding extends ContributionBinding {
+
+ @Override
+ public BindingType bindingType() {
+ return BindingType.PRODUCTION;
+ }
+
+ @Override
+ public abstract Optional<ProductionBinding> unresolved();
+
+ @Override
+ public ImmutableSet<DependencyRequest> implicitDependencies() {
+ return Stream.of(executorRequest(), monitorRequest())
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .collect(toImmutableSet());
+ }
+
+ /** What kind of object a {@code @Produces}-annotated method returns. */
+ public enum ProductionKind {
+ /** A value. */
+ IMMEDIATE,
+ /** A {@code ListenableFuture<T>}. */
+ FUTURE,
+ /** A {@code Set<ListenableFuture<T>>}. */
+ SET_OF_FUTURE;
+
+ /** Returns the kind of object a {@code @Produces}-annotated method returns. */
+ public static ProductionKind fromProducesMethod(ExecutableElement producesMethod) {
+ if (isFutureType(producesMethod.getReturnType())) {
+ return FUTURE;
+ } else if (ContributionType.fromBindingElement(producesMethod)
+ .equals(ContributionType.SET_VALUES)
+ && isFutureType(SetType.from(producesMethod.getReturnType()).elementType())) {
+ return SET_OF_FUTURE;
+ } else {
+ return IMMEDIATE;
+ }
+ }
+ }
+
+ /**
+ * Returns the kind of object the produces method returns. All production bindings from
+ * {@code @Produces} methods will have a production kind, but synthetic production bindings may
+ * not.
+ */
+ public abstract Optional<ProductionKind> productionKind();
+
+ /** Returns the list of types in the throws clause of the method. */
+ public abstract ImmutableList<? extends TypeMirror> thrownTypes();
+
+ /**
+ * If this production requires an executor, this will be the corresponding request. All
+ * production bindings from {@code @Produces} methods will have an executor request, but
+ * synthetic production bindings may not.
+ */
+ abstract Optional<DependencyRequest> executorRequest();
+
+ /** If this production requires a monitor, this will be the corresponding request. All
+ * production bindings from {@code @Produces} methods will have a monitor request, but synthetic
+ * production bindings may not.
+ */
+ abstract Optional<DependencyRequest> monitorRequest();
+
+ // Profiling determined that this method is called enough times that memoizing it had a measurable
+ // performance improvement for large components.
+ @Memoized
+ @Override
+ public boolean requiresModuleInstance() {
+ return super.requiresModuleInstance();
+ }
+
+ public static Builder builder() {
+ return new AutoValue_ProductionBinding.Builder()
+ .explicitDependencies(ImmutableList.<DependencyRequest>of())
+ .thrownTypes(ImmutableList.<TypeMirror>of());
+ }
+
+ @Override
+ public abstract Builder toBuilder();
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ // TODO(ronshapiro,dpb): simplify the equality semantics
+ @Override
+ public abstract boolean equals(Object obj);
+
+ /** A {@link ProductionBinding} builder. */
+ @AutoValue.Builder
+ @CanIgnoreReturnValue
+ public abstract static class Builder
+ extends ContributionBinding.Builder<ProductionBinding, Builder> {
+
+ @Override
+ public Builder dependencies(Iterable<DependencyRequest> dependencies) {
+ return explicitDependencies(dependencies);
+ }
+
+ abstract Builder explicitDependencies(Iterable<DependencyRequest> dependencies);
+
+ abstract Builder productionKind(ProductionKind productionKind);
+
+ @Override
+ public abstract Builder unresolved(ProductionBinding unresolved);
+
+ abstract Builder thrownTypes(Iterable<? extends TypeMirror> thrownTypes);
+
+ abstract Builder executorRequest(DependencyRequest executorRequest);
+
+ abstract Builder monitorRequest(DependencyRequest monitorRequest);
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/ProvisionBinding.java b/java/dagger/internal/codegen/binding/ProvisionBinding.java
new file mode 100644
index 000000000..c917dd6b5
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/ProvisionBinding.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.model.BindingKind.COMPONENT_PROVISION;
+import static dagger.model.BindingKind.PROVISION;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSortedSet;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import dagger.internal.codegen.binding.MembersInjectionBinding.InjectionSite;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.model.BindingKind;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.model.Scope;
+import java.util.Optional;
+
+/** A value object representing the mechanism by which a {@link Key} can be provided. */
+@AutoValue
+public abstract class ProvisionBinding extends ContributionBinding {
+
+ @Override
+ @Memoized
+ public ImmutableSet<DependencyRequest> explicitDependencies() {
+ return ImmutableSet.<DependencyRequest>builder()
+ .addAll(provisionDependencies())
+ .addAll(membersInjectionDependencies())
+ .build();
+ }
+
+ /**
+ * Dependencies necessary to invoke an {@code @Inject} constructor or {@code @Provides} method.
+ */
+ public abstract ImmutableSet<DependencyRequest> provisionDependencies();
+
+ @Memoized
+ ImmutableSet<DependencyRequest> membersInjectionDependencies() {
+ return injectionSites()
+ .stream()
+ .flatMap(i -> i.dependencies().stream())
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * {@link InjectionSite}s for all {@code @Inject} members if {@link #kind()} is {@link
+ * BindingKind#INJECTION}, otherwise empty.
+ */
+ public abstract ImmutableSortedSet<InjectionSite> injectionSites();
+
+ @Override
+ public BindingType bindingType() {
+ return BindingType.PROVISION;
+ }
+
+ @Override
+ public abstract Optional<ProvisionBinding> unresolved();
+
+ // TODO(ronshapiro): we should be able to remove this, but AutoValue barks on the Builder's scope
+ // method, saying that the method doesn't correspond to a property of ProvisionBinding
+ @Override
+ public abstract Optional<Scope> scope();
+
+ public static Builder builder() {
+ return new AutoValue_ProvisionBinding.Builder()
+ .provisionDependencies(ImmutableSet.of())
+ .injectionSites(ImmutableSortedSet.of());
+ }
+
+ @Override
+ public abstract Builder toBuilder();
+
+ private static final ImmutableSet<BindingKind> KINDS_TO_CHECK_FOR_NULL =
+ ImmutableSet.of(PROVISION, COMPONENT_PROVISION);
+
+ public boolean shouldCheckForNull(CompilerOptions compilerOptions) {
+ return KINDS_TO_CHECK_FOR_NULL.contains(kind())
+ && !contributedPrimitiveType().isPresent()
+ && !nullableType().isPresent()
+ && compilerOptions.doCheckForNulls();
+ }
+
+ // Profiling determined that this method is called enough times that memoizing it had a measurable
+ // performance improvement for large components.
+ @Memoized
+ @Override
+ public boolean requiresModuleInstance() {
+ return super.requiresModuleInstance();
+ }
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ // TODO(ronshapiro,dpb): simplify the equality semantics
+ @Override
+ public abstract boolean equals(Object obj);
+
+ /** A {@link ProvisionBinding} builder. */
+ @AutoValue.Builder
+ @CanIgnoreReturnValue
+ public abstract static class Builder
+ extends ContributionBinding.Builder<ProvisionBinding, Builder> {
+
+ @Override
+ public Builder dependencies(Iterable<DependencyRequest> dependencies) {
+ return provisionDependencies(dependencies);
+ }
+
+ abstract Builder provisionDependencies(Iterable<DependencyRequest> provisionDependencies);
+
+ public abstract Builder injectionSites(ImmutableSortedSet<InjectionSite> injectionSites);
+
+ @Override
+ public abstract Builder unresolved(ProvisionBinding unresolved);
+
+ public abstract Builder scope(Optional<Scope> scope);
+ }
+
+}
diff --git a/java/dagger/internal/codegen/binding/ResolvedBindings.java b/java/dagger/internal/codegen/binding/ResolvedBindings.java
new file mode 100644
index 000000000..74301feda
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/ResolvedBindings.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Iterables.getOnlyElement;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Multimap;
+import dagger.model.Key;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * The collection of bindings that have been resolved for a key. For valid graphs, contains exactly
+ * one binding.
+ *
+ * <p>Separate {@link ResolvedBindings} instances should be used if a {@link
+ * MembersInjectionBinding} and a {@link ProvisionBinding} for the same key exist in the same
+ * component. (This will only happen if a type has an {@code @Inject} constructor and members, the
+ * component has a members injection method, and the type is also requested normally.)
+ */
+@AutoValue
+abstract class ResolvedBindings {
+ /** The binding key for which the {@link #bindings()} have been resolved. */
+ abstract Key key();
+
+ /**
+ * The {@link ContributionBinding}s for {@link #key()} indexed by the component that owns the
+ * binding. Each key in the multimap is a part of the same component ancestry.
+ */
+ abstract ImmutableSetMultimap<TypeElement, ContributionBinding> allContributionBindings();
+
+ /**
+ * The {@link MembersInjectionBinding}s for {@link #key()} indexed by the component that owns the
+ * binding. Each key in the map is a part of the same component ancestry.
+ */
+ abstract ImmutableMap<TypeElement, MembersInjectionBinding> allMembersInjectionBindings();
+
+ /** The multibinding declarations for {@link #key()}. */
+ abstract ImmutableSet<MultibindingDeclaration> multibindingDeclarations();
+
+ /** The subcomponent declarations for {@link #key()}. */
+ abstract ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations();
+
+ /** The optional binding declarations for {@link #key()}. */
+ abstract ImmutableSet<OptionalBindingDeclaration> optionalBindingDeclarations();
+
+ // Computing the hash code is an expensive operation.
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ // Suppresses ErrorProne warning that hashCode was overridden w/o equals
+ @Override
+ public abstract boolean equals(Object other);
+
+ /** All bindings for {@link #key()}, indexed by the component that owns the binding. */
+ final ImmutableSetMultimap<TypeElement, ? extends Binding> allBindings() {
+ return !allMembersInjectionBindings().isEmpty()
+ ? allMembersInjectionBindings().asMultimap()
+ : allContributionBindings();
+ }
+
+ /** All bindings for {@link #key()}, regardless of which component owns them. */
+ final ImmutableCollection<? extends Binding> bindings() {
+ return allBindings().values();
+ }
+
+ /**
+ * {@code true} if there are no {@link #bindings()}, {@link #multibindingDeclarations()}, {@link
+ * #optionalBindingDeclarations()}, or {@link #subcomponentDeclarations()}.
+ */
+ final boolean isEmpty() {
+ return allMembersInjectionBindings().isEmpty()
+ && allContributionBindings().isEmpty()
+ && multibindingDeclarations().isEmpty()
+ && optionalBindingDeclarations().isEmpty()
+ && subcomponentDeclarations().isEmpty();
+ }
+
+ /** All bindings for {@link #key()} that are owned by a component. */
+ ImmutableSet<? extends Binding> bindingsOwnedBy(ComponentDescriptor component) {
+ return allBindings().get(component.typeElement());
+ }
+
+ /**
+ * All contribution bindings, regardless of owning component. Empty if this is a members-injection
+ * binding.
+ */
+ @Memoized
+ ImmutableSet<ContributionBinding> contributionBindings() {
+ // TODO(ronshapiro): consider optimizing ImmutableSet.copyOf(Collection) for small immutable
+ // collections so that it doesn't need to call toArray(). Even though this method is memoized,
+ // toArray() can take ~150ms for large components, and there are surely other places in the
+ // processor that can benefit from this.
+ return ImmutableSet.copyOf(allContributionBindings().values());
+ }
+
+ /** The component that owns {@code binding}. */
+ final TypeElement owningComponent(ContributionBinding binding) {
+ checkArgument(
+ contributionBindings().contains(binding),
+ "binding is not resolved for %s: %s",
+ key(),
+ binding);
+ return getOnlyElement(allContributionBindings().inverse().get(binding));
+ }
+
+ /** Creates a {@link ResolvedBindings} for contribution bindings. */
+ static ResolvedBindings forContributionBindings(
+ Key key,
+ Multimap<TypeElement, ContributionBinding> contributionBindings,
+ Iterable<MultibindingDeclaration> multibindings,
+ Iterable<SubcomponentDeclaration> subcomponentDeclarations,
+ Iterable<OptionalBindingDeclaration> optionalBindingDeclarations) {
+ return new AutoValue_ResolvedBindings(
+ key,
+ ImmutableSetMultimap.copyOf(contributionBindings),
+ ImmutableMap.of(),
+ ImmutableSet.copyOf(multibindings),
+ ImmutableSet.copyOf(subcomponentDeclarations),
+ ImmutableSet.copyOf(optionalBindingDeclarations));
+ }
+
+ /**
+ * Creates a {@link ResolvedBindings} for members injection bindings.
+ */
+ static ResolvedBindings forMembersInjectionBinding(
+ Key key,
+ ComponentDescriptor owningComponent,
+ MembersInjectionBinding ownedMembersInjectionBinding) {
+ return new AutoValue_ResolvedBindings(
+ key,
+ ImmutableSetMultimap.of(),
+ ImmutableMap.of(owningComponent.typeElement(), ownedMembersInjectionBinding),
+ ImmutableSet.of(),
+ ImmutableSet.of(),
+ ImmutableSet.of());
+ }
+
+ /**
+ * Creates a {@link ResolvedBindings} appropriate for when there are no bindings for the key.
+ */
+ static ResolvedBindings noBindings(Key key) {
+ return new AutoValue_ResolvedBindings(
+ key,
+ ImmutableSetMultimap.of(),
+ ImmutableMap.of(),
+ ImmutableSet.of(),
+ ImmutableSet.of(),
+ ImmutableSet.of());
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/SourceFiles.java b/java/dagger/internal/codegen/binding/SourceFiles.java
new file mode 100644
index 000000000..3b6d9d9d1
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/SourceFiles.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Verify.verify;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.javapoet.TypeNames.DOUBLE_CHECK;
+import static dagger.internal.codegen.javapoet.TypeNames.MAP_FACTORY;
+import static dagger.internal.codegen.javapoet.TypeNames.MAP_OF_PRODUCED_PRODUCER;
+import static dagger.internal.codegen.javapoet.TypeNames.MAP_OF_PRODUCER_PRODUCER;
+import static dagger.internal.codegen.javapoet.TypeNames.MAP_PRODUCER;
+import static dagger.internal.codegen.javapoet.TypeNames.MAP_PROVIDER_FACTORY;
+import static dagger.internal.codegen.javapoet.TypeNames.PROVIDER_OF_LAZY;
+import static dagger.internal.codegen.javapoet.TypeNames.SET_FACTORY;
+import static dagger.internal.codegen.javapoet.TypeNames.SET_OF_PRODUCED_PRODUCER;
+import static dagger.internal.codegen.javapoet.TypeNames.SET_PRODUCER;
+import static dagger.model.BindingKind.ASSISTED_INJECTION;
+import static dagger.model.BindingKind.INJECTION;
+import static dagger.model.BindingKind.MULTIBOUND_MAP;
+import static dagger.model.BindingKind.MULTIBOUND_SET;
+import static javax.lang.model.SourceVersion.isName;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeVariableName;
+import dagger.internal.SetFactory;
+import dagger.internal.codegen.base.MapType;
+import dagger.internal.codegen.base.SetType;
+import dagger.model.DependencyRequest;
+import dagger.model.RequestKind;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import dagger.producers.internal.SetOfProducedProducer;
+import dagger.producers.internal.SetProducer;
+import java.util.List;
+import javax.inject.Provider;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+
+/** Utilities for generating files. */
+public class SourceFiles {
+
+ private static final Joiner CLASS_FILE_NAME_JOINER = Joiner.on('_');
+
+ /**
+ * Generates names and keys for the factory class fields needed to hold the framework classes for
+ * all of the dependencies of {@code binding}. It is responsible for choosing a name that
+ *
+ * <ul>
+ * <li>represents all of the dependency requests for this key
+ * <li>is <i>probably</i> associated with the type being bound
+ * <li>is unique within the class
+ * </ul>
+ *
+ * @param binding must be an unresolved binding (type parameters must match its type element's)
+ */
+ public static ImmutableMap<DependencyRequest, FrameworkField>
+ generateBindingFieldsForDependencies(Binding binding) {
+ checkArgument(!binding.unresolved().isPresent(), "binding must be unresolved: %s", binding);
+
+ FrameworkTypeMapper frameworkTypeMapper =
+ FrameworkTypeMapper.forBindingType(binding.bindingType());
+
+ return Maps.toMap(
+ binding.dependencies(),
+ dependency ->
+ FrameworkField.create(
+ ClassName.get(
+ frameworkTypeMapper.getFrameworkType(dependency.kind()).frameworkClass()),
+ TypeName.get(dependency.key().type()),
+ DependencyVariableNamer.name(dependency)));
+ }
+
+ public static CodeBlock frameworkTypeUsageStatement(
+ CodeBlock frameworkTypeMemberSelect, RequestKind dependencyKind) {
+ switch (dependencyKind) {
+ case LAZY:
+ return CodeBlock.of("$T.lazy($L)", DOUBLE_CHECK, frameworkTypeMemberSelect);
+ case INSTANCE:
+ case FUTURE:
+ return CodeBlock.of("$L.get()", frameworkTypeMemberSelect);
+ case PROVIDER:
+ case PRODUCER:
+ return frameworkTypeMemberSelect;
+ case PROVIDER_OF_LAZY:
+ return CodeBlock.of("$T.create($L)", PROVIDER_OF_LAZY, frameworkTypeMemberSelect);
+ default: // including PRODUCED
+ throw new AssertionError(dependencyKind);
+ }
+ }
+
+ /**
+ * Returns a mapping of {@link DependencyRequest}s to {@link CodeBlock}s that {@linkplain
+ * #frameworkTypeUsageStatement(CodeBlock, RequestKind) use them}.
+ */
+ public static ImmutableMap<DependencyRequest, CodeBlock> frameworkFieldUsages(
+ ImmutableSet<DependencyRequest> dependencies,
+ ImmutableMap<DependencyRequest, FieldSpec> fields) {
+ return Maps.toMap(
+ dependencies,
+ dep -> frameworkTypeUsageStatement(CodeBlock.of("$N", fields.get(dep)), dep.kind()));
+ }
+
+ /** Returns the generated factory or members injector name for a binding. */
+ public static ClassName generatedClassNameForBinding(Binding binding) {
+ switch (binding.bindingType()) {
+ case PROVISION:
+ case PRODUCTION:
+ ContributionBinding contribution = (ContributionBinding) binding;
+ switch (contribution.kind()) {
+ case ASSISTED_INJECTION:
+ case INJECTION:
+ case PROVISION:
+ case PRODUCTION:
+ return elementBasedClassName(
+ MoreElements.asExecutable(binding.bindingElement().get()), "Factory");
+
+ case ASSISTED_FACTORY:
+ return siblingClassName(MoreElements.asType(binding.bindingElement().get()), "_Impl");
+
+ default:
+ throw new AssertionError();
+ }
+
+ case MEMBERS_INJECTION:
+ return membersInjectorNameForType(
+ ((MembersInjectionBinding) binding).membersInjectedType());
+ }
+ throw new AssertionError();
+ }
+
+ /**
+ * Calculates an appropriate {@link ClassName} for a generated class that is based on {@code
+ * element}, appending {@code suffix} at the end.
+ *
+ * <p>This will always return a {@linkplain ClassName#topLevelClassName() top level class name},
+ * even if {@code element}'s enclosing class is a nested type.
+ */
+ public static ClassName elementBasedClassName(ExecutableElement element, String suffix) {
+ ClassName enclosingClassName =
+ ClassName.get(MoreElements.asType(element.getEnclosingElement()));
+ String methodName =
+ element.getKind().equals(ElementKind.CONSTRUCTOR)
+ ? ""
+ : LOWER_CAMEL.to(UPPER_CAMEL, element.getSimpleName().toString());
+ return ClassName.get(
+ enclosingClassName.packageName(),
+ classFileName(enclosingClassName) + "_" + methodName + suffix);
+ }
+
+ public static TypeName parameterizedGeneratedTypeNameForBinding(Binding binding) {
+ ClassName className = generatedClassNameForBinding(binding);
+ ImmutableList<TypeVariableName> typeParameters = bindingTypeElementTypeVariableNames(binding);
+ return typeParameters.isEmpty()
+ ? className
+ : ParameterizedTypeName.get(className, Iterables.toArray(typeParameters, TypeName.class));
+ }
+
+ public static ClassName membersInjectorNameForType(TypeElement typeElement) {
+ return siblingClassName(typeElement, "_MembersInjector");
+ }
+
+ public static String memberInjectedFieldSignatureForVariable(VariableElement variableElement) {
+ return MoreElements.asType(variableElement.getEnclosingElement()).getQualifiedName()
+ + "."
+ + variableElement.getSimpleName();
+ }
+
+ public static String classFileName(ClassName className) {
+ return CLASS_FILE_NAME_JOINER.join(className.simpleNames());
+ }
+
+ public static ClassName generatedMonitoringModuleName(TypeElement componentElement) {
+ return siblingClassName(componentElement, "_MonitoringModule");
+ }
+
+ // TODO(ronshapiro): when JavaPoet migration is complete, replace the duplicated code
+ // which could use this.
+ private static ClassName siblingClassName(TypeElement typeElement, String suffix) {
+ ClassName className = ClassName.get(typeElement);
+ return className.topLevelClassName().peerClass(classFileName(className) + suffix);
+ }
+
+ /**
+ * The {@link java.util.Set} factory class name appropriate for set bindings.
+ *
+ * <ul>
+ * <li>{@link SetFactory} for provision bindings.
+ * <li>{@link SetProducer} for production bindings for {@code Set<T>}.
+ * <li>{@link SetOfProducedProducer} for production bindings for {@code Set<Produced<T>>}.
+ * </ul>
+ */
+ public static ClassName setFactoryClassName(ContributionBinding binding) {
+ checkArgument(binding.kind().equals(MULTIBOUND_SET));
+ if (binding.bindingType().equals(BindingType.PROVISION)) {
+ return SET_FACTORY;
+ } else {
+ SetType setType = SetType.from(binding.key());
+ return setType.elementsAreTypeOf(Produced.class) ? SET_OF_PRODUCED_PRODUCER : SET_PRODUCER;
+ }
+ }
+
+ /** The {@link java.util.Map} factory class name appropriate for map bindings. */
+ public static ClassName mapFactoryClassName(ContributionBinding binding) {
+ checkState(binding.kind().equals(MULTIBOUND_MAP), binding.kind());
+ MapType mapType = MapType.from(binding.key());
+ switch (binding.bindingType()) {
+ case PROVISION:
+ return mapType.valuesAreTypeOf(Provider.class) ? MAP_PROVIDER_FACTORY : MAP_FACTORY;
+ case PRODUCTION:
+ return mapType.valuesAreFrameworkType()
+ ? mapType.valuesAreTypeOf(Producer.class)
+ ? MAP_OF_PRODUCER_PRODUCER
+ : MAP_OF_PRODUCED_PRODUCER
+ : MAP_PRODUCER;
+ default:
+ throw new IllegalArgumentException(binding.bindingType().toString());
+ }
+ }
+
+ public static ImmutableList<TypeVariableName> bindingTypeElementTypeVariableNames(
+ Binding binding) {
+ if (binding instanceof ContributionBinding) {
+ ContributionBinding contributionBinding = (ContributionBinding) binding;
+ if (!(contributionBinding.kind() == INJECTION
+ || contributionBinding.kind() == ASSISTED_INJECTION)
+ && !contributionBinding.requiresModuleInstance()) {
+ return ImmutableList.of();
+ }
+ }
+ List<? extends TypeParameterElement> typeParameters =
+ binding.bindingTypeElement().get().getTypeParameters();
+ return typeParameters.stream().map(TypeVariableName::get).collect(toImmutableList());
+ }
+
+ /**
+ * Returns a name to be used for variables of the given {@linkplain TypeElement type}. Prefer
+ * semantically meaningful variable names, but if none can be derived, this will produce something
+ * readable.
+ */
+ // TODO(gak): maybe this should be a function of TypeMirrors instead of Elements?
+ public static String simpleVariableName(TypeElement typeElement) {
+ String candidateName = UPPER_CAMEL.to(LOWER_CAMEL, typeElement.getSimpleName().toString());
+ String variableName = protectAgainstKeywords(candidateName);
+ verify(isName(variableName), "'%s' was expected to be a valid variable name");
+ return variableName;
+ }
+
+ public static String protectAgainstKeywords(String candidateName) {
+ switch (candidateName) {
+ case "package":
+ return "pkg";
+ case "boolean":
+ return "b";
+ case "double":
+ return "d";
+ case "byte":
+ return "b";
+ case "int":
+ return "i";
+ case "short":
+ return "s";
+ case "char":
+ return "c";
+ case "void":
+ return "v";
+ case "class":
+ return "clazz";
+ case "float":
+ return "f";
+ case "long":
+ return "l";
+ default:
+ return SourceVersion.isKeyword(candidateName) ? candidateName + '_' : candidateName;
+ }
+ }
+
+ private SourceFiles() {}
+}
diff --git a/java/dagger/internal/codegen/binding/SubcomponentCreatorBindingEdgeImpl.java b/java/dagger/internal/codegen/binding/SubcomponentCreatorBindingEdgeImpl.java
new file mode 100644
index 000000000..93e79b5b9
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/SubcomponentCreatorBindingEdgeImpl.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.extension.DaggerStreams.presentValues;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static java.util.stream.Collectors.joining;
+
+import com.google.common.collect.ImmutableSet;
+import dagger.model.BindingGraph.SubcomponentCreatorBindingEdge;
+import javax.lang.model.element.TypeElement;
+
+/** An implementation of {@link SubcomponentCreatorBindingEdge}. */
+public final class SubcomponentCreatorBindingEdgeImpl implements SubcomponentCreatorBindingEdge {
+
+ private final ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations;
+
+ SubcomponentCreatorBindingEdgeImpl(
+ ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations) {
+ this.subcomponentDeclarations = subcomponentDeclarations;
+ }
+
+ @Override
+ public ImmutableSet<TypeElement> declaringModules() {
+ return subcomponentDeclarations.stream()
+ .map(SubcomponentDeclaration::contributingModule)
+ .flatMap(presentValues())
+ .collect(toImmutableSet());
+ }
+
+ @Override
+ public String toString() {
+ return "subcomponent declared by "
+ + (subcomponentDeclarations.size() == 1
+ ? getOnlyElement(declaringModules()).getQualifiedName()
+ : declaringModules().stream()
+ .map(TypeElement::getQualifiedName)
+ .collect(joining(", ", "{", "}")));
+ }
+}
diff --git a/java/dagger/internal/codegen/binding/SubcomponentDeclaration.java b/java/dagger/internal/codegen/binding/SubcomponentDeclaration.java
new file mode 100644
index 000000000..4f1f3efd2
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/SubcomponentDeclaration.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.binding;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationElementAndValue;
+import static dagger.internal.codegen.binding.ConfigurationAnnotations.getSubcomponentCreator;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableSet;
+import dagger.internal.codegen.base.ModuleAnnotation;
+import dagger.model.Key;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * A declaration for a subcomponent that is included in a module via {@link
+ * dagger.Module#subcomponents()}.
+ */
+@AutoValue
+public abstract class SubcomponentDeclaration extends BindingDeclaration {
+ /**
+ * Key for the {@link dagger.Subcomponent.Builder} or {@link
+ * dagger.producers.ProductionSubcomponent.Builder} of {@link #subcomponentType()}.
+ */
+ @Override
+ public abstract Key key();
+
+ /**
+ * The type element that defines the {@link dagger.Subcomponent} or {@link
+ * dagger.producers.ProductionSubcomponent} for this declaration.
+ */
+ abstract TypeElement subcomponentType();
+
+ /** The module annotation. */
+ public abstract ModuleAnnotation moduleAnnotation();
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object obj);
+
+ /** A {@link SubcomponentDeclaration} factory. */
+ public static class Factory {
+ private final KeyFactory keyFactory;
+
+ @Inject
+ Factory(KeyFactory keyFactory) {
+ this.keyFactory = keyFactory;
+ }
+
+ ImmutableSet<SubcomponentDeclaration> forModule(TypeElement module) {
+ ImmutableSet.Builder<SubcomponentDeclaration> declarations = ImmutableSet.builder();
+ ModuleAnnotation moduleAnnotation = ModuleAnnotation.moduleAnnotation(module).get();
+ Element subcomponentAttribute =
+ getAnnotationElementAndValue(moduleAnnotation.annotation(), "subcomponents").getKey();
+ for (TypeElement subcomponent : moduleAnnotation.subcomponents()) {
+ declarations.add(
+ new AutoValue_SubcomponentDeclaration(
+ Optional.of(subcomponentAttribute),
+ Optional.of(module),
+ keyFactory.forSubcomponentCreator(
+ getSubcomponentCreator(subcomponent).get().asType()),
+ subcomponent,
+ moduleAnnotation));
+ }
+ return declarations.build();
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/BUILD b/java/dagger/internal/codegen/bindinggraphvalidation/BUILD
new file mode 100644
index 000000000..da35b516a
--- /dev/null
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/BUILD
@@ -0,0 +1,45 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Classes related to BindingGraph validation.
+
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "bindinggraphvalidation",
+ srcs = glob(["*.java"]),
+ plugins = ["//java/dagger/internal/codegen/bootstrap"],
+ tags = ["maven:merged"],
+ deps = [
+ "//java/dagger:core",
+ "//java/dagger/internal/codegen/base",
+ "//java/dagger/internal/codegen/binding",
+ "//java/dagger/internal/codegen/compileroption",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/codegen/kotlin",
+ "//java/dagger/internal/codegen/langmodel",
+ "//java/dagger/internal/codegen/validation",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:graph",
+ "//java/dagger/producers",
+ "//java/dagger/spi",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/BindingGraphValidationModule.java b/java/dagger/internal/codegen/bindinggraphvalidation/BindingGraphValidationModule.java
new file mode 100644
index 000000000..a1f1848cf
--- /dev/null
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/BindingGraphValidationModule.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.bindinggraphvalidation;
+
+import com.google.common.collect.ImmutableSet;
+import dagger.Module;
+import dagger.Provides;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.validation.CompositeBindingGraphPlugin;
+import dagger.internal.codegen.validation.Validation;
+import dagger.spi.BindingGraphPlugin;
+
+/** Binds the set of {@link BindingGraphPlugin}s used to implement Dagger validation. */
+@Module
+public interface BindingGraphValidationModule {
+
+ @Provides
+ @Validation
+ static ImmutableSet<BindingGraphPlugin> providePlugins(
+ CompositeBindingGraphPlugin.Factory factory,
+ CompilerOptions compilerOptions,
+ DependencyCycleValidator validation1,
+ DependsOnProductionExecutorValidator validation2,
+ DuplicateBindingsValidator validation3,
+ IncompatiblyScopedBindingsValidator validation4,
+ InjectBindingValidator validation5,
+ MapMultibindingValidator validation6,
+ MissingBindingValidator validation7,
+ NullableBindingValidator validation8,
+ ProvisionDependencyOnProducerBindingValidator validation9,
+ SubcomponentFactoryMethodValidator validation10) {
+ ImmutableSet<BindingGraphPlugin> plugins = ImmutableSet.of(
+ validation1,
+ validation2,
+ validation3,
+ validation4,
+ validation5,
+ validation6,
+ validation7,
+ validation8,
+ validation9,
+ validation10);
+ if (compilerOptions.experimentalDaggerErrorMessages()) {
+ return ImmutableSet.of(factory.create(plugins, "Dagger/Validation"));
+ } else {
+ return plugins;
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/DependencyCycleValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/DependencyCycleValidator.java
new file mode 100644
index 000000000..bc89ebbb2
--- /dev/null
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/DependencyCycleValidator.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.bindinggraphvalidation;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Iterables.getLast;
+import static com.google.common.collect.Iterables.limit;
+import static com.google.common.collect.Iterables.skip;
+import static com.google.common.collect.Sets.newHashSetWithExpectedSize;
+import static dagger.internal.codegen.base.RequestKinds.extractKeyType;
+import static dagger.internal.codegen.base.RequestKinds.getRequestKind;
+import static dagger.internal.codegen.extension.DaggerGraphs.shortestPath;
+import static dagger.internal.codegen.extension.DaggerStreams.instancesOf;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.graph.EndpointPair;
+import com.google.common.graph.Graphs;
+import com.google.common.graph.ImmutableNetwork;
+import com.google.common.graph.MutableNetwork;
+import com.google.common.graph.NetworkBuilder;
+import dagger.internal.codegen.base.MapType;
+import dagger.internal.codegen.base.OptionalType;
+import dagger.internal.codegen.binding.DependencyRequestFormatter;
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.ComponentNode;
+import dagger.model.BindingGraph.DependencyEdge;
+import dagger.model.BindingGraph.Node;
+import dagger.model.BindingKind;
+import dagger.model.DependencyRequest;
+import dagger.model.RequestKind;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Stream;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.lang.model.type.TypeMirror;
+
+/** Reports errors for dependency cycles. */
+final class DependencyCycleValidator implements BindingGraphPlugin {
+
+ private final DependencyRequestFormatter dependencyRequestFormatter;
+
+ @Inject
+ DependencyCycleValidator(DependencyRequestFormatter dependencyRequestFormatter) {
+ this.dependencyRequestFormatter = dependencyRequestFormatter;
+ }
+
+ @Override
+ public String pluginName() {
+ return "Dagger/DependencyCycle";
+ }
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ ImmutableNetwork<Node, DependencyEdge> dependencyGraph =
+ nonCycleBreakingDependencyGraph(bindingGraph);
+ // First check the graph for a cycle. If there is one, then we'll do more work to report where.
+ if (!Graphs.hasCycle(dependencyGraph)) {
+ return;
+ }
+ // Check each endpoint pair only once, no matter how many parallel edges connect them.
+ Set<EndpointPair<Node>> dependencyEndpointPairs = dependencyGraph.asGraph().edges();
+ Set<EndpointPair<Node>> visited = newHashSetWithExpectedSize(dependencyEndpointPairs.size());
+ for (EndpointPair<Node> endpointPair : dependencyEndpointPairs) {
+ cycleContainingEndpointPair(endpointPair, dependencyGraph, visited)
+ .ifPresent(cycle -> reportCycle(cycle, bindingGraph, diagnosticReporter));
+ }
+ }
+
+ private Optional<Cycle<Node>> cycleContainingEndpointPair(
+ EndpointPair<Node> endpoints,
+ ImmutableNetwork<Node, DependencyEdge> dependencyGraph,
+ Set<EndpointPair<Node>> visited) {
+ if (!visited.add(endpoints)) {
+ // don't recheck endpoints we already know are part of a cycle
+ return Optional.empty();
+ }
+
+ // If there's a path from the target back to the source, there's a cycle.
+ ImmutableList<Node> cycleNodes =
+ shortestPath(dependencyGraph, endpoints.target(), endpoints.source());
+ if (cycleNodes.isEmpty()) {
+ return Optional.empty();
+ }
+
+ Cycle<Node> cycle = Cycle.fromPath(cycleNodes);
+ visited.addAll(cycle.endpointPairs()); // no need to check any edge in this cycle again
+ return Optional.of(cycle);
+ }
+
+ /**
+ * Reports a dependency cycle at the dependency into the cycle that is closest to an entry point.
+ *
+ * <p>For cycles found in reachable binding graphs, looks for the shortest path from the component
+ * that contains the cycle (all bindings in a cycle must be in the same component; see below) to
+ * some binding in the cycle. Then looks for the last dependency in that path that is not in the
+ * cycle; that is the dependency that will be reported, so that the dependency trace will end just
+ * before the cycle.
+ *
+ * <p>For cycles found during full binding graph validation, just reports the component that
+ * contains the cycle.
+ *
+ * <p>Proof (by counterexample) that all bindings in a cycle must be in the same component: Assume
+ * one binding in the cycle is in a parent component. Bindings cannot depend on bindings in child
+ * components, so that binding cannot depend on the next binding in the cycle.
+ */
+ private void reportCycle(
+ Cycle<Node> cycle, BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ if (bindingGraph.isFullBindingGraph()) {
+ diagnosticReporter.reportComponent(
+ ERROR,
+ bindingGraph.componentNode(cycle.nodes().asList().get(0).componentPath()).get(),
+ errorMessage(cycle, bindingGraph));
+ return;
+ }
+
+ ImmutableList<Node> path = shortestPathToCycleFromAnEntryPoint(cycle, bindingGraph);
+ Node cycleStartNode = path.get(path.size() - 1);
+ Node previousNode = path.get(path.size() - 2);
+ DependencyEdge dependencyToReport =
+ chooseDependencyEdgeConnecting(previousNode, cycleStartNode, bindingGraph);
+ diagnosticReporter.reportDependency(
+ ERROR, dependencyToReport, errorMessage(cycle.shift(cycleStartNode), bindingGraph));
+ }
+
+ private ImmutableList<Node> shortestPathToCycleFromAnEntryPoint(
+ Cycle<Node> cycle, BindingGraph bindingGraph) {
+ Node someCycleNode = cycle.nodes().asList().get(0);
+ ComponentNode componentContainingCycle =
+ bindingGraph.componentNode(someCycleNode.componentPath()).get();
+ ImmutableList<Node> pathToCycle =
+ shortestPath(bindingGraph.network(), componentContainingCycle, someCycleNode);
+ return subpathToCycle(pathToCycle, cycle);
+ }
+
+ /**
+ * Returns the subpath from the head of {@code path} to the first node in {@code path} that's in
+ * the cycle.
+ */
+ private ImmutableList<Node> subpathToCycle(ImmutableList<Node> path, Cycle<Node> cycle) {
+ ImmutableList.Builder<Node> subpath = ImmutableList.builder();
+ for (Node node : path) {
+ subpath.add(node);
+ if (cycle.nodes().contains(node)) {
+ return subpath.build();
+ }
+ }
+ throw new IllegalArgumentException(
+ "path " + path + " doesn't contain any nodes in cycle " + cycle);
+ }
+
+ private String errorMessage(Cycle<Node> cycle, BindingGraph graph) {
+ StringBuilder message = new StringBuilder("Found a dependency cycle:");
+ ImmutableList<DependencyRequest> cycleRequests =
+ cycle.endpointPairs().stream()
+ // TODO(dpb): Would be nice to take the dependency graph here.
+ .map(endpointPair -> nonCycleBreakingEdge(endpointPair, graph))
+ .map(DependencyEdge::dependencyRequest)
+ .collect(toImmutableList())
+ .reverse();
+ dependencyRequestFormatter.formatIndentedList(message, cycleRequests, 0);
+ return message.toString();
+ }
+
+ /**
+ * Returns one of the edges between two nodes that doesn't {@linkplain
+ * #breaksCycle(DependencyEdge, BindingGraph) break} a cycle.
+ */
+ private DependencyEdge nonCycleBreakingEdge(EndpointPair<Node> endpointPair, BindingGraph graph) {
+ return graph.network().edgesConnecting(endpointPair.source(), endpointPair.target()).stream()
+ .flatMap(instancesOf(DependencyEdge.class))
+ .filter(edge -> !breaksCycle(edge, graph))
+ .findFirst()
+ .get();
+ }
+
+ private boolean breaksCycle(DependencyEdge edge, BindingGraph graph) {
+ // Map<K, V> multibindings depend on Map<K, Provider<V>> entries, but those don't break any
+ // cycles, so ignore them.
+ if (edge.dependencyRequest().key().multibindingContributionIdentifier().isPresent()) {
+ return false;
+ }
+ if (breaksCycle(edge.dependencyRequest().key().type(), edge.dependencyRequest().kind())) {
+ return true;
+ }
+ Node target = graph.network().incidentNodes(edge).target();
+ if (target instanceof dagger.model.Binding
+ && ((dagger.model.Binding) target).kind().equals(BindingKind.OPTIONAL)) {
+ /* For @BindsOptionalOf bindings, unwrap the type inside the Optional. If the unwrapped type
+ * breaks the cycle, so does the optional binding. */
+ TypeMirror optionalValueType = OptionalType.from(edge.dependencyRequest().key()).valueType();
+ RequestKind requestKind = getRequestKind(optionalValueType);
+ return breaksCycle(extractKeyType(optionalValueType), requestKind);
+ }
+ return false;
+ }
+
+ private boolean breaksCycle(TypeMirror requestedType, RequestKind requestKind) {
+ switch (requestKind) {
+ case PROVIDER:
+ case LAZY:
+ case PROVIDER_OF_LAZY:
+ return true;
+
+ case INSTANCE:
+ if (MapType.isMap(requestedType)) {
+ MapType mapType = MapType.from(requestedType);
+ return !mapType.isRawType() && mapType.valuesAreTypeOf(Provider.class);
+ }
+ // fall through
+
+ default:
+ return false;
+ }
+ }
+
+ private DependencyEdge chooseDependencyEdgeConnecting(
+ Node source, Node target, BindingGraph bindingGraph) {
+ return bindingGraph.network().edgesConnecting(source, target).stream()
+ .flatMap(instancesOf(DependencyEdge.class))
+ .findFirst()
+ .get();
+ }
+
+ /** Returns the subgraph containing only {@link DependencyEdge}s that would not break a cycle. */
+ // TODO(dpb): Return a network containing only Binding nodes.
+ private ImmutableNetwork<Node, DependencyEdge> nonCycleBreakingDependencyGraph(
+ BindingGraph bindingGraph) {
+ MutableNetwork<Node, DependencyEdge> dependencyNetwork =
+ NetworkBuilder.from(bindingGraph.network())
+ .expectedNodeCount(bindingGraph.network().nodes().size())
+ .expectedEdgeCount(bindingGraph.dependencyEdges().size())
+ .build();
+ bindingGraph.dependencyEdges().stream()
+ .filter(edge -> !breaksCycle(edge, bindingGraph))
+ .forEach(
+ edge -> {
+ EndpointPair<Node> endpoints = bindingGraph.network().incidentNodes(edge);
+ dependencyNetwork.addEdge(endpoints.source(), endpoints.target(), edge);
+ });
+ return ImmutableNetwork.copyOf(dependencyNetwork);
+ }
+
+ /**
+ * An ordered set of endpoint pairs representing the edges in the cycle. The target of each pair
+ * is the source of the next pair. The target of the last pair is the source of the first pair.
+ */
+ @AutoValue
+ abstract static class Cycle<N> {
+ /**
+ * The ordered set of endpoint pairs representing the edges in the cycle. The target of each
+ * pair is the source of the next pair. The target of the last pair is the source of the first
+ * pair.
+ */
+ abstract ImmutableSet<EndpointPair<N>> endpointPairs();
+
+ /** Returns the nodes that participate in the cycle. */
+ ImmutableSet<N> nodes() {
+ return endpointPairs().stream()
+ .flatMap(pair -> Stream.of(pair.source(), pair.target()))
+ .collect(toImmutableSet());
+ }
+
+ /** Returns the number of edges in the cycle. */
+ int size() {
+ return endpointPairs().size();
+ }
+
+ /**
+ * Shifts this cycle so that it starts with a specific node.
+ *
+ * @return a cycle equivalent to this one but whose first pair starts with {@code startNode}
+ */
+ Cycle<N> shift(N startNode) {
+ int startIndex = Iterables.indexOf(endpointPairs(), pair -> pair.source().equals(startNode));
+ checkArgument(
+ startIndex >= 0, "startNode (%s) is not part of this cycle: %s", startNode, this);
+ if (startIndex == 0) {
+ return this;
+ }
+ ImmutableSet.Builder<EndpointPair<N>> shifted = ImmutableSet.builder();
+ shifted.addAll(skip(endpointPairs(), startIndex));
+ shifted.addAll(limit(endpointPairs(), size() - startIndex));
+ return new AutoValue_DependencyCycleValidator_Cycle<>(shifted.build());
+ }
+
+ @Override
+ public final String toString() {
+ return endpointPairs().toString();
+ }
+
+ /**
+ * Creates a {@link Cycle} from a nonempty list of nodes, assuming there is an edge between each
+ * pair of nodes as well as an edge from the last node to the first.
+ */
+ static <N> Cycle<N> fromPath(List<N> nodes) {
+ checkArgument(!nodes.isEmpty());
+ ImmutableSet.Builder<EndpointPair<N>> cycle = ImmutableSet.builder();
+ cycle.add(EndpointPair.ordered(getLast(nodes), nodes.get(0)));
+ for (int i = 0; i < nodes.size() - 1; i++) {
+ cycle.add(EndpointPair.ordered(nodes.get(i), nodes.get(i + 1)));
+ }
+ return new AutoValue_DependencyCycleValidator_Cycle<>(cycle.build());
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/DependsOnProductionExecutorValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/DependsOnProductionExecutorValidator.java
new file mode 100644
index 000000000..08e2c3e65
--- /dev/null
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/DependsOnProductionExecutorValidator.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.bindinggraphvalidation;
+
+import static dagger.internal.codegen.extension.DaggerStreams.instancesOf;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import dagger.internal.codegen.binding.KeyFactory;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.MaybeBinding;
+import dagger.model.Key;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import javax.inject.Inject;
+
+/**
+ * Reports an error on all bindings that depend explicitly on the {@code @Production Executor} key.
+ */
+// TODO(dpb,beder): Validate this during @Inject/@Provides/@Produces validation.
+final class DependsOnProductionExecutorValidator implements BindingGraphPlugin {
+ private final CompilerOptions compilerOptions;
+ private final KeyFactory keyFactory;
+
+ @Inject
+ DependsOnProductionExecutorValidator(CompilerOptions compilerOptions, KeyFactory keyFactory) {
+ this.compilerOptions = compilerOptions;
+ this.keyFactory = keyFactory;
+ }
+
+ @Override
+ public String pluginName() {
+ return "Dagger/DependsOnProductionExecutor";
+ }
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ if (!compilerOptions.usesProducers()) {
+ return;
+ }
+
+ Key productionImplementationExecutorKey = keyFactory.forProductionImplementationExecutor();
+ Key productionExecutorKey = keyFactory.forProductionExecutor();
+
+ bindingGraph.network().nodes().stream()
+ .flatMap(instancesOf(MaybeBinding.class))
+ .filter(node -> node.key().equals(productionExecutorKey))
+ .flatMap(productionExecutor -> bindingGraph.requestingBindings(productionExecutor).stream())
+ .filter(binding -> !binding.key().equals(productionImplementationExecutorKey))
+ .forEach(binding -> reportError(diagnosticReporter, binding));
+ }
+
+ private void reportError(DiagnosticReporter diagnosticReporter, dagger.model.Binding binding) {
+ diagnosticReporter.reportBinding(
+ ERROR, binding, "%s may not depend on the production executor", binding.key());
+ }
+}
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/DuplicateBindingsValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/DuplicateBindingsValidator.java
new file mode 100644
index 000000000..5c4da51b5
--- /dev/null
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/DuplicateBindingsValidator.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.bindinggraphvalidation;
+
+import static com.google.common.base.Verify.verify;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.base.Formatter.INDENT;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSetMultimap;
+import static dagger.model.BindingKind.INJECTION;
+import static dagger.model.BindingKind.MEMBERS_INJECTION;
+import static java.util.Comparator.comparing;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMultiset;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.Sets;
+import dagger.internal.codegen.base.Formatter;
+import dagger.internal.codegen.binding.BindingDeclaration;
+import dagger.internal.codegen.binding.BindingDeclarationFormatter;
+import dagger.internal.codegen.binding.BindingNode;
+import dagger.internal.codegen.binding.MultibindingDeclaration;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.model.Binding;
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.ComponentNode;
+import dagger.model.BindingKind;
+import dagger.model.ComponentPath;
+import dagger.model.Key;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Predicate;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic.Kind;
+
+/** Reports errors for conflicting bindings with the same key. */
+final class DuplicateBindingsValidator implements BindingGraphPlugin {
+
+ private static final Comparator<Binding> BY_LENGTH_OF_COMPONENT_PATH =
+ comparing(binding -> binding.componentPath().components().size());
+
+ private final BindingDeclarationFormatter bindingDeclarationFormatter;
+ private final CompilerOptions compilerOptions;
+
+ @Inject
+ DuplicateBindingsValidator(
+ BindingDeclarationFormatter bindingDeclarationFormatter, CompilerOptions compilerOptions) {
+ this.bindingDeclarationFormatter = bindingDeclarationFormatter;
+ this.compilerOptions = compilerOptions;
+ }
+
+ @Override
+ public String pluginName() {
+ return "Dagger/DuplicateBindings";
+ }
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ // If two unrelated subcomponents have the same duplicate bindings only because they install the
+ // same two modules, then fixing the error in one subcomponent will uncover the second
+ // subcomponent to fix.
+ // TODO(ronshapiro): Explore ways to address such underreporting without overreporting.
+ Set<ImmutableSet<BindingElement>> reportedDuplicateBindingSets = new HashSet<>();
+ duplicateBindingSets(bindingGraph)
+ .forEach(
+ duplicateBindings -> {
+ // Only report each set of duplicate bindings once, ignoring the installed component.
+ if (reportedDuplicateBindingSets.add(duplicateBindings.keySet())) {
+ reportDuplicateBindings(duplicateBindings, bindingGraph, diagnosticReporter);
+ }
+ });
+ }
+
+ /**
+ * Returns sets of duplicate bindings. Bindings are duplicates if they bind the same key and are
+ * visible from the same component. Two bindings that differ only in the component that owns them
+ * are not considered to be duplicates, because that means the same binding was "copied" down to a
+ * descendant component because it depends on local multibindings or optional bindings. Hence each
+ * "set" is represented as a multimap from binding element (ignoring component path) to binding.
+ */
+ private ImmutableSet<ImmutableSetMultimap<BindingElement, Binding>> duplicateBindingSets(
+ BindingGraph bindingGraph) {
+ return groupBindingsByKey(bindingGraph).stream()
+ .flatMap(bindings -> mutuallyVisibleSubsets(bindings).stream())
+ .map(BindingElement::index)
+ .filter(duplicates -> duplicates.keySet().size() > 1)
+ .collect(toImmutableSet());
+ }
+
+ private static ImmutableSet<ImmutableSet<Binding>> groupBindingsByKey(BindingGraph bindingGraph) {
+ return valueSetsForEachKey(
+ bindingGraph.bindings().stream()
+ .filter(binding -> !binding.kind().equals(MEMBERS_INJECTION))
+ .collect(toImmutableSetMultimap(Binding::key, binding -> binding)));
+ }
+
+ /**
+ * Returns the subsets of the input set that contain bindings that are all visible from the same
+ * component. A binding is visible from its component and all its descendants.
+ */
+ private static ImmutableSet<ImmutableSet<Binding>> mutuallyVisibleSubsets(
+ Set<Binding> duplicateBindings) {
+ ImmutableListMultimap<ComponentPath, Binding> bindingsByComponentPath =
+ Multimaps.index(duplicateBindings, Binding::componentPath);
+ ImmutableSetMultimap.Builder<ComponentPath, Binding> mutuallyVisibleBindings =
+ ImmutableSetMultimap.builder();
+ bindingsByComponentPath
+ .asMap()
+ .forEach(
+ (componentPath, bindings) -> {
+ mutuallyVisibleBindings.putAll(componentPath, bindings);
+ for (ComponentPath ancestor = componentPath; !ancestor.atRoot(); ) {
+ ancestor = ancestor.parent();
+ ImmutableList<Binding> bindingsInAncestor = bindingsByComponentPath.get(ancestor);
+ mutuallyVisibleBindings.putAll(componentPath, bindingsInAncestor);
+ }
+ });
+ return valueSetsForEachKey(mutuallyVisibleBindings.build());
+ }
+
+ private void reportDuplicateBindings(
+ ImmutableSetMultimap<BindingElement, Binding> duplicateBindings,
+ BindingGraph bindingGraph,
+ DiagnosticReporter diagnosticReporter) {
+ if (explicitBindingConfictsWithInject(duplicateBindings.keySet())) {
+ compilerOptions
+ .explicitBindingConflictsWithInjectValidationType()
+ .diagnosticKind()
+ .ifPresent(
+ diagnosticKind ->
+ reportExplicitBindingConflictsWithInject(
+ duplicateBindings,
+ diagnosticReporter,
+ diagnosticKind,
+ bindingGraph.rootComponentNode()));
+ return;
+ }
+ ImmutableSet<Binding> bindings = ImmutableSet.copyOf(duplicateBindings.values());
+ Binding oneBinding = bindings.asList().get(0);
+ String message = bindings.stream().anyMatch(binding -> binding.kind().isMultibinding())
+ ? incompatibleBindingsMessage(oneBinding, bindings, bindingGraph)
+ : duplicateBindingMessage(oneBinding, bindings, bindingGraph);
+ if (compilerOptions.experimentalDaggerErrorMessages()) {
+ diagnosticReporter.reportComponent(
+ ERROR,
+ bindingGraph.rootComponentNode(),
+ message);
+ } else {
+ diagnosticReporter.reportBinding(
+ ERROR,
+ oneBinding,
+ message);
+ }
+ }
+
+ /**
+ * Returns {@code true} if the bindings contain one {@code @Inject} binding and one that isn't.
+ */
+ private static boolean explicitBindingConfictsWithInject(
+ ImmutableSet<BindingElement> duplicateBindings) {
+ ImmutableMultiset<BindingKind> bindingKinds =
+ Multimaps.index(duplicateBindings, BindingElement::bindingKind).keys();
+ return bindingKinds.count(INJECTION) == 1 && bindingKinds.size() == 2;
+ }
+
+ private void reportExplicitBindingConflictsWithInject(
+ ImmutableSetMultimap<BindingElement, Binding> duplicateBindings,
+ DiagnosticReporter diagnosticReporter,
+ Kind diagnosticKind,
+ ComponentNode rootComponent) {
+ Binding injectBinding =
+ rootmostBindingWithKind(k -> k.equals(INJECTION), duplicateBindings.values());
+ Binding explicitBinding =
+ rootmostBindingWithKind(k -> !k.equals(INJECTION), duplicateBindings.values());
+ StringBuilder message =
+ new StringBuilder()
+ .append(explicitBinding.key())
+ .append(" is bound multiple times:")
+ .append(formatWithComponentPath(injectBinding))
+ .append(formatWithComponentPath(explicitBinding))
+ .append(
+ "\nThis condition was never validated before, and will soon be an error. "
+ + "See https://dagger.dev/conflicting-inject.");
+
+ if (compilerOptions.experimentalDaggerErrorMessages()) {
+ diagnosticReporter.reportComponent(diagnosticKind, rootComponent, message.toString());
+ } else {
+ diagnosticReporter.reportBinding(diagnosticKind, explicitBinding, message.toString());
+ }
+ }
+
+ private String formatWithComponentPath(Binding binding) {
+ return String.format(
+ "\n%s%s [%s]",
+ Formatter.INDENT,
+ bindingDeclarationFormatter.format(((BindingNode) binding).delegate()),
+ binding.componentPath());
+ }
+
+ private String duplicateBindingMessage(
+ Binding oneBinding, ImmutableSet<Binding> duplicateBindings, BindingGraph graph) {
+ StringBuilder message =
+ new StringBuilder().append(oneBinding.key()).append(" is bound multiple times:");
+ formatDeclarations(message, 1, declarations(graph, duplicateBindings));
+ if (compilerOptions.experimentalDaggerErrorMessages()) {
+ message.append(String.format("\n%sin component: [%s]", INDENT, oneBinding.componentPath()));
+ }
+ return message.toString();
+ }
+
+ private String incompatibleBindingsMessage(
+ Binding oneBinding, ImmutableSet<Binding> duplicateBindings, BindingGraph graph) {
+ Key key = oneBinding.key();
+ ImmutableSet<dagger.model.Binding> multibindings =
+ duplicateBindings.stream()
+ .filter(binding -> binding.kind().isMultibinding())
+ .collect(toImmutableSet());
+ verify(
+ multibindings.size() == 1, "expected only one multibinding for %s: %s", key, multibindings);
+ StringBuilder message = new StringBuilder();
+ java.util.Formatter messageFormatter = new java.util.Formatter(message);
+ messageFormatter.format("%s has incompatible bindings or declarations:\n", key);
+ message.append(INDENT);
+ dagger.model.Binding multibinding = getOnlyElement(multibindings);
+ messageFormatter.format("%s bindings and declarations:", multibindingTypeString(multibinding));
+ formatDeclarations(message, 2, declarations(graph, multibindings));
+
+ Set<dagger.model.Binding> uniqueBindings =
+ Sets.filter(duplicateBindings, binding -> !binding.equals(multibinding));
+ message.append('\n').append(INDENT).append("Unique bindings and declarations:");
+ formatDeclarations(
+ message,
+ 2,
+ Sets.filter(
+ declarations(graph, uniqueBindings),
+ declaration -> !(declaration instanceof MultibindingDeclaration)));
+ if (compilerOptions.experimentalDaggerErrorMessages()) {
+ message.append(String.format("\n%sin component: [%s]", INDENT, oneBinding.componentPath()));
+ }
+ return message.toString();
+ }
+
+ private void formatDeclarations(
+ StringBuilder builder,
+ int indentLevel,
+ Iterable<? extends BindingDeclaration> bindingDeclarations) {
+ bindingDeclarationFormatter.formatIndentedList(
+ builder, ImmutableList.copyOf(bindingDeclarations), indentLevel);
+ }
+
+ private ImmutableSet<BindingDeclaration> declarations(
+ BindingGraph graph, Set<dagger.model.Binding> bindings) {
+ return bindings.stream()
+ .flatMap(binding -> declarations(graph, binding).stream())
+ .distinct()
+ .sorted(BindingDeclaration.COMPARATOR)
+ .collect(toImmutableSet());
+ }
+
+ private ImmutableSet<BindingDeclaration> declarations(
+ BindingGraph graph, dagger.model.Binding binding) {
+ ImmutableSet.Builder<BindingDeclaration> declarations = ImmutableSet.builder();
+ BindingNode bindingNode = (BindingNode) binding;
+ bindingNode.associatedDeclarations().forEach(declarations::add);
+ if (bindingDeclarationFormatter.canFormat(bindingNode.delegate())) {
+ declarations.add(bindingNode.delegate());
+ } else {
+ graph.requestedBindings(binding).stream()
+ .flatMap(requestedBinding -> declarations(graph, requestedBinding).stream())
+ .forEach(declarations::add);
+ }
+ return declarations.build();
+ }
+
+ private String multibindingTypeString(dagger.model.Binding multibinding) {
+ switch (multibinding.kind()) {
+ case MULTIBOUND_MAP:
+ return "Map";
+ case MULTIBOUND_SET:
+ return "Set";
+ default:
+ throw new AssertionError(multibinding);
+ }
+ }
+
+ private static <E> ImmutableSet<ImmutableSet<E>> valueSetsForEachKey(Multimap<?, E> multimap) {
+ return multimap.asMap().values().stream().map(ImmutableSet::copyOf).collect(toImmutableSet());
+ }
+
+ /** Returns the binding of the given kind that is closest to the root component. */
+ private static Binding rootmostBindingWithKind(
+ Predicate<BindingKind> bindingKindPredicate, ImmutableCollection<Binding> bindings) {
+ return bindings.stream()
+ .filter(b -> bindingKindPredicate.test(b.kind()))
+ .min(BY_LENGTH_OF_COMPONENT_PATH)
+ .get();
+ }
+
+ /** The identifying information about a binding, excluding its {@link Binding#componentPath()}. */
+ @AutoValue
+ abstract static class BindingElement {
+
+ abstract BindingKind bindingKind();
+
+ abstract Optional<Element> bindingElement();
+
+ abstract Optional<TypeElement> contributingModule();
+
+ static ImmutableSetMultimap<BindingElement, Binding> index(Set<Binding> bindings) {
+ return bindings.stream().collect(toImmutableSetMultimap(BindingElement::forBinding, b -> b));
+ }
+
+ private static BindingElement forBinding(Binding binding) {
+ return new AutoValue_DuplicateBindingsValidator_BindingElement(
+ binding.kind(), binding.bindingElement(), binding.contributingModule());
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/IncompatiblyScopedBindingsValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/IncompatiblyScopedBindingsValidator.java
new file mode 100644
index 000000000..a01dbaf62
--- /dev/null
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/IncompatiblyScopedBindingsValidator.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.bindinggraphvalidation;
+
+import static dagger.internal.codegen.base.Formatter.INDENT;
+import static dagger.internal.codegen.base.Scopes.getReadableSource;
+import static dagger.internal.codegen.langmodel.DaggerElements.closestEnclosingTypeElement;
+import static dagger.model.BindingKind.INJECTION;
+import static java.util.stream.Collectors.joining;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Multimaps;
+import dagger.internal.codegen.base.Scopes;
+import dagger.internal.codegen.binding.MethodSignatureFormatter;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.model.Binding;
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.ComponentNode;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import java.util.Optional;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.tools.Diagnostic;
+
+/**
+ * Reports an error for any component that uses bindings with scopes that are not assigned to the
+ * component.
+ */
+final class IncompatiblyScopedBindingsValidator implements BindingGraphPlugin {
+
+ private final MethodSignatureFormatter methodSignatureFormatter;
+ private final CompilerOptions compilerOptions;
+
+ @Inject
+ IncompatiblyScopedBindingsValidator(
+ MethodSignatureFormatter methodSignatureFormatter, CompilerOptions compilerOptions) {
+ this.methodSignatureFormatter = methodSignatureFormatter;
+ this.compilerOptions = compilerOptions;
+ }
+
+ @Override
+ public String pluginName() {
+ return "Dagger/IncompatiblyScopedBindings";
+ }
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ ImmutableSetMultimap.Builder<ComponentNode, dagger.model.Binding> incompatibleBindings =
+ ImmutableSetMultimap.builder();
+ for (dagger.model.Binding binding : bindingGraph.bindings()) {
+ binding
+ .scope()
+ .filter(scope -> !scope.isReusable())
+ .ifPresent(
+ scope -> {
+ ComponentNode componentNode =
+ bindingGraph.componentNode(binding.componentPath()).get();
+ if (!componentNode.scopes().contains(scope)) {
+ // @Inject bindings in module or subcomponent binding graphs will appear at the
+ // properly scoped ancestor component, so ignore them here.
+ if (binding.kind().equals(INJECTION)
+ && (bindingGraph.rootComponentNode().isSubcomponent()
+ || !bindingGraph.rootComponentNode().isRealComponent())) {
+ return;
+ }
+ incompatibleBindings.put(componentNode, binding);
+ }
+ });
+ }
+ Multimaps.asMap(incompatibleBindings.build())
+ .forEach((componentNode, bindings) -> report(componentNode, bindings, diagnosticReporter));
+ }
+
+ private void report(
+ ComponentNode componentNode,
+ Set<Binding> bindings,
+ DiagnosticReporter diagnosticReporter) {
+ Diagnostic.Kind diagnosticKind = ERROR;
+ StringBuilder message =
+ new StringBuilder(componentNode.componentPath().currentComponent().getQualifiedName());
+
+ if (!componentNode.isRealComponent()) {
+ // If the "component" is really a module, it will have no scopes attached. We want to report
+ // if there is more than one scope in that component.
+ if (bindings.stream().map(Binding::scope).map(Optional::get).distinct().count() <= 1) {
+ return;
+ }
+ message.append(" contains bindings with different scopes:");
+ diagnosticKind = compilerOptions.moduleHasDifferentScopesDiagnosticKind();
+ } else if (componentNode.scopes().isEmpty()) {
+ message.append(" (unscoped) may not reference scoped bindings:");
+ } else {
+ message
+ .append(" scoped with ")
+ .append(
+ componentNode.scopes().stream().map(Scopes::getReadableSource).collect(joining(" ")))
+ .append(" may not reference bindings with different scopes:");
+ }
+
+ // TODO(ronshapiro): Should we group by scope?
+ for (Binding binding : bindings) {
+ message.append('\n').append(INDENT);
+
+ // TODO(dpb): Use BindingDeclarationFormatter.
+ // But that doesn't print scopes for @Inject-constructed types.
+ switch (binding.kind()) {
+ case DELEGATE:
+ case PROVISION:
+ message.append(
+ methodSignatureFormatter.format(
+ MoreElements.asExecutable(binding.bindingElement().get())));
+ break;
+
+ case INJECTION:
+ message
+ .append(getReadableSource(binding.scope().get()))
+ .append(" class ")
+ .append(
+ closestEnclosingTypeElement(binding.bindingElement().get()).getQualifiedName());
+ break;
+
+ default:
+ throw new AssertionError(binding);
+ }
+ }
+ diagnosticReporter.reportComponent(diagnosticKind, componentNode, message.toString());
+ }
+}
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/InjectBindingValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/InjectBindingValidator.java
new file mode 100644
index 000000000..fe1c3e044
--- /dev/null
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/InjectBindingValidator.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.bindinggraphvalidation;
+
+import static dagger.model.BindingKind.INJECTION;
+
+import com.google.auto.common.MoreTypes;
+import dagger.internal.codegen.validation.InjectValidator;
+import dagger.internal.codegen.validation.ValidationReport;
+import dagger.internal.codegen.validation.ValidationReport.Item;
+import dagger.model.BindingGraph;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import javax.inject.Inject;
+import javax.lang.model.element.TypeElement;
+
+/** Validates bindings from {@code @Inject}-annotated constructors. */
+final class InjectBindingValidator implements BindingGraphPlugin {
+
+ private final InjectValidator injectValidator;
+
+ @Inject
+ InjectBindingValidator(InjectValidator injectValidator) {
+ this.injectValidator = injectValidator.whenGeneratingCode();
+ }
+
+ @Override
+ public String pluginName() {
+ return "Dagger/InjectBinding";
+ }
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ bindingGraph.bindings().stream()
+ .filter(binding -> binding.kind().equals(INJECTION)) // TODO(dpb): Move to BindingGraph
+ .forEach(binding -> validateInjectionBinding(binding, diagnosticReporter));
+ }
+
+ private void validateInjectionBinding(
+ dagger.model.Binding node, DiagnosticReporter diagnosticReporter) {
+ ValidationReport<TypeElement> typeReport =
+ injectValidator.validateType(MoreTypes.asTypeElement(node.key().type()));
+ for (Item item : typeReport.allItems()) {
+ diagnosticReporter.reportBinding(item.kind(), node, item.message());
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/MapMultibindingValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/MapMultibindingValidator.java
new file mode 100644
index 000000000..481c6d82b
--- /dev/null
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/MapMultibindingValidator.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.bindinggraphvalidation;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Multimaps.filterKeys;
+import static dagger.internal.codegen.base.Formatter.INDENT;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSetMultimap;
+import static dagger.model.BindingKind.MULTIBOUND_MAP;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.base.Equivalence;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.SetMultimap;
+import dagger.internal.codegen.base.MapType;
+import dagger.internal.codegen.binding.BindingDeclaration;
+import dagger.internal.codegen.binding.BindingDeclarationFormatter;
+import dagger.internal.codegen.binding.BindingNode;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.binding.KeyFactory;
+import dagger.model.BindingGraph;
+import dagger.model.Key;
+import dagger.producers.Producer;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.lang.model.type.DeclaredType;
+
+/**
+ * Reports an error for any map binding with either more than one contribution with the same map key
+ * or contributions with inconsistent map key annotation types.
+ */
+final class MapMultibindingValidator implements BindingGraphPlugin {
+
+ private final BindingDeclarationFormatter bindingDeclarationFormatter;
+ private final KeyFactory keyFactory;
+
+ @Inject
+ MapMultibindingValidator(
+ BindingDeclarationFormatter bindingDeclarationFormatter, KeyFactory keyFactory) {
+ this.bindingDeclarationFormatter = bindingDeclarationFormatter;
+ this.keyFactory = keyFactory;
+ }
+
+ @Override
+ public String pluginName() {
+ return "Dagger/MapKeys";
+ }
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ mapMultibindings(bindingGraph)
+ .forEach(
+ binding -> {
+ ImmutableSet<ContributionBinding> contributions =
+ mapBindingContributions(binding, bindingGraph);
+ checkForDuplicateMapKeys(binding, contributions, diagnosticReporter);
+ checkForInconsistentMapKeyAnnotationTypes(binding, contributions, diagnosticReporter);
+ });
+ }
+
+ /**
+ * Returns the map multibindings in the binding graph. If a graph contains bindings for more than
+ * one of the following for the same {@code K} and {@code V}, then only the first one found will
+ * be returned so we don't report the same map contribution problem more than once.
+ *
+ * <ol>
+ * <li>{@code Map<K, V>}
+ * <li>{@code Map<K, Provider<V>>}
+ * <li>{@code Map<K, Producer<V>>}
+ * </ol>
+ */
+ private ImmutableSet<dagger.model.Binding> mapMultibindings(BindingGraph bindingGraph) {
+ ImmutableSetMultimap<Key, dagger.model.Binding> mapMultibindings =
+ bindingGraph.bindings().stream()
+ .filter(node -> node.kind().equals(MULTIBOUND_MAP))
+ .collect(toImmutableSetMultimap(dagger.model.Binding::key, node -> node));
+
+ // Mutlbindings for Map<K, V>
+ SetMultimap<Key, dagger.model.Binding> plainValueMapMultibindings =
+ filterKeys(mapMultibindings, key -> !MapType.from(key).valuesAreFrameworkType());
+
+ // Multibindings for Map<K, Provider<V>> where Map<K, V> isn't in plainValueMapMultibindings
+ SetMultimap<Key, dagger.model.Binding> providerValueMapMultibindings =
+ filterKeys(
+ mapMultibindings,
+ key ->
+ MapType.from(key).valuesAreTypeOf(Provider.class)
+ && !plainValueMapMultibindings.containsKey(keyFactory.unwrapMapValueType(key)));
+
+ // Multibindings for Map<K, Producer<V>> where Map<K, V> isn't in plainValueMapMultibindings and
+ // Map<K, Provider<V>> isn't in providerValueMapMultibindings
+ SetMultimap<Key, dagger.model.Binding> producerValueMapMultibindings =
+ filterKeys(
+ mapMultibindings,
+ key ->
+ MapType.from(key).valuesAreTypeOf(Producer.class)
+ && !plainValueMapMultibindings.containsKey(keyFactory.unwrapMapValueType(key))
+ && !providerValueMapMultibindings.containsKey(
+ keyFactory.rewrapMapKey(key, Producer.class, Provider.class).get()));
+
+ return new ImmutableSet.Builder<dagger.model.Binding>()
+ .addAll(plainValueMapMultibindings.values())
+ .addAll(providerValueMapMultibindings.values())
+ .addAll(producerValueMapMultibindings.values())
+ .build();
+ }
+
+ private ImmutableSet<ContributionBinding> mapBindingContributions(
+ dagger.model.Binding binding, BindingGraph bindingGraph) {
+ checkArgument(binding.kind().equals(MULTIBOUND_MAP));
+ return bindingGraph.requestedBindings(binding).stream()
+ .map(b -> (BindingNode) b)
+ .map(b -> (ContributionBinding) b.delegate())
+ .collect(toImmutableSet());
+ }
+
+ private void checkForDuplicateMapKeys(
+ dagger.model.Binding multiboundMapBinding,
+ ImmutableSet<ContributionBinding> contributions,
+ DiagnosticReporter diagnosticReporter) {
+ ImmutableSetMultimap<?, ContributionBinding> contributionsByMapKey =
+ ImmutableSetMultimap.copyOf(
+ Multimaps.index(contributions, ContributionBinding::wrappedMapKeyAnnotation));
+
+ for (Set<ContributionBinding> contributionsForOneMapKey :
+ Multimaps.asMap(contributionsByMapKey).values()) {
+ if (contributionsForOneMapKey.size() > 1) {
+ diagnosticReporter.reportBinding(
+ ERROR,
+ multiboundMapBinding,
+ duplicateMapKeyErrorMessage(contributionsForOneMapKey, multiboundMapBinding.key()));
+ }
+ }
+ }
+
+ private void checkForInconsistentMapKeyAnnotationTypes(
+ dagger.model.Binding multiboundMapBinding,
+ ImmutableSet<ContributionBinding> contributions,
+ DiagnosticReporter diagnosticReporter) {
+ ImmutableSetMultimap<Equivalence.Wrapper<DeclaredType>, ContributionBinding>
+ contributionsByMapKeyAnnotationType = indexByMapKeyAnnotationType(contributions);
+
+ if (contributionsByMapKeyAnnotationType.keySet().size() > 1) {
+ diagnosticReporter.reportBinding(
+ ERROR,
+ multiboundMapBinding,
+ inconsistentMapKeyAnnotationTypesErrorMessage(
+ contributionsByMapKeyAnnotationType, multiboundMapBinding.key()));
+ }
+ }
+
+ private static ImmutableSetMultimap<Equivalence.Wrapper<DeclaredType>, ContributionBinding>
+ indexByMapKeyAnnotationType(ImmutableSet<ContributionBinding> contributions) {
+ return ImmutableSetMultimap.copyOf(
+ Multimaps.index(
+ contributions,
+ mapBinding ->
+ MoreTypes.equivalence()
+ .wrap(mapBinding.mapKeyAnnotation().get().getAnnotationType())));
+ }
+
+ private String inconsistentMapKeyAnnotationTypesErrorMessage(
+ ImmutableSetMultimap<Equivalence.Wrapper<DeclaredType>, ContributionBinding>
+ contributionsByMapKeyAnnotationType,
+ Key mapBindingKey) {
+ StringBuilder message =
+ new StringBuilder(mapBindingKey.toString())
+ .append(" uses more than one @MapKey annotation type");
+ Multimaps.asMap(contributionsByMapKeyAnnotationType)
+ .forEach(
+ (annotationType, contributions) -> {
+ message.append('\n').append(INDENT).append(annotationType.get()).append(':');
+ bindingDeclarationFormatter.formatIndentedList(message, contributions, 2);
+ });
+ return message.toString();
+ }
+
+ private String duplicateMapKeyErrorMessage(
+ Set<ContributionBinding> contributionsForOneMapKey, Key mapBindingKey) {
+ StringBuilder message =
+ new StringBuilder("The same map key is bound more than once for ").append(mapBindingKey);
+
+ bindingDeclarationFormatter.formatIndentedList(
+ message,
+ ImmutableList.sortedCopyOf(BindingDeclaration.COMPARATOR, contributionsForOneMapKey),
+ 1);
+ return message.toString();
+ }
+}
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/MissingBindingValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/MissingBindingValidator.java
new file mode 100644
index 000000000..7334cd9c8
--- /dev/null
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/MissingBindingValidator.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.bindinggraphvalidation;
+
+import static com.google.common.base.Verify.verify;
+import static dagger.internal.codegen.base.Keys.isValidImplicitProvisionKey;
+import static dagger.internal.codegen.base.Keys.isValidMembersInjectionKey;
+import static dagger.internal.codegen.base.RequestKinds.canBeSatisfiedByProductionBinding;
+import static dagger.internal.codegen.extension.DaggerStreams.instancesOf;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import dagger.internal.codegen.binding.InjectBindingRegistry;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.ComponentNode;
+import dagger.model.BindingGraph.DependencyEdge;
+import dagger.model.BindingGraph.MissingBinding;
+import dagger.model.BindingGraph.Node;
+import dagger.model.Key;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import javax.inject.Inject;
+import javax.lang.model.type.TypeKind;
+
+/** Reports errors for missing bindings. */
+final class MissingBindingValidator implements BindingGraphPlugin {
+
+ private final DaggerTypes types;
+ private final InjectBindingRegistry injectBindingRegistry;
+
+ @Inject
+ MissingBindingValidator(
+ DaggerTypes types, InjectBindingRegistry injectBindingRegistry) {
+ this.types = types;
+ this.injectBindingRegistry = injectBindingRegistry;
+ }
+
+ @Override
+ public String pluginName() {
+ return "Dagger/MissingBinding";
+ }
+
+ @Override
+ public void visitGraph(BindingGraph graph, DiagnosticReporter diagnosticReporter) {
+ // Don't report missing bindings when validating a full binding graph or a graph built from a
+ // subcomponent.
+ if (graph.isFullBindingGraph() || graph.rootComponentNode().isSubcomponent()) {
+ return;
+ }
+ graph
+ .missingBindings()
+ .forEach(missingBinding -> reportMissingBinding(missingBinding, graph, diagnosticReporter));
+ }
+
+ private void reportMissingBinding(
+ MissingBinding missingBinding, BindingGraph graph, DiagnosticReporter diagnosticReporter) {
+ diagnosticReporter.reportBinding(
+ ERROR, missingBinding, missingBindingErrorMessage(missingBinding, graph));
+ }
+
+ private String missingBindingErrorMessage(MissingBinding missingBinding, BindingGraph graph) {
+ Key key = missingBinding.key();
+ StringBuilder errorMessage = new StringBuilder();
+ // Wildcards should have already been checked by DependencyRequestValidator.
+ verify(!key.type().getKind().equals(TypeKind.WILDCARD), "unexpected wildcard request: %s", key);
+ // TODO(ronshapiro): replace "provided" with "satisfied"?
+ errorMessage.append(key).append(" cannot be provided without ");
+ if (isValidImplicitProvisionKey(key, types)) {
+ errorMessage.append("an @Inject constructor or ");
+ }
+ errorMessage.append("an @Provides-"); // TODO(dpb): s/an/a
+ if (allIncomingDependenciesCanUseProduction(missingBinding, graph)) {
+ errorMessage.append(" or @Produces-");
+ }
+ errorMessage.append("annotated method.");
+ if (isValidMembersInjectionKey(key) && typeHasInjectionSites(key)) {
+ errorMessage.append(
+ " This type supports members injection but cannot be implicitly provided.");
+ }
+ graph.bindings(key).stream()
+ .map(binding -> binding.componentPath().currentComponent())
+ .distinct()
+ .forEach(
+ component ->
+ errorMessage
+ .append("\nA binding with matching key exists in component: ")
+ .append(component.getQualifiedName()));
+ return errorMessage.toString();
+ }
+
+ private boolean allIncomingDependenciesCanUseProduction(
+ MissingBinding missingBinding, BindingGraph graph) {
+ return graph.network().inEdges(missingBinding).stream()
+ .flatMap(instancesOf(DependencyEdge.class))
+ .allMatch(edge -> dependencyCanBeProduction(edge, graph));
+ }
+
+ // TODO(ronshapiro): merge with
+ // ProvisionDependencyOnProduerBindingValidator.dependencyCanUseProduction
+ private boolean dependencyCanBeProduction(DependencyEdge edge, BindingGraph graph) {
+ Node source = graph.network().incidentNodes(edge).source();
+ if (source instanceof ComponentNode) {
+ return canBeSatisfiedByProductionBinding(edge.dependencyRequest().kind());
+ }
+ if (source instanceof dagger.model.Binding) {
+ return ((dagger.model.Binding) source).isProduction();
+ }
+ throw new IllegalArgumentException(
+ "expected a dagger.model.Binding or ComponentNode: " + source);
+ }
+
+ private boolean typeHasInjectionSites(Key key) {
+ return injectBindingRegistry
+ .getOrFindMembersInjectionBinding(key)
+ .map(binding -> !binding.injectionSites().isEmpty())
+ .orElse(false);
+ }
+}
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/NullableBindingValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/NullableBindingValidator.java
new file mode 100644
index 000000000..9800b15f2
--- /dev/null
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/NullableBindingValidator.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.bindinggraphvalidation;
+
+import static dagger.internal.codegen.extension.DaggerStreams.instancesOf;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.DependencyEdge;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import javax.inject.Inject;
+
+/**
+ * Reports errors or warnings (depending on the {@code -Adagger.nullableValidation} value) for each
+ * non-nullable dependency request that is satisfied by a nullable binding.
+ */
+final class NullableBindingValidator implements BindingGraphPlugin {
+
+ private final CompilerOptions compilerOptions;
+
+ @Inject
+ NullableBindingValidator(CompilerOptions compilerOptions) {
+ this.compilerOptions = compilerOptions;
+ }
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ for (dagger.model.Binding binding : nullableBindings(bindingGraph)) {
+ for (DependencyEdge dependencyEdge : nonNullableDependencies(bindingGraph, binding)) {
+ diagnosticReporter.reportDependency(
+ compilerOptions.nullableValidationKind(),
+ dependencyEdge,
+ nullableToNonNullable(
+ binding.key().toString(),
+ binding.toString())); // binding.toString() will include the @Nullable
+ }
+ }
+ }
+
+ @Override
+ public String pluginName() {
+ return "Dagger/Nullable";
+ }
+
+ private ImmutableList<dagger.model.Binding> nullableBindings(BindingGraph bindingGraph) {
+ return bindingGraph.bindings().stream()
+ .filter(binding -> binding.isNullable())
+ .collect(toImmutableList());
+ }
+
+ private ImmutableSet<DependencyEdge> nonNullableDependencies(
+ BindingGraph bindingGraph, dagger.model.Binding binding) {
+ return bindingGraph.network().inEdges(binding).stream()
+ .flatMap(instancesOf(DependencyEdge.class))
+ .filter(edge -> !edge.dependencyRequest().isNullable())
+ .collect(toImmutableSet());
+ }
+
+ @VisibleForTesting
+ static String nullableToNonNullable(String key, String binding) {
+ return String.format("%s is not nullable, but is being provided by %s", key, binding);
+ }
+}
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/ProvisionDependencyOnProducerBindingValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/ProvisionDependencyOnProducerBindingValidator.java
new file mode 100644
index 000000000..7d742f96f
--- /dev/null
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/ProvisionDependencyOnProducerBindingValidator.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.bindinggraphvalidation;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Verify.verify;
+import static dagger.internal.codegen.base.RequestKinds.canBeSatisfiedByProductionBinding;
+import static dagger.internal.codegen.extension.DaggerStreams.instancesOf;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.DependencyEdge;
+import dagger.model.BindingGraph.Node;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import java.util.stream.Stream;
+import javax.inject.Inject;
+
+/**
+ * Reports an error for each provision-only dependency request that is satisfied by a production
+ * binding.
+ */
+// TODO(b/29509141): Clarify the error.
+final class ProvisionDependencyOnProducerBindingValidator implements BindingGraphPlugin {
+
+ @Inject
+ ProvisionDependencyOnProducerBindingValidator() {}
+
+ @Override
+ public String pluginName() {
+ return "Dagger/ProviderDependsOnProducer";
+ }
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ provisionDependenciesOnProductionBindings(bindingGraph)
+ .forEach(
+ provisionDependent ->
+ diagnosticReporter.reportDependency(
+ ERROR,
+ provisionDependent,
+ provisionDependent.isEntryPoint()
+ ? entryPointErrorMessage(provisionDependent)
+ : dependencyErrorMessage(provisionDependent, bindingGraph)));
+ }
+
+ private Stream<DependencyEdge> provisionDependenciesOnProductionBindings(
+ BindingGraph bindingGraph) {
+ return bindingGraph.bindings().stream()
+ .filter(binding -> binding.isProduction())
+ .flatMap(binding -> incomingDependencies(binding, bindingGraph))
+ .filter(edge -> !dependencyCanUseProduction(edge, bindingGraph));
+ }
+
+ /** Returns the dependencies on {@code binding}. */
+ // TODO(dpb): Move to BindingGraph.
+ private Stream<DependencyEdge> incomingDependencies(
+ dagger.model.Binding binding, BindingGraph bindingGraph) {
+ return bindingGraph.network().inEdges(binding).stream()
+ .flatMap(instancesOf(DependencyEdge.class));
+ }
+
+ // TODO(ronshapiro): merge with MissingBindingValidator.dependencyCanUseProduction
+ private boolean dependencyCanUseProduction(DependencyEdge edge, BindingGraph bindingGraph) {
+ return edge.isEntryPoint()
+ ? canBeSatisfiedByProductionBinding(edge.dependencyRequest().kind())
+ : bindingRequestingDependency(edge, bindingGraph).isProduction();
+ }
+
+ /**
+ * Returns the binding that requests a dependency.
+ *
+ * @throws IllegalArgumentException if {@code dependency} is an {@linkplain
+ * DependencyEdge#isEntryPoint() entry point}.
+ */
+ // TODO(dpb): Move to BindingGraph.
+ private dagger.model.Binding bindingRequestingDependency(
+ DependencyEdge dependency, BindingGraph bindingGraph) {
+ checkArgument(!dependency.isEntryPoint());
+ Node source = bindingGraph.network().incidentNodes(dependency).source();
+ verify(
+ source instanceof dagger.model.Binding,
+ "expected source of %s to be a binding, but was: %s",
+ dependency,
+ source);
+ return (dagger.model.Binding) source;
+ }
+
+ private String entryPointErrorMessage(DependencyEdge entryPoint) {
+ return String.format(
+ "%s is a provision entry-point, which cannot depend on a production.",
+ entryPoint.dependencyRequest().key());
+ }
+
+ private String dependencyErrorMessage(
+ DependencyEdge dependencyOnProduction, BindingGraph bindingGraph) {
+ return String.format(
+ "%s is a provision, which cannot depend on a production.",
+ bindingRequestingDependency(dependencyOnProduction, bindingGraph).key());
+ }
+}
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/SubcomponentFactoryMethodValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/SubcomponentFactoryMethodValidator.java
new file mode 100644
index 000000000..bf83a69f9
--- /dev/null
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/SubcomponentFactoryMethodValidator.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.bindinggraphvalidation;
+
+import static com.google.auto.common.MoreTypes.asDeclared;
+import static com.google.auto.common.MoreTypes.asExecutable;
+import static com.google.auto.common.MoreTypes.asTypeElements;
+import static com.google.common.collect.Sets.union;
+import static dagger.internal.codegen.binding.ComponentRequirement.componentCanMakeNewInstances;
+import static dagger.internal.codegen.extension.DaggerStreams.instancesOf;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Sets.SetView;
+import dagger.internal.codegen.base.Util;
+import dagger.internal.codegen.binding.ComponentNodeImpl;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.ChildFactoryMethodEdge;
+import dagger.model.BindingGraph.ComponentNode;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import javax.inject.Inject;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+
+/** Reports an error if a subcomponent factory method is missing required modules. */
+final class SubcomponentFactoryMethodValidator implements BindingGraphPlugin {
+
+ private final DaggerTypes types;
+ private final KotlinMetadataUtil metadataUtil;
+ private final Map<ComponentNode, Set<TypeElement>> inheritedModulesCache = new HashMap<>();
+
+ @Inject
+ SubcomponentFactoryMethodValidator(DaggerTypes types, KotlinMetadataUtil metadataUtil) {
+ this.types = types;
+ this.metadataUtil = metadataUtil;
+ }
+
+ @Override
+ public String pluginName() {
+ return "Dagger/SubcomponentFactoryMethodMissingModule";
+ }
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ if (!bindingGraph.rootComponentNode().isRealComponent()
+ || bindingGraph.rootComponentNode().isSubcomponent()) {
+ // We don't know all the modules that might be owned by the child until we know the real root
+ // component, which we don't if the root component node is really a module or a subcomponent.
+ return;
+ }
+ bindingGraph.network().edges().stream()
+ .flatMap(instancesOf(ChildFactoryMethodEdge.class))
+ .forEach(
+ edge -> {
+ ImmutableSet<TypeElement> missingModules = findMissingModules(edge, bindingGraph);
+ if (!missingModules.isEmpty()) {
+ reportMissingModuleParameters(
+ edge, missingModules, bindingGraph, diagnosticReporter);
+ }
+ });
+ }
+
+ private ImmutableSet<TypeElement> findMissingModules(
+ ChildFactoryMethodEdge edge, BindingGraph graph) {
+ ImmutableSet<TypeElement> factoryMethodParameters =
+ subgraphFactoryMethodParameters(edge, graph);
+ ComponentNode child = (ComponentNode) graph.network().incidentNodes(edge).target();
+ SetView<TypeElement> modulesOwnedByChild = ownedModules(child, graph);
+ return graph.bindings().stream()
+ // bindings owned by child
+ .filter(binding -> binding.componentPath().equals(child.componentPath()))
+ // that require a module instance
+ .filter(binding -> binding.requiresModuleInstance())
+ .map(binding -> binding.contributingModule().get())
+ .distinct()
+ // module owned by child
+ .filter(module -> modulesOwnedByChild.contains(module))
+ // module not in the method parameters
+ .filter(module -> !factoryMethodParameters.contains(module))
+ // module doesn't have an accessible no-arg constructor
+ .filter(moduleType -> !componentCanMakeNewInstances(moduleType, metadataUtil))
+ .collect(toImmutableSet());
+ }
+
+ private ImmutableSet<TypeElement> subgraphFactoryMethodParameters(
+ ChildFactoryMethodEdge edge, BindingGraph bindingGraph) {
+ ComponentNode parent = (ComponentNode) bindingGraph.network().incidentNodes(edge).source();
+ DeclaredType parentType = asDeclared(parent.componentPath().currentComponent().asType());
+ ExecutableType factoryMethodType =
+ asExecutable(types.asMemberOf(parentType, edge.factoryMethod()));
+ return asTypeElements(factoryMethodType.getParameterTypes());
+ }
+
+ private SetView<TypeElement> ownedModules(ComponentNode component, BindingGraph graph) {
+ return Sets.difference(
+ ((ComponentNodeImpl) component).componentDescriptor().moduleTypes(),
+ inheritedModules(component, graph));
+ }
+
+ private Set<TypeElement> inheritedModules(ComponentNode component, BindingGraph graph) {
+ return Util.reentrantComputeIfAbsent(
+ inheritedModulesCache, component, uncachedInheritedModules(graph));
+ }
+
+ private Function<ComponentNode, Set<TypeElement>> uncachedInheritedModules(BindingGraph graph) {
+ return componentNode ->
+ componentNode.componentPath().atRoot()
+ ? ImmutableSet.of()
+ : graph
+ .componentNode(componentNode.componentPath().parent())
+ .map(parent -> union(ownedModules(parent, graph), inheritedModules(parent, graph)))
+ .get();
+ }
+
+ private void reportMissingModuleParameters(
+ ChildFactoryMethodEdge edge,
+ ImmutableSet<TypeElement> missingModules,
+ BindingGraph graph,
+ DiagnosticReporter diagnosticReporter) {
+ diagnosticReporter.reportSubcomponentFactoryMethod(
+ ERROR,
+ edge,
+ "%s requires modules which have no visible default constructors. "
+ + "Add the following modules as parameters to this method: %s",
+ graph
+ .network()
+ .incidentNodes(edge)
+ .target()
+ .componentPath()
+ .currentComponent()
+ .getQualifiedName(),
+ Joiner.on(", ").join(missingModules));
+ }
+}
diff --git a/java/dagger/internal/codegen/bootstrap/BUILD b/java/dagger/internal/codegen/bootstrap/BUILD
new file mode 100644
index 000000000..2527d3ccd
--- /dev/null
+++ b/java/dagger/internal/codegen/bootstrap/BUILD
@@ -0,0 +1,33 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Bootstrap libraries for building Dagger with Dagger.
+
+load("@rules_java//java:defs.bzl", "java_import", "java_plugin")
+
+package(default_visibility = ["//:src"])
+
+java_plugin(
+ name = "bootstrap",
+ generates_api = 1,
+ processor_class = "dagger.internal.codegen.ComponentProcessor",
+ deps = [":bootstrap_compiler"],
+)
+
+java_import(
+ name = "bootstrap_compiler",
+ jars = ["bootstrap_compiler_deploy.jar"],
+ visibility = ["//visibility:private"],
+)
diff --git a/java/dagger/internal/codegen/bootstrap/bootstrap_compiler_deploy.jar b/java/dagger/internal/codegen/bootstrap/bootstrap_compiler_deploy.jar
new file mode 100644
index 000000000..b69dc65d5
--- /dev/null
+++ b/java/dagger/internal/codegen/bootstrap/bootstrap_compiler_deploy.jar
Binary files differ
diff --git a/java/dagger/internal/codegen/bootstrap_compiler_deploy.jar b/java/dagger/internal/codegen/bootstrap_compiler_deploy.jar
deleted file mode 100644
index ad9761de4..000000000
--- a/java/dagger/internal/codegen/bootstrap_compiler_deploy.jar
+++ /dev/null
Binary files differ
diff --git a/java/dagger/internal/codegen/compileroption/BUILD b/java/dagger/internal/codegen/compileroption/BUILD
new file mode 100644
index 000000000..ef39b34b4
--- /dev/null
+++ b/java/dagger/internal/codegen/compileroption/BUILD
@@ -0,0 +1,38 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Sources related to compiler options.
+
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "compileroption",
+ srcs = glob(["*.java"]),
+ plugins = ["//java/dagger/internal/codegen/bootstrap"],
+ tags = ["maven:merged"],
+ deps = [
+ "//java/dagger:core",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/codegen/langmodel",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/producers",
+ "@google_bazel_common//third_party/java/google_java_format",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
diff --git a/java/dagger/internal/codegen/compileroption/CompilerOptions.java b/java/dagger/internal/codegen/compileroption/CompilerOptions.java
new file mode 100644
index 000000000..a0d1cda34
--- /dev/null
+++ b/java/dagger/internal/codegen/compileroption/CompilerOptions.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.compileroption;
+
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic;
+
+/** A collection of options that dictate how the compiler will run. */
+public abstract class CompilerOptions {
+ public abstract boolean usesProducers();
+
+ /**
+ * Returns true if the fast initialization flag, {@code fastInit}, is enabled.
+ *
+ * <p>If enabled, the generated code will attempt to optimize for fast component initialization.
+ * This is done by reducing the number of factory classes loaded during initialization and the
+ * number of eagerly initialized fields at the cost of potential memory leaks and higher
+ * per-provision instantiation time.
+ */
+ public abstract boolean fastInit(TypeElement element);
+
+ public abstract boolean formatGeneratedSource();
+
+ public abstract boolean writeProducerNameInToken();
+
+ public abstract Diagnostic.Kind nullableValidationKind();
+
+ public final boolean doCheckForNulls() {
+ return nullableValidationKind().equals(Diagnostic.Kind.ERROR);
+ }
+
+ public abstract Diagnostic.Kind privateMemberValidationKind();
+
+ public abstract Diagnostic.Kind staticMemberValidationKind();
+
+ /**
+ * If {@code true}, Dagger will generate factories and components even if some members-injected
+ * types have {@code private} or {@code static} {@code @Inject}-annotated members.
+ *
+ * <p>This should only ever be enabled by the TCK tests. Disabling this validation could lead to
+ * generating code that does not compile.
+ */
+ public abstract boolean ignorePrivateAndStaticInjectionForComponent();
+
+ public abstract ValidationType scopeCycleValidationType();
+
+ /**
+ * If {@code true}, Dagger will validate all transitive component dependencies of a component.
+ * Otherwise, Dagger will only validate the direct component dependencies.
+ *
+ * <p>Note: this is different from scopeCycleValidationType, which lets you silence errors of
+ * transitive component dependencies, but still requires the full transitive dependencies in the
+ * classpath.
+ *
+ * <p>The main motivation for this flag is to prevent requiring the transitive component
+ * dependencies in the classpath to speed up builds. See
+ * https://github.com/google/dagger/issues/970.
+ */
+ public abstract boolean validateTransitiveComponentDependencies();
+
+ public abstract boolean warnIfInjectionFactoryNotGeneratedUpstream();
+
+ public abstract boolean headerCompilation();
+
+ public abstract ValidationType fullBindingGraphValidationType();
+
+ /**
+ * If {@code true}, each plugin will visit the full binding graph for the given element.
+ *
+ * @throws IllegalArgumentException if {@code element} is not a module or (sub)component
+ */
+ public abstract boolean pluginsVisitFullBindingGraphs(TypeElement element);
+
+ public abstract Diagnostic.Kind moduleHasDifferentScopesDiagnosticKind();
+
+ public abstract ValidationType explicitBindingConflictsWithInjectValidationType();
+
+ public abstract boolean experimentalDaggerErrorMessages();
+
+ /** Returns the number of bindings allowed per shard. */
+ public int keysPerComponentShard(TypeElement component) {
+ return 3500;
+ }
+
+ /**
+ * This option enables a fix to an issue where Dagger previously would erroneously allow
+ * multibinding contributions in a component to have dependencies on child components. This will
+ * eventually become the default and enforced.
+ */
+ public abstract boolean strictMultibindingValidation();
+}
diff --git a/java/dagger/internal/codegen/compileroption/FeatureStatus.java b/java/dagger/internal/codegen/compileroption/FeatureStatus.java
new file mode 100644
index 000000000..d989679b8
--- /dev/null
+++ b/java/dagger/internal/codegen/compileroption/FeatureStatus.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.compileroption;
+
+/** Allows options to control how features in component processing are enabled. */
+public enum FeatureStatus {
+ ENABLED,
+ DISABLED;
+}
diff --git a/java/dagger/internal/codegen/compileroption/JavacPluginCompilerOptions.java b/java/dagger/internal/codegen/compileroption/JavacPluginCompilerOptions.java
new file mode 100644
index 000000000..a86cc1b7c
--- /dev/null
+++ b/java/dagger/internal/codegen/compileroption/JavacPluginCompilerOptions.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.compileroption;
+
+import static dagger.internal.codegen.compileroption.ValidationType.NONE;
+import static javax.tools.Diagnostic.Kind.NOTE;
+
+import javax.inject.Inject;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic;
+
+/** {@link CompilerOptions} for Javac plugins (e.g. for Dagger statistics or Kythe). */
+public final class JavacPluginCompilerOptions extends CompilerOptions {
+
+ @Inject
+ JavacPluginCompilerOptions() {}
+
+ @Override
+ public boolean usesProducers() {
+ return true;
+ }
+
+ @Override
+ public boolean fastInit(TypeElement element) {
+ return false;
+ }
+
+ @Override
+ public boolean formatGeneratedSource() {
+ return false;
+ }
+
+ @Override
+ public boolean writeProducerNameInToken() {
+ return true;
+ }
+
+ @Override
+ public Diagnostic.Kind nullableValidationKind() {
+ return NOTE;
+ }
+
+ @Override
+ public Diagnostic.Kind privateMemberValidationKind() {
+ return NOTE;
+ }
+
+ @Override
+ public Diagnostic.Kind staticMemberValidationKind() {
+ return NOTE;
+ }
+
+ @Override
+ public boolean ignorePrivateAndStaticInjectionForComponent() {
+ return false;
+ }
+
+ @Override
+ public ValidationType scopeCycleValidationType() {
+ return NONE;
+ }
+
+ @Override
+ public boolean validateTransitiveComponentDependencies() {
+ return true;
+ }
+
+ @Override
+ public boolean warnIfInjectionFactoryNotGeneratedUpstream() {
+ return false;
+ }
+
+ @Override
+ public boolean headerCompilation() {
+ return false;
+ }
+
+ @Override
+ public ValidationType fullBindingGraphValidationType() {
+ return NONE;
+ }
+
+ @Override
+ public boolean pluginsVisitFullBindingGraphs(TypeElement element) {
+ return false;
+ }
+
+ @Override
+ public Diagnostic.Kind moduleHasDifferentScopesDiagnosticKind() {
+ return NOTE;
+ }
+
+ @Override
+ public ValidationType explicitBindingConflictsWithInjectValidationType() {
+ return NONE;
+ }
+
+ @Override
+ public boolean experimentalDaggerErrorMessages() {
+ return false;
+ }
+
+ @Override
+ public boolean strictMultibindingValidation() {
+ return false;
+ }
+}
diff --git a/java/dagger/internal/codegen/compileroption/ProcessingEnvironmentCompilerOptions.java b/java/dagger/internal/codegen/compileroption/ProcessingEnvironmentCompilerOptions.java
new file mode 100644
index 000000000..06d15a236
--- /dev/null
+++ b/java/dagger/internal/codegen/compileroption/ProcessingEnvironmentCompilerOptions.java
@@ -0,0 +1,520 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.compileroption;
+
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Sets.immutableEnumSet;
+import static dagger.internal.codegen.compileroption.FeatureStatus.DISABLED;
+import static dagger.internal.codegen.compileroption.FeatureStatus.ENABLED;
+import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.EXPERIMENTAL_AHEAD_OF_TIME_SUBCOMPONENTS;
+import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.EXPERIMENTAL_ANDROID_MODE;
+import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.EXPERIMENTAL_DAGGER_ERROR_MESSAGES;
+import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.FAST_INIT;
+import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.FLOATING_BINDS_METHODS;
+import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.FORMAT_GENERATED_SOURCE;
+import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.IGNORE_PRIVATE_AND_STATIC_INJECTION_FOR_COMPONENT;
+import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.PLUGINS_VISIT_FULL_BINDING_GRAPHS;
+import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.STRICT_MULTIBINDING_VALIDATION;
+import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.VALIDATE_TRANSITIVE_COMPONENT_DEPENDENCIES;
+import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.WARN_IF_INJECTION_FACTORY_NOT_GENERATED_UPSTREAM;
+import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Feature.WRITE_PRODUCER_NAME_IN_TOKEN;
+import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.KeyOnlyOption.HEADER_COMPILATION;
+import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.KeyOnlyOption.USE_GRADLE_INCREMENTAL_PROCESSING;
+import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Validation.DISABLE_INTER_COMPONENT_SCOPE_VALIDATION;
+import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Validation.EXPLICIT_BINDING_CONFLICTS_WITH_INJECT;
+import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Validation.FULL_BINDING_GRAPH_VALIDATION;
+import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Validation.MODULE_HAS_DIFFERENT_SCOPES_VALIDATION;
+import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Validation.NULLABLE_VALIDATION;
+import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Validation.PRIVATE_MEMBER_VALIDATION;
+import static dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions.Validation.STATIC_MEMBER_VALIDATION;
+import static dagger.internal.codegen.compileroption.ValidationType.ERROR;
+import static dagger.internal.codegen.compileroption.ValidationType.NONE;
+import static dagger.internal.codegen.compileroption.ValidationType.WARNING;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Stream.concat;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.base.Ascii;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.producers.Produces;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Stream;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.inject.Inject;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic;
+
+/** {@link CompilerOptions} for the given {@link ProcessingEnvironment}. */
+public final class ProcessingEnvironmentCompilerOptions extends CompilerOptions {
+ // EnumOption<T> doesn't support integer inputs so just doing this as a 1-off for now.
+ private static final String KEYS_PER_COMPONENT_SHARD = "dagger.keysPerComponentShard";
+
+ private final ProcessingEnvironment processingEnvironment;
+ private final DaggerElements daggerElements;
+ private final Map<EnumOption<?>, Object> enumOptions = new HashMap<>();
+ private final Map<EnumOption<?>, ImmutableMap<String, ? extends Enum<?>>> allCommandLineOptions =
+ new HashMap<>();
+
+ @Inject
+ ProcessingEnvironmentCompilerOptions(
+ ProcessingEnvironment processingEnvironment, DaggerElements daggerElements) {
+ this.processingEnvironment = processingEnvironment;
+ this.daggerElements = daggerElements;
+ checkValid();
+ }
+
+ @Override
+ public boolean usesProducers() {
+ return processingEnvironment.getElementUtils().getTypeElement(Produces.class.getCanonicalName())
+ != null;
+ }
+
+ @Override
+ public boolean headerCompilation() {
+ return isEnabled(HEADER_COMPILATION);
+ }
+
+ @Override
+ public boolean fastInit(TypeElement component) {
+ return isEnabled(FAST_INIT);
+ }
+
+ @Override
+ public boolean formatGeneratedSource() {
+ return isEnabled(FORMAT_GENERATED_SOURCE);
+ }
+
+ @Override
+ public boolean writeProducerNameInToken() {
+ return isEnabled(WRITE_PRODUCER_NAME_IN_TOKEN);
+ }
+
+ @Override
+ public Diagnostic.Kind nullableValidationKind() {
+ return diagnosticKind(NULLABLE_VALIDATION);
+ }
+
+ @Override
+ public Diagnostic.Kind privateMemberValidationKind() {
+ return diagnosticKind(PRIVATE_MEMBER_VALIDATION);
+ }
+
+ @Override
+ public Diagnostic.Kind staticMemberValidationKind() {
+ return diagnosticKind(STATIC_MEMBER_VALIDATION);
+ }
+
+ @Override
+ public boolean ignorePrivateAndStaticInjectionForComponent() {
+ return isEnabled(IGNORE_PRIVATE_AND_STATIC_INJECTION_FOR_COMPONENT);
+ }
+
+ @Override
+ public ValidationType scopeCycleValidationType() {
+ return parseOption(DISABLE_INTER_COMPONENT_SCOPE_VALIDATION);
+ }
+
+ @Override
+ public boolean validateTransitiveComponentDependencies() {
+ return isEnabled(VALIDATE_TRANSITIVE_COMPONENT_DEPENDENCIES);
+ }
+
+ @Override
+ public boolean warnIfInjectionFactoryNotGeneratedUpstream() {
+ return isEnabled(WARN_IF_INJECTION_FACTORY_NOT_GENERATED_UPSTREAM);
+ }
+
+ @Override
+ public ValidationType fullBindingGraphValidationType() {
+ return parseOption(FULL_BINDING_GRAPH_VALIDATION);
+ }
+
+ @Override
+ public boolean pluginsVisitFullBindingGraphs(TypeElement component) {
+ return isEnabled(PLUGINS_VISIT_FULL_BINDING_GRAPHS);
+ }
+
+ @Override
+ public Diagnostic.Kind moduleHasDifferentScopesDiagnosticKind() {
+ return diagnosticKind(MODULE_HAS_DIFFERENT_SCOPES_VALIDATION);
+ }
+
+ @Override
+ public ValidationType explicitBindingConflictsWithInjectValidationType() {
+ return parseOption(EXPLICIT_BINDING_CONFLICTS_WITH_INJECT);
+ }
+
+ @Override
+ public boolean experimentalDaggerErrorMessages() {
+ return isEnabled(EXPERIMENTAL_DAGGER_ERROR_MESSAGES);
+ }
+
+ @Override
+ public boolean strictMultibindingValidation() {
+ return isEnabled(STRICT_MULTIBINDING_VALIDATION);
+ }
+
+ @Override
+ public int keysPerComponentShard(TypeElement component) {
+ if (processingEnvironment.getOptions().containsKey(KEYS_PER_COMPONENT_SHARD)) {
+ checkArgument(
+ "dagger.internal.codegen".contentEquals(
+ MoreElements.getPackage(component).getQualifiedName()),
+ "Cannot set %s. It is only meant for internal testing.", KEYS_PER_COMPONENT_SHARD);
+ return Integer.parseInt(
+ processingEnvironment.getOptions().get(KEYS_PER_COMPONENT_SHARD));
+ }
+ return super.keysPerComponentShard(component);
+ }
+
+ private boolean isEnabled(KeyOnlyOption keyOnlyOption) {
+ return processingEnvironment.getOptions().containsKey(keyOnlyOption.toString());
+ }
+
+ private boolean isEnabled(Feature feature) {
+ return parseOption(feature).equals(ENABLED);
+ }
+
+ private Diagnostic.Kind diagnosticKind(Validation validation) {
+ return parseOption(validation).diagnosticKind().get();
+ }
+
+ @SuppressWarnings("CheckReturnValue")
+ private ProcessingEnvironmentCompilerOptions checkValid() {
+ for (KeyOnlyOption keyOnlyOption : KeyOnlyOption.values()) {
+ isEnabled(keyOnlyOption);
+ }
+ for (Feature feature : Feature.values()) {
+ parseOption(feature);
+ }
+ for (Validation validation : Validation.values()) {
+ parseOption(validation);
+ }
+ noLongerRecognized(EXPERIMENTAL_ANDROID_MODE);
+ noLongerRecognized(FLOATING_BINDS_METHODS);
+ noLongerRecognized(EXPERIMENTAL_AHEAD_OF_TIME_SUBCOMPONENTS);
+ noLongerRecognized(USE_GRADLE_INCREMENTAL_PROCESSING);
+ return this;
+ }
+
+ private void noLongerRecognized(CommandLineOption commandLineOption) {
+ if (processingEnvironment.getOptions().containsKey(commandLineOption.toString())) {
+ processingEnvironment
+ .getMessager()
+ .printMessage(
+ Diagnostic.Kind.WARNING, commandLineOption + " is no longer recognized by Dagger");
+ }
+ }
+
+ private interface CommandLineOption {
+ /** The key of the option (appears after "-A"). */
+ @Override
+ String toString();
+
+ /**
+ * Returns all aliases besides {@link #toString()}, such as old names for an option, in order of
+ * precedence.
+ */
+ default ImmutableList<String> aliases() {
+ return ImmutableList.of();
+ }
+
+ /** All the command-line names for this option, in order of precedence. */
+ default Stream<String> allNames() {
+ return concat(Stream.of(toString()), aliases().stream());
+ }
+ }
+
+ /** An option that can be set on the command line. */
+ private interface EnumOption<E extends Enum<E>> extends CommandLineOption {
+ /** The default value for this option. */
+ E defaultValue();
+
+ /** The valid values for this option. */
+ Set<E> validValues();
+ }
+
+ enum KeyOnlyOption implements CommandLineOption {
+ HEADER_COMPILATION {
+ @Override
+ public String toString() {
+ return "experimental_turbine_hjar";
+ }
+ },
+
+ USE_GRADLE_INCREMENTAL_PROCESSING {
+ @Override
+ public String toString() {
+ return "dagger.gradle.incremental";
+ }
+ },
+ }
+
+ /**
+ * A feature that can be enabled or disabled on the command line by setting {@code -Akey=ENABLED}
+ * or {@code -Akey=DISABLED}.
+ */
+ enum Feature implements EnumOption<FeatureStatus> {
+ FAST_INIT,
+
+ EXPERIMENTAL_ANDROID_MODE,
+
+ FORMAT_GENERATED_SOURCE,
+
+ WRITE_PRODUCER_NAME_IN_TOKEN,
+
+ WARN_IF_INJECTION_FACTORY_NOT_GENERATED_UPSTREAM,
+
+ IGNORE_PRIVATE_AND_STATIC_INJECTION_FOR_COMPONENT,
+
+ EXPERIMENTAL_AHEAD_OF_TIME_SUBCOMPONENTS,
+
+ FORCE_USE_SERIALIZED_COMPONENT_IMPLEMENTATIONS,
+
+ EMIT_MODIFIABLE_METADATA_ANNOTATIONS(ENABLED),
+
+ PLUGINS_VISIT_FULL_BINDING_GRAPHS,
+
+ FLOATING_BINDS_METHODS,
+
+ EXPERIMENTAL_DAGGER_ERROR_MESSAGES,
+
+ STRICT_MULTIBINDING_VALIDATION,
+
+ VALIDATE_TRANSITIVE_COMPONENT_DEPENDENCIES(ENABLED)
+ ;
+
+ final FeatureStatus defaultValue;
+
+ Feature() {
+ this(DISABLED);
+ }
+
+ Feature(FeatureStatus defaultValue) {
+ this.defaultValue = defaultValue;
+ }
+
+ @Override
+ public FeatureStatus defaultValue() {
+ return defaultValue;
+ }
+
+ @Override
+ public Set<FeatureStatus> validValues() {
+ return EnumSet.allOf(FeatureStatus.class);
+ }
+
+ @Override
+ public String toString() {
+ return optionName(this);
+ }
+ }
+
+ /** The diagnostic kind or validation type for a kind of validation. */
+ enum Validation implements EnumOption<ValidationType> {
+ DISABLE_INTER_COMPONENT_SCOPE_VALIDATION(),
+
+ NULLABLE_VALIDATION(ERROR, WARNING),
+
+ PRIVATE_MEMBER_VALIDATION(ERROR, WARNING),
+
+ STATIC_MEMBER_VALIDATION(ERROR, WARNING),
+
+ /** Whether to validate full binding graphs for components, subcomponents, and modules. */
+ FULL_BINDING_GRAPH_VALIDATION(NONE, ERROR, WARNING) {
+ @Override
+ public ImmutableList<String> aliases() {
+ return ImmutableList.of("dagger.moduleBindingValidation");
+ }
+ },
+
+ /**
+ * How to report conflicting scoped bindings when validating partial binding graphs associated
+ * with modules.
+ */
+ MODULE_HAS_DIFFERENT_SCOPES_VALIDATION(ERROR, WARNING),
+
+ /**
+ * How to report that an explicit binding in a subcomponent conflicts with an {@code @Inject}
+ * constructor used in an ancestor component.
+ */
+ EXPLICIT_BINDING_CONFLICTS_WITH_INJECT(WARNING, ERROR, NONE),
+ ;
+
+ final ValidationType defaultType;
+ final ImmutableSet<ValidationType> validTypes;
+
+ Validation() {
+ this(ERROR, WARNING, NONE);
+ }
+
+ Validation(ValidationType defaultType, ValidationType... moreValidTypes) {
+ this.defaultType = defaultType;
+ this.validTypes = immutableEnumSet(defaultType, moreValidTypes);
+ }
+
+ @Override
+ public ValidationType defaultValue() {
+ return defaultType;
+ }
+
+ @Override
+ public Set<ValidationType> validValues() {
+ return validTypes;
+ }
+
+ @Override
+ public String toString() {
+ return optionName(this);
+ }
+ }
+
+ private static String optionName(Enum<? extends EnumOption<?>> option) {
+ return "dagger." + UPPER_UNDERSCORE.to(LOWER_CAMEL, option.name());
+ }
+
+ /** The supported command-line options. */
+ public static ImmutableSet<String> supportedOptions() {
+ // need explicit type parameter to avoid a runtime stream error
+ return ImmutableSet.<String>builder()
+ .addAll(
+ Stream.<CommandLineOption[]>of(
+ KeyOnlyOption.values(), Feature.values(), Validation.values())
+ .flatMap(Arrays::stream)
+ .flatMap(CommandLineOption::allNames)
+ .collect(toImmutableSet()))
+ .add(KEYS_PER_COMPONENT_SHARD)
+ .build();
+ }
+
+ /**
+ * Returns the value for the option as set on the command line by any name, or the default value
+ * if not set.
+ *
+ * <p>If more than one name is used to set the value, but all names specify the same value,
+ * reports a warning and returns that value.
+ *
+ * <p>If more than one name is used to set the value, and not all names specify the same value,
+ * reports an error and returns the default value.
+ */
+ private <T extends Enum<T>> T parseOption(EnumOption<T> option) {
+ @SuppressWarnings("unchecked") // we only put covariant values into the map
+ T value = (T) enumOptions.computeIfAbsent(option, this::parseOptionUncached);
+ return value;
+ }
+
+ private boolean isSetOnCommandLine(Feature feature) {
+ return getUsedNames(feature).count() > 0;
+ }
+
+ private <T extends Enum<T>> T parseOptionUncached(EnumOption<T> option) {
+ ImmutableMap<String, T> values = parseOptionWithAllNames(option);
+
+ // If no value is specified, return the default value.
+ if (values.isEmpty()) {
+ return option.defaultValue();
+ }
+
+ // If all names have the same value, return that.
+ if (values.asMultimap().inverse().keySet().size() == 1) {
+ // Warn if an option was set with more than one name. That would be an error if the values
+ // differed.
+ if (values.size() > 1) {
+ reportUseOfDifferentNamesForOption(Diagnostic.Kind.WARNING, option, values.keySet());
+ }
+ return values.values().asList().get(0);
+ }
+
+ // If different names have different values, report an error and return the default
+ // value.
+ reportUseOfDifferentNamesForOption(Diagnostic.Kind.ERROR, option, values.keySet());
+ return option.defaultValue();
+ }
+
+ private void reportUseOfDifferentNamesForOption(
+ Diagnostic.Kind diagnosticKind, EnumOption<?> option, ImmutableSet<String> usedNames) {
+ processingEnvironment
+ .getMessager()
+ .printMessage(
+ diagnosticKind,
+ String.format(
+ "Only one of the equivalent options (%s) should be used; prefer -A%s",
+ usedNames.stream().map(name -> "-A" + name).collect(joining(", ")), option));
+ }
+
+ private <T extends Enum<T>> ImmutableMap<String, T> parseOptionWithAllNames(
+ EnumOption<T> option) {
+ @SuppressWarnings("unchecked") // map is covariant
+ ImmutableMap<String, T> aliasValues =
+ (ImmutableMap<String, T>)
+ allCommandLineOptions.computeIfAbsent(option, this::parseOptionWithAllNamesUncached);
+ return aliasValues;
+ }
+
+ private <T extends Enum<T>> ImmutableMap<String, T> parseOptionWithAllNamesUncached(
+ EnumOption<T> option) {
+ ImmutableMap.Builder<String, T> values = ImmutableMap.builder();
+ getUsedNames(option)
+ .forEach(
+ name -> parseOptionWithName(option, name).ifPresent(value -> values.put(name, value)));
+ return values.build();
+ }
+
+ private <T extends Enum<T>> Optional<T> parseOptionWithName(EnumOption<T> option, String key) {
+ checkArgument(processingEnvironment.getOptions().containsKey(key), "key %s not found", key);
+ String stringValue = processingEnvironment.getOptions().get(key);
+ if (stringValue == null) {
+ processingEnvironment
+ .getMessager()
+ .printMessage(Diagnostic.Kind.ERROR, "Processor option -A" + key + " needs a value");
+ } else {
+ try {
+ T value =
+ Enum.valueOf(option.defaultValue().getDeclaringClass(), Ascii.toUpperCase(stringValue));
+ if (option.validValues().contains(value)) {
+ return Optional.of(value);
+ }
+ } catch (IllegalArgumentException e) {
+ // handled below
+ }
+ processingEnvironment
+ .getMessager()
+ .printMessage(
+ Diagnostic.Kind.ERROR,
+ String.format(
+ "Processor option -A%s may only have the values %s "
+ + "(case insensitive), found: %s",
+ key, option.validValues(), stringValue));
+ }
+ return Optional.empty();
+ }
+
+ private Stream<String> getUsedNames(CommandLineOption option) {
+ return option.allNames().filter(name -> processingEnvironment.getOptions().containsKey(name));
+ }
+}
diff --git a/java/dagger/internal/codegen/compileroption/ProcessingOptions.java b/java/dagger/internal/codegen/compileroption/ProcessingOptions.java
new file mode 100644
index 000000000..39c47288b
--- /dev/null
+++ b/java/dagger/internal/codegen/compileroption/ProcessingOptions.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.compileroption;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/**
+ * A qualifier for the {@link javax.annotation.processing.ProcessingEnvironment#getOptions()
+ * processing options} passed to the current invocation of {@code javac}.
+ */
+@Retention(RUNTIME)
+@Qualifier
+public @interface ProcessingOptions {}
diff --git a/java/dagger/internal/codegen/compileroption/ValidationType.java b/java/dagger/internal/codegen/compileroption/ValidationType.java
new file mode 100644
index 000000000..d7be4ca65
--- /dev/null
+++ b/java/dagger/internal/codegen/compileroption/ValidationType.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.compileroption;
+
+import java.util.Optional;
+import javax.tools.Diagnostic;
+
+/**
+ * Allows options to control how component process validates things such as scope cycles or
+ * nullability.
+ */
+public enum ValidationType {
+ ERROR,
+ WARNING,
+ NONE;
+
+ public Optional<Diagnostic.Kind> diagnosticKind() {
+ switch (this) {
+ case ERROR:
+ return Optional.of(Diagnostic.Kind.ERROR);
+ case WARNING:
+ return Optional.of(Diagnostic.Kind.WARNING);
+ default:
+ return Optional.empty();
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/componentgenerator/BUILD b/java/dagger/internal/codegen/componentgenerator/BUILD
new file mode 100644
index 000000000..d898d4d91
--- /dev/null
+++ b/java/dagger/internal/codegen/componentgenerator/BUILD
@@ -0,0 +1,43 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# A JSR-330 compliant dependency injection system for android and java
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "componentgenerator",
+ srcs = glob(["*.java"]),
+ plugins = ["//java/dagger/internal/codegen/bootstrap"],
+ deps = [
+ "//java/dagger:core",
+ "//java/dagger/internal/codegen/base",
+ "//java/dagger/internal/codegen/binding",
+ "//java/dagger/internal/codegen/compileroption",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/codegen/javapoet",
+ "//java/dagger/internal/codegen/kotlin",
+ "//java/dagger/internal/codegen/langmodel",
+ "//java/dagger/internal/codegen/writing",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/producers",
+ "//java/dagger/spi",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
diff --git a/java/dagger/internal/codegen/componentgenerator/ComponentCreatorImplementationFactory.java b/java/dagger/internal/codegen/componentgenerator/ComponentCreatorImplementationFactory.java
new file mode 100644
index 000000000..e1b35daf9
--- /dev/null
+++ b/java/dagger/internal/codegen/componentgenerator/ComponentCreatorImplementationFactory.java
@@ -0,0 +1,542 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.componentgenerator;
+
+import static com.google.auto.common.MoreTypes.asDeclared;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.binding.SourceFiles.simpleVariableName;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeSpecs.addSupertype;
+import static dagger.internal.codegen.langmodel.Accessibility.isElementAccessibleFrom;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.Preconditions;
+import dagger.internal.codegen.base.UniqueNameSet;
+import dagger.internal.codegen.binding.ComponentCreatorDescriptor;
+import dagger.internal.codegen.binding.ComponentDescriptor;
+import dagger.internal.codegen.binding.ComponentRequirement;
+import dagger.internal.codegen.binding.ComponentRequirement.NullPolicy;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.writing.ComponentCreatorImplementation;
+import dagger.internal.codegen.writing.ComponentImplementation;
+import dagger.internal.codegen.writing.ModuleProxies;
+import java.util.Optional;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+
+/** Factory for creating {@link ComponentCreatorImplementation} instances. */
+final class ComponentCreatorImplementationFactory {
+
+ private final ComponentImplementation componentImplementation;
+ private final DaggerElements elements;
+ private final DaggerTypes types;
+ private final KotlinMetadataUtil metadataUtil;
+ private final ModuleProxies moduleProxies;
+
+ @Inject
+ ComponentCreatorImplementationFactory(
+ ComponentImplementation componentImplementation,
+ DaggerElements elements,
+ DaggerTypes types,
+ KotlinMetadataUtil metadataUtil,
+ ModuleProxies moduleProxies) {
+ this.componentImplementation = componentImplementation;
+ this.elements = elements;
+ this.types = types;
+ this.metadataUtil = metadataUtil;
+ this.moduleProxies = moduleProxies;
+ }
+
+ /** Returns a new creator implementation for the given component, if necessary. */
+ Optional<ComponentCreatorImplementation> create() {
+ if (!componentImplementation.componentDescriptor().hasCreator()) {
+ return Optional.empty();
+ }
+
+ Optional<ComponentCreatorDescriptor> creatorDescriptor =
+ componentImplementation.componentDescriptor().creatorDescriptor();
+
+ Builder builder =
+ creatorDescriptor.isPresent()
+ ? new BuilderForCreatorDescriptor(componentImplementation, creatorDescriptor.get())
+ : new BuilderForGeneratedRootComponentBuilder(componentImplementation);
+ return Optional.of(builder.build());
+ }
+
+ /** Base class for building a creator implementation. */
+ private abstract class Builder {
+ final ComponentImplementation componentImplementation;
+ final ClassName className;
+ final TypeSpec.Builder classBuilder;
+
+ private ImmutableMap<ComponentRequirement, FieldSpec> fields;
+
+ Builder(ComponentImplementation componentImplementation) {
+ this.componentImplementation = componentImplementation;
+ this.className = componentImplementation.getCreatorName();
+ this.classBuilder = classBuilder(className);
+ }
+
+ /** Builds the {@link ComponentCreatorImplementation}. */
+ ComponentCreatorImplementation build() {
+ setModifiers();
+ setSupertype();
+ this.fields = addFields();
+ addConstructor();
+ addSetterMethods();
+ addFactoryMethod();
+ return ComponentCreatorImplementation.create(classBuilder.build(), className, fields);
+ }
+
+ /** Returns the descriptor for the component. */
+ final ComponentDescriptor componentDescriptor() {
+ return componentImplementation.componentDescriptor();
+ }
+
+ /**
+ * The set of requirements that must be passed to the component's constructor in the order
+ * they must be passed.
+ */
+ final ImmutableSet<ComponentRequirement> componentConstructorRequirements() {
+ return componentImplementation.graph().componentRequirements();
+ }
+
+ /** Returns the requirements that have setter methods on the creator type. */
+ abstract ImmutableSet<ComponentRequirement> setterMethods();
+
+ /**
+ * Returns the component requirements that have factory method parameters, mapped to the name
+ * for that parameter.
+ */
+ abstract ImmutableMap<ComponentRequirement, String> factoryMethodParameters();
+
+ /**
+ * The {@link ComponentRequirement}s that this creator allows users to set. Values are a status
+ * for each requirement indicating what's needed for that requirement in the implementation
+ * class currently being generated.
+ */
+ abstract ImmutableMap<ComponentRequirement, RequirementStatus> userSettableRequirements();
+
+ /**
+ * Component requirements that are both settable by the creator and needed to construct the
+ * component.
+ */
+ private Set<ComponentRequirement> neededUserSettableRequirements() {
+ return Sets.intersection(
+ userSettableRequirements().keySet(), componentConstructorRequirements());
+ }
+
+ private void setModifiers() {
+ visibility().ifPresent(classBuilder::addModifiers);
+ if (!componentImplementation.isNested()) {
+ classBuilder.addModifiers(STATIC);
+ }
+ classBuilder.addModifiers(FINAL);
+ }
+
+ /** Returns the visibility modifier the generated class should have, if any. */
+ protected abstract Optional<Modifier> visibility();
+
+ /** Sets the superclass being extended or interface being implemented for this creator. */
+ protected abstract void setSupertype();
+
+ /** Adds a constructor for the creator type, if needed. */
+ protected abstract void addConstructor();
+
+ private ImmutableMap<ComponentRequirement, FieldSpec> addFields() {
+ // Fields in an abstract creator class need to be visible from subclasses.
+ UniqueNameSet fieldNames = new UniqueNameSet();
+ ImmutableMap<ComponentRequirement, FieldSpec> result =
+ Maps.toMap(
+ Sets.intersection(neededUserSettableRequirements(), setterMethods()),
+ requirement ->
+ FieldSpec.builder(
+ TypeName.get(requirement.type()),
+ fieldNames.getUniqueName(requirement.variableName()),
+ PRIVATE)
+ .build());
+ classBuilder.addFields(result.values());
+ return result;
+ }
+
+ private void addSetterMethods() {
+ Maps.filterKeys(userSettableRequirements(), setterMethods()::contains)
+ .forEach(
+ (requirement, status) ->
+ createSetterMethod(requirement, status).ifPresent(classBuilder::addMethod));
+ }
+
+ /** Creates a new setter method builder, with no method body, for the given requirement. */
+ protected abstract MethodSpec.Builder setterMethodBuilder(ComponentRequirement requirement);
+
+ private Optional<MethodSpec> createSetterMethod(
+ ComponentRequirement requirement, RequirementStatus status) {
+ switch (status) {
+ case NEEDED:
+ return Optional.of(normalSetterMethod(requirement));
+ case UNNEEDED:
+ // TODO(bcorso): Don't generate noop setters for any unneeded requirements.
+ // However, since this is a breaking change we can at least avoid trying
+ // to generate noop setters for impossible cases like when the requirement type
+ // is in another package. This avoids unnecessary breakages in Dagger's generated
+ // due to the noop setters.
+ if (isElementAccessibleFrom(requirement.typeElement(), className.packageName())) {
+ return Optional.of(noopSetterMethod(requirement));
+ } else {
+ return Optional.empty();
+ }
+ case UNSETTABLE_REPEATED_MODULE:
+ return Optional.of(repeatedModuleSetterMethod(requirement));
+ }
+ throw new AssertionError();
+ }
+
+ private MethodSpec normalSetterMethod(ComponentRequirement requirement) {
+ MethodSpec.Builder method = setterMethodBuilder(requirement);
+ ParameterSpec parameter = parameter(method.build());
+ method.addStatement(
+ "this.$N = $L",
+ fields.get(requirement),
+ requirement.nullPolicy(elements, types, metadataUtil).equals(NullPolicy.ALLOW)
+ ? CodeBlock.of("$N", parameter)
+ : CodeBlock.of("$T.checkNotNull($N)", Preconditions.class, parameter));
+ return maybeReturnThis(method);
+ }
+
+ private MethodSpec noopSetterMethod(ComponentRequirement requirement) {
+ MethodSpec.Builder method = setterMethodBuilder(requirement);
+ ParameterSpec parameter = parameter(method.build());
+ method
+ .addAnnotation(Deprecated.class)
+ .addJavadoc(
+ "@deprecated This module is declared, but an instance is not used in the component. "
+ + "This method is a no-op. For more, see https://dagger.dev/unused-modules.\n")
+ .addStatement("$T.checkNotNull($N)", Preconditions.class, parameter);
+ return maybeReturnThis(method);
+ }
+
+ private MethodSpec repeatedModuleSetterMethod(ComponentRequirement requirement) {
+ return setterMethodBuilder(requirement)
+ .addStatement(
+ "throw new $T($T.format($S, $T.class.getCanonicalName()))",
+ UnsupportedOperationException.class,
+ String.class,
+ "%s cannot be set because it is inherited from the enclosing component",
+ TypeNames.rawTypeName(TypeName.get(requirement.type())))
+ .build();
+ }
+
+ private ParameterSpec parameter(MethodSpec method) {
+ return getOnlyElement(method.parameters);
+ }
+
+ private MethodSpec maybeReturnThis(MethodSpec.Builder method) {
+ MethodSpec built = method.build();
+ return built.returnType.equals(TypeName.VOID)
+ ? built
+ : method.addStatement("return this").build();
+ }
+
+ private void addFactoryMethod() {
+ classBuilder.addMethod(factoryMethod());
+ }
+
+ MethodSpec factoryMethod() {
+ MethodSpec.Builder factoryMethod = factoryMethodBuilder();
+ factoryMethod
+ .returns(ClassName.get(componentDescriptor().typeElement()))
+ .addModifiers(PUBLIC);
+
+ ImmutableMap<ComponentRequirement, String> factoryMethodParameters =
+ factoryMethodParameters();
+ userSettableRequirements()
+ .keySet()
+ .forEach(
+ requirement -> {
+ if (fields.containsKey(requirement)) {
+ FieldSpec field = fields.get(requirement);
+ addNullHandlingForField(requirement, field, factoryMethod);
+ } else if (factoryMethodParameters.containsKey(requirement)) {
+ String parameterName = factoryMethodParameters.get(requirement);
+ addNullHandlingForParameter(requirement, parameterName, factoryMethod);
+ }
+ });
+ factoryMethod.addStatement(
+ "return new $T($L)",
+ componentImplementation.name(),
+ componentConstructorArgs(factoryMethodParameters));
+ return factoryMethod.build();
+ }
+
+ private void addNullHandlingForField(
+ ComponentRequirement requirement, FieldSpec field, MethodSpec.Builder factoryMethod) {
+ switch (requirement.nullPolicy(elements, types, metadataUtil)) {
+ case NEW:
+ checkState(requirement.kind().isModule());
+ factoryMethod
+ .beginControlFlow("if ($N == null)", field)
+ .addStatement("this.$N = $L", field, newModuleInstance(requirement))
+ .endControlFlow();
+ break;
+ case THROW:
+ // TODO(cgdecker,ronshapiro): ideally this should use the key instead of a class for
+ // @BindsInstance requirements, but that's not easily proguardable.
+ factoryMethod.addStatement(
+ "$T.checkBuilderRequirement($N, $T.class)",
+ Preconditions.class,
+ field,
+ TypeNames.rawTypeName(field.type));
+ break;
+ case ALLOW:
+ break;
+ }
+ }
+
+ private void addNullHandlingForParameter(
+ ComponentRequirement requirement, String parameter, MethodSpec.Builder factoryMethod) {
+ if (!requirement.nullPolicy(elements, types, metadataUtil).equals(NullPolicy.ALLOW)) {
+ // Factory method parameters are always required unless they are a nullable
+ // binds-instance (i.e. ALLOW)
+ factoryMethod.addStatement("$T.checkNotNull($L)", Preconditions.class, parameter);
+ }
+ }
+
+ /** Returns a builder for the creator's factory method. */
+ protected abstract MethodSpec.Builder factoryMethodBuilder();
+
+ private CodeBlock componentConstructorArgs(
+ ImmutableMap<ComponentRequirement, String> factoryMethodParameters) {
+ return componentConstructorRequirements().stream()
+ .map(
+ requirement -> {
+ if (fields.containsKey(requirement)) {
+ return CodeBlock.of("$N", fields.get(requirement));
+ } else if (factoryMethodParameters.containsKey(requirement)) {
+ return CodeBlock.of("$L", factoryMethodParameters.get(requirement));
+ } else {
+ return newModuleInstance(requirement);
+ }
+ })
+ .collect(toParametersCodeBlock());
+ }
+
+ private CodeBlock newModuleInstance(ComponentRequirement requirement) {
+ checkArgument(requirement.kind().isModule()); // this should be guaranteed to be true here
+ return moduleProxies.newModuleInstance(requirement.typeElement(), className);
+ }
+ }
+
+ /** Builder for a creator type defined by a {@code ComponentCreatorDescriptor}. */
+ private final class BuilderForCreatorDescriptor extends Builder {
+ final ComponentCreatorDescriptor creatorDescriptor;
+
+ BuilderForCreatorDescriptor(
+ ComponentImplementation componentImplementation,
+ ComponentCreatorDescriptor creatorDescriptor) {
+ super(componentImplementation);
+ this.creatorDescriptor = creatorDescriptor;
+ }
+
+ @Override
+ protected ImmutableMap<ComponentRequirement, RequirementStatus> userSettableRequirements() {
+ return Maps.toMap(creatorDescriptor.userSettableRequirements(), this::requirementStatus);
+ }
+
+ @Override
+ protected Optional<Modifier> visibility() {
+ return Optional.of(PRIVATE);
+ }
+
+ @Override
+ protected void setSupertype() {
+ addSupertype(classBuilder, creatorDescriptor.typeElement());
+ }
+
+ @Override
+ protected void addConstructor() {
+ // Just use the implicit no-arg public constructor.
+ }
+
+ @Override
+ protected ImmutableSet<ComponentRequirement> setterMethods() {
+ return ImmutableSet.copyOf(creatorDescriptor.setterMethods().keySet());
+ }
+
+ @Override
+ protected ImmutableMap<ComponentRequirement, String> factoryMethodParameters() {
+ return ImmutableMap.copyOf(
+ Maps.transformValues(
+ creatorDescriptor.factoryParameters(),
+ element -> element.getSimpleName().toString()));
+ }
+
+ private DeclaredType creatorType() {
+ return asDeclared(creatorDescriptor.typeElement().asType());
+ }
+
+ @Override
+ protected MethodSpec.Builder factoryMethodBuilder() {
+ return MethodSpec.overriding(creatorDescriptor.factoryMethod(), creatorType(), types);
+ }
+
+ private RequirementStatus requirementStatus(ComponentRequirement requirement) {
+ if (isRepeatedModule(requirement)) {
+ return RequirementStatus.UNSETTABLE_REPEATED_MODULE;
+ }
+
+ return componentConstructorRequirements().contains(requirement)
+ ? RequirementStatus.NEEDED
+ : RequirementStatus.UNNEEDED;
+ }
+
+ /**
+ * Returns whether the given requirement is for a repeat of a module inherited from an ancestor
+ * component. This creator is not allowed to set such a module.
+ */
+ final boolean isRepeatedModule(ComponentRequirement requirement) {
+ return !componentConstructorRequirements().contains(requirement)
+ && !isOwnedModule(requirement);
+ }
+
+ /**
+ * Returns whether the given {@code requirement} is for a module type owned by the component.
+ */
+ private boolean isOwnedModule(ComponentRequirement requirement) {
+ return componentImplementation.graph().ownedModuleTypes().contains(requirement.typeElement());
+ }
+
+ @Override
+ protected MethodSpec.Builder setterMethodBuilder(ComponentRequirement requirement) {
+ ExecutableElement supertypeMethod = creatorDescriptor.setterMethods().get(requirement);
+ MethodSpec.Builder method = MethodSpec.overriding(supertypeMethod, creatorType(), types);
+ if (!supertypeMethod.getReturnType().getKind().equals(TypeKind.VOID)) {
+ // Take advantage of covariant returns so that we don't have to worry about type variables
+ method.returns(className);
+ }
+ return method;
+ }
+ }
+
+ /**
+ * Builder for a component builder class that is automatically generated for a root component that
+ * does not have its own user-defined creator type (i.e. a {@code ComponentCreatorDescriptor}).
+ */
+ private final class BuilderForGeneratedRootComponentBuilder extends Builder {
+ BuilderForGeneratedRootComponentBuilder(ComponentImplementation componentImplementation) {
+ super(componentImplementation);
+ }
+
+ @Override
+ protected ImmutableMap<ComponentRequirement, RequirementStatus> userSettableRequirements() {
+ return Maps.toMap(
+ setterMethods(),
+ requirement ->
+ componentConstructorRequirements().contains(requirement)
+ ? RequirementStatus.NEEDED
+ : RequirementStatus.UNNEEDED);
+ }
+
+ @Override
+ protected Optional<Modifier> visibility() {
+ return componentImplementation
+ .componentDescriptor()
+ .typeElement()
+ .getModifiers()
+ .contains(PUBLIC) ? Optional.of(PUBLIC) : Optional.empty();
+ }
+
+ @Override
+ protected void setSupertype() {
+ // There's never a supertype for a root component auto-generated builder type.
+ }
+
+ @Override
+ protected void addConstructor() {
+ classBuilder.addMethod(constructorBuilder().addModifiers(PRIVATE).build());
+ }
+
+ @Override
+ protected ImmutableSet<ComponentRequirement> setterMethods() {
+ return componentDescriptor().dependenciesAndConcreteModules();
+ }
+
+ @Override
+ protected ImmutableMap<ComponentRequirement, String> factoryMethodParameters() {
+ return ImmutableMap.of();
+ }
+
+ @Override
+ protected MethodSpec.Builder factoryMethodBuilder() {
+ return methodBuilder("build");
+ }
+
+ @Override
+ protected MethodSpec.Builder setterMethodBuilder(ComponentRequirement requirement) {
+ String name = simpleVariableName(requirement.typeElement());
+ return methodBuilder(name)
+ .addModifiers(PUBLIC)
+ .addParameter(TypeName.get(requirement.type()), name)
+ .returns(className);
+ }
+ }
+
+ /** Enumeration of statuses a component requirement may have in a creator. */
+ enum RequirementStatus {
+ /** An instance is needed to create the component. */
+ NEEDED,
+
+ /**
+ * An instance is not needed to create the component, but the requirement is for a module owned
+ * by the component. Setting the requirement is a no-op and any setter method should be marked
+ * deprecated on the generated type as a warning to the user.
+ */
+ UNNEEDED,
+
+ /**
+ * The requirement may not be set in this creator because the module it is for is already
+ * inherited from an ancestor component. Any setter method for it should throw an exception.
+ */
+ UNSETTABLE_REPEATED_MODULE,
+ ;
+ }
+}
diff --git a/java/dagger/internal/codegen/componentgenerator/ComponentGenerator.java b/java/dagger/internal/codegen/componentgenerator/ComponentGenerator.java
new file mode 100644
index 000000000..e04ee14a5
--- /dev/null
+++ b/java/dagger/internal/codegen/componentgenerator/ComponentGenerator.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.componentgenerator;
+
+import static com.google.common.base.Verify.verify;
+import static dagger.internal.codegen.binding.SourceFiles.classFileName;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.Component;
+import dagger.internal.codegen.base.SourceFileGenerator;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.writing.ComponentImplementation;
+import java.util.Optional;
+import javax.annotation.processing.Filer;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+/** Generates the implementation of the abstract types annotated with {@link Component}. */
+final class ComponentGenerator extends SourceFileGenerator<BindingGraph> {
+ private final ComponentImplementationFactory componentImplementationFactory;
+
+ @Inject
+ ComponentGenerator(
+ Filer filer,
+ DaggerElements elements,
+ SourceVersion sourceVersion,
+ ComponentImplementationFactory componentImplementationFactory) {
+ super(filer, elements, sourceVersion);
+ this.componentImplementationFactory = componentImplementationFactory;
+ }
+
+ @Override
+ public ClassName nameGeneratedType(BindingGraph input) {
+ return componentName(input.componentTypeElement());
+ }
+
+ static ClassName componentName(TypeElement componentDefinitionType) {
+ ClassName componentName = ClassName.get(componentDefinitionType);
+ return ClassName.get(componentName.packageName(), "Dagger" + classFileName(componentName));
+ }
+
+ @Override
+ public Element originatingElement(BindingGraph input) {
+ return input.componentTypeElement();
+ }
+
+ @Override
+ public Optional<TypeSpec.Builder> write(BindingGraph bindingGraph) {
+ ComponentImplementation componentImplementation =
+ componentImplementationFactory.createComponentImplementation(bindingGraph);
+ verify(componentImplementation.name().equals(nameGeneratedType(bindingGraph)));
+ return Optional.of(componentImplementation.generate());
+ }
+}
diff --git a/java/dagger/internal/codegen/componentgenerator/ComponentGeneratorModule.java b/java/dagger/internal/codegen/componentgenerator/ComponentGeneratorModule.java
new file mode 100644
index 000000000..84179d602
--- /dev/null
+++ b/java/dagger/internal/codegen/componentgenerator/ComponentGeneratorModule.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.componentgenerator;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.internal.codegen.base.ClearableCache;
+import dagger.internal.codegen.base.SourceFileGenerator;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.ComponentDescriptor;
+import dagger.multibindings.IntoSet;
+
+/** Provides bindings needed to generated the component. */
+@Module(subcomponents = TopLevelImplementationComponent.class)
+public interface ComponentGeneratorModule {
+
+ @Binds
+ abstract SourceFileGenerator<BindingGraph> componentGenerator(ComponentGenerator generator);
+
+ // The HjarSourceFileGenerator wrapper first generates the entire TypeSpec before stripping out
+ // things that aren't needed for the hjar. However, this can be really expensive for the component
+ // because it is usually the most expensive file to generate, and most of its content is not
+ // needed in the hjar. Thus, instead of wrapping the ComponentGenerator in HjarSourceFileGenerator
+ // we provide a completely separate processing step, ComponentHjarProcessingStep, and generator,
+ // ComponentHjarGenerator, for when generating hjars for components, which can avoid generating
+ // the parts of the component that would have been stripped out by the HjarSourceFileGenerator.
+ @Binds
+ abstract SourceFileGenerator<ComponentDescriptor> componentHjarGenerator(
+ ComponentHjarGenerator hjarGenerator);
+
+ @Binds
+ @IntoSet
+ ClearableCache componentImplementationFactory(ComponentImplementationFactory cache);
+}
diff --git a/java/dagger/internal/codegen/componentgenerator/ComponentHjarGenerator.java b/java/dagger/internal/codegen/componentgenerator/ComponentHjarGenerator.java
new file mode 100644
index 000000000..b386a4f23
--- /dev/null
+++ b/java/dagger/internal/codegen/componentgenerator/ComponentHjarGenerator.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.componentgenerator;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static dagger.internal.codegen.binding.ComponentCreatorKind.BUILDER;
+import static dagger.internal.codegen.componentgenerator.ComponentGenerator.componentName;
+import static dagger.internal.codegen.javapoet.TypeSpecs.addSupertype;
+import static dagger.internal.codegen.langmodel.Accessibility.isElementAccessibleFrom;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.base.Ascii;
+import com.google.common.collect.Sets;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.BindsInstance;
+import dagger.internal.codegen.base.SourceFileGenerator;
+import dagger.internal.codegen.binding.ComponentCreatorDescriptor;
+import dagger.internal.codegen.binding.ComponentCreatorKind;
+import dagger.internal.codegen.binding.ComponentDescriptor;
+import dagger.internal.codegen.binding.ComponentRequirement;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.producers.internal.CancellationListener;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Stream;
+import javax.annotation.processing.Filer;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+
+/**
+ * A component generator that emits only API, without any actual implementation.
+ *
+ * <p>When compiling a header jar (hjar), Bazel needs to run annotation processors that generate
+ * API, like Dagger, to see what code they might output. Full binding graph analysis is costly and
+ * unnecessary from the perspective of the header compiler; it's sole goal is to pass along a
+ * slimmed down version of what will be the jar for a particular compilation, whether or not that
+ * compilation succeeds. If it does not, the compilation pipeline will fail, even if header
+ * compilation succeeded.
+ *
+ * <p>The components emitted by this processing step include all of the API elements exposed by the
+ * normal step. Method bodies are omitted as Turbine ignores them entirely.
+ */
+final class ComponentHjarGenerator extends SourceFileGenerator<ComponentDescriptor> {
+ private final DaggerElements elements;
+ private final DaggerTypes types;
+ private final KotlinMetadataUtil metadataUtil;
+
+ @Inject
+ ComponentHjarGenerator(
+ Filer filer,
+ DaggerElements elements,
+ DaggerTypes types,
+ SourceVersion sourceVersion,
+ KotlinMetadataUtil metadataUtil) {
+ super(filer, elements, sourceVersion);
+ this.elements = elements;
+ this.types = types;
+ this.metadataUtil = metadataUtil;
+ }
+
+ @Override
+ public ClassName nameGeneratedType(ComponentDescriptor input) {
+ return componentName(input.typeElement());
+ }
+
+ @Override
+ public Element originatingElement(ComponentDescriptor input) {
+ return input.typeElement();
+ }
+
+ @Override
+ public Optional<TypeSpec.Builder> write(ComponentDescriptor componentDescriptor) {
+ ClassName generatedTypeName = nameGeneratedType(componentDescriptor);
+ TypeSpec.Builder generatedComponent =
+ TypeSpec.classBuilder(generatedTypeName)
+ .addModifiers(FINAL)
+ .addMethod(privateConstructor());
+ if (componentDescriptor.typeElement().getModifiers().contains(PUBLIC)) {
+ generatedComponent.addModifiers(PUBLIC);
+ }
+
+ TypeElement componentElement = componentDescriptor.typeElement();
+ addSupertype(generatedComponent, componentElement);
+
+ TypeName builderMethodReturnType;
+ ComponentCreatorKind creatorKind;
+ boolean noArgFactoryMethod;
+ if (componentDescriptor.creatorDescriptor().isPresent()) {
+ ComponentCreatorDescriptor creatorDescriptor = componentDescriptor.creatorDescriptor().get();
+ builderMethodReturnType = ClassName.get(creatorDescriptor.typeElement());
+ creatorKind = creatorDescriptor.kind();
+ noArgFactoryMethod = creatorDescriptor.factoryParameters().isEmpty();
+ } else {
+ TypeSpec.Builder builder =
+ TypeSpec.classBuilder("Builder")
+ .addModifiers(STATIC, FINAL)
+ .addMethod(privateConstructor());
+ if (componentDescriptor.typeElement().getModifiers().contains(PUBLIC)) {
+ builder.addModifiers(PUBLIC);
+ }
+
+ ClassName builderClassName = generatedTypeName.nestedClass("Builder");
+ builderMethodReturnType = builderClassName;
+ creatorKind = BUILDER;
+ noArgFactoryMethod = true;
+ componentRequirements(componentDescriptor)
+ .map(requirement -> builderSetterMethod(requirement.typeElement(), builderClassName))
+ .forEach(builder::addMethod);
+ builder.addMethod(builderBuildMethod(componentDescriptor));
+ generatedComponent.addType(builder.build());
+ }
+
+ generatedComponent.addMethod(staticCreatorMethod(builderMethodReturnType, creatorKind));
+
+ if (noArgFactoryMethod
+ && !hasBindsInstanceMethods(componentDescriptor)
+ && componentRequirements(componentDescriptor)
+ .noneMatch(
+ requirement ->
+ requirement.requiresAPassedInstance(elements, types, metadataUtil))) {
+ generatedComponent.addMethod(createMethod(componentDescriptor));
+ }
+
+ DeclaredType componentType = MoreTypes.asDeclared(componentElement.asType());
+ // TODO(ronshapiro): unify with ComponentImplementationBuilder
+ Set<MethodSignature> methodSignatures =
+ Sets.newHashSetWithExpectedSize(componentDescriptor.componentMethods().size());
+ componentDescriptor.componentMethods().stream()
+ .filter(
+ method -> {
+ return methodSignatures.add(
+ MethodSignature.forComponentMethod(method, componentType, types));
+ })
+ .forEach(
+ method ->
+ generatedComponent.addMethod(
+ emptyComponentMethod(componentElement, method.methodElement())));
+
+ if (componentDescriptor.isProduction()) {
+ generatedComponent
+ .addSuperinterface(ClassName.get(CancellationListener.class))
+ .addMethod(onProducerFutureCancelledMethod());
+ }
+
+ return Optional.of(generatedComponent);
+ }
+
+ private MethodSpec emptyComponentMethod(TypeElement typeElement, ExecutableElement baseMethod) {
+ return MethodSpec.overriding(baseMethod, MoreTypes.asDeclared(typeElement.asType()), types)
+ .build();
+ }
+
+ private static MethodSpec privateConstructor() {
+ return constructorBuilder().addModifiers(PRIVATE).build();
+ }
+
+ /**
+ * Returns the {@link ComponentRequirement}s for a component that does not have a {@link
+ * ComponentDescriptor#creatorDescriptor()}.
+ */
+ private static Stream<ComponentRequirement> componentRequirements(ComponentDescriptor component) {
+ // TODO(b/152802759): See if you can merge logics that normal component processing and hjar
+ // component processing use. So that there would't be a duplicated logic (like the lines below)
+ // everytime we modify the generated code for the component.
+ checkArgument(!component.isSubcomponent());
+ return Stream.concat(
+ component.dependencies().stream(),
+ component.modules().stream()
+ .filter(
+ module ->
+ !module.moduleElement().getModifiers().contains(ABSTRACT)
+ && isElementAccessibleFrom(
+ module.moduleElement(),
+ ClassName.get(component.typeElement()).packageName()))
+ .map(module -> ComponentRequirement.forModule(module.moduleElement().asType())));
+ }
+
+ private boolean hasBindsInstanceMethods(ComponentDescriptor componentDescriptor) {
+ return componentDescriptor.creatorDescriptor().isPresent()
+ && elements
+ .getUnimplementedMethods(componentDescriptor.creatorDescriptor().get().typeElement())
+ .stream()
+ .anyMatch(method -> isBindsInstance(method));
+ }
+
+ private static boolean isBindsInstance(ExecutableElement method) {
+ if (isAnnotationPresent(method, BindsInstance.class)) {
+ return true;
+ }
+
+ if (method.getParameters().size() == 1) {
+ return isAnnotationPresent(method.getParameters().get(0), BindsInstance.class);
+ }
+
+ return false;
+ }
+
+ private static MethodSpec builderSetterMethod(
+ TypeElement componentRequirement, ClassName builderClass) {
+ String simpleName =
+ UPPER_CAMEL.to(LOWER_CAMEL, componentRequirement.getSimpleName().toString());
+ return MethodSpec.methodBuilder(simpleName)
+ .addModifiers(PUBLIC)
+ .addParameter(ClassName.get(componentRequirement), simpleName)
+ .returns(builderClass)
+ .build();
+ }
+
+ private static MethodSpec builderBuildMethod(ComponentDescriptor component) {
+ return MethodSpec.methodBuilder("build")
+ .addModifiers(PUBLIC)
+ .returns(ClassName.get(component.typeElement()))
+ .build();
+ }
+
+ private static MethodSpec staticCreatorMethod(
+ TypeName creatorMethodReturnType, ComponentCreatorKind creatorKind) {
+ return MethodSpec.methodBuilder(Ascii.toLowerCase(creatorKind.typeName()))
+ .addModifiers(PUBLIC, STATIC)
+ .returns(creatorMethodReturnType)
+ .build();
+ }
+
+ private static MethodSpec createMethod(ComponentDescriptor componentDescriptor) {
+ return MethodSpec.methodBuilder("create")
+ .addModifiers(PUBLIC, STATIC)
+ .returns(ClassName.get(componentDescriptor.typeElement()))
+ .build();
+ }
+
+ private static MethodSpec onProducerFutureCancelledMethod() {
+ return MethodSpec.methodBuilder("onProducerFutureCancelled")
+ .addModifiers(PUBLIC)
+ .addParameter(TypeName.BOOLEAN, "mayInterruptIfRunning")
+ .build();
+ }
+}
diff --git a/java/dagger/internal/codegen/componentgenerator/ComponentImplementationBuilder.java b/java/dagger/internal/codegen/componentgenerator/ComponentImplementationBuilder.java
new file mode 100644
index 000000000..04cb80f94
--- /dev/null
+++ b/java/dagger/internal/codegen/componentgenerator/ComponentImplementationBuilder.java
@@ -0,0 +1,524 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.componentgenerator;
+
+import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
+import static com.google.auto.common.MoreTypes.asDeclared;
+import static com.google.common.base.Preconditions.checkState;
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
+import static dagger.internal.codegen.binding.ComponentCreatorKind.BUILDER;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
+import static dagger.internal.codegen.javapoet.CodeBlocks.parameterNames;
+import static dagger.internal.codegen.writing.ComponentImplementation.MethodSpecKind.BUILDER_METHOD;
+import static dagger.internal.codegen.writing.ComponentImplementation.MethodSpecKind.CANCELLATION_LISTENER_METHOD;
+import static dagger.internal.codegen.writing.ComponentImplementation.MethodSpecKind.COMPONENT_METHOD;
+import static dagger.internal.codegen.writing.ComponentImplementation.MethodSpecKind.CONSTRUCTOR;
+import static dagger.internal.codegen.writing.ComponentImplementation.MethodSpecKind.INITIALIZE_METHOD;
+import static dagger.internal.codegen.writing.ComponentImplementation.TypeSpecKind.COMPONENT_CREATOR;
+import static dagger.internal.codegen.writing.ComponentImplementation.TypeSpecKind.SUBCOMPONENT;
+import static dagger.producers.CancellationPolicy.Propagation.PROPAGATE;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimaps;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.Preconditions;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.ComponentCreatorDescriptor;
+import dagger.internal.codegen.binding.ComponentCreatorKind;
+import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.binding.ComponentRequirement;
+import dagger.internal.codegen.binding.FrameworkType;
+import dagger.internal.codegen.javapoet.AnnotationSpecs;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.writing.ComponentBindingExpressions;
+import dagger.internal.codegen.writing.ComponentCreatorImplementation;
+import dagger.internal.codegen.writing.ComponentImplementation;
+import dagger.internal.codegen.writing.ComponentRequirementExpressions;
+import dagger.internal.codegen.writing.ParentComponent;
+import dagger.model.Key;
+import dagger.producers.internal.CancellationListener;
+import dagger.producers.internal.Producers;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.DeclaredType;
+
+/** A builder of {@link ComponentImplementation}s. */
+// This only needs to be public because it's referenced in an entry point.
+public final class ComponentImplementationBuilder {
+ private static final String MAY_INTERRUPT_IF_RUNNING = "mayInterruptIfRunning";
+
+ /**
+ * How many statements per {@code initialize()} or {@code onProducerFutureCancelled()} method
+ * before they get partitioned.
+ */
+ private static final int STATEMENTS_PER_METHOD = 100;
+
+ private static final String CANCELLATION_LISTENER_METHOD_NAME = "onProducerFutureCancelled";
+
+ private final Optional<ComponentImplementationBuilder> parent;
+ private final BindingGraph graph;
+ private final ComponentBindingExpressions bindingExpressions;
+ private final ComponentRequirementExpressions componentRequirementExpressions;
+ private final ComponentImplementation componentImplementation;
+ private final ComponentCreatorImplementationFactory componentCreatorImplementationFactory;
+ private final TopLevelImplementationComponent topLevelImplementationComponent;
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+ private final KotlinMetadataUtil metadataUtil;
+ private boolean done;
+
+ @Inject
+ ComponentImplementationBuilder(
+ @ParentComponent Optional<ComponentImplementationBuilder> parent,
+ BindingGraph graph,
+ ComponentBindingExpressions bindingExpressions,
+ ComponentRequirementExpressions componentRequirementExpressions,
+ ComponentImplementation componentImplementation,
+ ComponentCreatorImplementationFactory componentCreatorImplementationFactory,
+ TopLevelImplementationComponent topLevelImplementationComponent,
+ DaggerTypes types,
+ DaggerElements elements,
+ KotlinMetadataUtil metadataUtil) {
+ this.parent = parent;
+ this.graph = graph;
+ this.bindingExpressions = bindingExpressions;
+ this.componentRequirementExpressions = componentRequirementExpressions;
+ this.componentImplementation = componentImplementation;
+ this.componentCreatorImplementationFactory = componentCreatorImplementationFactory;
+ this.types = types;
+ this.elements = elements;
+ this.topLevelImplementationComponent = topLevelImplementationComponent;
+ this.metadataUtil = metadataUtil;
+ }
+
+ /**
+ * Returns a {@link ComponentImplementation} for this component. This is only intended to be
+ * called once (and will throw on successive invocations). If the component must be regenerated,
+ * use a new instance.
+ */
+ ComponentImplementation build() {
+ checkState(
+ !done,
+ "ComponentImplementationBuilder has already built the ComponentImplementation for [%s].",
+ componentImplementation.name());
+ setSupertype();
+
+ componentCreatorImplementationFactory.create()
+ .map(ComponentCreatorImplementation::spec)
+ .ifPresent(this::addCreatorClass);
+
+ getLocalAndInheritedMethods(graph.componentTypeElement(), types, elements)
+ .forEach(method -> componentImplementation.claimMethodName(method.getSimpleName()));
+
+ addFactoryMethods();
+ addInterfaceMethods();
+ addChildComponents();
+
+ addConstructorAndInitializationMethods();
+
+ if (graph.componentDescriptor().isProduction()) {
+ addCancellationListenerImplementation();
+ }
+
+ done = true;
+ return componentImplementation;
+ }
+
+ /** Set the supertype for this generated class. */
+ private void setSupertype() {
+ componentImplementation.addSupertype(graph.componentTypeElement());
+ }
+
+ private void addCreatorClass(TypeSpec creator) {
+ if (parent.isPresent()) {
+ // In an inner implementation of a subcomponent the creator is a peer class.
+ parent.get().componentImplementation.addType(SUBCOMPONENT, creator);
+ } else {
+ componentImplementation.addType(COMPONENT_CREATOR, creator);
+ }
+ }
+
+ private void addFactoryMethods() {
+ if (parent.isPresent()) {
+ graph.factoryMethod().ifPresent(this::createSubcomponentFactoryMethod);
+ } else {
+ createRootComponentFactoryMethod();
+ }
+ }
+
+ private void addInterfaceMethods() {
+ // Each component method may have been declared by several supertypes. We want to implement
+ // only one method for each distinct signature.
+ ImmutableListMultimap<MethodSignature, ComponentMethodDescriptor> componentMethodsBySignature =
+ Multimaps.index(graph.componentDescriptor().entryPointMethods(), this::getMethodSignature);
+ for (List<ComponentMethodDescriptor> methodsWithSameSignature :
+ Multimaps.asMap(componentMethodsBySignature).values()) {
+ ComponentMethodDescriptor anyOneMethod = methodsWithSameSignature.stream().findAny().get();
+ MethodSpec methodSpec = bindingExpressions.getComponentMethod(anyOneMethod);
+
+ componentImplementation.addMethod(COMPONENT_METHOD, methodSpec);
+ }
+ }
+
+ private void addCancellationListenerImplementation() {
+ componentImplementation.addSupertype(elements.getTypeElement(CancellationListener.class));
+ componentImplementation.claimMethodName(CANCELLATION_LISTENER_METHOD_NAME);
+
+ ImmutableList<ParameterSpec> parameters =
+ ImmutableList.of(ParameterSpec.builder(boolean.class, MAY_INTERRUPT_IF_RUNNING).build());
+
+ MethodSpec.Builder methodBuilder =
+ methodBuilder(CANCELLATION_LISTENER_METHOD_NAME)
+ .addModifiers(PUBLIC)
+ .addAnnotation(Override.class)
+ .addParameters(parameters);
+
+ ImmutableList<CodeBlock> cancellationStatements = cancellationStatements();
+
+ if (cancellationStatements.size() < STATEMENTS_PER_METHOD) {
+ methodBuilder.addCode(CodeBlocks.concat(cancellationStatements)).build();
+ } else {
+ ImmutableList<MethodSpec> cancelProducersMethods =
+ createPartitionedMethods(
+ "cancelProducers",
+ parameters,
+ cancellationStatements,
+ methodName -> methodBuilder(methodName).addModifiers(PRIVATE));
+ for (MethodSpec cancelProducersMethod : cancelProducersMethods) {
+ methodBuilder.addStatement("$N($L)", cancelProducersMethod, MAY_INTERRUPT_IF_RUNNING);
+ componentImplementation.addMethod(CANCELLATION_LISTENER_METHOD, cancelProducersMethod);
+ }
+ }
+
+ cancelParentStatement().ifPresent(methodBuilder::addCode);
+
+ componentImplementation.addMethod(CANCELLATION_LISTENER_METHOD, methodBuilder.build());
+ }
+
+ private ImmutableList<CodeBlock> cancellationStatements() {
+ // Reversing should order cancellations starting from entry points and going down to leaves
+ // rather than the other way around. This shouldn't really matter but seems *slightly*
+ // preferable because:
+ // When a future that another future depends on is cancelled, that cancellation will propagate
+ // up the future graph toward the entry point. Cancelling in reverse order should ensure that
+ // everything that depends on a particular node has already been cancelled when that node is
+ // cancelled, so there's no need to propagate. Otherwise, when we cancel a leaf node, it might
+ // propagate through most of the graph, making most of the cancel calls that follow in the
+ // onProducerFutureCancelled method do nothing.
+ ImmutableList<Key> cancellationKeys =
+ componentImplementation.getCancellableProducerKeys().reverse();
+
+ ImmutableList.Builder<CodeBlock> cancellationStatements = ImmutableList.builder();
+ for (Key cancellationKey : cancellationKeys) {
+ cancellationStatements.add(
+ CodeBlock.of(
+ "$T.cancel($L, $N);",
+ Producers.class,
+ bindingExpressions
+ .getDependencyExpression(
+ bindingRequest(cancellationKey, FrameworkType.PRODUCER_NODE),
+ componentImplementation.name())
+ .codeBlock(),
+ MAY_INTERRUPT_IF_RUNNING));
+ }
+ return cancellationStatements.build();
+ }
+
+ private Optional<CodeBlock> cancelParentStatement() {
+ if (!shouldPropagateCancellationToParent()) {
+ return Optional.empty();
+ }
+ return Optional.of(
+ CodeBlock.builder()
+ .addStatement(
+ "$T.this.$N($N)",
+ parent.get().componentImplementation.name(),
+ CANCELLATION_LISTENER_METHOD_NAME,
+ MAY_INTERRUPT_IF_RUNNING)
+ .build());
+ }
+
+ private boolean shouldPropagateCancellationToParent() {
+ return parent.isPresent()
+ && parent
+ .get()
+ .componentImplementation
+ .componentDescriptor()
+ .cancellationPolicy()
+ .map(policy -> policy.fromSubcomponents().equals(PROPAGATE))
+ .orElse(false);
+ }
+
+ private MethodSignature getMethodSignature(ComponentMethodDescriptor method) {
+ return MethodSignature.forComponentMethod(
+ method, MoreTypes.asDeclared(graph.componentTypeElement().asType()), types);
+ }
+
+ private void addChildComponents() {
+ for (BindingGraph subgraph : graph.subgraphs()) {
+ componentImplementation.addType(SUBCOMPONENT, childComponent(subgraph));
+ }
+ }
+
+ private TypeSpec childComponent(BindingGraph childGraph) {
+ return topLevelImplementationComponent
+ .currentImplementationSubcomponentBuilder()
+ .componentImplementation(subcomponent(childGraph))
+ .bindingGraph(childGraph)
+ .parentBuilder(Optional.of(this))
+ .parentBindingExpressions(Optional.of(bindingExpressions))
+ .parentRequirementExpressions(Optional.of(componentRequirementExpressions))
+ .build()
+ .componentImplementationBuilder()
+ .build()
+ .generate()
+ .build();
+ }
+
+ /** Creates an inner subcomponent implementation. */
+ private ComponentImplementation subcomponent(BindingGraph childGraph) {
+ return componentImplementation.childComponentImplementation(childGraph);
+ }
+
+ /** Creates and adds the constructor and methods needed for initializing the component. */
+ private void addConstructorAndInitializationMethods() {
+ MethodSpec.Builder constructor = constructorBuilder().addModifiers(PRIVATE);
+ implementInitializationMethod(constructor, initializationParameters());
+ componentImplementation.addMethod(CONSTRUCTOR, constructor.build());
+ }
+
+ /** Adds parameters and code to the given {@code initializationMethod}. */
+ private void implementInitializationMethod(
+ MethodSpec.Builder initializationMethod,
+ ImmutableMap<ComponentRequirement, ParameterSpec> initializationParameters) {
+ initializationMethod.addParameters(initializationParameters.values());
+ initializationMethod.addCode(
+ CodeBlocks.concat(componentImplementation.getComponentRequirementInitializations()));
+ addInitializeMethods(initializationMethod, initializationParameters.values().asList());
+ }
+
+ /**
+ * Adds any necessary {@code initialize} methods to the component and adds calls to them to the
+ * given {@code callingMethod}.
+ */
+ private void addInitializeMethods(
+ MethodSpec.Builder callingMethod, ImmutableList<ParameterSpec> parameters) {
+ // TODO(cgdecker): It's not the case that each initialize() method has need for all of the
+ // given parameters. In some cases, those parameters may have already been assigned to fields
+ // which could be referenced instead. In other cases, an initialize method may just not need
+ // some of the parameters because the set of initializations in that partition does not
+ // include any reference to them. Right now, the Dagger code has no way of getting that
+ // information because, among other things, componentImplementation.getImplementations() just
+ // returns a bunch of CodeBlocks with no semantic information. Additionally, we may not know
+ // yet whether a field will end up needing to be created for a specific requirement, and we
+ // don't want to create a field that ends up only being used during initialization.
+ CodeBlock args = parameterNames(parameters);
+ ImmutableList<MethodSpec> methods =
+ createPartitionedMethods(
+ "initialize",
+ makeFinal(parameters),
+ componentImplementation.getInitializations(),
+ methodName ->
+ methodBuilder(methodName)
+ .addModifiers(PRIVATE)
+ /* TODO(gak): Strictly speaking, we only need the suppression here if we are
+ * also initializing a raw field in this method, but the structure of this
+ * code makes it awkward to pass that bit through. This will be cleaned up
+ * when we no longer separate fields and initialization as we do now. */
+ .addAnnotation(AnnotationSpecs.suppressWarnings(UNCHECKED)));
+ for (MethodSpec method : methods) {
+ callingMethod.addStatement("$N($L)", method, args);
+ componentImplementation.addMethod(INITIALIZE_METHOD, method);
+ }
+ }
+
+ /**
+ * Creates one or more methods, all taking the given {@code parameters}, which partition the given
+ * list of {@code statements} among themselves such that no method has more than {@code
+ * STATEMENTS_PER_METHOD} statements in it and such that the returned methods, if called in order,
+ * will execute the {@code statements} in the given order.
+ */
+ private ImmutableList<MethodSpec> createPartitionedMethods(
+ String methodName,
+ Iterable<ParameterSpec> parameters,
+ List<CodeBlock> statements,
+ Function<String, MethodSpec.Builder> methodBuilderCreator) {
+ return Lists.partition(statements, STATEMENTS_PER_METHOD).stream()
+ .map(
+ partition ->
+ methodBuilderCreator
+ .apply(componentImplementation.getUniqueMethodName(methodName))
+ .addParameters(parameters)
+ .addCode(CodeBlocks.concat(partition))
+ .build())
+ .collect(toImmutableList());
+ }
+
+ /** Returns the given parameters with a final modifier added. */
+ private final ImmutableList<ParameterSpec> makeFinal(Collection<ParameterSpec> parameters) {
+ return parameters.stream()
+ .map(param -> param.toBuilder().addModifiers(FINAL).build())
+ .collect(toImmutableList());
+ }
+
+ /**
+ * Returns the parameters for the constructor as a map from the requirement the parameter fulfills
+ * to the spec for the parameter.
+ */
+ private final ImmutableMap<ComponentRequirement, ParameterSpec> initializationParameters() {
+ Map<ComponentRequirement, ParameterSpec> parameters;
+ if (componentImplementation.componentDescriptor().hasCreator()) {
+ parameters = Maps.toMap(graph.componentRequirements(), ComponentRequirement::toParameterSpec);
+ } else if (graph.factoryMethod().isPresent()) {
+ parameters = getFactoryMethodParameters(graph);
+ } else {
+ throw new AssertionError(
+ "Expected either a component creator or factory method but found neither.");
+ }
+
+ return renameParameters(parameters);
+ }
+
+ /**
+ * Renames the given parameters to guarantee their names do not conflict with fields in the
+ * component to ensure that a parameter is never referenced where a reference to a field was
+ * intended.
+ */
+ // TODO(cgdecker): This is a bit kludgy; it would be preferable to either qualify the field
+ // references with "this." or "super." when needed to disambiguate between field and parameter,
+ // but that would require more context than is currently available when the code referencing a
+ // field is generated.
+ private ImmutableMap<ComponentRequirement, ParameterSpec> renameParameters(
+ Map<ComponentRequirement, ParameterSpec> parameters) {
+ return ImmutableMap.copyOf(
+ Maps.transformEntries(
+ parameters,
+ (requirement, parameter) ->
+ renameParameter(
+ parameter,
+ componentImplementation.getParameterName(requirement, parameter.name))));
+ }
+
+ private ParameterSpec renameParameter(ParameterSpec parameter, String newName) {
+ return ParameterSpec.builder(parameter.type, newName)
+ .addAnnotations(parameter.annotations)
+ .addModifiers(parameter.modifiers)
+ .build();
+ }
+
+ private void createRootComponentFactoryMethod() {
+ checkState(!parent.isPresent());
+ // Top-level components have a static method that returns a builder or factory for the
+ // component. If the user defined a @Component.Builder or @Component.Factory, an
+ // implementation of their type is returned. Otherwise, an autogenerated Builder type is
+ // returned.
+ // TODO(cgdecker): Replace this abomination with a small class?
+ // Better yet, change things so that an autogenerated builder type has a descriptor of sorts
+ // just like a user-defined creator type.
+ ComponentCreatorKind creatorKind;
+ ClassName creatorType;
+ String factoryMethodName;
+ boolean noArgFactoryMethod;
+ Optional<ComponentCreatorDescriptor> creatorDescriptor =
+ graph.componentDescriptor().creatorDescriptor();
+ if (creatorDescriptor.isPresent()) {
+ ComponentCreatorDescriptor descriptor = creatorDescriptor.get();
+ creatorKind = descriptor.kind();
+ creatorType = ClassName.get(descriptor.typeElement());
+ factoryMethodName = descriptor.factoryMethod().getSimpleName().toString();
+ noArgFactoryMethod = descriptor.factoryParameters().isEmpty();
+ } else {
+ creatorKind = BUILDER;
+ creatorType = componentImplementation.getCreatorName();
+ factoryMethodName = "build";
+ noArgFactoryMethod = true;
+ }
+
+ MethodSpec creatorFactoryMethod =
+ methodBuilder(creatorKind.methodName())
+ .addModifiers(PUBLIC, STATIC)
+ .returns(creatorType)
+ .addStatement("return new $T()", componentImplementation.getCreatorName())
+ .build();
+ componentImplementation.addMethod(BUILDER_METHOD, creatorFactoryMethod);
+ if (noArgFactoryMethod && canInstantiateAllRequirements()) {
+ componentImplementation.addMethod(
+ BUILDER_METHOD,
+ methodBuilder("create")
+ .returns(ClassName.get(graph.componentTypeElement()))
+ .addModifiers(PUBLIC, STATIC)
+ .addStatement("return new $L().$L()", creatorKind.typeName(), factoryMethodName)
+ .build());
+ }
+ }
+
+ /** {@code true} if all of the graph's required dependencies can be automatically constructed */
+ private boolean canInstantiateAllRequirements() {
+ return !Iterables.any(
+ graph.componentRequirements(),
+ dependency -> dependency.requiresAPassedInstance(elements, types, metadataUtil));
+ }
+
+ private void createSubcomponentFactoryMethod(ExecutableElement factoryMethod) {
+ checkState(parent.isPresent());
+ Collection<ParameterSpec> params = getFactoryMethodParameters(graph).values();
+ MethodSpec.Builder method = MethodSpec.overriding(factoryMethod, parentType(), types);
+ params.forEach(
+ param -> method.addStatement("$T.checkNotNull($N)", Preconditions.class, param));
+ method.addStatement(
+ "return new $T($L)", componentImplementation.name(), parameterNames(params));
+
+ parent.get().componentImplementation.addMethod(COMPONENT_METHOD, method.build());
+ }
+
+ private DeclaredType parentType() {
+ return asDeclared(parent.get().graph.componentTypeElement().asType());
+ }
+ /**
+ * Returns the map of {@link ComponentRequirement}s to {@link ParameterSpec}s for the given
+ * graph's factory method.
+ */
+ private static Map<ComponentRequirement, ParameterSpec> getFactoryMethodParameters(
+ BindingGraph graph) {
+ return Maps.transformValues(graph.factoryMethodParameters(), ParameterSpec::get);
+ }
+}
diff --git a/java/dagger/internal/codegen/componentgenerator/ComponentImplementationFactory.java b/java/dagger/internal/codegen/componentgenerator/ComponentImplementationFactory.java
new file mode 100644
index 000000000..fdfcc9dce
--- /dev/null
+++ b/java/dagger/internal/codegen/componentgenerator/ComponentImplementationFactory.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.componentgenerator;
+
+import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.componentgenerator.ComponentGenerator.componentName;
+
+import dagger.internal.codegen.base.ClearableCache;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.KeyFactory;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.writing.ComponentImplementation;
+import dagger.internal.codegen.writing.SubcomponentNames;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.lang.model.element.TypeElement;
+
+/** Factory for {@link ComponentImplementation}s. */
+@Singleton
+final class ComponentImplementationFactory implements ClearableCache {
+ private final Map<TypeElement, ComponentImplementation> topLevelComponentCache = new HashMap<>();
+ private final KeyFactory keyFactory;
+ private final CompilerOptions compilerOptions;
+ private final TopLevelImplementationComponent.Builder topLevelImplementationComponentBuilder;
+
+ @Inject
+ ComponentImplementationFactory(
+ KeyFactory keyFactory,
+ CompilerOptions compilerOptions,
+ TopLevelImplementationComponent.Builder topLevelImplementationComponentBuilder) {
+ this.keyFactory = keyFactory;
+ this.compilerOptions = compilerOptions;
+ this.topLevelImplementationComponentBuilder = topLevelImplementationComponentBuilder;
+ }
+
+ /**
+ * Returns a top-level (non-nested) component implementation for a binding graph.
+ */
+ ComponentImplementation createComponentImplementation(BindingGraph bindingGraph) {
+ return reentrantComputeIfAbsent(
+ topLevelComponentCache,
+ bindingGraph.componentTypeElement(),
+ component -> createComponentImplementationUncached(bindingGraph));
+ }
+
+ private ComponentImplementation createComponentImplementationUncached(BindingGraph bindingGraph) {
+ ComponentImplementation componentImplementation =
+ ComponentImplementation.topLevelComponentImplementation(
+ bindingGraph,
+ componentName(bindingGraph.componentTypeElement()),
+ new SubcomponentNames(bindingGraph, keyFactory),
+ compilerOptions);
+
+ // TODO(dpb): explore using optional bindings for the "parent" bindings
+ return topLevelImplementationComponentBuilder
+ .topLevelComponent(componentImplementation)
+ .build()
+ .currentImplementationSubcomponentBuilder()
+ .componentImplementation(componentImplementation)
+ .bindingGraph(bindingGraph)
+ .parentBuilder(Optional.empty())
+ .parentBindingExpressions(Optional.empty())
+ .parentRequirementExpressions(Optional.empty())
+ .build()
+ .componentImplementationBuilder()
+ .build();
+ }
+
+ @Override
+ public void clearCache() {
+ topLevelComponentCache.clear();
+ }
+}
diff --git a/java/dagger/internal/codegen/componentgenerator/CurrentImplementationSubcomponent.java b/java/dagger/internal/codegen/componentgenerator/CurrentImplementationSubcomponent.java
new file mode 100644
index 000000000..558437239
--- /dev/null
+++ b/java/dagger/internal/codegen/componentgenerator/CurrentImplementationSubcomponent.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.componentgenerator;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.writing.ComponentBindingExpressions;
+import dagger.internal.codegen.writing.ComponentImplementation;
+import dagger.internal.codegen.writing.ComponentRequirementExpressions;
+import dagger.internal.codegen.writing.ParentComponent;
+import dagger.internal.codegen.writing.PerComponentImplementation;
+import java.util.Optional;
+
+/**
+ * A subcomponent that injects all objects that are responsible for creating a single {@link
+ * ComponentImplementation} instance. Each child {@link ComponentImplementation} will have its own
+ * instance of {@link CurrentImplementationSubcomponent}.
+ */
+@Subcomponent
+@PerComponentImplementation
+// This only needs to be public because the type is referenced by generated component.
+public interface CurrentImplementationSubcomponent {
+ ComponentImplementationBuilder componentImplementationBuilder();
+
+ /** Returns the builder for {@link CurrentImplementationSubcomponent}. */
+ @Subcomponent.Builder
+ interface Builder {
+ @BindsInstance
+ Builder componentImplementation(ComponentImplementation componentImplementation);
+
+ @BindsInstance
+ Builder bindingGraph(BindingGraph bindingGraph);
+
+ @BindsInstance
+ Builder parentBuilder(@ParentComponent Optional<ComponentImplementationBuilder> parentBuilder);
+
+ @BindsInstance
+ Builder parentBindingExpressions(
+ @ParentComponent Optional<ComponentBindingExpressions> parentBindingExpressions);
+
+ @BindsInstance
+ Builder parentRequirementExpressions(
+ @ParentComponent Optional<ComponentRequirementExpressions> parentRequirementExpressions);
+
+ CurrentImplementationSubcomponent build();
+ }
+}
diff --git a/java/dagger/internal/codegen/componentgenerator/MethodSignature.java b/java/dagger/internal/codegen/componentgenerator/MethodSignature.java
new file mode 100644
index 000000000..99b05a44a
--- /dev/null
+++ b/java/dagger/internal/codegen/componentgenerator/MethodSignature.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.componentgenerator;
+
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Equivalence;
+import com.google.common.collect.ImmutableList;
+import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.util.List;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+
+/** A class that defines proper {@code equals} and {@code hashcode} for a method signature. */
+@AutoValue
+abstract class MethodSignature {
+
+ abstract String name();
+
+ abstract ImmutableList<? extends Equivalence.Wrapper<? extends TypeMirror>> parameterTypes();
+
+ abstract ImmutableList<? extends Equivalence.Wrapper<? extends TypeMirror>> thrownTypes();
+
+ static MethodSignature forComponentMethod(
+ ComponentMethodDescriptor componentMethod, DeclaredType componentType, DaggerTypes types) {
+ ExecutableType methodType =
+ MoreTypes.asExecutable(types.asMemberOf(componentType, componentMethod.methodElement()));
+ return new AutoValue_MethodSignature(
+ componentMethod.methodElement().getSimpleName().toString(),
+ wrapInEquivalence(methodType.getParameterTypes()),
+ wrapInEquivalence(methodType.getThrownTypes()));
+ }
+
+ private static ImmutableList<? extends Equivalence.Wrapper<? extends TypeMirror>>
+ wrapInEquivalence(List<? extends TypeMirror> types) {
+ return types.stream().map(MoreTypes.equivalence()::wrap).collect(toImmutableList());
+ }
+}
diff --git a/java/dagger/internal/codegen/componentgenerator/TopLevelImplementationComponent.java b/java/dagger/internal/codegen/componentgenerator/TopLevelImplementationComponent.java
new file mode 100644
index 000000000..7919e471d
--- /dev/null
+++ b/java/dagger/internal/codegen/componentgenerator/TopLevelImplementationComponent.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.componentgenerator;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+import dagger.internal.codegen.writing.ComponentImplementation;
+import dagger.internal.codegen.writing.PerGeneratedFile;
+import dagger.internal.codegen.writing.TopLevel;
+
+/**
+ * A shared subcomponent for a top-level {@link ComponentImplementation} and any nested child
+ * implementations.
+ */
+@PerGeneratedFile
+@Subcomponent
+// This only needs to be public because the type is referenced by generated component.
+public interface TopLevelImplementationComponent {
+ CurrentImplementationSubcomponent.Builder currentImplementationSubcomponentBuilder();
+
+ /** Returns the builder for {@link TopLevelImplementationComponent}. */
+ @Subcomponent.Builder
+ interface Builder {
+ @BindsInstance
+ Builder topLevelComponent(@TopLevel ComponentImplementation topLevelImplementation);
+ TopLevelImplementationComponent build();
+ }
+}
diff --git a/java/dagger/internal/codegen/dagger_statistics.proto b/java/dagger/internal/codegen/dagger_statistics.proto
deleted file mode 100644
index 273e47204..000000000
--- a/java/dagger/internal/codegen/dagger_statistics.proto
+++ /dev/null
@@ -1,25 +0,0 @@
-syntax = "proto2";
-
-package dagger.internal.codegen.proto;
-option java_package = "dagger.internal.codegen.proto";
-
-import "google/protobuf/duration.proto";
-
-message DaggerBuildStatistics {
- optional google.protobuf.Duration total_processing_time = 1;
- repeated DaggerRound rounds = 2;
-}
-
-// Duration of each Dagger ProcessingStep for a single annotation processing
-// round.
-message DaggerRound {
- optional google.protobuf.Duration map_key_step_time = 1;
- optional google.protobuf.Duration inject_step_time = 2;
- optional google.protobuf.Duration monitoring_module_step_time = 3;
- optional google.protobuf.Duration multibinding_annotations_step_time = 4;
- optional google.protobuf.Duration binds_instance_step_time = 5;
- optional google.protobuf.Duration module_step_time = 6;
- optional google.protobuf.Duration component_step_time = 7;
- optional google.protobuf.Duration component_hjar_step_time = 8;
- optional google.protobuf.Duration binding_method_step_time = 9;
-}
diff --git a/java/dagger/internal/codegen/extension/BUILD b/java/dagger/internal/codegen/extension/BUILD
new file mode 100644
index 000000000..468a68583
--- /dev/null
+++ b/java/dagger/internal/codegen/extension/BUILD
@@ -0,0 +1,33 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Extra features for the JDK and Guava. This code is merged into both
+# the dagger-compiler and dagger-spi artifacts that are sent to Maven
+
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "extension",
+ srcs = glob(["*.java"]),
+ tags = ["maven:merged"],
+ deps = [
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:graph",
+ "@google_bazel_common//third_party/java/jsr305_annotations",
+ ],
+)
diff --git a/java/dagger/internal/codegen/extension/DaggerCollectors.java b/java/dagger/internal/codegen/extension/DaggerCollectors.java
new file mode 100644
index 000000000..7b0167683
--- /dev/null
+++ b/java/dagger/internal/codegen/extension/DaggerCollectors.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.extension;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.stream.Collector;
+import javax.annotation.Nullable;
+
+/**
+ * A copy of {@link com.google.common.collect.MoreCollectors} to avoid issues with the '-android'
+ * variant of Guava. See b/68008628
+ */
+public final class DaggerCollectors {
+
+ private static final Collector<Object, ?, Optional<Object>> TO_OPTIONAL =
+ Collector.of(
+ ToOptionalState::new,
+ ToOptionalState::add,
+ ToOptionalState::combine,
+ ToOptionalState::getOptional,
+ Collector.Characteristics.UNORDERED);
+
+ /**
+ * A collector that converts a stream of zero or one elements to an {@code Optional}. The returned
+ * collector throws an {@code IllegalArgumentException} if the stream consists of two or more
+ * elements, and a {@code NullPointerException} if the stream consists of exactly one element,
+ * which is null.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> Collector<T, ?, Optional<T>> toOptional() {
+ return (Collector) TO_OPTIONAL;
+ }
+
+ private static final Object NULL_PLACEHOLDER = new Object();
+
+ private static final Collector<Object, ?, Object> ONLY_ELEMENT =
+ Collector.of(
+ ToOptionalState::new,
+ (state, o) -> state.add((o == null) ? NULL_PLACEHOLDER : o),
+ ToOptionalState::combine,
+ state -> {
+ Object result = state.getElement();
+ return (result == NULL_PLACEHOLDER) ? null : result;
+ },
+ Collector.Characteristics.UNORDERED);
+
+ /**
+ * A collector that takes a stream containing exactly one element and returns that element. The
+ * returned collector throws an {@code IllegalArgumentException} if the stream consists of two or
+ * more elements, and a {@code NoSuchElementException} if the stream is empty.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> Collector<T, ?, T> onlyElement() {
+ return (Collector) ONLY_ELEMENT;
+ }
+
+ private static final class ToOptionalState {
+ static final int MAX_EXTRAS = 4;
+
+ @Nullable Object element;
+ @Nullable List<Object> extras;
+
+ ToOptionalState() {
+ element = null;
+ extras = null;
+ }
+
+ IllegalArgumentException multiples(boolean overflow) {
+ StringBuilder sb =
+ new StringBuilder().append("expected one element but was: <").append(element);
+ for (Object o : extras) {
+ sb.append(", ").append(o);
+ }
+ if (overflow) {
+ sb.append(", ...");
+ }
+ sb.append('>');
+ throw new IllegalArgumentException(sb.toString());
+ }
+
+ void add(Object o) {
+ checkNotNull(o);
+ if (element == null) {
+ this.element = o;
+ } else if (extras == null) {
+ extras = new ArrayList<>(MAX_EXTRAS);
+ extras.add(o);
+ } else if (extras.size() < MAX_EXTRAS) {
+ extras.add(o);
+ } else {
+ throw multiples(true);
+ }
+ }
+
+ ToOptionalState combine(ToOptionalState other) {
+ if (element == null) {
+ return other;
+ } else if (other.element == null) {
+ return this;
+ } else {
+ if (extras == null) {
+ extras = new ArrayList<>();
+ }
+ extras.add(other.element);
+ if (other.extras != null) {
+ this.extras.addAll(other.extras);
+ }
+ if (extras.size() > MAX_EXTRAS) {
+ extras.subList(MAX_EXTRAS, extras.size()).clear();
+ throw multiples(true);
+ }
+ return this;
+ }
+ }
+
+ Optional<Object> getOptional() {
+ if (extras == null) {
+ return Optional.ofNullable(element);
+ } else {
+ throw multiples(false);
+ }
+ }
+
+ Object getElement() {
+ if (element == null) {
+ throw new NoSuchElementException();
+ } else if (extras == null) {
+ return element;
+ } else {
+ throw multiples(false);
+ }
+ }
+ }
+
+ private DaggerCollectors() {}
+}
diff --git a/java/dagger/internal/codegen/extension/DaggerGraphs.java b/java/dagger/internal/codegen/extension/DaggerGraphs.java
new file mode 100644
index 000000000..587445b31
--- /dev/null
+++ b/java/dagger/internal/codegen/extension/DaggerGraphs.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.extension;
+
+import static com.google.common.collect.Sets.difference;
+import static com.google.common.graph.Graphs.reachableNodes;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.graph.Graph;
+import com.google.common.graph.SuccessorsFunction;
+import java.util.ArrayDeque;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+
+/** Utility methods for {@link com.google.common.graph} types. */
+public final class DaggerGraphs {
+ /**
+ * Returns a shortest path from {@code nodeU} to {@code nodeV} in {@code graph} as a list of the
+ * nodes visited in sequence, including both {@code nodeU} and {@code nodeV}. (Note that there may
+ * be many possible shortest paths.)
+ *
+ * <p>If {@code nodeV} is not {@link
+ * com.google.common.graph.Graphs#reachableNodes(com.google.common.graph.Graph, Object) reachable}
+ * from {@code nodeU}, the list returned is empty.
+ *
+ * @throws IllegalArgumentException if {@code nodeU} or {@code nodeV} is not present in {@code
+ * graph}
+ */
+ public static <N> ImmutableList<N> shortestPath(SuccessorsFunction<N> graph, N nodeU, N nodeV) {
+ if (nodeU.equals(nodeV)) {
+ return ImmutableList.of(nodeU);
+ }
+ Set<N> successors = ImmutableSet.copyOf(graph.successors(nodeU));
+ if (successors.contains(nodeV)) {
+ return ImmutableList.of(nodeU, nodeV);
+ }
+
+ Map<N, N> visitedNodeToPathPredecessor = new HashMap<>(); // encodes shortest path tree
+ for (N node : successors) {
+ visitedNodeToPathPredecessor.put(node, nodeU);
+ }
+ Queue<N> currentNodes = new ArrayDeque<N>(successors);
+ Queue<N> nextNodes = new ArrayDeque<N>();
+
+ // Perform a breadth-first traversal starting with the successors of nodeU.
+ while (!currentNodes.isEmpty()) {
+ while (!currentNodes.isEmpty()) {
+ N currentNode = currentNodes.remove();
+ for (N nextNode : graph.successors(currentNode)) {
+ if (visitedNodeToPathPredecessor.containsKey(nextNode)) {
+ continue; // we already have a shortest path to nextNode
+ }
+ visitedNodeToPathPredecessor.put(nextNode, currentNode);
+ if (nextNode.equals(nodeV)) {
+ ImmutableList.Builder<N> builder = ImmutableList.builder();
+ N node = nodeV;
+ builder.add(node);
+ while (!node.equals(nodeU)) {
+ node = visitedNodeToPathPredecessor.get(node);
+ builder.add(node);
+ }
+ return builder.build().reverse();
+ }
+ nextNodes.add(nextNode);
+ }
+ }
+ Queue<N> emptyQueue = currentNodes;
+ currentNodes = nextNodes;
+ nextNodes = emptyQueue; // reusing empty queue faster than allocating new one
+ }
+
+ return ImmutableList.of();
+ }
+
+ /** Returns the nodes in a graph that are not reachable from a node. */
+ public static <N> ImmutableSet<N> unreachableNodes(Graph<N> graph, N node) {
+ return ImmutableSet.copyOf(difference(graph.nodes(), reachableNodes(graph, node)));
+ }
+
+ private DaggerGraphs() {}
+}
diff --git a/java/dagger/internal/codegen/extension/DaggerStreams.java b/java/dagger/internal/codegen/extension/DaggerStreams.java
new file mode 100644
index 000000000..136280d12
--- /dev/null
+++ b/java/dagger/internal/codegen/extension/DaggerStreams.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2013 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.extension;
+
+import static java.util.stream.Collectors.collectingAndThen;
+import static java.util.stream.Collectors.toList;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Maps;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+/** Utilities for streams. */
+public final class DaggerStreams {
+
+ /**
+ * Returns a {@link Collector} that accumulates the input elements into a new {@link
+ * ImmutableList}, in encounter order.
+ */
+ // TODO(b/68008628): Use ImmutableList.toImmutableList().
+ public static <T> Collector<T, ?, ImmutableList<T>> toImmutableList() {
+ return collectingAndThen(toList(), ImmutableList::copyOf);
+ }
+
+ /**
+ * Returns a {@link Collector} that accumulates the input elements into a new {@link
+ * ImmutableSet}, in encounter order.
+ */
+ // TODO(b/68008628): Use ImmutableSet.toImmutableSet().
+ public static <T> Collector<T, ?, ImmutableSet<T>> toImmutableSet() {
+ return collectingAndThen(toList(), ImmutableSet::copyOf);
+ }
+
+ /**
+ * Returns a {@link Collector} that accumulates elements into an {@code ImmutableMap} whose keys
+ * and values are the result of applying the provided mapping functions to the input elements.
+ * Entries appear in the result {@code ImmutableMap} in encounter order.
+ */
+ // TODO(b/68008628): Use ImmutableMap.toImmutableMap().
+ public static <T, K, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableMap(
+ Function<? super T, K> keyMapper, Function<? super T, V> valueMapper) {
+ return Collectors.mapping(
+ value -> Maps.immutableEntry(keyMapper.apply(value), valueMapper.apply(value)),
+ Collector.of(
+ ImmutableMap::builder,
+ (ImmutableMap.Builder<K, V> builder, Map.Entry<K, V> entry) -> builder.put(entry),
+ (left, right) -> left.putAll(right.build()),
+ ImmutableMap.Builder::build));
+ }
+
+ /**
+ * Returns a {@link Collector} that accumulates elements into an {@code ImmutableSetMultimap}
+ * whose keys and values are the result of applying the provided mapping functions to the input
+ * elements. Entries appear in the result {@code ImmutableSetMultimap} in encounter order.
+ */
+ // TODO(b/68008628): Use ImmutableSetMultimap.toImmutableSetMultimap().
+ public static <T, K, V> Collector<T, ?, ImmutableSetMultimap<K, V>> toImmutableSetMultimap(
+ Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends V> valueMapper) {
+ return Collectors.mapping(
+ value -> Maps.immutableEntry(keyMapper.apply(value), valueMapper.apply(value)),
+ Collector.of(
+ ImmutableSetMultimap::builder,
+ (ImmutableSetMultimap.Builder<K, V> builder, Map.Entry<K, V> entry) ->
+ builder.put(entry),
+ (left, right) -> left.putAll(right.build()),
+ ImmutableSetMultimap.Builder::build));
+ }
+
+ /**
+ * Returns a function from {@link Object} to {@code Stream<T>}, which returns a stream containing
+ * its input if its input is an instance of {@code T}.
+ *
+ * <p>Use as an argument to {@link Stream#flatMap(Function)}:
+ *
+ * <pre>{@code Stream<Bar>} barStream = fooStream.flatMap(instancesOf(Bar.class));</pre>
+ */
+ public static <T> Function<Object, Stream<T>> instancesOf(Class<T> to) {
+ return f -> to.isInstance(f) ? Stream.of(to.cast(f)) : Stream.empty();
+ }
+
+ /** Returns a stream of all values of the given {@code enumType}. */
+ public static <E extends Enum<E>> Stream<E> valuesOf(Class<E> enumType) {
+ return EnumSet.allOf(enumType).stream();
+ }
+
+ /**
+ * A function that you can use to extract the present values from a stream of {@link Optional}s.
+ *
+ * <pre>{@code
+ * Set<Foo> foos =
+ * optionalFoos()
+ * .flatMap(DaggerStreams.presentValues())
+ * .collect(toSet());
+ * }</pre>
+ */
+ public static <T> Function<Optional<T>, Stream<T>> presentValues() {
+ return optional -> optional.map(Stream::of).orElse(Stream.empty());
+ }
+
+ /**
+ * Returns a sequential {@link Stream} of the contents of {@code iterable}, delegating to {@link
+ * Collection#stream} if possible.
+ */
+ public static <T> Stream<T> stream(Iterable<T> iterable) {
+ return (iterable instanceof Collection)
+ ? ((Collection<T>) iterable).stream()
+ : StreamSupport.stream(iterable.spliterator(), false);
+ }
+
+ private DaggerStreams() {}
+}
diff --git a/java/dagger/internal/codegen/extension/Optionals.java b/java/dagger/internal/codegen/extension/Optionals.java
new file mode 100644
index 000000000..57494a2f3
--- /dev/null
+++ b/java/dagger/internal/codegen/extension/Optionals.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.extension;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Lists.asList;
+
+import java.util.Comparator;
+import java.util.Optional;
+import java.util.function.Function;
+
+/** Utilities for {@link Optional}s. */
+public final class Optionals {
+ /**
+ * A {@link Comparator} that puts empty {@link Optional}s before present ones, and compares
+ * present {@link Optional}s by their values.
+ */
+ public static <C extends Comparable<C>> Comparator<Optional<C>> optionalComparator() {
+ return Comparator.comparing((Optional<C> optional) -> optional.isPresent())
+ .thenComparing(Optional::get);
+ }
+
+ public static <T> Comparator<Optional<T>> emptiesLast(Comparator<? super T> valueComparator) {
+ checkNotNull(valueComparator);
+ return Comparator.comparing(o -> o.orElse(null), Comparator.nullsLast(valueComparator));
+ }
+
+ /** Returns the first argument that is present, or empty if none are. */
+ @SafeVarargs
+ public static <T> Optional<T> firstPresent(
+ Optional<T> first, Optional<T> second, Optional<T>... rest) {
+ return asList(first, second, rest).stream()
+ .filter(Optional::isPresent)
+ .findFirst()
+ .orElse(Optional.empty());
+ }
+
+ /**
+ * Walks a chain of present optionals as defined by successive calls to {@code nextFunction},
+ * returning the value of the final optional that is present. The first optional in the chain is
+ * the result of {@code nextFunction(start)}.
+ */
+ public static <T> T rootmostValue(T start, Function<T, Optional<T>> nextFunction) {
+ T current = start;
+ for (Optional<T> next = nextFunction.apply(start);
+ next.isPresent();
+ next = nextFunction.apply(current)) {
+ current = next.get();
+ }
+ return current;
+ }
+
+ private Optionals() {}
+}
diff --git a/java/dagger/internal/codegen/javac/BUILD b/java/dagger/internal/codegen/javac/BUILD
new file mode 100644
index 000000000..b8cb37c8a
--- /dev/null
+++ b/java/dagger/internal/codegen/javac/BUILD
@@ -0,0 +1,44 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# A library for javac the javac plugin module.
+
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "javac",
+ srcs = glob(["*.java"]),
+ plugins = ["//java/dagger/internal/codegen:component-codegen"],
+ exports = [
+ ":javac-import",
+ ],
+ deps = [
+ ":javac-import",
+ "//java/dagger:core",
+ "//java/dagger/internal/codegen/binding",
+ "//java/dagger/internal/codegen/compileroption",
+ "//java/dagger/internal/codegen/langmodel",
+ ],
+)
+
+load("@rules_java//java:defs.bzl", "java_import")
+
+# Replacement for @bazel_tools//third_party/java/jdk/langtools:javac, which seems to have gone away?
+java_import(
+ name = "javac-import",
+ jars = ["@bazel_tools//third_party/java/jdk/langtools:javac_jar"],
+)
diff --git a/java/dagger/internal/codegen/javac/JavacPluginModule.java b/java/dagger/internal/codegen/javac/JavacPluginModule.java
new file mode 100644
index 000000000..214191a50
--- /dev/null
+++ b/java/dagger/internal/codegen/javac/JavacPluginModule.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.javac;
+
+import com.sun.tools.javac.model.JavacElements;
+import com.sun.tools.javac.model.JavacTypes;
+import com.sun.tools.javac.util.Context;
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import dagger.internal.codegen.binding.BindingGraphFactory;
+import dagger.internal.codegen.binding.ComponentDescriptorFactory;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.compileroption.JavacPluginCompilerOptions;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import javax.annotation.processing.Messager;
+import javax.inject.Inject;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.util.Types;
+import javax.tools.Diagnostic;
+
+/**
+ * A module that provides a {@link BindingGraphFactory} and {@link ComponentDescriptorFactory} for
+ * use in {@code javac} plugins. Requires a binding for the {@code javac} {@link Context}.
+ */
+@Module
+public abstract class JavacPluginModule {
+ @Binds
+ abstract CompilerOptions compilerOptions(JavacPluginCompilerOptions compilerOptions);
+
+ @Binds
+ abstract Messager messager(NullMessager nullMessager);
+
+ static final class NullMessager implements Messager {
+
+ @Inject
+ NullMessager() {}
+
+ @Override
+ public void printMessage(Diagnostic.Kind kind, CharSequence charSequence) {}
+
+ @Override
+ public void printMessage(Diagnostic.Kind kind, CharSequence charSequence, Element element) {}
+
+ @Override
+ public void printMessage(
+ Diagnostic.Kind kind,
+ CharSequence charSequence,
+ Element element,
+ AnnotationMirror annotationMirror) {}
+
+ @Override
+ public void printMessage(
+ Diagnostic.Kind kind,
+ CharSequence charSequence,
+ Element element,
+ AnnotationMirror annotationMirror,
+ AnnotationValue annotationValue) {}
+ }
+
+ @Provides
+ static DaggerElements daggerElements(Context javaContext) {
+ return new DaggerElements(
+ JavacElements.instance(javaContext), JavacTypes.instance(javaContext));
+ }
+
+ @Provides
+ static DaggerTypes daggerTypes(Context javaContext, DaggerElements elements) {
+ return new DaggerTypes(JavacTypes.instance(javaContext), elements);
+ }
+
+ @Binds abstract Types types(DaggerTypes daggerTypes);
+
+ private JavacPluginModule() {}
+}
diff --git a/java/dagger/internal/codegen/javapoet/AnnotationSpecs.java b/java/dagger/internal/codegen/javapoet/AnnotationSpecs.java
index cc0d7de0f..b66d266c2 100644
--- a/java/dagger/internal/codegen/javapoet/AnnotationSpecs.java
+++ b/java/dagger/internal/codegen/javapoet/AnnotationSpecs.java
@@ -16,34 +16,38 @@
package dagger.internal.codegen.javapoet;
-import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkArgument;
-import com.google.common.base.Ascii;
-import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.squareup.javapoet.AnnotationSpec;
-import java.util.Arrays;
/** Static factories to create {@link AnnotationSpec}s. */
public final class AnnotationSpecs {
/** Values for an {@link SuppressWarnings} annotation. */
public enum Suppression {
- RAWTYPES,
- UNCHECKED,
+ RAWTYPES("rawtypes"),
+ UNCHECKED("unchecked"),
+ FUTURE_RETURN_VALUE_IGNORED("FutureReturnValueIgnored"),
;
- @Override
- public String toString() {
- return Ascii.toLowerCase(name());
+ private final String value;
+
+ Suppression(String value) {
+ this.value = value;
}
}
/** Creates an {@link AnnotationSpec} for {@link SuppressWarnings}. */
public static AnnotationSpec suppressWarnings(Suppression first, Suppression... rest) {
- checkNotNull(first);
- Arrays.stream(rest).forEach(Preconditions::checkNotNull);
+ return suppressWarnings(ImmutableSet.copyOf(Lists.asList(first, rest)));
+ }
+
+ /** Creates an {@link AnnotationSpec} for {@link SuppressWarnings}. */
+ public static AnnotationSpec suppressWarnings(ImmutableSet<Suppression> suppressions) {
+ checkArgument(!suppressions.isEmpty());
AnnotationSpec.Builder builder = AnnotationSpec.builder(SuppressWarnings.class);
- Lists.asList(first, rest).forEach(suppression -> builder.addMember("value", "$S", suppression));
+ suppressions.forEach(suppression -> builder.addMember("value", "$S", suppression.value));
return builder.build();
}
diff --git a/java/dagger/internal/codegen/javapoet/BUILD b/java/dagger/internal/codegen/javapoet/BUILD
index f829d4912..ddb9f8802 100644
--- a/java/dagger/internal/codegen/javapoet/BUILD
+++ b/java/dagger/internal/codegen/javapoet/BUILD
@@ -15,20 +15,22 @@
# Description:
# JavaPoet extensions for use in Dagger
+load("@rules_java//java:defs.bzl", "java_library")
+
package(default_visibility = ["//:src"])
java_library(
name = "javapoet",
srcs = glob(["*.java"]),
- plugins = ["//java/dagger/internal/codegen:bootstrap_compiler_plugin"],
+ plugins = ["//java/dagger/internal/codegen/bootstrap"],
tags = ["maven:merged"],
deps = [
"//java/dagger:core",
"//java/dagger/internal/codegen/langmodel",
+ "//java/dagger/internal/guava:collect",
"//java/dagger/producers",
- "@google_bazel_common//third_party/java/auto:common",
"@google_bazel_common//third_party/java/error_prone:annotations",
- "@google_bazel_common//third_party/java/guava",
"@google_bazel_common//third_party/java/javapoet",
+ "@maven//:com_google_auto_auto_common",
],
)
diff --git a/java/dagger/internal/codegen/javapoet/TypeNames.java b/java/dagger/internal/codegen/javapoet/TypeNames.java
index 9301dbc84..71f03f0e4 100644
--- a/java/dagger/internal/codegen/javapoet/TypeNames.java
+++ b/java/dagger/internal/codegen/javapoet/TypeNames.java
@@ -25,6 +25,7 @@ import dagger.Lazy;
import dagger.MembersInjector;
import dagger.internal.DoubleCheck;
import dagger.internal.Factory;
+import dagger.internal.InjectedFieldSignature;
import dagger.internal.InstanceFactory;
import dagger.internal.MapFactory;
import dagger.internal.MapProviderFactory;
@@ -34,6 +35,7 @@ import dagger.internal.SetFactory;
import dagger.internal.SingleCheck;
import dagger.producers.Produced;
import dagger.producers.Producer;
+import dagger.producers.ProducerModule;
import dagger.producers.internal.AbstractProducer;
import dagger.producers.internal.DependencyMethodProducer;
import dagger.producers.internal.MapOfProducedProducer;
@@ -57,6 +59,8 @@ public final class TypeNames {
public static final ClassName DOUBLE_CHECK = ClassName.get(DoubleCheck.class);
public static final ClassName FACTORY = ClassName.get(Factory.class);
public static final ClassName FUTURES = ClassName.get(Futures.class);
+ public static final ClassName INJECTED_FIELD_SIGNATURE =
+ ClassName.get(InjectedFieldSignature.class);
public static final ClassName INSTANCE_FACTORY = ClassName.get(InstanceFactory.class);
public static final ClassName LAZY = ClassName.get(Lazy.class);
public static final ClassName LIST = ClassName.get(List.class);
@@ -74,6 +78,7 @@ public final class TypeNames {
public static final ClassName PRODUCED = ClassName.get(Produced.class);
public static final ClassName PRODUCER = ClassName.get(Producer.class);
public static final ClassName PRODUCERS = ClassName.get(Producers.class);
+ public static final ClassName PRODUCER_MODULE = ClassName.get(ProducerModule.class);
public static final ClassName PRODUCTION_COMPONENT_MONITOR_FACTORY =
ClassName.get(ProductionComponentMonitor.Factory.class);
public static final ClassName PROVIDER = ClassName.get(Provider.class);
diff --git a/java/dagger/internal/codegen/kotlin/BUILD b/java/dagger/internal/codegen/kotlin/BUILD
new file mode 100644
index 000000000..d1c5458a1
--- /dev/null
+++ b/java/dagger/internal/codegen/kotlin/BUILD
@@ -0,0 +1,41 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Sources related to Kotlin metadata.
+
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "kotlin",
+ srcs = glob(["*.java"]),
+ plugins = ["//java/dagger/internal/codegen/bootstrap"],
+ tags = ["maven:merged"],
+ deps = [
+ "//java/dagger:core",
+ "//java/dagger/internal/codegen/base:shared",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/codegen/langmodel",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/jsr305_annotations",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ "@maven//:com_google_auto_auto_common",
+ "@maven//:org_jetbrains_kotlin_kotlin_stdlib",
+ "@maven//:org_jetbrains_kotlinx_kotlinx_metadata_jvm",
+ ],
+)
diff --git a/java/dagger/internal/codegen/kotlin/KotlinMetadata.java b/java/dagger/internal/codegen/kotlin/KotlinMetadata.java
new file mode 100644
index 000000000..296da4465
--- /dev/null
+++ b/java/dagger/internal/codegen/kotlin/KotlinMetadata.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.kotlin;
+
+import static dagger.internal.codegen.base.MoreAnnotationValues.getIntArrayValue;
+import static dagger.internal.codegen.base.MoreAnnotationValues.getIntValue;
+import static dagger.internal.codegen.base.MoreAnnotationValues.getOptionalIntValue;
+import static dagger.internal.codegen.base.MoreAnnotationValues.getOptionalStringValue;
+import static dagger.internal.codegen.base.MoreAnnotationValues.getStringArrayValue;
+import static dagger.internal.codegen.base.MoreAnnotationValues.getStringValue;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
+import static dagger.internal.codegen.langmodel.DaggerElements.getFieldDescriptor;
+import static dagger.internal.codegen.langmodel.DaggerElements.getMethodDescriptor;
+import static kotlinx.metadata.Flag.ValueParameter.DECLARES_DEFAULT_VALUE;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import dagger.internal.codegen.extension.DaggerCollectors;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+import javax.annotation.Nullable;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.util.ElementFilter;
+import kotlin.Metadata;
+import kotlinx.metadata.Flag;
+import kotlinx.metadata.KmClassVisitor;
+import kotlinx.metadata.KmConstructorExtensionVisitor;
+import kotlinx.metadata.KmConstructorVisitor;
+import kotlinx.metadata.KmExtensionType;
+import kotlinx.metadata.KmFunctionExtensionVisitor;
+import kotlinx.metadata.KmFunctionVisitor;
+import kotlinx.metadata.KmPropertyExtensionVisitor;
+import kotlinx.metadata.KmPropertyVisitor;
+import kotlinx.metadata.KmValueParameterVisitor;
+import kotlinx.metadata.jvm.JvmConstructorExtensionVisitor;
+import kotlinx.metadata.jvm.JvmFieldSignature;
+import kotlinx.metadata.jvm.JvmFunctionExtensionVisitor;
+import kotlinx.metadata.jvm.JvmMethodSignature;
+import kotlinx.metadata.jvm.JvmPropertyExtensionVisitor;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+
+/** Data class of a TypeElement and its Kotlin metadata. */
+@AutoValue
+abstract class KotlinMetadata {
+ // Kotlin suffix for fields that are for a delegated property.
+ // See:
+ // https://github.com/JetBrains/kotlin/blob/master/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/JvmAbi.kt#L32
+ private static final String DELEGATED_PROPERTY_NAME_SUFFIX = "$delegate";
+
+ // Map that associates field elements with its Kotlin synthetic method for annotations.
+ private final Map<VariableElement, Optional<MethodForAnnotations>>
+ elementFieldAnnotationMethodMap = new HashMap<>();
+
+ // Map that associates field elements with its Kotlin getter method.
+ private final Map<VariableElement, Optional<ExecutableElement>> elementFieldGetterMethodMap =
+ new HashMap<>();
+
+ abstract TypeElement typeElement();
+
+ abstract ClassMetadata classMetadata();
+
+ @Memoized
+ ImmutableMap<String, ExecutableElement> methodDescriptors() {
+ return ElementFilter.methodsIn(typeElement().getEnclosedElements()).stream()
+ .collect(toImmutableMap(DaggerElements::getMethodDescriptor, Function.identity()));
+ }
+
+ /** Returns true if any constructor of the defined a default parameter. */
+ @Memoized
+ boolean containsConstructorWithDefaultParam() {
+ return classMetadata().constructors().stream()
+ .flatMap(constructor -> constructor.parameters().stream())
+ .anyMatch(parameter -> parameter.flags(DECLARES_DEFAULT_VALUE));
+ }
+
+ /** Gets the synthetic method for annotations of a given field element. */
+ Optional<ExecutableElement> getSyntheticAnnotationMethod(VariableElement fieldElement) {
+ return getAnnotationMethod(fieldElement)
+ .map(
+ methodForAnnotations -> {
+ if (methodForAnnotations == MethodForAnnotations.MISSING) {
+ throw new IllegalStateException(
+ "Method for annotations is missing for " + fieldElement);
+ }
+ return methodForAnnotations.method();
+ });
+ }
+
+ /**
+ * Returns true if the synthetic method for annotations is missing. This can occur when inspecting
+ * the Kotlin metadata of a property from another compilation unit.
+ */
+ boolean isMissingSyntheticAnnotationMethod(VariableElement fieldElement) {
+ return getAnnotationMethod(fieldElement)
+ .map(methodForAnnotations -> methodForAnnotations == MethodForAnnotations.MISSING)
+ // This can be missing if there was no property annotation at all (e.g. no annotations or
+ // the qualifier is already properly attached to the field). For these cases, it isn't
+ // considered missing since there was no method to look for in the first place.
+ .orElse(false);
+ }
+
+ private Optional<MethodForAnnotations> getAnnotationMethod(VariableElement fieldElement) {
+ return elementFieldAnnotationMethodMap.computeIfAbsent(
+ fieldElement, this::getAnnotationMethodUncached);
+ }
+
+ private Optional<MethodForAnnotations> getAnnotationMethodUncached(VariableElement fieldElement) {
+ return findProperty(fieldElement)
+ .methodForAnnotationsSignature()
+ .map(
+ signature ->
+ Optional.ofNullable(methodDescriptors().get(signature))
+ .map(MethodForAnnotations::create)
+ // The method may be missing across different compilations.
+ // See https://youtrack.jetbrains.com/issue/KT-34684
+ .orElse(MethodForAnnotations.MISSING));
+ }
+
+ /** Gets the getter method of a given field element corresponding to a property. */
+ Optional<ExecutableElement> getPropertyGetter(VariableElement fieldElement) {
+ return elementFieldGetterMethodMap.computeIfAbsent(
+ fieldElement, this::getPropertyGetterUncached);
+ }
+
+ private Optional<ExecutableElement> getPropertyGetterUncached(VariableElement fieldElement) {
+ return findProperty(fieldElement)
+ .getterSignature()
+ .flatMap(signature -> Optional.ofNullable(methodDescriptors().get(signature)));
+ }
+
+ private PropertyMetadata findProperty(VariableElement field) {
+ String fieldDescriptor = getFieldDescriptor(field);
+ if (classMetadata().propertiesByFieldSignature().containsKey(fieldDescriptor)) {
+ return classMetadata().propertiesByFieldSignature().get(fieldDescriptor);
+ } else {
+ // Fallback to finding property by name, see: https://youtrack.jetbrains.com/issue/KT-35124
+ final String propertyName = getPropertyNameFromField(field);
+ return classMetadata().propertiesByFieldSignature().values().stream()
+ .filter(property -> propertyName.contentEquals(property.name()))
+ .collect(DaggerCollectors.onlyElement());
+ }
+ }
+
+ private static String getPropertyNameFromField(VariableElement field) {
+ String name = field.getSimpleName().toString();
+ if (name.endsWith(DELEGATED_PROPERTY_NAME_SUFFIX)) {
+ return name.substring(0, name.length() - DELEGATED_PROPERTY_NAME_SUFFIX.length());
+ } else {
+ return name;
+ }
+ }
+
+ FunctionMetadata getFunctionMetadata(ExecutableElement method) {
+ return classMetadata().functionsBySignature().get(getMethodDescriptor(method));
+ }
+
+ /** Parse Kotlin class metadata from a given type element * */
+ static KotlinMetadata from(TypeElement typeElement) {
+ return new AutoValue_KotlinMetadata(
+ typeElement, ClassVisitor.createClassMetadata(metadataOf(typeElement)));
+ }
+
+ private static KotlinClassMetadata.Class metadataOf(TypeElement typeElement) {
+ Optional<AnnotationMirror> metadataAnnotation =
+ getAnnotationMirror(typeElement, Metadata.class);
+ Preconditions.checkState(metadataAnnotation.isPresent());
+ KotlinClassHeader header =
+ new KotlinClassHeader(
+ getIntValue(metadataAnnotation.get(), "k"),
+ getIntArrayValue(metadataAnnotation.get(), "mv"),
+ getIntArrayValue(metadataAnnotation.get(), "bv"),
+ getStringArrayValue(metadataAnnotation.get(), "d1"),
+ getStringArrayValue(metadataAnnotation.get(), "d2"),
+ getStringValue(metadataAnnotation.get(), "xs"),
+ getOptionalStringValue(metadataAnnotation.get(), "pn").orElse(null),
+ getOptionalIntValue(metadataAnnotation.get(), "xi").orElse(null));
+ KotlinClassMetadata metadata = KotlinClassMetadata.read(header);
+ if (metadata == null) {
+ // Should only happen on Kotlin < 1.0 (i.e. metadata version < 1.1)
+ throw new IllegalStateException(
+ "Unsupported metadata version. Check that your Kotlin version is >= 1.0");
+ }
+ if (metadata instanceof KotlinClassMetadata.Class) {
+ // TODO(danysantiago): If when we need other types of metadata then move to right method.
+ return (KotlinClassMetadata.Class) metadata;
+ } else {
+ throw new IllegalStateException("Unsupported metadata type: " + metadata);
+ }
+ }
+
+ private static final class ClassVisitor extends KmClassVisitor {
+ static ClassMetadata createClassMetadata(KotlinClassMetadata.Class data) {
+ ClassVisitor visitor = new ClassVisitor();
+ data.accept(visitor);
+ return visitor.classMetadata.build();
+ }
+
+ private final ClassMetadata.Builder classMetadata = ClassMetadata.builder();
+
+ @Override
+ public void visit(int flags, String name) {
+ classMetadata.flags(flags).name(name);
+ }
+
+ @Override
+ public KmConstructorVisitor visitConstructor(int flags) {
+ return new KmConstructorVisitor() {
+ private final FunctionMetadata.Builder constructor =
+ FunctionMetadata.builder(flags, "<init>");
+
+ @Override
+ public KmValueParameterVisitor visitValueParameter(int flags, String name) {
+ constructor.addParameter(ValueParameterMetadata.create(flags, name));
+ return super.visitValueParameter(flags, name);
+ }
+
+ @Override
+ public KmConstructorExtensionVisitor visitExtensions(KmExtensionType kmExtensionType) {
+ return kmExtensionType.equals(JvmConstructorExtensionVisitor.TYPE)
+ ? new JvmConstructorExtensionVisitor() {
+ @Override
+ public void visit(JvmMethodSignature jvmMethodSignature) {
+ constructor.signature(jvmMethodSignature.asString());
+ }
+ }
+ : null;
+ }
+
+ @Override
+ public void visitEnd() {
+ classMetadata.addConstructor(constructor.build());
+ }
+ };
+ }
+
+ @Override
+ public KmFunctionVisitor visitFunction(int flags, String name) {
+ return new KmFunctionVisitor() {
+ private final FunctionMetadata.Builder function = FunctionMetadata.builder(flags, name);
+
+ @Override
+ public KmValueParameterVisitor visitValueParameter(int flags, String name) {
+ function.addParameter(ValueParameterMetadata.create(flags, name));
+ return super.visitValueParameter(flags, name);
+ }
+
+ @Override
+ public KmFunctionExtensionVisitor visitExtensions(KmExtensionType kmExtensionType) {
+ return kmExtensionType.equals(JvmFunctionExtensionVisitor.TYPE)
+ ? new JvmFunctionExtensionVisitor() {
+ @Override
+ public void visit(JvmMethodSignature jvmMethodSignature) {
+ function.signature(jvmMethodSignature.asString());
+ }
+ }
+ : null;
+ }
+
+ @Override
+ public void visitEnd() {
+ classMetadata.addFunction(function.build());
+ }
+ };
+ }
+
+ @Override
+ public void visitCompanionObject(String companionObjectName) {
+ classMetadata.companionObjectName(companionObjectName);
+ }
+
+ @Override
+ public KmPropertyVisitor visitProperty(
+ int flags, String name, int getterFlags, int setterFlags) {
+ return new KmPropertyVisitor() {
+ private final PropertyMetadata.Builder property = PropertyMetadata.builder(flags, name);
+
+ @Override
+ public KmPropertyExtensionVisitor visitExtensions(KmExtensionType kmExtensionType) {
+ if (!kmExtensionType.equals(JvmPropertyExtensionVisitor.TYPE)) {
+ return null;
+ }
+
+ return new JvmPropertyExtensionVisitor() {
+ @Override
+ public void visit(
+ int jvmFlags,
+ @Nullable JvmFieldSignature jvmFieldSignature,
+ @Nullable JvmMethodSignature jvmGetterSignature,
+ @Nullable JvmMethodSignature jvmSetterSignature) {
+ property.fieldSignature(
+ Optional.ofNullable(jvmFieldSignature).map(JvmFieldSignature::asString));
+ property.getterSignature(
+ Optional.ofNullable(jvmGetterSignature).map(JvmMethodSignature::asString));
+ }
+
+ @Override
+ public void visitSyntheticMethodForAnnotations(
+ @Nullable JvmMethodSignature methodSignature) {
+ property.methodForAnnotationsSignature(
+ Optional.ofNullable(methodSignature).map(JvmMethodSignature::asString));
+ }
+ };
+ }
+
+ @Override
+ public void visitEnd() {
+ classMetadata.addProperty(property.build());
+ }
+ };
+ }
+ }
+
+ @AutoValue
+ abstract static class ClassMetadata extends BaseMetadata {
+ abstract Optional<String> companionObjectName();
+
+ abstract ImmutableSet<FunctionMetadata> constructors();
+
+ abstract ImmutableMap<String, FunctionMetadata> functionsBySignature();
+
+ abstract ImmutableMap<String, PropertyMetadata> propertiesByFieldSignature();
+
+ static Builder builder() {
+ return new AutoValue_KotlinMetadata_ClassMetadata.Builder();
+ }
+
+ @AutoValue.Builder
+ abstract static class Builder implements BaseMetadata.Builder<Builder> {
+ abstract Builder companionObjectName(String companionObjectName);
+
+ abstract ImmutableSet.Builder<FunctionMetadata> constructorsBuilder();
+
+ abstract ImmutableMap.Builder<String, FunctionMetadata> functionsBySignatureBuilder();
+
+ abstract ImmutableMap.Builder<String, PropertyMetadata> propertiesByFieldSignatureBuilder();
+
+ Builder addConstructor(FunctionMetadata constructor) {
+ constructorsBuilder().add(constructor);
+ return this;
+ }
+
+ Builder addFunction(FunctionMetadata function) {
+ functionsBySignatureBuilder().put(function.signature(), function);
+ return this;
+ }
+
+ Builder addProperty(PropertyMetadata property) {
+ if (property.fieldSignature().isPresent()) {
+ propertiesByFieldSignatureBuilder().put(property.fieldSignature().get(), property);
+ }
+ return this;
+ }
+
+ abstract ClassMetadata build();
+ }
+ }
+
+ @AutoValue
+ abstract static class FunctionMetadata extends BaseMetadata {
+ abstract String signature();
+
+ abstract ImmutableList<ValueParameterMetadata> parameters();
+
+ static Builder builder(int flags, String name) {
+ return new AutoValue_KotlinMetadata_FunctionMetadata.Builder().flags(flags).name(name);
+ }
+
+ @AutoValue.Builder
+ abstract static class Builder implements BaseMetadata.Builder<Builder> {
+ abstract Builder signature(String signature);
+
+ abstract ImmutableList.Builder<ValueParameterMetadata> parametersBuilder();
+
+ Builder addParameter(ValueParameterMetadata parameter) {
+ parametersBuilder().add(parameter);
+ return this;
+ }
+
+ abstract FunctionMetadata build();
+ }
+ }
+
+ @AutoValue
+ abstract static class PropertyMetadata extends BaseMetadata {
+ /** Returns the JVM field descriptor of the backing field of this property. */
+ abstract Optional<String> fieldSignature();
+
+ abstract Optional<String> getterSignature();
+
+ /** Returns JVM method descriptor of the synthetic method for property annotations. */
+ abstract Optional<String> methodForAnnotationsSignature();
+
+ static Builder builder(int flags, String name) {
+ return new AutoValue_KotlinMetadata_PropertyMetadata.Builder().flags(flags).name(name);
+ }
+
+ @AutoValue.Builder
+ interface Builder extends BaseMetadata.Builder<Builder> {
+ Builder fieldSignature(Optional<String> signature);
+
+ Builder getterSignature(Optional<String> signature);
+
+ Builder methodForAnnotationsSignature(Optional<String> signature);
+
+ PropertyMetadata build();
+ }
+ }
+
+ @AutoValue
+ abstract static class ValueParameterMetadata extends BaseMetadata {
+ private static ValueParameterMetadata create(int flags, String name) {
+ return new AutoValue_KotlinMetadata_ValueParameterMetadata(flags, name);
+ }
+ }
+
+ abstract static class BaseMetadata {
+ /** Returns the Kotlin metadata flags for this property. */
+ abstract int flags();
+
+ /** returns {@code true} if the given flag (e.g. {@link Flag.IS_PRIVATE}) applies. */
+ boolean flags(Flag flag) {
+ return flag.invoke(flags());
+ }
+
+ /** Returns the simple name of this property. */
+ abstract String name();
+
+ interface Builder<BuilderT> {
+ BuilderT flags(int flags);
+
+ BuilderT name(String name);
+ }
+ }
+
+ @AutoValue
+ abstract static class MethodForAnnotations {
+ static MethodForAnnotations create(ExecutableElement method) {
+ return new AutoValue_KotlinMetadata_MethodForAnnotations(method);
+ }
+
+ static final MethodForAnnotations MISSING = MethodForAnnotations.create(null);
+
+ @Nullable
+ abstract ExecutableElement method();
+ }
+}
diff --git a/java/dagger/internal/codegen/kotlin/KotlinMetadataFactory.java b/java/dagger/internal/codegen/kotlin/KotlinMetadataFactory.java
new file mode 100644
index 000000000..15154692e
--- /dev/null
+++ b/java/dagger/internal/codegen/kotlin/KotlinMetadataFactory.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.kotlin;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static dagger.internal.codegen.langmodel.DaggerElements.closestEnclosingTypeElement;
+
+import dagger.internal.codegen.base.ClearableCache;
+import java.util.HashMap;
+import java.util.Map;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import kotlin.Metadata;
+
+/**
+ * Factory creating Kotlin metadata data objects.
+ *
+ * <p>The metadata is cache since it can be expensive to parse the information stored in a proto
+ * binary string format in the metadata annotation values.
+ */
+@Singleton
+public final class KotlinMetadataFactory implements ClearableCache {
+ private final Map<TypeElement, KotlinMetadata> metadataCache = new HashMap<>();
+
+ @Inject
+ KotlinMetadataFactory() {}
+
+ /**
+ * Parses and returns the {@link KotlinMetadata} out of a given element.
+ *
+ * @throws IllegalStateException if the element has no metadata or is not enclosed in a type
+ * element with metadata. To check if an element has metadata use {@link
+ * KotlinMetadataUtil#hasMetadata(Element)}
+ */
+ public KotlinMetadata create(Element element) {
+ TypeElement enclosingElement = closestEnclosingTypeElement(element);
+ if (!isAnnotationPresent(enclosingElement, Metadata.class)) {
+ throw new IllegalStateException("Missing @Metadata for: " + enclosingElement);
+ }
+ return metadataCache.computeIfAbsent(enclosingElement, KotlinMetadata::from);
+ }
+
+ @Override
+ public void clearCache() {
+ metadataCache.clear();
+ }
+}
diff --git a/java/dagger/internal/codegen/kotlin/KotlinMetadataUtil.java b/java/dagger/internal/codegen/kotlin/KotlinMetadataUtil.java
new file mode 100644
index 000000000..76d28f0ce
--- /dev/null
+++ b/java/dagger/internal/codegen/kotlin/KotlinMetadataUtil.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.kotlin;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static dagger.internal.codegen.langmodel.DaggerElements.closestEnclosingTypeElement;
+import static kotlinx.metadata.Flag.Class.IS_COMPANION_OBJECT;
+import static kotlinx.metadata.Flag.Class.IS_DATA;
+import static kotlinx.metadata.Flag.Class.IS_OBJECT;
+import static kotlinx.metadata.Flag.IS_PRIVATE;
+
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableList;
+import dagger.internal.codegen.extension.DaggerCollectors;
+import java.lang.annotation.Annotation;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.util.ElementFilter;
+import kotlin.Metadata;
+import kotlin.jvm.JvmStatic;
+import kotlinx.metadata.Flag;
+
+/** Utility class for interacting with Kotlin Metadata. */
+public final class KotlinMetadataUtil {
+
+ private final KotlinMetadataFactory metadataFactory;
+
+ @Inject
+ KotlinMetadataUtil(KotlinMetadataFactory metadataFactory) {
+ this.metadataFactory = metadataFactory;
+ }
+
+ /**
+ * Returns {@code true} if this element has the Kotlin Metadata annotation or if it is enclosed in
+ * an element that does.
+ */
+ public boolean hasMetadata(Element element) {
+ return isAnnotationPresent(closestEnclosingTypeElement(element), Metadata.class);
+ }
+
+ /**
+ * Returns the synthetic annotations of a Kotlin property.
+ *
+ * <p>Note that this method only looks for additional annotations in the synthetic property
+ * method, if any, of a Kotlin property and not for annotations in its backing field.
+ */
+ public ImmutableCollection<? extends AnnotationMirror> getSyntheticPropertyAnnotations(
+ VariableElement fieldElement, Class<? extends Annotation> annotationType) {
+ return metadataFactory
+ .create(fieldElement)
+ .getSyntheticAnnotationMethod(fieldElement)
+ .map(methodElement -> getAnnotatedAnnotations(methodElement, annotationType).asList())
+ .orElse(ImmutableList.of());
+ }
+
+ /**
+ * Returns {@code true} if the synthetic method for annotations is missing. This can occur when
+ * the Kotlin metadata of the property reports that it contains a synthetic method for annotations
+ * but such method is not found since it is synthetic and ignored by the processor.
+ */
+ public boolean isMissingSyntheticPropertyForAnnotations(VariableElement fieldElement) {
+ return metadataFactory.create(fieldElement).isMissingSyntheticAnnotationMethod(fieldElement);
+ }
+
+ /** Returns {@code true} if this type element is a Kotlin Object. */
+ public boolean isObjectClass(TypeElement typeElement) {
+ return hasMetadata(typeElement)
+ && metadataFactory.create(typeElement).classMetadata().flags(IS_OBJECT);
+ }
+
+ /** Returns {@code true} if this type element is a Kotlin data class. */
+ public boolean isDataClass(TypeElement typeElement) {
+ return hasMetadata(typeElement)
+ && metadataFactory.create(typeElement).classMetadata().flags(IS_DATA);
+ }
+
+ /* Returns {@code true} if this type element is a Kotlin Companion Object. */
+ public boolean isCompanionObjectClass(TypeElement typeElement) {
+ return hasMetadata(typeElement)
+ && metadataFactory.create(typeElement).classMetadata().flags(IS_COMPANION_OBJECT);
+ }
+
+ /* Returns {@code true} if this type element has a Kotlin Companion Object. */
+ public boolean hasEnclosedCompanionObject(TypeElement typeElement) {
+ return hasMetadata(typeElement)
+ && metadataFactory.create(typeElement).classMetadata().companionObjectName().isPresent();
+ }
+
+ /* Returns the Companion Object element enclosed by the given type element. */
+ public TypeElement getEnclosedCompanionObject(TypeElement typeElement) {
+ return metadataFactory
+ .create(typeElement)
+ .classMetadata()
+ .companionObjectName()
+ .map(
+ companionObjectName ->
+ ElementFilter.typesIn(typeElement.getEnclosedElements()).stream()
+ .filter(
+ innerType -> innerType.getSimpleName().contentEquals(companionObjectName))
+ .collect(DaggerCollectors.onlyElement()))
+ .get();
+ }
+
+ /**
+ * Returns {@code true} if the given type element was declared <code>private</code> in its Kotlin
+ * source.
+ */
+ public boolean isVisibilityPrivate(TypeElement typeElement) {
+ return hasMetadata(typeElement)
+ && metadataFactory.create(typeElement).classMetadata().flags(IS_PRIVATE);
+ }
+
+ /**
+ * Returns {@code true} if the given executable element was declared {@code internal} in its
+ * Kotlin source.
+ */
+ public boolean isVisibilityInternal(ExecutableElement method) {
+ return hasMetadata(method)
+ && metadataFactory.create(method).getFunctionMetadata(method).flags(Flag.IS_INTERNAL);
+ }
+
+ public Optional<ExecutableElement> getPropertyGetter(VariableElement fieldElement) {
+ return metadataFactory.create(fieldElement).getPropertyGetter(fieldElement);
+ }
+
+ public boolean containsConstructorWithDefaultParam(TypeElement typeElement) {
+ return hasMetadata(typeElement)
+ && metadataFactory.create(typeElement).containsConstructorWithDefaultParam();
+ }
+
+ /**
+ * Returns {@code true} if the <code>@JvmStatic</code> annotation is present in the given element.
+ */
+ public static boolean isJvmStaticPresent(ExecutableElement element) {
+ return isAnnotationPresent(element, JvmStatic.class);
+ }
+}
diff --git a/java/dagger/internal/codegen/kythe/BUILD b/java/dagger/internal/codegen/kythe/BUILD
new file mode 100644
index 000000000..9e8dea127
--- /dev/null
+++ b/java/dagger/internal/codegen/kythe/BUILD
@@ -0,0 +1,51 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# A library for the kythe plugin.
+
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "kythe",
+ srcs = glob(["*.java"]),
+ plugins = ["//java/dagger/internal/codegen:component-codegen"],
+ deps = [
+ ":kythe_plugin",
+ "//java/dagger:core",
+ "//java/dagger/internal/codegen/binding",
+ "//java/dagger/internal/codegen/javac",
+ "//java/dagger/internal/codegen/langmodel",
+ "//java/dagger/internal/codegen/validation",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/producers",
+ "//java/dagger/spi",
+ "@google_bazel_common//third_party/java/auto:service",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
+
+load("@rules_java//java:defs.bzl", "java_import")
+
+# A _deploy.jar consisting of the java_librarys in https://github.com/kythe/kythe needed to build a
+# Kythe plugin
+# TODO(ronshapiro): replace this with a http_archive of the next release in
+# https://github.com/kythe/kythe/releases
+java_import(
+ name = "kythe_plugin",
+ jars = ["kythe_plugin_deploy.jar"],
+ neverlink = 1,
+)
diff --git a/java/dagger/internal/codegen/kythe/DaggerKythePlugin.java b/java/dagger/internal/codegen/kythe/DaggerKythePlugin.java
new file mode 100644
index 000000000..4c6f85e21
--- /dev/null
+++ b/java/dagger/internal/codegen/kythe/DaggerKythePlugin.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This must be in the dagger.internal.codegen package since Dagger doesn't expose its APIs publicly
+// https://github.com/google/dagger/issues/773 could present an opportunity to put this somewhere in
+// the regular kythe/java tree.
+package dagger.internal.codegen.kythe;
+
+import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.Iterables;
+import com.google.devtools.kythe.analyzers.base.EntrySet;
+import com.google.devtools.kythe.analyzers.base.FactEmitter;
+import com.google.devtools.kythe.analyzers.base.KytheEntrySets;
+import com.google.devtools.kythe.analyzers.java.Plugin;
+import com.google.devtools.kythe.proto.Storage.VName;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.util.Context;
+import dagger.BindsInstance;
+import dagger.Component;
+import dagger.internal.codegen.binding.Binding;
+import dagger.internal.codegen.binding.BindingDeclaration;
+import dagger.internal.codegen.binding.BindingGraphFactory;
+import dagger.internal.codegen.binding.BindingNode;
+import dagger.internal.codegen.binding.ComponentDescriptorFactory;
+import dagger.internal.codegen.binding.ModuleDescriptor;
+import dagger.internal.codegen.javac.JavacPluginModule;
+import dagger.internal.codegen.validation.InjectBindingRegistryModule;
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.DependencyEdge;
+import dagger.model.BindingGraph.Edge;
+import dagger.model.BindingGraph.Node;
+import dagger.model.DependencyRequest;
+import dagger.producers.ProductionComponent;
+import java.util.Optional;
+import java.util.logging.Logger;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.lang.model.element.Element;
+
+/**
+ * A plugin which emits nodes and edges for <a href="https://github.com/google/dagger">Dagger</a>
+ * specific code.
+ */
+@AutoService(Plugin.class)
+public class DaggerKythePlugin extends Plugin.Scanner<Void, Void> {
+ // TODO(ronshapiro): use flogger
+ private static final Logger logger = Logger.getLogger(DaggerKythePlugin.class.getCanonicalName());
+ private FactEmitter emitter;
+ @Inject ComponentDescriptorFactory componentDescriptorFactory;
+ @Inject BindingGraphFactory bindingGraphFactory;
+
+ @Override
+ public Void visitClassDef(JCClassDecl tree, Void p) {
+ if (tree.sym != null
+ && isAnyAnnotationPresent(tree.sym, Component.class, ProductionComponent.class)) {
+ addNodesForGraph(
+ bindingGraphFactory.create(
+ componentDescriptorFactory.rootComponentDescriptor(tree.sym), false));
+ }
+ return super.visitClassDef(tree, p);
+ }
+
+ private void addNodesForGraph(dagger.internal.codegen.binding.BindingGraph graph) {
+ addDependencyEdges(graph.topLevelBindingGraph());
+
+ // TODO(bcorso): Convert these to use the new BindingGraph
+ addModuleEdges(graph);
+ addChildComponentEdges(graph);
+ }
+
+ private void addDependencyEdges(BindingGraph graph) {
+ for (DependencyEdge dependencyEdge : graph.dependencyEdges()) {
+ DependencyRequest dependency = dependencyEdge.dependencyRequest();
+ Node node = graph.network().incidentNodes(dependencyEdge).target();
+ addEdgesForDependencyRequest(dependency, (BindingNode) node, graph);
+ }
+ }
+
+ /**
+ * Add {@code /inject/satisfiedby} edges from {@code dependency}'s {@link
+ * DependencyRequest#requestElement()} to any {@link BindingDeclaration#bindingElement() binding
+ * elements} that satisfy the request.
+ *
+ * <p>This collapses requests for synthetic bindings so that a request for a multibound key
+ * points to all of the contributions for the multibound object. It does so by recursively calling
+ * this method, with each dependency's key as the {@code targetKey}.
+ */
+ private void addEdgesForDependencyRequest(
+ DependencyRequest dependency, BindingNode bindingNode, BindingGraph graph) {
+ if (!dependency.requestElement().isPresent()) {
+ return;
+ }
+ Binding binding = bindingNode.delegate();
+ if (binding.bindingElement().isPresent()) {
+ addDependencyEdge(dependency, binding);
+ } else {
+ for (Edge outEdge : graph.network().outEdges(bindingNode)) {
+ if (outEdge instanceof DependencyEdge) {
+ Node outNode = graph.network().incidentNodes(outEdge).target();
+ addEdgesForDependencyRequest(dependency, (BindingNode) outNode, graph);
+ }
+ }
+ }
+ for (BindingDeclaration bindingDeclaration :
+ Iterables.concat(
+ bindingNode.multibindingDeclarations(),
+ bindingNode.optionalBindingDeclarations())) {
+ addDependencyEdge(dependency, bindingDeclaration);
+ }
+ }
+
+ private void addDependencyEdge(
+ DependencyRequest dependency, BindingDeclaration bindingDeclaration) {
+ Element requestElement = dependency.requestElement().get();
+ Element bindingElement = bindingDeclaration.bindingElement().get();
+ Optional<VName> requestElementNode = jvmNode(requestElement, "request element");
+ Optional<VName> bindingElementNode = jvmNode(bindingElement, "binding element");
+ emitEdge(requestElementNode, "/inject/satisfiedby", bindingElementNode);
+ // TODO(ronshapiro): emit facts about the component that satisfies the edge
+ }
+
+ private void addModuleEdges(dagger.internal.codegen.binding.BindingGraph graph) {
+ Optional<VName> componentNode = jvmNode(graph.componentTypeElement(), "component");
+ for (ModuleDescriptor module : graph.componentDescriptor().modules()) {
+ Optional<VName> moduleNode = jvmNode(module.moduleElement(), "module");
+ emitEdge(componentNode, "/inject/installsmodule", moduleNode);
+ }
+ graph.subgraphs().forEach(this::addModuleEdges);
+ }
+
+ private void addChildComponentEdges(dagger.internal.codegen.binding.BindingGraph graph) {
+ Optional<VName> componentNode = jvmNode(graph.componentTypeElement(), "component");
+ for (dagger.internal.codegen.binding.BindingGraph subgraph : graph.subgraphs()) {
+ Optional<VName> subcomponentNode =
+ jvmNode(subgraph.componentTypeElement(), "child component");
+ emitEdge(componentNode, "/inject/childcomponent", subcomponentNode);
+ }
+ graph.subgraphs().forEach(this::addChildComponentEdges);
+ }
+
+ private Optional<VName> jvmNode(Element element, String name) {
+ Optional<VName> jvmNode = kytheGraph.getJvmNode((Symbol) element).map(KytheNode::getVName);
+ if (!jvmNode.isPresent()) {
+ logger.warning(String.format("Missing JVM node for %s: %s", name, element));
+ }
+ return jvmNode;
+ }
+
+ private void emitEdge(Optional<VName> source, String edgeName, Optional<VName> target) {
+ source.ifPresent(
+ s -> target.ifPresent(t -> new EntrySet.Builder(s, edgeName, t).build().emit(emitter)));
+ }
+
+ @Override
+ public void run(
+ JCCompilationUnit compilationUnit, KytheEntrySets entrySets, KytheGraph kytheGraph) {
+ if (bindingGraphFactory == null) {
+ emitter = entrySets.getEmitter();
+ DaggerDaggerKythePlugin_PluginComponent.builder()
+ .context(kytheGraph.getJavaContext())
+ .build()
+ .inject(this);
+ }
+ super.run(compilationUnit, entrySets, kytheGraph);
+ }
+
+ @Singleton
+ @Component(modules = {InjectBindingRegistryModule.class, JavacPluginModule.class})
+ interface PluginComponent {
+ void inject(DaggerKythePlugin plugin);
+
+ @Component.Builder
+ interface Builder {
+ @BindsInstance
+ Builder context(Context context);
+
+ PluginComponent build();
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/kythe_plugin_deploy.jar b/java/dagger/internal/codegen/kythe/kythe_plugin_deploy.jar
index 3a1eed256..3a1eed256 100644
--- a/java/dagger/internal/codegen/kythe_plugin_deploy.jar
+++ b/java/dagger/internal/codegen/kythe/kythe_plugin_deploy.jar
Binary files differ
diff --git a/java/dagger/internal/codegen/langmodel/BUILD b/java/dagger/internal/codegen/langmodel/BUILD
index 16fa5d8d2..670f4aa99 100644
--- a/java/dagger/internal/codegen/langmodel/BUILD
+++ b/java/dagger/internal/codegen/langmodel/BUILD
@@ -15,17 +15,22 @@
# Description:
# Dagger-specific extensions to the javax.lang.model APIs
+load("@rules_java//java:defs.bzl", "java_library")
+
package(default_visibility = ["//:src"])
java_library(
name = "langmodel",
srcs = glob(["*.java"]),
- plugins = ["//java/dagger/internal/codegen:bootstrap_compiler_plugin"],
+ plugins = ["//java/dagger/internal/codegen/bootstrap"],
tags = ["maven:merged"],
deps = [
"//java/dagger:core",
- "@google_bazel_common//third_party/java/auto:common",
- "@google_bazel_common//third_party/java/guava",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:concurrent",
+ "//java/dagger/internal/guava:graph",
"@google_bazel_common//third_party/java/javapoet",
+ "@maven//:com_google_auto_auto_common",
],
)
diff --git a/java/dagger/internal/codegen/langmodel/DaggerElements.java b/java/dagger/internal/codegen/langmodel/DaggerElements.java
index 873ad3d1d..12cec31d5 100644
--- a/java/dagger/internal/codegen/langmodel/DaggerElements.java
+++ b/java/dagger/internal/codegen/langmodel/DaggerElements.java
@@ -28,6 +28,7 @@ import static javax.lang.model.element.Modifier.ABSTRACT;
import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.graph.Traverser;
@@ -42,6 +43,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
@@ -51,8 +53,22 @@ import javax.lang.model.element.ElementVisitor;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Name;
import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.QualifiedNameable;
import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ErrorType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.IntersectionType;
+import javax.lang.model.type.NoType;
+import javax.lang.model.type.NullType;
+import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVariable;
+import javax.lang.model.type.UnionType;
+import javax.lang.model.type.WildcardType;
+import javax.lang.model.util.AbstractTypeVisitor8;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleElementVisitor8;
import javax.lang.model.util.Types;
@@ -231,6 +247,161 @@ public final class DaggerElements implements Elements {
}
/**
+ * Returns the field descriptor of the given {@code element}.
+ *
+ * <p>This is useful for matching Kotlin Metadata JVM Signatures with elements from the AST.
+ *
+ * <p>For reference, see the <a
+ * href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3.2">JVM
+ * specification, section 4.3.2</a>.
+ */
+ public static String getFieldDescriptor(VariableElement element) {
+ return element.getSimpleName() + ":" + getDescriptor(element.asType());
+ }
+
+ /**
+ * Returns the method descriptor of the given {@code element}.
+ *
+ * <p>This is useful for matching Kotlin Metadata JVM Signatures with elements from the AST.
+ *
+ * <p>For reference, see the <a
+ * href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3.3">JVM
+ * specification, section 4.3.3</a>.
+ */
+ public static String getMethodDescriptor(ExecutableElement element) {
+ return element.getSimpleName() + getDescriptor(element.asType());
+ }
+
+ private static String getDescriptor(TypeMirror t) {
+ return t.accept(JVM_DESCRIPTOR_TYPE_VISITOR, null);
+ }
+
+ private static final AbstractTypeVisitor8<String, Void> JVM_DESCRIPTOR_TYPE_VISITOR =
+ new AbstractTypeVisitor8<String, Void>() {
+
+ @Override
+ public String visitArray(ArrayType arrayType, Void v) {
+ return "[" + getDescriptor(arrayType.getComponentType());
+ }
+
+ @Override
+ public String visitDeclared(DeclaredType declaredType, Void v) {
+ return "L" + getInternalName(declaredType.asElement()) + ";";
+ }
+
+ @Override
+ public String visitError(ErrorType errorType, Void v) {
+ // For descriptor generating purposes we don't need a fully modeled type since we are
+ // only interested in obtaining the class name in its "internal form".
+ return visitDeclared(errorType, v);
+ }
+
+ @Override
+ public String visitExecutable(ExecutableType executableType, Void v) {
+ String parameterDescriptors =
+ executableType.getParameterTypes().stream()
+ .map(DaggerElements::getDescriptor)
+ .collect(Collectors.joining());
+ String returnDescriptor = getDescriptor(executableType.getReturnType());
+ return "(" + parameterDescriptors + ")" + returnDescriptor;
+ }
+
+ @Override
+ public String visitIntersection(IntersectionType intersectionType, Void v) {
+ // For a type variable with multiple bounds: "the erasure of a type variable is determined
+ // by the first type in its bound" - JVM Spec Sec 4.4
+ return getDescriptor(intersectionType.getBounds().get(0));
+ }
+
+ @Override
+ public String visitNoType(NoType noType, Void v) {
+ return "V";
+ }
+
+ @Override
+ public String visitNull(NullType nullType, Void v) {
+ return visitUnknown(nullType, null);
+ }
+
+ @Override
+ public String visitPrimitive(PrimitiveType primitiveType, Void v) {
+ switch (primitiveType.getKind()) {
+ case BOOLEAN:
+ return "Z";
+ case BYTE:
+ return "B";
+ case SHORT:
+ return "S";
+ case INT:
+ return "I";
+ case LONG:
+ return "J";
+ case CHAR:
+ return "C";
+ case FLOAT:
+ return "F";
+ case DOUBLE:
+ return "D";
+ default:
+ throw new IllegalArgumentException("Unknown primitive type.");
+ }
+ }
+
+ @Override
+ public String visitTypeVariable(TypeVariable typeVariable, Void v) {
+ // The erasure of a type variable is the erasure of its leftmost bound. - JVM Spec Sec 4.6
+ return getDescriptor(typeVariable.getUpperBound());
+ }
+
+ @Override
+ public String visitUnion(UnionType unionType, Void v) {
+ return visitUnknown(unionType, null);
+ }
+
+ @Override
+ public String visitUnknown(TypeMirror typeMirror, Void v) {
+ throw new IllegalArgumentException("Unsupported type: " + typeMirror);
+ }
+
+ @Override
+ public String visitWildcard(WildcardType wildcardType, Void v) {
+ return "";
+ }
+
+ /**
+ * Returns the name of this element in its "internal form".
+ *
+ * <p>For reference, see the <a
+ * href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.2">JVM
+ * specification, section 4.2</a>.
+ */
+ private String getInternalName(Element element) {
+ try {
+ TypeElement typeElement = MoreElements.asType(element);
+ switch (typeElement.getNestingKind()) {
+ case TOP_LEVEL:
+ return typeElement.getQualifiedName().toString().replace('.', '/');
+ case MEMBER:
+ return getInternalName(typeElement.getEnclosingElement())
+ + "$"
+ + typeElement.getSimpleName();
+ default:
+ throw new IllegalArgumentException("Unsupported nesting kind.");
+ }
+ } catch (IllegalArgumentException e) {
+ // Not a TypeElement, try something else...
+ }
+
+ if (element instanceof QualifiedNameable) {
+ QualifiedNameable qualifiedNameElement = (QualifiedNameable) element;
+ return qualifiedNameElement.getQualifiedName().toString().replace('.', '/');
+ }
+
+ return element.getSimpleName().toString();
+ }
+ };
+
+ /**
* Invokes {@link Elements#getTypeElement(CharSequence)}, throwing {@link TypeNotPresentException}
* if it is not accessible in the current compilation.
*/
@@ -253,6 +424,14 @@ public final class DaggerElements implements Elements {
return elements.getElementValuesWithDefaults(a);
}
+ /** Returns a map of annotation values keyed by attribute name. */
+ public Map<String, ? extends AnnotationValue> getElementValuesWithDefaultsByName(
+ AnnotationMirror a) {
+ ImmutableMap.Builder<String, AnnotationValue> builder = ImmutableMap.builder();
+ getElementValuesWithDefaults(a).forEach((k, v) -> builder.put(k.getSimpleName().toString(), v));
+ return builder.build();
+ }
+
@Override
public String getDocComment(Element e) {
return elements.getDocComment(e);
diff --git a/java/dagger/internal/codegen/langmodel/DaggerTypes.java b/java/dagger/internal/codegen/langmodel/DaggerTypes.java
index e588fbd33..fb291dbdb 100644
--- a/java/dagger/internal/codegen/langmodel/DaggerTypes.java
+++ b/java/dagger/internal/codegen/langmodel/DaggerTypes.java
@@ -83,7 +83,7 @@ public final class DaggerTypes implements Types {
* @throws IllegalArgumentException if {@code type} is not a declared type or has zero or more
* than one type arguments.
*/
- public TypeMirror unwrapType(TypeMirror type) {
+ public static TypeMirror unwrapType(TypeMirror type) {
TypeMirror unwrapped = unwrapTypeOrDefault(type, null);
checkArgument(unwrapped != null, "%s is a raw type", type);
return unwrapped;
@@ -101,7 +101,7 @@ public final class DaggerTypes implements Types {
return unwrapTypeOrDefault(type, elements.getTypeElement(Object.class).asType());
}
- private TypeMirror unwrapTypeOrDefault(TypeMirror type, TypeMirror defaultType) {
+ private static TypeMirror unwrapTypeOrDefault(TypeMirror type, TypeMirror defaultType) {
DeclaredType declaredType = MoreTypes.asDeclared(type);
TypeElement typeElement = MoreElements.asType(declaredType.asElement());
checkArgument(
diff --git a/java/dagger/internal/codegen/package-info.java b/java/dagger/internal/codegen/package-info.java
index c8cc40430..4c478d620 100644
--- a/java/dagger/internal/codegen/package-info.java
+++ b/java/dagger/internal/codegen/package-info.java
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+/** Apply {@link CheckReturnValue} by default to every method in this package. */
@CheckReturnValue
package dagger.internal.codegen;
diff --git a/java/dagger/internal/codegen/serialization/BUILD b/java/dagger/internal/codegen/serialization/BUILD
deleted file mode 100644
index 2bc02b420..000000000
--- a/java/dagger/internal/codegen/serialization/BUILD
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright (C) 2019 The Dagger Authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Description:
-# Serialized forms of types used in the Dagger processor.
-
-package(default_visibility = ["//:src"])
-
-proto_library(
- name = "serialization_proto",
- srcs = ["serialization.proto"],
- visibility = ["//visibility:private"],
-)
-
-java_proto_library(
- name = "serialization_java_proto",
- visibility = ["//visibility:private"],
- deps = [":serialization_proto"],
-)
-
-java_library(
- name = "serialization",
- srcs = glob(["*.java"]),
- exports = [":serialization_java_proto"],
- deps = [
- "@google_bazel_common//third_party/java/guava",
- "@google_bazel_common//third_party/java/javapoet",
- "@google_bazel_common//third_party/java/protobuf",
- ],
-)
diff --git a/java/dagger/internal/codegen/serialization/ProtoSerialization.java b/java/dagger/internal/codegen/serialization/ProtoSerialization.java
deleted file mode 100644
index 1449e9d0b..000000000
--- a/java/dagger/internal/codegen/serialization/ProtoSerialization.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2019 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen.serialization;
-
-import static com.google.common.io.BaseEncoding.base64;
-
-import com.google.common.io.BaseEncoding;
-import com.google.protobuf.InvalidProtocolBufferException;
-import com.google.protobuf.Message;
-import com.squareup.javapoet.CodeBlock;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.AnnotationValueVisitor;
-import javax.lang.model.util.SimpleAnnotationValueVisitor8;
-
-/**
- * Serializes and deserializes {@link Message}s using {@link BaseEncoding#base64()} for use in
- * annotation values.
- */
-public final class ProtoSerialization {
- /** Returns a {@link CodeBlock} of {@code message} serialized as a String. */
- public static CodeBlock toAnnotationValue(Message message) {
- return CodeBlock.of("$S", base64().encode(message.toByteArray()));
- }
-
- /**
- * Returns a {@link Message T} from the deserialized the String {@code value}.
- *
- * @throws IllegalArgumentException if {@code value} represents an {@link AnnotationValue} who's
- * type is not {@link String}
- */
- public static <T extends Message> T fromAnnotationValue(
- AnnotationValue value, T defaultInstance) {
- byte[] bytes = base64().decode(value.accept(STRING_VALUE, null));
- Message message;
- try {
- message = defaultInstance.getParserForType().parseFrom(bytes);
- } catch (InvalidProtocolBufferException e) {
- throw new InconsistentSerializedProtoException(e);
- }
- @SuppressWarnings("unchecked") // guaranteed by proto API
- T t = (T) message;
- return t;
- }
-
- private static final AnnotationValueVisitor<String, Void> STRING_VALUE =
- new SimpleAnnotationValueVisitor8<String, Void>() {
- @Override
- public String visitString(String s, Void ignored) {
- return s;
- }
-
- @Override
- protected String defaultAction(Object o, Void ignored) {
- throw new IllegalArgumentException(o + " is not a String");
- }
- };
-
- /**
- * An exception thrown when the proto that's serialized in a compiled subcomponent implementation
- * is from a different version than the current compiler's.
- */
- public static final class InconsistentSerializedProtoException extends RuntimeException {
- InconsistentSerializedProtoException(Throwable cause) {
- super(cause);
- }
- }
-}
diff --git a/java/dagger/internal/codegen/serialization/serialization.proto b/java/dagger/internal/codegen/serialization/serialization.proto
deleted file mode 100644
index e6c957765..000000000
--- a/java/dagger/internal/codegen/serialization/serialization.proto
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2019 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// Serialized forms of types used in the Dagger processor. The wire format of
-// these types is not guaranteed to remain compatible over time; serialization
-// is only expected to function correctly within an individual version of the
-// Dagger processor.
-
-syntax = "proto3";
-
-package dagger.internal.codegen.serialization;
-option java_package = "dagger.internal.codegen.serialization";
-option java_multiple_files = true;
-
-// TODO(ronshapiro): consider exposing some of these in
-// dagger.model.serialization
-
-// Serialized form of `dagger.internal.codegen.BindingRequest`
-message BindingRequestProto {
- KeyProto key = 1;
- RequestKindWrapper.RequestKind request_kind = 2;
- FrameworkTypeWrapper.FrameworkType framework_type = 3;
-}
-
-message RequestKindWrapper {
- // Serialized form of `dagger.model.RequestKind`
- enum RequestKind {
- UNKNOWN = 0;
- INSTANCE = 1;
- PROVIDER = 2;
- LAZY = 3;
- PROVIDER_OF_LAZY = 4;
- MEMBERS_INJECTION = 5;
- PRODUCER = 6;
- PRODUCED = 7;
- FUTURE = 8;
- }
-}
-
-message FrameworkTypeWrapper {
- // Serialized form of `dagger.internal.codegen.FrameworkType`
- enum FrameworkType {
- UNKNOWN = 0;
- PROVIDER = 1;
- PRODUCER_NODE = 2;
- }
-}
-
-// Serialized form of `dagger.model.Key`
-message KeyProto {
- TypeProto type = 1;
- AnnotationProto qualifier = 2;
- MultibindingContributionIdentifier multibinding_contribution_identifier =
- 3;
-
- // Serialized form of `dagger.model.Key.MultibindingContributionIdentifier`
- message MultibindingContributionIdentifier {
- string module = 1;
- string binding_element = 2;
- }
-}
-
-// Serialized form of `javax.lang.model.type.TypeMirror`
-message TypeProto {
- PrimitiveKind primitive_kind = 1;
-
- // The qualified name of the type. Absent if this is an inner type.
- string qualified_name = 2;
-
- // The enclosing type if this is an inner type, otherwise absent.
- TypeProto enclosing_type = 3;
-
- // Simple name of the type if this is an inner type, otherwise absent.
- string simple_name = 4;
-
- repeated TypeProto type_arguments = 5;
-
- message Wildcard {
- TypeProto extends_bound = 1;
- TypeProto super_bound = 2;
- }
- Wildcard wildcard = 6;
-
- int32 array_dimensions = 7;
-
- // Kinds of primitive types
- enum PrimitiveKind {
- UNKNOWN = 0;
- BOOLEAN = 1;
- BYTE = 2;
- SHORT = 3;
- CHAR = 4;
- INT = 5;
- FLOAT = 6;
- LONG = 7;
- DOUBLE = 8;
- }
-}
-
-// Serialized form of `javax.lang.model.element.AnnotationMirror`
-message AnnotationProto {
- TypeProto annotation_type = 1;
- map<string, AnnotationValueProto> values = 2;
-}
-
-// Serialized form of `javax.lang.model.element.AnnotationValue`
-message AnnotationValueProto {
- Kind kind = 1;
- bool boolean_value = 2;
- int32 int_value = 3;
- int64 long_value = 4;
- float float_value = 5;
- double double_value = 6;
- string string_value = 7;
- TypeProto class_literal = 8;
- TypeProto enum_type = 9;
- string enum_name = 10;
- AnnotationProto nested_annotation = 11;
-
- repeated AnnotationValueProto array_values = 12;
-
- // The type of annotation value
- enum Kind {
- UNKNOWN = 0;
- BOOLEAN = 1;
- BYTE = 2;
- SHORT = 3;
- CHAR = 4;
- INT = 5;
- FLOAT = 6;
- LONG = 7;
- DOUBLE = 8;
- STRING = 9;
- CLASS_LITERAL = 10;
- ENUM = 11;
- ANNOTATION = 12;
- ARRAY = 13;
- }
-}
-
-// Serialized form of `dagger.internal.codegen.ComponentRequirement`
-message ComponentRequirementProto {
- oneof requirement {
- TypeProto dependency = 1;
- TypeProto module = 2;
- BoundInstanceRequirement bound_instance = 3;
- }
-
- message BoundInstanceRequirement {
- KeyProto key = 1;
- bool nullable = 2;
- string variable_name = 3;
- }
-}
diff --git a/java/dagger/internal/codegen/validation/AnyBindingMethodValidator.java b/java/dagger/internal/codegen/validation/AnyBindingMethodValidator.java
new file mode 100644
index 000000000..140afd215
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/AnyBindingMethodValidator.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
+import static java.util.stream.Collectors.joining;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import dagger.internal.codegen.base.ClearableCache;
+import java.lang.annotation.Annotation;
+import java.util.HashMap;
+import java.util.Map;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.lang.model.element.ExecutableElement;
+
+/** Validates any binding method. */
+@Singleton
+public final class AnyBindingMethodValidator implements ClearableCache {
+ private final ImmutableMap<Class<? extends Annotation>, BindingMethodValidator> validators;
+ private final Map<ExecutableElement, ValidationReport<ExecutableElement>> reports =
+ new HashMap<>();
+
+ @Inject
+ AnyBindingMethodValidator(
+ ImmutableMap<Class<? extends Annotation>, BindingMethodValidator> validators) {
+ this.validators = validators;
+ }
+
+ @Override
+ public void clearCache() {
+ reports.clear();
+ }
+
+ /** Returns the binding method annotations considered by this validator. */
+ ImmutableSet<Class<? extends Annotation>> methodAnnotations() {
+ return validators.keySet();
+ }
+
+ /**
+ * Returns {@code true} if {@code method} is annotated with at least one of {@link
+ * #methodAnnotations()}.
+ */
+ boolean isBindingMethod(ExecutableElement method) {
+ return isAnyAnnotationPresent(method, methodAnnotations());
+ }
+
+ /**
+ * Returns a validation report for a method.
+ *
+ * <ul>
+ * <li>Reports an error if {@code method} is annotated with more than one {@linkplain
+ * #methodAnnotations() binding method annotation}.
+ * <li>Validates {@code method} with the {@link BindingMethodValidator} for the single
+ * {@linkplain #methodAnnotations() binding method annotation}.
+ * </ul>
+ *
+ * @throws IllegalArgumentException if {@code method} is not annotated by any {@linkplain
+ * #methodAnnotations() binding method annotation}
+ */
+ ValidationReport<ExecutableElement> validate(ExecutableElement method) {
+ return reentrantComputeIfAbsent(reports, method, this::validateUncached);
+ }
+
+ /**
+ * Returns {@code true} if {@code method} was already {@linkplain #validate(ExecutableElement)
+ * validated}.
+ */
+ boolean wasAlreadyValidated(ExecutableElement method) {
+ return reports.containsKey(method);
+ }
+
+ private ValidationReport<ExecutableElement> validateUncached(ExecutableElement method) {
+ ValidationReport.Builder<ExecutableElement> report = ValidationReport.about(method);
+ ImmutableSet<? extends Class<? extends Annotation>> bindingMethodAnnotations =
+ methodAnnotations()
+ .stream()
+ .filter(annotation -> isAnnotationPresent(method, annotation))
+ .collect(toImmutableSet());
+ switch (bindingMethodAnnotations.size()) {
+ case 0:
+ throw new IllegalArgumentException(
+ String.format("%s has no binding method annotation", method));
+
+ case 1:
+ report.addSubreport(
+ validators.get(getOnlyElement(bindingMethodAnnotations)).validate(method));
+ break;
+
+ default:
+ report.addError(
+ String.format(
+ "%s is annotated with more than one of (%s)",
+ method.getSimpleName(),
+ methodAnnotations().stream().map(Class::getCanonicalName).collect(joining(", "))),
+ method);
+ break;
+ }
+ return report.build();
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/BUILD b/java/dagger/internal/codegen/validation/BUILD
new file mode 100644
index 000000000..602157b2f
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/BUILD
@@ -0,0 +1,50 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Code related to validating the user-written Dagger code
+
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "validation",
+ srcs = glob(["*.java"]),
+ plugins = ["//java/dagger/internal/codegen/bootstrap"],
+ tags = ["maven:merged"],
+ deps = [
+ "//java/dagger:core",
+ "//java/dagger/internal/codegen/base",
+ "//java/dagger/internal/codegen/binding",
+ "//java/dagger/internal/codegen/compileroption",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/codegen/javapoet",
+ "//java/dagger/internal/codegen/kotlin",
+ "//java/dagger/internal/codegen/langmodel",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:cache",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:concurrent",
+ "//java/dagger/internal/guava:graph",
+ "//java/dagger/producers",
+ "//java/dagger/spi",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/checker_framework_annotations",
+ "@google_bazel_common//third_party/java/error_prone:annotations",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
diff --git a/java/dagger/internal/codegen/validation/BindingElementValidator.java b/java/dagger/internal/codegen/validation/BindingElementValidator.java
new file mode 100644
index 000000000..4557745ee
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/BindingElementValidator.java
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.google.auto.common.MoreTypes.asTypeElement;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Verify.verifyNotNull;
+import static dagger.internal.codegen.base.Scopes.scopesOf;
+import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.isAssistedFactoryType;
+import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.isAssistedInjectionType;
+import static dagger.internal.codegen.binding.MapKeys.getMapKeys;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
+import static javax.lang.model.type.TypeKind.ARRAY;
+import static javax.lang.model.type.TypeKind.DECLARED;
+import static javax.lang.model.type.TypeKind.TYPEVAR;
+import static javax.lang.model.type.TypeKind.VOID;
+
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableSet;
+import com.google.errorprone.annotations.FormatMethod;
+import dagger.MapKey;
+import dagger.Provides;
+import dagger.internal.codegen.base.ContributionType;
+import dagger.internal.codegen.base.FrameworkTypes;
+import dagger.internal.codegen.base.MultibindingAnnotations;
+import dagger.internal.codegen.base.SetType;
+import dagger.internal.codegen.binding.InjectionAnnotations;
+import dagger.model.Key;
+import dagger.model.Scope;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.multibindings.IntoMap;
+import dagger.producers.Produces;
+import java.lang.annotation.Annotation;
+import java.util.Formatter;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import javax.inject.Qualifier;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+/** A validator for elements that represent binding declarations. */
+public abstract class BindingElementValidator<E extends Element> {
+ private final Class<? extends Annotation> bindingAnnotation;
+ private final AllowsMultibindings allowsMultibindings;
+ private final AllowsScoping allowsScoping;
+ private final Map<E, ValidationReport<E>> cache = new HashMap<>();
+ private final InjectionAnnotations injectionAnnotations;
+
+ /**
+ * Creates a validator object.
+ *
+ * @param bindingAnnotation the annotation on an element that identifies it as a binding element
+ */
+ protected BindingElementValidator(
+ Class<? extends Annotation> bindingAnnotation,
+ AllowsMultibindings allowsMultibindings,
+ AllowsScoping allowsScoping,
+ InjectionAnnotations injectionAnnotations) {
+ this.bindingAnnotation = bindingAnnotation;
+ this.allowsMultibindings = allowsMultibindings;
+ this.allowsScoping = allowsScoping;
+ this.injectionAnnotations = injectionAnnotations;
+ }
+
+ /** Returns a {@link ValidationReport} for {@code element}. */
+ final ValidationReport<E> validate(E element) {
+ return reentrantComputeIfAbsent(cache, element, this::validateUncached);
+ }
+
+ private ValidationReport<E> validateUncached(E element) {
+ return elementValidator(element).validate();
+ }
+
+ /**
+ * Returns an error message of the form "&lt;{@link #bindingElements()}&gt; <i>rule</i>", where
+ * <i>rule</i> comes from calling {@link String#format(String, Object...)} on {@code ruleFormat}
+ * and the other arguments.
+ */
+ @FormatMethod
+ protected final String bindingElements(String ruleFormat, Object... args) {
+ return new Formatter().format("%s ", bindingElements()).format(ruleFormat, args).toString();
+ }
+
+ /**
+ * The kind of elements that this validator validates. Should be plural. Used for error reporting.
+ */
+ protected abstract String bindingElements();
+
+ /** The verb describing the {@link ElementValidator#bindingElementType()} in error messages. */
+ // TODO(ronshapiro,dpb): improve the name of this method and it's documentation.
+ protected abstract String bindingElementTypeVerb();
+
+ /** The error message when a binding element has a bad type. */
+ protected String badTypeMessage() {
+ return bindingElements(
+ "must %s a primitive, an array, a type variable, or a declared type",
+ bindingElementTypeVerb());
+ }
+
+ /**
+ * The error message when a the type for a binding element with {@link
+ * ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES} is a not set type.
+ */
+ protected String elementsIntoSetNotASetMessage() {
+ return bindingElements(
+ "annotated with @ElementsIntoSet must %s a Set", bindingElementTypeVerb());
+ }
+
+ /**
+ * The error message when a the type for a binding element with {@link
+ * ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES} is a raw set.
+ */
+ protected String elementsIntoSetRawSetMessage() {
+ return bindingElements(
+ "annotated with @ElementsIntoSet cannot %s a raw Set", bindingElementTypeVerb());
+ }
+
+ /*** Returns an {@link ElementValidator} for validating the given {@code element}. */
+ protected abstract ElementValidator elementValidator(E element);
+
+ /** Validator for a single binding element. */
+ protected abstract class ElementValidator {
+ protected final E element;
+ protected final ValidationReport.Builder<E> report;
+
+ protected ElementValidator(E element) {
+ this.element = element;
+ this.report = ValidationReport.about(element);
+ }
+
+ /** Checks the element for validity. */
+ private ValidationReport<E> validate() {
+ checkType();
+ checkQualifiers();
+ checkMapKeys();
+ checkMultibindings();
+ checkScopes();
+ checkAdditionalProperties();
+ return report.build();
+ }
+
+ /** Check any additional properties of the element. Does nothing by default. */
+ protected void checkAdditionalProperties() {}
+
+ /**
+ * The type declared by this binding element. This may differ from a binding's {@link
+ * Key#type()}, for example in multibindings. An {@link Optional#empty()} return value indicates
+ * that the contributed type is ambiguous or missing, i.e. a {@code @BindsInstance} method with
+ * zero or many parameters.
+ */
+ // TODO(dpb): should this be an ImmutableList<TypeMirror>, with this class checking the size?
+ protected abstract Optional<TypeMirror> bindingElementType();
+
+ /**
+ * Adds an error if the {@link #bindingElementType() binding element type} is not appropriate.
+ *
+ * <p>Adds an error if the type is not a primitive, array, declared type, or type variable.
+ *
+ * <p>If the binding is not a multibinding contribution, adds an error if the type is a
+ * framework type.
+ *
+ * <p>If the element has {@link ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES}, adds an
+ * error if the type is not a {@code Set<T>} for some {@code T}
+ */
+ protected void checkType() {
+ switch (ContributionType.fromBindingElement(element)) {
+ case UNIQUE:
+ /* Validate that a unique binding is not attempting to bind a framework type. This
+ * validation is only appropriate for unique bindings because multibindings may collect
+ * framework types. E.g. Set<Provider<Foo>> is perfectly reasonable. */
+ checkFrameworkType();
+ // fall through
+
+ case SET:
+ case MAP:
+ bindingElementType().ifPresent(type -> checkKeyType(type));
+ break;
+
+ case SET_VALUES:
+ checkSetValuesType();
+ }
+ }
+
+ /**
+ * Adds an error if {@code keyType} is not a primitive, declared type, array, or type variable.
+ */
+ protected void checkKeyType(TypeMirror keyType) {
+ TypeKind kind = keyType.getKind();
+ if (kind.equals(VOID)) {
+ report.addError(bindingElements("must %s a value (not void)", bindingElementTypeVerb()));
+ } else if (kind == DECLARED) {
+ checkNotAssistedInject(keyType);
+ } else if (!(kind.isPrimitive() || kind.equals(ARRAY) || kind.equals(TYPEVAR))) {
+ report.addError(badTypeMessage());
+ }
+ }
+
+ /** Adds errors for a method return type. */
+ private void checkNotAssistedInject(TypeMirror keyType) {
+ checkState(keyType.getKind() == TypeKind.DECLARED);
+ TypeElement keyElement = asTypeElement(keyType);
+ if (isAssistedInjectionType(keyElement)) {
+ report.addError("Dagger does not support providing @AssistedInject types.", keyElement);
+ }
+ if (isAssistedFactoryType(keyElement)) {
+ report.addError("Dagger does not support providing @AssistedFactory types.", keyElement);
+ }
+ }
+
+ /**
+ * Adds an error if the type for an element with {@link ElementsIntoSet @ElementsIntoSet} or
+ * {@code SET_VALUES} is not a a {@code Set<T>} for a reasonable {@code T}.
+ */
+ // TODO(gak): should we allow "covariant return" for set values?
+ protected void checkSetValuesType() {
+ bindingElementType().ifPresent(keyType -> checkSetValuesType(keyType));
+ }
+
+ /** Adds an error if {@code type} is not a {@code Set<T>} for a reasonable {@code T}. */
+ protected final void checkSetValuesType(TypeMirror type) {
+ if (!SetType.isSet(type)) {
+ report.addError(elementsIntoSetNotASetMessage());
+ } else {
+ SetType setType = SetType.from(type);
+ if (setType.isRawType()) {
+ report.addError(elementsIntoSetRawSetMessage());
+ } else {
+ checkKeyType(setType.elementType());
+ }
+ }
+ }
+
+ /**
+ * Adds an error if the element has more than one {@linkplain Qualifier qualifier} annotation.
+ */
+ private void checkQualifiers() {
+ ImmutableCollection<? extends AnnotationMirror> qualifiers =
+ injectionAnnotations.getQualifiers(element);
+ if (qualifiers.size() > 1) {
+ for (AnnotationMirror qualifier : qualifiers) {
+ report.addError(
+ bindingElements("may not use more than one @Qualifier"),
+ element,
+ qualifier);
+ }
+ }
+ }
+
+ /**
+ * Adds an error if an {@link IntoMap @IntoMap} element doesn't have exactly one {@link
+ * MapKey @MapKey} annotation, or if an element that is {@link IntoMap @IntoMap} has any.
+ */
+ private void checkMapKeys() {
+ if (!allowsMultibindings.allowsMultibindings()) {
+ return;
+ }
+ ImmutableSet<? extends AnnotationMirror> mapKeys = getMapKeys(element);
+ if (ContributionType.fromBindingElement(element).equals(ContributionType.MAP)) {
+ switch (mapKeys.size()) {
+ case 0:
+ report.addError(bindingElements("of type map must declare a map key"));
+ break;
+ case 1:
+ break;
+ default:
+ report.addError(bindingElements("may not have more than one map key"));
+ break;
+ }
+ } else if (!mapKeys.isEmpty()) {
+ report.addError(bindingElements("of non map type cannot declare a map key"));
+ }
+ }
+
+ /**
+ * Adds errors if:
+ *
+ * <ul>
+ * <li>the element doesn't allow {@linkplain MultibindingAnnotations multibinding annotations}
+ * and has any
+ * <li>the element does allow them but has more than one
+ * <li>the element has a multibinding annotation and its {@link Provides} or {@link Produces}
+ * annotation has a {@code type} parameter.
+ * </ul>
+ */
+ private void checkMultibindings() {
+ ImmutableSet<AnnotationMirror> multibindingAnnotations =
+ MultibindingAnnotations.forElement(element);
+
+ switch (allowsMultibindings) {
+ case NO_MULTIBINDINGS:
+ for (AnnotationMirror annotation : multibindingAnnotations) {
+ report.addError(
+ bindingElements("cannot have multibinding annotations"),
+ element,
+ annotation);
+ }
+ break;
+
+ case ALLOWS_MULTIBINDINGS:
+ if (multibindingAnnotations.size() > 1) {
+ for (AnnotationMirror annotation : multibindingAnnotations) {
+ report.addError(
+ bindingElements("cannot have more than one multibinding annotation"),
+ element,
+ annotation);
+ }
+ }
+ break;
+ }
+
+ // TODO(ronshapiro): move this into ProvidesMethodValidator
+ if (bindingAnnotation.equals(Provides.class)) {
+ AnnotationMirror bindingAnnotationMirror =
+ getAnnotationMirror(element, bindingAnnotation).get();
+ boolean usesProvidesType = false;
+ for (ExecutableElement member : bindingAnnotationMirror.getElementValues().keySet()) {
+ usesProvidesType |= member.getSimpleName().contentEquals("type");
+ }
+ if (usesProvidesType && !multibindingAnnotations.isEmpty()) {
+ report.addError(
+ "@Provides.type cannot be used with multibinding annotations", element);
+ }
+ }
+ }
+
+ /**
+ * Adds an error if the element has a scope but doesn't allow scoping, or if it has more than
+ * one {@linkplain Scope scope} annotation.
+ */
+ private void checkScopes() {
+ ImmutableSet<Scope> scopes = scopesOf(element);
+ String error = null;
+ switch (allowsScoping) {
+ case ALLOWS_SCOPING:
+ if (scopes.size() <= 1) {
+ return;
+ }
+ error = bindingElements("cannot use more than one @Scope");
+ break;
+ case NO_SCOPING:
+ error = bindingElements("cannot be scoped");
+ break;
+ }
+ verifyNotNull(error);
+ for (Scope scope : scopes) {
+ report.addError(error, element, scope.scopeAnnotation());
+ }
+ }
+
+ /**
+ * Adds an error if the {@link #bindingElementType() type} is a {@linkplain FrameworkTypes
+ * framework type}.
+ */
+ private void checkFrameworkType() {
+ if (bindingElementType().filter(FrameworkTypes::isFrameworkType).isPresent()) {
+ report.addError(bindingElements("must not %s framework types", bindingElementTypeVerb()));
+ }
+ }
+ }
+
+ /** Whether to check multibinding annotations. */
+ enum AllowsMultibindings {
+ /**
+ * This element disallows multibinding annotations, so don't bother checking for their validity.
+ * {@link MultibindingAnnotationsProcessingStep} will add errors if the element has any
+ * multibinding annotations.
+ */
+ NO_MULTIBINDINGS,
+
+ /** This element allows multibinding annotations, so validate them. */
+ ALLOWS_MULTIBINDINGS,
+ ;
+
+ private boolean allowsMultibindings() {
+ return this == ALLOWS_MULTIBINDINGS;
+ }
+ }
+
+ /** How to check scoping annotations. */
+ enum AllowsScoping {
+ /** This element disallows scoping, so check that no scope annotations are present. */
+ NO_SCOPING,
+
+ /** This element allows scoping, so validate that there's at most one scope annotation. */
+ ALLOWS_SCOPING,
+ ;
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/BindingGraphPlugins.java b/java/dagger/internal/codegen/validation/BindingGraphPlugins.java
new file mode 100644
index 000000000..16ef78f79
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/BindingGraphPlugins.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import dagger.internal.codegen.compileroption.ProcessingOptions;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.spi.BindingGraphPlugin;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.processing.Filer;
+import javax.inject.Inject;
+
+/** Initializes {@link BindingGraphPlugin}s. */
+public final class BindingGraphPlugins {
+ private final ImmutableSet<BindingGraphPlugin> plugins;
+ private final Filer filer;
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+ private final Map<String, String> processingOptions;
+
+ @Inject
+ BindingGraphPlugins(
+ @Validation ImmutableSet<BindingGraphPlugin> validationPlugins,
+ ImmutableSet<BindingGraphPlugin> externalPlugins,
+ Filer filer,
+ DaggerTypes types,
+ DaggerElements elements,
+ @ProcessingOptions Map<String, String> processingOptions) {
+ this.plugins = Sets.union(validationPlugins, externalPlugins).immutableCopy();
+ this.filer = filer;
+ this.types = types;
+ this.elements = elements;
+ this.processingOptions = processingOptions;
+ }
+
+ /** Returns {@link BindingGraphPlugin#supportedOptions()} from all the plugins. */
+ public ImmutableSet<String> allSupportedOptions() {
+ return plugins.stream()
+ .flatMap(plugin -> plugin.supportedOptions().stream())
+ .collect(toImmutableSet());
+ }
+
+ /** Initializes the plugins. */
+ // TODO(ronshapiro): Should we validate the uniqueness of plugin names?
+ public void initializePlugins() {
+ plugins.forEach(this::initializePlugin);
+ }
+
+ private void initializePlugin(BindingGraphPlugin plugin) {
+ plugin.initFiler(filer);
+ plugin.initTypes(types);
+ plugin.initElements(elements);
+ Set<String> supportedOptions = plugin.supportedOptions();
+ if (!supportedOptions.isEmpty()) {
+ plugin.initOptions(Maps.filterKeys(processingOptions, supportedOptions::contains));
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/BindingGraphValidator.java b/java/dagger/internal/codegen/validation/BindingGraphValidator.java
new file mode 100644
index 000000000..99e86e762
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/BindingGraphValidator.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.common.collect.ImmutableSet;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.compileroption.ValidationType;
+import dagger.internal.codegen.validation.DiagnosticReporterFactory.DiagnosticReporterImpl;
+import dagger.model.BindingGraph;
+import dagger.spi.BindingGraphPlugin;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.lang.model.element.TypeElement;
+
+/** Validates a {@link BindingGraph}. */
+@Singleton
+public final class BindingGraphValidator {
+ private final ImmutableSet<BindingGraphPlugin> validationPlugins;
+ private final ImmutableSet<BindingGraphPlugin> externalPlugins;
+ private final DiagnosticReporterFactory diagnosticReporterFactory;
+ private final CompilerOptions compilerOptions;
+
+ @Inject
+ BindingGraphValidator(
+ @Validation ImmutableSet<BindingGraphPlugin> validationPlugins,
+ ImmutableSet<BindingGraphPlugin> externalPlugins,
+ DiagnosticReporterFactory diagnosticReporterFactory,
+ CompilerOptions compilerOptions) {
+ this.validationPlugins = validationPlugins;
+ this.externalPlugins = externalPlugins;
+ this.diagnosticReporterFactory = checkNotNull(diagnosticReporterFactory);
+ this.compilerOptions = compilerOptions;
+ }
+
+ /** Returns {@code true} if validation or analysis is required on the full binding graph. */
+ public boolean shouldDoFullBindingGraphValidation(TypeElement component) {
+ return requiresFullBindingGraphValidation()
+ || compilerOptions.pluginsVisitFullBindingGraphs(component);
+ }
+
+ private boolean requiresFullBindingGraphValidation() {
+ return !compilerOptions.fullBindingGraphValidationType().equals(ValidationType.NONE);
+ }
+
+ /** Returns {@code true} if no errors are reported for {@code graph}. */
+ public boolean isValid(BindingGraph graph) {
+ return validate(graph) && visitPlugins(graph);
+ }
+
+ /** Returns {@code true} if validation plugins report no errors. */
+ private boolean validate(BindingGraph graph) {
+ if (graph.isFullBindingGraph() && !requiresFullBindingGraphValidation()) {
+ return true;
+ }
+
+ boolean errorsAsWarnings =
+ graph.isFullBindingGraph()
+ && compilerOptions.fullBindingGraphValidationType().equals(ValidationType.WARNING);
+
+ return runPlugins(validationPlugins, graph, errorsAsWarnings);
+ }
+
+ /** Returns {@code true} if external plugins report no errors. */
+ private boolean visitPlugins(BindingGraph graph) {
+ TypeElement component = graph.rootComponentNode().componentPath().currentComponent();
+ if (graph.isFullBindingGraph()
+ // TODO(b/135938915): Consider not visiting plugins if only
+ // fullBindingGraphValidation is enabled.
+ && !requiresFullBindingGraphValidation()
+ && !compilerOptions.pluginsVisitFullBindingGraphs(component)) {
+ return true;
+ }
+ return runPlugins(externalPlugins, graph, /*errorsAsWarnings=*/ false);
+ }
+
+ /** Returns {@code false} if any of the plugins reported an error. */
+ private boolean runPlugins(
+ ImmutableSet<BindingGraphPlugin> plugins, BindingGraph graph, boolean errorsAsWarnings) {
+ boolean isClean = true;
+ for (BindingGraphPlugin plugin : plugins) {
+ DiagnosticReporterImpl reporter =
+ diagnosticReporterFactory.reporter(graph, plugin, errorsAsWarnings);
+ plugin.visitGraph(graph, reporter);
+ if (reporter.reportedDiagnosticKinds().contains(ERROR)) {
+ isClean = false;
+ }
+ }
+ return isClean;
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/BindingMethodProcessingStep.java b/java/dagger/internal/codegen/validation/BindingMethodProcessingStep.java
new file mode 100644
index 000000000..10aec063f
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/BindingMethodProcessingStep.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableSet;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+import javax.annotation.processing.Messager;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+
+/** A step that validates all binding methods that were not validated while processing modules. */
+public final class BindingMethodProcessingStep
+ extends TypeCheckingProcessingStep<ExecutableElement> {
+
+ private final Messager messager;
+ private final AnyBindingMethodValidator anyBindingMethodValidator;
+
+ @Inject
+ BindingMethodProcessingStep(
+ Messager messager, AnyBindingMethodValidator anyBindingMethodValidator) {
+ super(MoreElements::asExecutable);
+ this.messager = messager;
+ this.anyBindingMethodValidator = anyBindingMethodValidator;
+ }
+
+ @Override
+ public Set<? extends Class<? extends Annotation>> annotations() {
+ return anyBindingMethodValidator.methodAnnotations();
+ }
+
+ @Override
+ protected void process(
+ ExecutableElement method, ImmutableSet<Class<? extends Annotation>> annotations) {
+ checkArgument(
+ anyBindingMethodValidator.isBindingMethod(method),
+ "%s is not annotated with any of %s",
+ method,
+ annotations());
+ if (!anyBindingMethodValidator.wasAlreadyValidated(method)) {
+ anyBindingMethodValidator.validate(method).printMessagesTo(messager);
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/BindingMethodValidator.java b/java/dagger/internal/codegen/validation/BindingMethodValidator.java
new file mode 100644
index 000000000..81349b9fb
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/BindingMethodValidator.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.google.auto.common.MoreElements.asType;
+import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
+import static java.util.stream.Collectors.joining;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.PRIVATE;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.errorprone.annotations.FormatMethod;
+import dagger.internal.codegen.binding.InjectionAnnotations;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.lang.annotation.Annotation;
+import java.util.Optional;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+
+/** A validator for methods that represent binding declarations. */
+abstract class BindingMethodValidator extends BindingElementValidator<ExecutableElement> {
+
+ private final DaggerElements elements;
+ private final DaggerTypes types;
+ private final KotlinMetadataUtil metadataUtil;
+ private final DependencyRequestValidator dependencyRequestValidator;
+ private final Class<? extends Annotation> methodAnnotation;
+ private final ImmutableSet<? extends Class<? extends Annotation>> enclosingElementAnnotations;
+ private final Abstractness abstractness;
+ private final ExceptionSuperclass exceptionSuperclass;
+
+ /**
+ * Creates a validator object.
+ *
+ * @param methodAnnotation the annotation on a method that identifies it as a binding method
+ * @param enclosingElementAnnotation the method must be declared in a class or interface annotated
+ * with this annotation
+ */
+ protected BindingMethodValidator(
+ DaggerElements elements,
+ DaggerTypes types,
+ KotlinMetadataUtil metadataUtil,
+ DependencyRequestValidator dependencyRequestValidator,
+ Class<? extends Annotation> methodAnnotation,
+ Class<? extends Annotation> enclosingElementAnnotation,
+ Abstractness abstractness,
+ ExceptionSuperclass exceptionSuperclass,
+ AllowsMultibindings allowsMultibindings,
+ AllowsScoping allowsScoping,
+ InjectionAnnotations injectionAnnotations) {
+ this(
+ elements,
+ types,
+ metadataUtil,
+ methodAnnotation,
+ ImmutableSet.of(enclosingElementAnnotation),
+ dependencyRequestValidator,
+ abstractness,
+ exceptionSuperclass,
+ allowsMultibindings,
+ allowsScoping,
+ injectionAnnotations);
+ }
+
+ /**
+ * Creates a validator object.
+ *
+ * @param methodAnnotation the annotation on a method that identifies it as a binding method
+ * @param enclosingElementAnnotations the method must be declared in a class or interface
+ * annotated with one of these annotations
+ */
+ protected BindingMethodValidator(
+ DaggerElements elements,
+ DaggerTypes types,
+ KotlinMetadataUtil metadataUtil,
+ Class<? extends Annotation> methodAnnotation,
+ Iterable<? extends Class<? extends Annotation>> enclosingElementAnnotations,
+ DependencyRequestValidator dependencyRequestValidator,
+ Abstractness abstractness,
+ ExceptionSuperclass exceptionSuperclass,
+ AllowsMultibindings allowsMultibindings,
+ AllowsScoping allowsScoping,
+ InjectionAnnotations injectionAnnotations) {
+ super(methodAnnotation, allowsMultibindings, allowsScoping, injectionAnnotations);
+ this.elements = elements;
+ this.types = types;
+ this.metadataUtil = metadataUtil;
+ this.methodAnnotation = methodAnnotation;
+ this.enclosingElementAnnotations = ImmutableSet.copyOf(enclosingElementAnnotations);
+ this.dependencyRequestValidator = dependencyRequestValidator;
+ this.abstractness = abstractness;
+ this.exceptionSuperclass = exceptionSuperclass;
+ }
+
+ /** The annotation that identifies binding methods validated by this object. */
+ final Class<? extends Annotation> methodAnnotation() {
+ return methodAnnotation;
+ }
+
+ /**
+ * Returns an error message of the form "@<i>annotation</i> methods <i>rule</i>", where
+ * <i>rule</i> comes from calling {@link String#format(String, Object...)} on {@code ruleFormat}
+ * and the other arguments.
+ */
+ @FormatMethod
+ protected final String bindingMethods(String ruleFormat, Object... args) {
+ return bindingElements(ruleFormat, args);
+ }
+
+ @Override
+ protected final String bindingElements() {
+ return String.format("@%s methods", methodAnnotation.getSimpleName());
+ }
+
+ @Override
+ protected final String bindingElementTypeVerb() {
+ return "return";
+ }
+
+ /** Abstract validator for individual binding method elements. */
+ protected abstract class MethodValidator extends ElementValidator {
+ protected MethodValidator(ExecutableElement element) {
+ super(element);
+ }
+
+ @Override
+ protected final Optional<TypeMirror> bindingElementType() {
+ return Optional.of(element.getReturnType());
+ }
+
+ @Override
+ protected final void checkAdditionalProperties() {
+ checkEnclosingElement();
+ checkTypeParameters();
+ checkNotPrivate();
+ checkAbstractness();
+ checkThrows();
+ checkParameters();
+ checkAdditionalMethodProperties();
+ }
+
+ /** Checks additional properties of the binding method. */
+ protected void checkAdditionalMethodProperties() {}
+
+ /**
+ * Adds an error if the method is not declared in a class or interface annotated with one of the
+ * {@link #enclosingElementAnnotations}.
+ */
+ private void checkEnclosingElement() {
+ TypeElement enclosingElement = asType(element.getEnclosingElement());
+ if (metadataUtil.isCompanionObjectClass(enclosingElement)) {
+ // Binding method is in companion object, use companion object's enclosing class instead.
+ enclosingElement = asType(enclosingElement.getEnclosingElement());
+ }
+ if (!isAnyAnnotationPresent(enclosingElement, enclosingElementAnnotations)) {
+ report.addError(
+ bindingMethods(
+ "can only be present within a @%s",
+ enclosingElementAnnotations.stream()
+ .map(Class::getSimpleName)
+ .collect(joining(" or @"))));
+ }
+ }
+
+ /** Adds an error if the method is generic. */
+ private void checkTypeParameters() {
+ if (!element.getTypeParameters().isEmpty()) {
+ report.addError(bindingMethods("may not have type parameters"));
+ }
+ }
+
+ /** Adds an error if the method is private. */
+ private void checkNotPrivate() {
+ if (element.getModifiers().contains(PRIVATE)) {
+ report.addError(bindingMethods("cannot be private"));
+ }
+ }
+
+ /** Adds an error if the method is abstract but must not be, or is not and must be. */
+ private void checkAbstractness() {
+ boolean isAbstract = element.getModifiers().contains(ABSTRACT);
+ switch (abstractness) {
+ case MUST_BE_ABSTRACT:
+ if (!isAbstract) {
+ report.addError(bindingMethods("must be abstract"));
+ }
+ break;
+
+ case MUST_BE_CONCRETE:
+ if (isAbstract) {
+ report.addError(bindingMethods("cannot be abstract"));
+ }
+ }
+ }
+
+ /**
+ * Adds an error if the method declares throws anything but an {@link Error} or an appropriate
+ * subtype of {@link Exception}.
+ */
+ private void checkThrows() {
+ exceptionSuperclass.checkThrows(BindingMethodValidator.this, element, report);
+ }
+
+ /** Adds errors for the method parameters. */
+ protected void checkParameters() {
+ for (VariableElement parameter : element.getParameters()) {
+ checkParameter(parameter);
+ }
+ }
+
+ /**
+ * Adds errors for a method parameter. This implementation reports an error if the parameter has
+ * more than one qualifier.
+ */
+ protected void checkParameter(VariableElement parameter) {
+ dependencyRequestValidator.validateDependencyRequest(report, parameter, parameter.asType());
+ }
+ }
+
+ /** An abstract/concrete restriction on methods. */
+ protected enum Abstractness {
+ MUST_BE_ABSTRACT,
+ MUST_BE_CONCRETE
+ }
+
+ /**
+ * The exception class that all {@code throws}-declared throwables must extend, other than {@link
+ * Error}.
+ */
+ protected enum ExceptionSuperclass {
+ /** Methods may not declare any throwable types. */
+ NO_EXCEPTIONS {
+ @Override
+ protected String errorMessage(BindingMethodValidator validator) {
+ return validator.bindingMethods("may not throw");
+ }
+
+ @Override
+ protected void checkThrows(
+ BindingMethodValidator validator,
+ ExecutableElement element,
+ ValidationReport.Builder<ExecutableElement> report) {
+ if (!element.getThrownTypes().isEmpty()) {
+ report.addError(validator.bindingMethods("may not throw"));
+ return;
+ }
+ }
+ },
+
+ /** Methods may throw checked or unchecked exceptions or errors. */
+ EXCEPTION(Exception.class) {
+ @Override
+ protected String errorMessage(BindingMethodValidator validator) {
+ return validator.bindingMethods(
+ "may only throw unchecked exceptions or exceptions subclassing Exception");
+ }
+ },
+
+ /** Methods may throw unchecked exceptions or errors. */
+ RUNTIME_EXCEPTION(RuntimeException.class) {
+ @Override
+ protected String errorMessage(BindingMethodValidator validator) {
+ return validator.bindingMethods("may only throw unchecked exceptions");
+ }
+ },
+ ;
+
+ private final Class<? extends Exception> superclass;
+
+ ExceptionSuperclass() {
+ this(null);
+ }
+
+ ExceptionSuperclass(Class<? extends Exception> superclass) {
+ this.superclass = superclass;
+ }
+
+ /**
+ * Adds an error if the method declares throws anything but an {@link Error} or an appropriate
+ * subtype of {@link Exception}.
+ *
+ * <p>This method is overridden in {@link #NO_EXCEPTIONS}.
+ */
+ protected void checkThrows(
+ BindingMethodValidator validator,
+ ExecutableElement element,
+ ValidationReport.Builder<ExecutableElement> report) {
+ TypeMirror exceptionSupertype = validator.elements.getTypeElement(superclass).asType();
+ TypeMirror errorType = validator.elements.getTypeElement(Error.class).asType();
+ for (TypeMirror thrownType : element.getThrownTypes()) {
+ if (!validator.types.isSubtype(thrownType, exceptionSupertype)
+ && !validator.types.isSubtype(thrownType, errorType)) {
+ report.addError(errorMessage(validator));
+ break;
+ }
+ }
+ }
+
+ protected abstract String errorMessage(BindingMethodValidator validator);
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/BindingMethodValidatorsModule.java b/java/dagger/internal/codegen/validation/BindingMethodValidatorsModule.java
new file mode 100644
index 000000000..08afbc81f
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/BindingMethodValidatorsModule.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.google.common.collect.Maps.uniqueIndex;
+
+import com.google.common.collect.ImmutableMap;
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoSet;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+
+/**
+ * Binds each {@link BindingMethodValidator} into a map, keyed by {@link
+ * BindingMethodValidator#methodAnnotation()}.
+ */
+@Module
+public interface BindingMethodValidatorsModule {
+ @Provides
+ static ImmutableMap<Class<? extends Annotation>, BindingMethodValidator> indexValidators(
+ Set<BindingMethodValidator> validators) {
+ return uniqueIndex(validators, BindingMethodValidator::methodAnnotation);
+ }
+
+ @Binds
+ @IntoSet
+ BindingMethodValidator provides(ProvidesMethodValidator validator);
+
+ @Binds
+ @IntoSet
+ BindingMethodValidator produces(ProducesMethodValidator validator);
+
+ @Binds
+ @IntoSet
+ BindingMethodValidator binds(BindsMethodValidator validator);
+
+ @Binds
+ @IntoSet
+ BindingMethodValidator multibinds(MultibindsMethodValidator validator);
+
+ @Binds
+ @IntoSet
+ BindingMethodValidator bindsOptionalOf(BindsOptionalOfMethodValidator validator);
+}
diff --git a/java/dagger/internal/codegen/validation/BindsInstanceElementValidator.java b/java/dagger/internal/codegen/validation/BindsInstanceElementValidator.java
new file mode 100644
index 000000000..283af7d0b
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/BindsInstanceElementValidator.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import dagger.BindsInstance;
+import dagger.internal.codegen.binding.InjectionAnnotations;
+import javax.lang.model.element.Element;
+
+abstract class BindsInstanceElementValidator<E extends Element> extends BindingElementValidator<E> {
+ BindsInstanceElementValidator(InjectionAnnotations injectionAnnotations) {
+ super(
+ BindsInstance.class,
+ AllowsMultibindings.NO_MULTIBINDINGS,
+ AllowsScoping.NO_SCOPING,
+ injectionAnnotations);
+ }
+
+ @Override
+ protected final String bindingElements() {
+ // Even though @BindsInstance may be placed on methods, the subject of errors is the
+ // parameter
+ return "@BindsInstance parameters";
+ }
+
+ @Override
+ protected final String bindingElementTypeVerb() {
+ return "be";
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/BindsInstanceMethodValidator.java b/java/dagger/internal/codegen/validation/BindsInstanceMethodValidator.java
new file mode 100644
index 000000000..6d144bd28
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/BindsInstanceMethodValidator.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.base.ComponentAnnotation.anyComponentAnnotation;
+import static dagger.internal.codegen.base.ModuleAnnotation.moduleAnnotation;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+
+import com.google.auto.common.MoreElements;
+import dagger.internal.codegen.base.ModuleAnnotation;
+import dagger.internal.codegen.binding.InjectionAnnotations;
+import java.util.List;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+
+final class BindsInstanceMethodValidator extends BindsInstanceElementValidator<ExecutableElement> {
+ @Inject
+ BindsInstanceMethodValidator(InjectionAnnotations injectionAnnotations) {
+ super(injectionAnnotations);
+ }
+
+ @Override
+ protected ElementValidator elementValidator(ExecutableElement element) {
+ return new Validator(element);
+ }
+
+ private class Validator extends ElementValidator {
+ Validator(ExecutableElement element) {
+ super(element);
+ }
+
+ @Override
+ protected void checkAdditionalProperties() {
+ if (!element.getModifiers().contains(ABSTRACT)) {
+ report.addError("@BindsInstance methods must be abstract");
+ }
+ if (element.getParameters().size() != 1) {
+ report.addError(
+ "@BindsInstance methods should have exactly one parameter for the bound type");
+ }
+ TypeElement enclosingType = MoreElements.asType(element.getEnclosingElement());
+ moduleAnnotation(enclosingType)
+ .ifPresent(moduleAnnotation -> report.addError(didYouMeanBinds(moduleAnnotation)));
+ anyComponentAnnotation(enclosingType)
+ .ifPresent(
+ componentAnnotation ->
+ report.addError(
+ String.format(
+ "@BindsInstance methods should not be included in @%1$ss. "
+ + "Did you mean to put it in a @%1$s.Builder?",
+ componentAnnotation.simpleName())));
+ }
+
+ @Override
+ protected Optional<TypeMirror> bindingElementType() {
+ List<? extends VariableElement> parameters =
+ MoreElements.asExecutable(element).getParameters();
+ return parameters.size() == 1
+ ? Optional.of(getOnlyElement(parameters).asType())
+ : Optional.empty();
+ }
+ }
+
+ private static String didYouMeanBinds(ModuleAnnotation moduleAnnotation) {
+ return String.format(
+ "@BindsInstance methods should not be included in @%ss. Did you mean @Binds?",
+ moduleAnnotation.annotationName());
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/BindsInstanceParameterValidator.java b/java/dagger/internal/codegen/validation/BindsInstanceParameterValidator.java
new file mode 100644
index 000000000..24d65a979
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/BindsInstanceParameterValidator.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static javax.lang.model.element.ElementKind.METHOD;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.type.TypeKind.DECLARED;
+import static javax.lang.model.type.TypeKind.TYPEVAR;
+
+import com.google.auto.common.MoreElements;
+import dagger.internal.codegen.binding.InjectionAnnotations;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+final class BindsInstanceParameterValidator extends BindsInstanceElementValidator<VariableElement> {
+ @Inject
+ BindsInstanceParameterValidator(InjectionAnnotations injectionAnnotations) {
+ super(injectionAnnotations);
+ }
+
+ @Override
+ protected ElementValidator elementValidator(VariableElement element) {
+ return new Validator(element);
+ }
+
+ private class Validator extends ElementValidator {
+ Validator(VariableElement element) {
+ super(element);
+ }
+
+ @Override
+ protected void checkAdditionalProperties() {
+ Element enclosing = element.getEnclosingElement();
+ if (!enclosing.getKind().equals(METHOD)) {
+ report.addError(
+ "@BindsInstance should only be applied to methods or parameters of methods");
+ return;
+ }
+
+ ExecutableElement method = MoreElements.asExecutable(enclosing);
+ if (!method.getModifiers().contains(ABSTRACT)) {
+ report.addError("@BindsInstance parameters may only be used in abstract methods");
+ }
+
+ TypeKind returnKind = method.getReturnType().getKind();
+ if (!(returnKind.equals(DECLARED) || returnKind.equals(TYPEVAR))) {
+ report.addError(
+ "@BindsInstance parameters may not be used in methods with a void, array or primitive "
+ + "return type");
+ }
+ }
+
+ @Override
+ protected Optional<TypeMirror> bindingElementType() {
+ return Optional.of(element.asType());
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/BindsInstanceProcessingStep.java b/java/dagger/internal/codegen/validation/BindsInstanceProcessingStep.java
new file mode 100644
index 000000000..0e79b910f
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/BindsInstanceProcessingStep.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableSet;
+import dagger.BindsInstance;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+import javax.annotation.processing.Messager;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+
+/**
+ * Processing step that validates that the {@code BindsInstance} annotation is applied to the
+ * correct elements.
+ */
+public final class BindsInstanceProcessingStep extends TypeCheckingProcessingStep<Element> {
+ private final BindsInstanceMethodValidator methodValidator;
+ private final BindsInstanceParameterValidator parameterValidator;
+ private final Messager messager;
+
+ @Inject
+ BindsInstanceProcessingStep(
+ BindsInstanceMethodValidator methodValidator,
+ BindsInstanceParameterValidator parameterValidator,
+ Messager messager) {
+ super(element -> element);
+ this.methodValidator = methodValidator;
+ this.parameterValidator = parameterValidator;
+ this.messager = messager;
+ }
+
+ @Override
+ public Set<? extends Class<? extends Annotation>> annotations() {
+ return ImmutableSet.of(BindsInstance.class);
+ }
+
+ @Override
+ protected void process(Element element, ImmutableSet<Class<? extends Annotation>> annotations) {
+ switch (element.getKind()) {
+ case PARAMETER:
+ parameterValidator.validate(MoreElements.asVariable(element)).printMessagesTo(messager);
+ break;
+ case METHOD:
+ methodValidator.validate(MoreElements.asExecutable(element)).printMessagesTo(messager);
+ break;
+ default:
+ throw new AssertionError(element);
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/BindsMethodValidator.java b/java/dagger/internal/codegen/validation/BindsMethodValidator.java
new file mode 100644
index 000000000..6d2dd1878
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/BindsMethodValidator.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static dagger.internal.codegen.validation.BindingElementValidator.AllowsMultibindings.ALLOWS_MULTIBINDINGS;
+import static dagger.internal.codegen.validation.BindingElementValidator.AllowsScoping.ALLOWS_SCOPING;
+import static dagger.internal.codegen.validation.BindingMethodValidator.Abstractness.MUST_BE_ABSTRACT;
+import static dagger.internal.codegen.validation.BindingMethodValidator.ExceptionSuperclass.NO_EXCEPTIONS;
+import static dagger.internal.codegen.validation.TypeHierarchyValidator.validateTypeHierarchy;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSet;
+import dagger.Binds;
+import dagger.Module;
+import dagger.internal.codegen.base.ContributionType;
+import dagger.internal.codegen.base.SetType;
+import dagger.internal.codegen.binding.BindsTypeChecker;
+import dagger.internal.codegen.binding.InjectionAnnotations;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.producers.ProducerModule;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+
+/** A validator for {@link Binds} methods. */
+final class BindsMethodValidator extends BindingMethodValidator {
+ private final DaggerTypes types;
+ private final BindsTypeChecker bindsTypeChecker;
+
+ @Inject
+ BindsMethodValidator(
+ DaggerElements elements,
+ DaggerTypes types,
+ KotlinMetadataUtil kotlinMetadataUtil,
+ BindsTypeChecker bindsTypeChecker,
+ DependencyRequestValidator dependencyRequestValidator,
+ InjectionAnnotations injectionAnnotations) {
+ super(
+ elements,
+ types,
+ kotlinMetadataUtil,
+ Binds.class,
+ ImmutableSet.of(Module.class, ProducerModule.class),
+ dependencyRequestValidator,
+ MUST_BE_ABSTRACT,
+ NO_EXCEPTIONS,
+ ALLOWS_MULTIBINDINGS,
+ ALLOWS_SCOPING,
+ injectionAnnotations);
+ this.types = types;
+ this.bindsTypeChecker = bindsTypeChecker;
+ }
+
+ @Override
+ protected ElementValidator elementValidator(ExecutableElement element) {
+ return new Validator(element);
+ }
+
+ private class Validator extends MethodValidator {
+ Validator(ExecutableElement element) {
+ super(element);
+ }
+
+ @Override
+ protected void checkParameters() {
+ if (element.getParameters().size() != 1) {
+ report.addError(
+ bindingMethods(
+ "must have exactly one parameter, whose type is assignable to the return type"));
+ } else {
+ super.checkParameters();
+ }
+ }
+
+ @Override
+ protected void checkParameter(VariableElement parameter) {
+ super.checkParameter(parameter);
+ TypeMirror leftHandSide = boxIfNecessary(element.getReturnType());
+ TypeMirror rightHandSide = parameter.asType();
+ ContributionType contributionType = ContributionType.fromBindingElement(element);
+ if (contributionType.equals(ContributionType.SET_VALUES) && !SetType.isSet(leftHandSide)) {
+ report.addError(
+ "@Binds @ElementsIntoSet methods must return a Set and take a Set parameter");
+ }
+
+ if (!bindsTypeChecker.isAssignable(rightHandSide, leftHandSide, contributionType)) {
+ // Validate the type hierarchy of both sides to make sure they're both valid.
+ // If one of the types isn't valid it means we need to delay validation to the next round.
+ // Note: BasicAnnotationProcessor only performs superficial validation on the referenced
+ // types within the module. Thus, we're guaranteed that the types in the @Binds method are
+ // valid, but it says nothing about their supertypes, which are needed for isAssignable.
+ validateTypeHierarchy(leftHandSide, types);
+ validateTypeHierarchy(rightHandSide, types);
+ // TODO(ronshapiro): clarify this error message for @ElementsIntoSet cases, where the
+ // right-hand-side might not be assignable to the left-hand-side, but still compatible with
+ // Set.addAll(Collection<? extends E>)
+ report.addError("@Binds methods' parameter type must be assignable to the return type");
+ }
+ }
+
+ private TypeMirror boxIfNecessary(TypeMirror maybePrimitive) {
+ if (maybePrimitive.getKind().isPrimitive()) {
+ return types.boxedClass(MoreTypes.asPrimitiveType(maybePrimitive)).asType();
+ }
+ return maybePrimitive;
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/BindsOptionalOfMethodValidator.java b/java/dagger/internal/codegen/validation/BindsOptionalOfMethodValidator.java
new file mode 100644
index 000000000..cb776e196
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/BindsOptionalOfMethodValidator.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.google.auto.common.MoreTypes.asTypeElement;
+import static dagger.internal.codegen.base.Keys.isValidImplicitProvisionKey;
+import static dagger.internal.codegen.binding.InjectionAnnotations.injectedConstructors;
+import static dagger.internal.codegen.validation.BindingElementValidator.AllowsMultibindings.NO_MULTIBINDINGS;
+import static dagger.internal.codegen.validation.BindingElementValidator.AllowsScoping.NO_SCOPING;
+import static dagger.internal.codegen.validation.BindingMethodValidator.Abstractness.MUST_BE_ABSTRACT;
+import static dagger.internal.codegen.validation.BindingMethodValidator.ExceptionSuperclass.NO_EXCEPTIONS;
+
+import com.google.common.collect.ImmutableSet;
+import dagger.BindsOptionalOf;
+import dagger.Module;
+import dagger.internal.codegen.binding.InjectionAnnotations;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.producers.ProducerModule;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.TypeMirror;
+
+/** A validator for {@link BindsOptionalOf} methods. */
+final class BindsOptionalOfMethodValidator extends BindingMethodValidator {
+
+ private final DaggerTypes types;
+ private final InjectionAnnotations injectionAnnotations;
+
+ @Inject
+ BindsOptionalOfMethodValidator(
+ DaggerElements elements,
+ DaggerTypes types,
+ KotlinMetadataUtil kotlinMetadataUtil,
+ DependencyRequestValidator dependencyRequestValidator,
+ InjectionAnnotations injectionAnnotations) {
+ super(
+ elements,
+ types,
+ kotlinMetadataUtil,
+ BindsOptionalOf.class,
+ ImmutableSet.of(Module.class, ProducerModule.class),
+ dependencyRequestValidator,
+ MUST_BE_ABSTRACT,
+ NO_EXCEPTIONS,
+ NO_MULTIBINDINGS,
+ NO_SCOPING,
+ injectionAnnotations);
+ this.types = types;
+ this.injectionAnnotations = injectionAnnotations;
+ }
+
+
+ @Override
+ protected ElementValidator elementValidator(ExecutableElement element) {
+ return new Validator(element);
+ }
+
+ private class Validator extends MethodValidator {
+ Validator(ExecutableElement element) {
+ super(element);
+ }
+
+ @Override
+ protected void checkKeyType(TypeMirror keyType) {
+ super.checkKeyType(keyType);
+ if (isValidImplicitProvisionKey(
+ injectionAnnotations.getQualifiers(element).stream().findFirst(), keyType, types)
+ && !injectedConstructors(asTypeElement(keyType)).isEmpty()) {
+ report.addError(
+ "@BindsOptionalOf methods cannot return unqualified types that have an @Inject-"
+ + "annotated constructor because those are always present");
+ }
+ }
+
+ @Override
+ protected void checkParameters() {
+ if (!element.getParameters().isEmpty()) {
+ report.addError("@BindsOptionalOf methods cannot have parameters");
+ }
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/ComponentCreatorValidator.java b/java/dagger/internal/codegen/validation/ComponentCreatorValidator.java
new file mode 100644
index 000000000..a6d9a3fb5
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/ComponentCreatorValidator.java
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.getCreatorAnnotations;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.STATIC;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ObjectArrays;
+import dagger.BindsInstance;
+import dagger.internal.codegen.base.ClearableCache;
+import dagger.internal.codegen.binding.ComponentCreatorAnnotation;
+import dagger.internal.codegen.binding.ErrorMessages;
+import dagger.internal.codegen.binding.ErrorMessages.ComponentCreatorMessages;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+
+/** Validates types annotated with component creator annotations. */
+@Singleton
+public final class ComponentCreatorValidator implements ClearableCache {
+
+ private final DaggerElements elements;
+ private final DaggerTypes types;
+ private final Map<TypeElement, ValidationReport<TypeElement>> reports = new HashMap<>();
+
+ @Inject
+ ComponentCreatorValidator(DaggerElements elements, DaggerTypes types) {
+ this.elements = elements;
+ this.types = types;
+ }
+
+ @Override
+ public void clearCache() {
+ reports.clear();
+ }
+
+ /** Validates that the given {@code type} is potentially a valid component creator type. */
+ public ValidationReport<TypeElement> validate(TypeElement type) {
+ return reentrantComputeIfAbsent(reports, type, this::validateUncached);
+ }
+
+ private ValidationReport<TypeElement> validateUncached(TypeElement type) {
+ ValidationReport.Builder<TypeElement> report = ValidationReport.about(type);
+
+ ImmutableSet<ComponentCreatorAnnotation> creatorAnnotations = getCreatorAnnotations(type);
+ if (!validateOnlyOneCreatorAnnotation(creatorAnnotations, report)) {
+ return report.build();
+ }
+
+ // Note: there's more validation in ComponentDescriptorValidator:
+ // - to make sure the setter methods/factory parameters mirror the deps
+ // - to make sure each type or key is set by only one method or parameter
+ ElementValidator validator =
+ new ElementValidator(type, report, getOnlyElement(creatorAnnotations));
+ return validator.validate();
+ }
+
+ private boolean validateOnlyOneCreatorAnnotation(
+ ImmutableSet<ComponentCreatorAnnotation> creatorAnnotations,
+ ValidationReport.Builder<?> report) {
+ // creatorAnnotations should never be empty because this should only ever be called for
+ // types that have been found to have some creator annotation
+ if (creatorAnnotations.size() > 1) {
+ String error =
+ "May not have more than one component Factory or Builder annotation on a type"
+ + ": found "
+ + creatorAnnotations;
+ report.addError(error);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Validator for a single {@link TypeElement} that is annotated with a {@code Builder} or {@code
+ * Factory} annotation.
+ */
+ private final class ElementValidator {
+ private final TypeElement type;
+ private final Element component;
+ private final ValidationReport.Builder<TypeElement> report;
+ private final ComponentCreatorAnnotation annotation;
+ private final ComponentCreatorMessages messages;
+
+ private ElementValidator(
+ TypeElement type,
+ ValidationReport.Builder<TypeElement> report,
+ ComponentCreatorAnnotation annotation) {
+ this.type = type;
+ this.component = type.getEnclosingElement();
+ this.report = report;
+ this.annotation = annotation;
+ this.messages = ErrorMessages.creatorMessagesFor(annotation);
+ }
+
+ /** Validates the creator type. */
+ final ValidationReport<TypeElement> validate() {
+ if (!isAnnotationPresent(component, annotation.componentAnnotation())) {
+ report.addError(messages.mustBeInComponent());
+ }
+
+ // If the type isn't a class or interface, don't validate anything else since the rest of the
+ // messages will be bogus.
+ if (!validateIsClassOrInterface()) {
+ return report.build();
+ }
+
+ validateTypeRequirements();
+ switch (annotation.creatorKind()) {
+ case FACTORY:
+ validateFactory();
+ break;
+ case BUILDER:
+ validateBuilder();
+ }
+
+ return report.build();
+ }
+
+ /** Validates that the type is a class or interface type and returns true if it is. */
+ private boolean validateIsClassOrInterface() {
+ switch (type.getKind()) {
+ case CLASS:
+ validateConstructor();
+ return true;
+ case INTERFACE:
+ return true;
+ default:
+ report.addError(messages.mustBeClassOrInterface());
+ }
+ return false;
+ }
+
+ private void validateConstructor() {
+ List<? extends Element> allElements = type.getEnclosedElements();
+ List<ExecutableElement> constructors = ElementFilter.constructorsIn(allElements);
+
+ boolean valid = true;
+ if (constructors.size() != 1) {
+ valid = false;
+ } else {
+ ExecutableElement constructor = getOnlyElement(constructors);
+ valid =
+ constructor.getParameters().isEmpty() && !constructor.getModifiers().contains(PRIVATE);
+ }
+
+ if (!valid) {
+ report.addError(messages.invalidConstructor());
+ }
+ }
+
+ /** Validates basic requirements about the type that are common to both creator kinds. */
+ private void validateTypeRequirements() {
+ if (!type.getTypeParameters().isEmpty()) {
+ report.addError(messages.generics());
+ }
+
+ Set<Modifier> modifiers = type.getModifiers();
+ if (modifiers.contains(PRIVATE)) {
+ report.addError(messages.isPrivate());
+ }
+ if (!modifiers.contains(STATIC)) {
+ report.addError(messages.mustBeStatic());
+ }
+ // Note: Must be abstract, so no need to check for final.
+ if (!modifiers.contains(ABSTRACT)) {
+ report.addError(messages.mustBeAbstract());
+ }
+ }
+
+ private void validateBuilder() {
+ ExecutableElement buildMethod = null;
+ for (ExecutableElement method : elements.getUnimplementedMethods(type)) {
+ switch (method.getParameters().size()) {
+ case 0: // If this is potentially a build() method, validate it returns the correct type.
+ if (validateFactoryMethodReturnType(method)) {
+ if (buildMethod != null) {
+ // If we found more than one build-like method, fail.
+ error(
+ method,
+ messages.twoFactoryMethods(),
+ messages.inheritedTwoFactoryMethods(),
+ buildMethod);
+ }
+ }
+ // We set the buildMethod regardless of the return type to reduce error spam.
+ buildMethod = method;
+ break;
+
+ case 1: // If this correctly had one parameter, make sure the return types are valid.
+ validateSetterMethod(method);
+ break;
+
+ default: // more than one parameter
+ error(
+ method,
+ messages.setterMethodsMustTakeOneArg(),
+ messages.inheritedSetterMethodsMustTakeOneArg());
+ break;
+ }
+ }
+
+ if (buildMethod == null) {
+ report.addError(messages.missingFactoryMethod());
+ } else {
+ validateNotGeneric(buildMethod);
+ }
+ }
+
+ private void validateSetterMethod(ExecutableElement method) {
+ TypeMirror returnType = types.resolveExecutableType(method, type.asType()).getReturnType();
+ if (returnType.getKind() != TypeKind.VOID && !types.isSubtype(type.asType(), returnType)) {
+ error(
+ method,
+ messages.setterMethodsMustReturnVoidOrBuilder(),
+ messages.inheritedSetterMethodsMustReturnVoidOrBuilder());
+ }
+
+ validateNotGeneric(method);
+
+ VariableElement parameter = method.getParameters().get(0);
+
+ boolean methodIsBindsInstance = isAnnotationPresent(method, BindsInstance.class);
+ boolean parameterIsBindsInstance = isAnnotationPresent(parameter, BindsInstance.class);
+ boolean bindsInstance = methodIsBindsInstance || parameterIsBindsInstance;
+
+ if (methodIsBindsInstance && parameterIsBindsInstance) {
+ error(
+ method,
+ messages.bindsInstanceNotAllowedOnBothSetterMethodAndParameter(),
+ messages.inheritedBindsInstanceNotAllowedOnBothSetterMethodAndParameter());
+ }
+
+ if (!bindsInstance && parameter.asType().getKind().isPrimitive()) {
+ error(
+ method,
+ messages.nonBindsInstanceParametersMayNotBePrimitives(),
+ messages.inheritedNonBindsInstanceParametersMayNotBePrimitives());
+ }
+ }
+
+ private void validateFactory() {
+ ImmutableList<ExecutableElement> abstractMethods =
+ elements.getUnimplementedMethods(type).asList();
+ switch (abstractMethods.size()) {
+ case 0:
+ report.addError(messages.missingFactoryMethod());
+ return;
+ case 1:
+ break; // good
+ default:
+ error(
+ abstractMethods.get(1),
+ messages.twoFactoryMethods(),
+ messages.inheritedTwoFactoryMethods(),
+ abstractMethods.get(0));
+ return;
+ }
+
+ validateFactoryMethod(getOnlyElement(abstractMethods));
+ }
+
+ /** Validates that the given {@code method} is a valid component factory method. */
+ private void validateFactoryMethod(ExecutableElement method) {
+ validateNotGeneric(method);
+
+ if (!validateFactoryMethodReturnType(method)) {
+ // If we can't determine that the single method is a valid factory method, don't bother
+ // validating its parameters.
+ return;
+ }
+
+ for (VariableElement parameter : method.getParameters()) {
+ if (!isAnnotationPresent(parameter, BindsInstance.class)
+ && parameter.asType().getKind().isPrimitive()) {
+ error(
+ method,
+ messages.nonBindsInstanceParametersMayNotBePrimitives(),
+ messages.inheritedNonBindsInstanceParametersMayNotBePrimitives());
+ }
+ }
+ }
+
+ /**
+ * Validates that the factory method that actually returns a new component instance. Returns
+ * true if the return type was valid.
+ */
+ private boolean validateFactoryMethodReturnType(ExecutableElement method) {
+ TypeMirror returnType = types.resolveExecutableType(method, type.asType()).getReturnType();
+
+ if (!types.isSubtype(component.asType(), returnType)) {
+ error(
+ method,
+ messages.factoryMethodMustReturnComponentType(),
+ messages.inheritedFactoryMethodMustReturnComponentType());
+ return false;
+ }
+
+ if (isAnnotationPresent(method, BindsInstance.class)) {
+ error(
+ method,
+ messages.factoryMethodMayNotBeAnnotatedWithBindsInstance(),
+ messages.inheritedFactoryMethodMayNotBeAnnotatedWithBindsInstance());
+ return false;
+ }
+
+ TypeElement componentType = MoreElements.asType(component);
+ if (!types.isSameType(componentType.asType(), returnType)) {
+ ImmutableSet<ExecutableElement> methodsOnlyInComponent =
+ methodsOnlyInComponent(componentType);
+ if (!methodsOnlyInComponent.isEmpty()) {
+ report.addWarning(
+ messages.factoryMethodReturnsSupertypeWithMissingMethods(
+ componentType, type, returnType, method, methodsOnlyInComponent),
+ method);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Generates one of two error messages. If the method is enclosed in the subject, we target the
+ * error to the method itself. Otherwise we target the error to the subject and list the method
+ * as an argument. (Otherwise we have no way of knowing if the method is being compiled in this
+ * pass too, so javac might not be able to pinpoint it's line of code.)
+ */
+ /*
+ * For Component.Builder, the prototypical example would be if someone had:
+ * libfoo: interface SharedBuilder { void badSetter(A a, B b); }
+ * libbar: BarComponent { BarBuilder extends SharedBuilder } }
+ * ... the compiler only validates BarBuilder when compiling libbar, but it fails because
+ * of libfoo's SharedBuilder (which could have been compiled in a previous pass).
+ * So we can't point to SharedBuilder#badSetter as the subject of the BarBuilder validation
+ * failure.
+ *
+ * This check is a little more strict than necessary -- ideally we'd check if method's enclosing
+ * class was included in this compile run. But that's hard, and this is close enough.
+ */
+ private void error(
+ ExecutableElement method,
+ String enclosedError,
+ String inheritedError,
+ Object... extraArgs) {
+ if (method.getEnclosingElement().equals(type)) {
+ report.addError(String.format(enclosedError, extraArgs), method);
+ } else {
+ report.addError(String.format(inheritedError, ObjectArrays.concat(extraArgs, method)));
+ }
+ }
+
+ /** Validates that the given {@code method} is not generic. * */
+ private void validateNotGeneric(ExecutableElement method) {
+ if (!method.getTypeParameters().isEmpty()) {
+ error(
+ method,
+ messages.methodsMayNotHaveTypeParameters(),
+ messages.inheritedMethodsMayNotHaveTypeParameters());
+ }
+ }
+
+ /**
+ * Returns all methods defind in {@code componentType} which are not inherited from a supertype.
+ */
+ private ImmutableSet<ExecutableElement> methodsOnlyInComponent(TypeElement componentType) {
+ // TODO(ronshapiro): Ideally this shouldn't return methods which are redeclared from a
+ // supertype, but do not change the return type. We don't have a good/simple way of checking
+ // that, and it doesn't seem likely, so the warning won't be too bad.
+ return ImmutableSet.copyOf(methodsIn(componentType.getEnclosedElements()));
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/ComponentDescriptorValidator.java b/java/dagger/internal/codegen/validation/ComponentDescriptorValidator.java
new file mode 100644
index 000000000..aa20232b9
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/ComponentDescriptorValidator.java
@@ -0,0 +1,493 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.google.auto.common.MoreTypes.asDeclared;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Predicates.in;
+import static com.google.common.collect.Collections2.transform;
+import static dagger.internal.codegen.base.ComponentAnnotation.rootComponentAnnotation;
+import static dagger.internal.codegen.base.DiagnosticFormatting.stripCommonTypePrefixes;
+import static dagger.internal.codegen.base.Formatter.INDENT;
+import static dagger.internal.codegen.base.Scopes.getReadableSource;
+import static dagger.internal.codegen.base.Scopes.scopesOf;
+import static dagger.internal.codegen.base.Scopes.singletonScope;
+import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSetMultimap;
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toList;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.Sets;
+import dagger.internal.codegen.binding.ComponentCreatorDescriptor;
+import dagger.internal.codegen.binding.ComponentDescriptor;
+import dagger.internal.codegen.binding.ComponentRequirement;
+import dagger.internal.codegen.binding.ComponentRequirement.NullPolicy;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.binding.ErrorMessages;
+import dagger.internal.codegen.binding.ErrorMessages.ComponentCreatorMessages;
+import dagger.internal.codegen.binding.MethodSignatureFormatter;
+import dagger.internal.codegen.binding.ModuleDescriptor;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.compileroption.ValidationType;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Scope;
+import java.util.ArrayDeque;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import java.util.StringJoiner;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+import javax.tools.Diagnostic;
+
+/**
+ * Reports errors in the component hierarchy.
+ *
+ * <ul>
+ * <li>Validates scope hierarchy of component dependencies and subcomponents.
+ * <li>Reports errors if there are component dependency cycles.
+ * <li>Reports errors if any abstract modules have non-abstract instance binding methods.
+ * <li>Validates component creator types.
+ * </ul>
+ */
+// TODO(dpb): Combine with ComponentHierarchyValidator.
+public final class ComponentDescriptorValidator {
+
+ private final DaggerElements elements;
+ private final DaggerTypes types;
+ private final CompilerOptions compilerOptions;
+ private final MethodSignatureFormatter methodSignatureFormatter;
+ private final ComponentHierarchyValidator componentHierarchyValidator;
+ private final KotlinMetadataUtil metadataUtil;
+
+ @Inject
+ ComponentDescriptorValidator(
+ DaggerElements elements,
+ DaggerTypes types,
+ CompilerOptions compilerOptions,
+ MethodSignatureFormatter methodSignatureFormatter,
+ ComponentHierarchyValidator componentHierarchyValidator,
+ KotlinMetadataUtil metadataUtil) {
+ this.elements = elements;
+ this.types = types;
+ this.compilerOptions = compilerOptions;
+ this.methodSignatureFormatter = methodSignatureFormatter;
+ this.componentHierarchyValidator = componentHierarchyValidator;
+ this.metadataUtil = metadataUtil;
+ }
+
+ public ValidationReport<TypeElement> validate(ComponentDescriptor component) {
+ ComponentValidation validation = new ComponentValidation(component);
+ validation.visitComponent(component);
+ validation.report(component).addSubreport(componentHierarchyValidator.validate(component));
+ return validation.buildReport();
+ }
+
+ private final class ComponentValidation {
+ final ComponentDescriptor rootComponent;
+ final Map<ComponentDescriptor, ValidationReport.Builder<TypeElement>> reports =
+ new LinkedHashMap<>();
+
+ ComponentValidation(ComponentDescriptor rootComponent) {
+ this.rootComponent = checkNotNull(rootComponent);
+ }
+
+ /** Returns a report that contains all validation messages found during traversal. */
+ ValidationReport<TypeElement> buildReport() {
+ ValidationReport.Builder<TypeElement> report =
+ ValidationReport.about(rootComponent.typeElement());
+ reports.values().forEach(subreport -> report.addSubreport(subreport.build()));
+ return report.build();
+ }
+
+ /** Returns the report builder for a (sub)component. */
+ private ValidationReport.Builder<TypeElement> report(ComponentDescriptor component) {
+ return reentrantComputeIfAbsent(
+ reports, component, descriptor -> ValidationReport.about(descriptor.typeElement()));
+ }
+
+ private void reportComponentItem(
+ Diagnostic.Kind kind, ComponentDescriptor component, String message) {
+ report(component)
+ .addItem(message, kind, component.typeElement(), component.annotation().annotation());
+ }
+
+ private void reportComponentError(ComponentDescriptor component, String error) {
+ reportComponentItem(ERROR, component, error);
+ }
+
+ void visitComponent(ComponentDescriptor component) {
+ validateDependencyScopes(component);
+ validateComponentDependencyHierarchy(component);
+ validateModules(component);
+ validateCreators(component);
+ component.childComponents().forEach(this::visitComponent);
+ }
+
+ /** Validates that component dependencies do not form a cycle. */
+ private void validateComponentDependencyHierarchy(ComponentDescriptor component) {
+ validateComponentDependencyHierarchy(component, component.typeElement(), new ArrayDeque<>());
+ }
+
+ /** Recursive method to validate that component dependencies do not form a cycle. */
+ private void validateComponentDependencyHierarchy(
+ ComponentDescriptor component, TypeElement dependency, Deque<TypeElement> dependencyStack) {
+ if (dependencyStack.contains(dependency)) {
+ // Current component has already appeared in the component chain.
+ StringBuilder message = new StringBuilder();
+ message.append(component.typeElement().getQualifiedName());
+ message.append(" contains a cycle in its component dependencies:\n");
+ dependencyStack.push(dependency);
+ appendIndentedComponentsList(message, dependencyStack);
+ dependencyStack.pop();
+ reportComponentItem(
+ compilerOptions.scopeCycleValidationType().diagnosticKind().get(),
+ component,
+ message.toString());
+ } else if (compilerOptions.validateTransitiveComponentDependencies()
+ // Always validate direct component dependencies referenced by this component regardless
+ // of the flag value
+ || dependencyStack.isEmpty()) {
+ rootComponentAnnotation(dependency)
+ .ifPresent(
+ componentAnnotation -> {
+ dependencyStack.push(dependency);
+
+ for (TypeElement nextDependency : componentAnnotation.dependencies()) {
+ validateComponentDependencyHierarchy(
+ component, nextDependency, dependencyStack);
+ }
+
+ dependencyStack.pop();
+ });
+ }
+ }
+
+ /**
+ * Validates that among the dependencies there are no cycles within the scoping chain, and that
+ * singleton components have no scoped dependencies.
+ */
+ private void validateDependencyScopes(ComponentDescriptor component) {
+ ImmutableSet<Scope> scopes = component.scopes();
+ ImmutableSet<TypeElement> scopedDependencies =
+ scopedTypesIn(
+ component
+ .dependencies()
+ .stream()
+ .map(ComponentRequirement::typeElement)
+ .collect(toImmutableSet()));
+ if (!scopes.isEmpty()) {
+ Scope singletonScope = singletonScope(elements);
+ // Dagger 1.x scope compatibility requires this be suppress-able.
+ if (compilerOptions.scopeCycleValidationType().diagnosticKind().isPresent()
+ && scopes.contains(singletonScope)) {
+ // Singleton is a special-case representing the longest lifetime, and therefore
+ // @Singleton components may not depend on scoped components
+ if (!scopedDependencies.isEmpty()) {
+ StringBuilder message =
+ new StringBuilder(
+ "This @Singleton component cannot depend on scoped components:\n");
+ appendIndentedComponentsList(message, scopedDependencies);
+ reportComponentItem(
+ compilerOptions.scopeCycleValidationType().diagnosticKind().get(),
+ component,
+ message.toString());
+ }
+ } else {
+ // Dagger 1.x scope compatibility requires this be suppress-able.
+ if (!compilerOptions.scopeCycleValidationType().equals(ValidationType.NONE)) {
+ validateDependencyScopeHierarchy(
+ component, component.typeElement(), new ArrayDeque<>(), new ArrayDeque<>());
+ }
+ }
+ } else {
+ // Scopeless components may not depend on scoped components.
+ if (!scopedDependencies.isEmpty()) {
+ StringBuilder message =
+ new StringBuilder(component.typeElement().getQualifiedName())
+ .append(" (unscoped) cannot depend on scoped components:\n");
+ appendIndentedComponentsList(message, scopedDependencies);
+ reportComponentError(component, message.toString());
+ }
+ }
+ }
+
+ private void validateModules(ComponentDescriptor component) {
+ for (ModuleDescriptor module : component.modules()) {
+ if (module.moduleElement().getModifiers().contains(Modifier.ABSTRACT)) {
+ for (ContributionBinding binding : module.bindings()) {
+ if (binding.requiresModuleInstance()) {
+ report(component).addError(abstractModuleHasInstanceBindingMethodsError(module));
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ private String abstractModuleHasInstanceBindingMethodsError(ModuleDescriptor module) {
+ String methodAnnotations;
+ switch (module.kind()) {
+ case MODULE:
+ methodAnnotations = "@Provides";
+ break;
+ case PRODUCER_MODULE:
+ methodAnnotations = "@Provides or @Produces";
+ break;
+ default:
+ throw new AssertionError(module.kind());
+ }
+ return String.format(
+ "%s is abstract and has instance %s methods. Consider making the methods static or "
+ + "including a non-abstract subclass of the module instead.",
+ module.moduleElement(), methodAnnotations);
+ }
+
+ private void validateCreators(ComponentDescriptor component) {
+ if (!component.creatorDescriptor().isPresent()) {
+ // If no builder, nothing to validate.
+ return;
+ }
+
+ ComponentCreatorDescriptor creator = component.creatorDescriptor().get();
+ ComponentCreatorMessages messages = ErrorMessages.creatorMessagesFor(creator.annotation());
+
+ // Requirements for modules and dependencies that the creator can set
+ Set<ComponentRequirement> creatorModuleAndDependencyRequirements =
+ creator.moduleAndDependencyRequirements();
+ // Modules and dependencies the component requires
+ Set<ComponentRequirement> componentModuleAndDependencyRequirements =
+ component.dependenciesAndConcreteModules();
+
+ // Requirements that the creator can set that don't match any requirements that the component
+ // actually has.
+ Set<ComponentRequirement> inapplicableRequirementsOnCreator =
+ Sets.difference(
+ creatorModuleAndDependencyRequirements, componentModuleAndDependencyRequirements);
+
+ DeclaredType container = asDeclared(creator.typeElement().asType());
+ if (!inapplicableRequirementsOnCreator.isEmpty()) {
+ Collection<Element> excessElements =
+ Multimaps.filterKeys(
+ creator.unvalidatedRequirementElements(), in(inapplicableRequirementsOnCreator))
+ .values();
+ String formatted =
+ excessElements.stream()
+ .map(element -> formatElement(element, container))
+ .collect(joining(", ", "[", "]"));
+ report(component)
+ .addError(String.format(messages.extraSetters(), formatted), creator.typeElement());
+ }
+
+ // Component requirements that the creator must be able to set
+ Set<ComponentRequirement> mustBePassed =
+ Sets.filter(
+ componentModuleAndDependencyRequirements,
+ input -> input.nullPolicy(elements, types, metadataUtil).equals(NullPolicy.THROW));
+ // Component requirements that the creator must be able to set, but can't
+ Set<ComponentRequirement> missingRequirements =
+ Sets.difference(mustBePassed, creatorModuleAndDependencyRequirements);
+
+ if (!missingRequirements.isEmpty()) {
+ report(component)
+ .addError(
+ String.format(
+ messages.missingSetters(),
+ missingRequirements.stream().map(ComponentRequirement::type).collect(toList())),
+ creator.typeElement());
+ }
+
+ // Validate that declared creator requirements (modules, dependencies) have unique types.
+ ImmutableSetMultimap<Wrapper<TypeMirror>, Element> declaredRequirementsByType =
+ Multimaps.filterKeys(
+ creator.unvalidatedRequirementElements(),
+ creatorModuleAndDependencyRequirements::contains)
+ .entries().stream()
+ .collect(
+ toImmutableSetMultimap(entry -> entry.getKey().wrappedType(), Entry::getValue));
+ declaredRequirementsByType
+ .asMap()
+ .forEach(
+ (typeWrapper, elementsForType) -> {
+ if (elementsForType.size() > 1) {
+ TypeMirror type = typeWrapper.get();
+ // TODO(cgdecker): Attach this error message to the factory method rather than
+ // the component type if the elements are factory method parameters AND the
+ // factory method is defined by the factory type itself and not by a supertype.
+ report(component)
+ .addError(
+ String.format(
+ messages.multipleSettersForModuleOrDependencyType(),
+ type,
+ transform(
+ elementsForType, element -> formatElement(element, container))),
+ creator.typeElement());
+ }
+ });
+
+ // TODO(cgdecker): Duplicate binding validation should handle the case of multiple elements
+ // that set the same bound-instance Key, but validating that here would make it fail faster
+ // for subcomponents.
+ }
+
+ private String formatElement(Element element, DeclaredType container) {
+ // TODO(cgdecker): Extract some or all of this to another class?
+ // But note that it does different formatting for parameters than
+ // DaggerElements.elementToString(Element).
+ switch (element.getKind()) {
+ case METHOD:
+ return methodSignatureFormatter.format(
+ MoreElements.asExecutable(element), Optional.of(container));
+ case PARAMETER:
+ return formatParameter(MoreElements.asVariable(element), container);
+ default:
+ // This method shouldn't be called with any other type of element.
+ throw new AssertionError();
+ }
+ }
+
+ private String formatParameter(VariableElement parameter, DeclaredType container) {
+ // TODO(cgdecker): Possibly leave the type (and annotations?) off of the parameters here and
+ // just use their names, since the type will be redundant in the context of the error message.
+ StringJoiner joiner = new StringJoiner(" ");
+ parameter.getAnnotationMirrors().stream().map(Object::toString).forEach(joiner::add);
+ TypeMirror parameterType = resolveParameterType(parameter, container);
+ return joiner
+ .add(stripCommonTypePrefixes(parameterType.toString()))
+ .add(parameter.getSimpleName())
+ .toString();
+ }
+
+ private TypeMirror resolveParameterType(VariableElement parameter, DeclaredType container) {
+ ExecutableElement method =
+ MoreElements.asExecutable(parameter.getEnclosingElement());
+ int parameterIndex = method.getParameters().indexOf(parameter);
+
+ ExecutableType methodType = MoreTypes.asExecutable(types.asMemberOf(container, method));
+ return methodType.getParameterTypes().get(parameterIndex);
+ }
+
+ /**
+ * Validates that scopes do not participate in a scoping cycle - that is to say, scoped
+ * components are in a hierarchical relationship terminating with Singleton.
+ *
+ * <p>As a side-effect, this means scoped components cannot have a dependency cycle between
+ * themselves, since a component's presence within its own dependency path implies a cyclical
+ * relationship between scopes. However, cycles in component dependencies are explicitly checked
+ * in {@link #validateComponentDependencyHierarchy(ComponentDescriptor)}.
+ */
+ private void validateDependencyScopeHierarchy(
+ ComponentDescriptor component,
+ TypeElement dependency,
+ Deque<ImmutableSet<Scope>> scopeStack,
+ Deque<TypeElement> scopedDependencyStack) {
+ ImmutableSet<Scope> scopes = scopesOf(dependency);
+ if (stackOverlaps(scopeStack, scopes)) {
+ scopedDependencyStack.push(dependency);
+ // Current scope has already appeared in the component chain.
+ StringBuilder message = new StringBuilder();
+ message.append(component.typeElement().getQualifiedName());
+ message.append(" depends on scoped components in a non-hierarchical scope ordering:\n");
+ appendIndentedComponentsList(message, scopedDependencyStack);
+ if (compilerOptions.scopeCycleValidationType().diagnosticKind().isPresent()) {
+ reportComponentItem(
+ compilerOptions.scopeCycleValidationType().diagnosticKind().get(),
+ component,
+ message.toString());
+ }
+ scopedDependencyStack.pop();
+ } else if (compilerOptions.validateTransitiveComponentDependencies()
+ // Always validate direct component dependencies referenced by this component regardless
+ // of the flag value
+ || scopedDependencyStack.isEmpty()) {
+ // TODO(beder): transitively check scopes of production components too.
+ rootComponentAnnotation(dependency)
+ .filter(componentAnnotation -> !componentAnnotation.isProduction())
+ .ifPresent(
+ componentAnnotation -> {
+ ImmutableSet<TypeElement> scopedDependencies =
+ scopedTypesIn(componentAnnotation.dependencies());
+ if (!scopedDependencies.isEmpty()) {
+ // empty can be ignored (base-case)
+ scopeStack.push(scopes);
+ scopedDependencyStack.push(dependency);
+ for (TypeElement scopedDependency : scopedDependencies) {
+ validateDependencyScopeHierarchy(
+ component,
+ scopedDependency,
+ scopeStack,
+ scopedDependencyStack);
+ }
+ scopedDependencyStack.pop();
+ scopeStack.pop();
+ }
+ }); // else: we skip component dependencies which are not components
+ }
+ }
+
+ private <T> boolean stackOverlaps(Deque<ImmutableSet<T>> stack, ImmutableSet<T> set) {
+ for (ImmutableSet<T> entry : stack) {
+ if (!Sets.intersection(entry, set).isEmpty()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Appends and formats a list of indented component types (with their scope annotations). */
+ private void appendIndentedComponentsList(StringBuilder message, Iterable<TypeElement> types) {
+ for (TypeElement scopedComponent : types) {
+ message.append(INDENT);
+ for (Scope scope : scopesOf(scopedComponent)) {
+ message.append(getReadableSource(scope)).append(' ');
+ }
+ message
+ .append(stripCommonTypePrefixes(scopedComponent.getQualifiedName().toString()))
+ .append('\n');
+ }
+ }
+
+ /**
+ * Returns a set of type elements containing only those found in the input set that have a
+ * scoping annotation.
+ */
+ private ImmutableSet<TypeElement> scopedTypesIn(Collection<TypeElement> types) {
+ return types.stream().filter(type -> !scopesOf(type).isEmpty()).collect(toImmutableSet());
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/ComponentHierarchyValidator.java b/java/dagger/internal/codegen/validation/ComponentHierarchyValidator.java
new file mode 100644
index 000000000..1861636d3
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/ComponentHierarchyValidator.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.google.common.base.Functions.constant;
+import static com.google.common.base.Predicates.and;
+import static com.google.common.base.Predicates.in;
+import static com.google.common.base.Predicates.not;
+import static dagger.internal.codegen.base.Scopes.getReadableSource;
+import static dagger.internal.codegen.base.Scopes.uniqueScopeOf;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.base.Joiner;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Sets;
+import dagger.internal.codegen.binding.ComponentDescriptor;
+import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.binding.ModuleDescriptor;
+import dagger.internal.codegen.binding.ModuleKind;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.model.Scope;
+import java.util.Collection;
+import java.util.Formatter;
+import java.util.Map;
+import javax.inject.Inject;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+
+/** Validates the relationships between parent components and subcomponents. */
+final class ComponentHierarchyValidator {
+ private static final Joiner COMMA_SEPARATED_JOINER = Joiner.on(", ");
+ private final CompilerOptions compilerOptions;
+
+ @Inject
+ ComponentHierarchyValidator(CompilerOptions compilerOptions) {
+ this.compilerOptions = compilerOptions;
+ }
+
+ ValidationReport<TypeElement> validate(ComponentDescriptor componentDescriptor) {
+ ValidationReport.Builder<TypeElement> report =
+ ValidationReport.about(componentDescriptor.typeElement());
+ validateSubcomponentMethods(
+ report,
+ componentDescriptor,
+ Maps.toMap(componentDescriptor.moduleTypes(), constant(componentDescriptor.typeElement())));
+ validateRepeatedScopedDeclarations(report, componentDescriptor, LinkedHashMultimap.create());
+
+ if (compilerOptions.scopeCycleValidationType().diagnosticKind().isPresent()) {
+ validateScopeHierarchy(
+ report, componentDescriptor, LinkedHashMultimap.<ComponentDescriptor, Scope>create());
+ }
+ validateProductionModuleUniqueness(report, componentDescriptor, LinkedHashMultimap.create());
+ return report.build();
+ }
+
+ private void validateSubcomponentMethods(
+ ValidationReport.Builder<?> report,
+ ComponentDescriptor componentDescriptor,
+ ImmutableMap<TypeElement, TypeElement> existingModuleToOwners) {
+ componentDescriptor
+ .childComponentsDeclaredByFactoryMethods()
+ .forEach(
+ (method, childComponent) -> {
+ if (childComponent.hasCreator()) {
+ report.addError(
+ "Components may not have factory methods for subcomponents that define a "
+ + "builder.",
+ method.methodElement());
+ } else {
+ validateFactoryMethodParameters(report, method, existingModuleToOwners);
+ }
+
+ validateSubcomponentMethods(
+ report,
+ childComponent,
+ new ImmutableMap.Builder<TypeElement, TypeElement>()
+ .putAll(existingModuleToOwners)
+ .putAll(
+ Maps.toMap(
+ Sets.difference(
+ childComponent.moduleTypes(), existingModuleToOwners.keySet()),
+ constant(childComponent.typeElement())))
+ .build());
+ });
+ }
+
+ private void validateFactoryMethodParameters(
+ ValidationReport.Builder<?> report,
+ ComponentMethodDescriptor subcomponentMethodDescriptor,
+ ImmutableMap<TypeElement, TypeElement> existingModuleToOwners) {
+ for (VariableElement factoryMethodParameter :
+ subcomponentMethodDescriptor.methodElement().getParameters()) {
+ TypeElement moduleType = MoreTypes.asTypeElement(factoryMethodParameter.asType());
+ TypeElement originatingComponent = existingModuleToOwners.get(moduleType);
+ if (originatingComponent != null) {
+ /* Factory method tries to pass a module that is already present in the parent.
+ * This is an error. */
+ report.addError(
+ String.format(
+ "%s is present in %s. A subcomponent cannot use an instance of a "
+ + "module that differs from its parent.",
+ moduleType.getSimpleName(), originatingComponent.getQualifiedName()),
+ factoryMethodParameter);
+ }
+ }
+ }
+
+ /**
+ * Checks that components do not have any scopes that are also applied on any of their ancestors.
+ */
+ private void validateScopeHierarchy(
+ ValidationReport.Builder<TypeElement> report,
+ ComponentDescriptor subject,
+ SetMultimap<ComponentDescriptor, Scope> scopesByComponent) {
+ scopesByComponent.putAll(subject, subject.scopes());
+
+ for (ComponentDescriptor childComponent : subject.childComponents()) {
+ validateScopeHierarchy(report, childComponent, scopesByComponent);
+ }
+
+ scopesByComponent.removeAll(subject);
+
+ Predicate<Scope> subjectScopes =
+ subject.isProduction()
+ // TODO(beder): validate that @ProductionScope is only applied on production components
+ ? and(in(subject.scopes()), not(Scope::isProductionScope))
+ : in(subject.scopes());
+ SetMultimap<ComponentDescriptor, Scope> overlappingScopes =
+ Multimaps.filterValues(scopesByComponent, subjectScopes);
+ if (!overlappingScopes.isEmpty()) {
+ StringBuilder error =
+ new StringBuilder()
+ .append(subject.typeElement().getQualifiedName())
+ .append(" has conflicting scopes:");
+ for (Map.Entry<ComponentDescriptor, Scope> entry : overlappingScopes.entries()) {
+ Scope scope = entry.getValue();
+ error
+ .append("\n ")
+ .append(entry.getKey().typeElement().getQualifiedName())
+ .append(" also has ")
+ .append(getReadableSource(scope));
+ }
+ report.addItem(
+ error.toString(),
+ compilerOptions.scopeCycleValidationType().diagnosticKind().get(),
+ subject.typeElement());
+ }
+ }
+
+ private void validateProductionModuleUniqueness(
+ ValidationReport.Builder<TypeElement> report,
+ ComponentDescriptor componentDescriptor,
+ SetMultimap<ComponentDescriptor, ModuleDescriptor> producerModulesByComponent) {
+ ImmutableSet<ModuleDescriptor> producerModules =
+ componentDescriptor.modules().stream()
+ .filter(module -> module.kind().equals(ModuleKind.PRODUCER_MODULE))
+ .collect(toImmutableSet());
+
+ producerModulesByComponent.putAll(componentDescriptor, producerModules);
+ for (ComponentDescriptor childComponent : componentDescriptor.childComponents()) {
+ validateProductionModuleUniqueness(report, childComponent, producerModulesByComponent);
+ }
+ producerModulesByComponent.removeAll(componentDescriptor);
+
+
+ SetMultimap<ComponentDescriptor, ModuleDescriptor> repeatedModules =
+ Multimaps.filterValues(producerModulesByComponent, producerModules::contains);
+ if (repeatedModules.isEmpty()) {
+ return;
+ }
+
+ StringBuilder error = new StringBuilder();
+ Formatter formatter = new Formatter(error);
+
+ formatter.format("%s repeats @ProducerModules:", componentDescriptor.typeElement());
+
+ for (Map.Entry<ComponentDescriptor, Collection<ModuleDescriptor>> entry :
+ repeatedModules.asMap().entrySet()) {
+ formatter.format("\n %s also installs: ", entry.getKey().typeElement());
+ COMMA_SEPARATED_JOINER
+ .appendTo(error, Iterables.transform(entry.getValue(), m -> m.moduleElement()));
+ }
+
+ report.addError(error.toString());
+ }
+
+ private void validateRepeatedScopedDeclarations(
+ ValidationReport.Builder<TypeElement> report,
+ ComponentDescriptor component,
+ // TODO(ronshapiro): optimize ModuleDescriptor.hashCode()/equals. Otherwise this could be
+ // quite costly
+ SetMultimap<ComponentDescriptor, ModuleDescriptor> modulesWithScopes) {
+ ImmutableSet<ModuleDescriptor> modules =
+ component.modules().stream().filter(this::hasScopedDeclarations).collect(toImmutableSet());
+ modulesWithScopes.putAll(component, modules);
+ for (ComponentDescriptor childComponent : component.childComponents()) {
+ validateRepeatedScopedDeclarations(report, childComponent, modulesWithScopes);
+ }
+ modulesWithScopes.removeAll(component);
+
+ SetMultimap<ComponentDescriptor, ModuleDescriptor> repeatedModules =
+ Multimaps.filterValues(modulesWithScopes, modules::contains);
+ if (repeatedModules.isEmpty()) {
+ return;
+ }
+
+ report.addError(
+ repeatedModulesWithScopeError(component, ImmutableSetMultimap.copyOf(repeatedModules)));
+ }
+
+ private boolean hasScopedDeclarations(ModuleDescriptor module) {
+ return !moduleScopes(module).isEmpty();
+ }
+
+ private String repeatedModulesWithScopeError(
+ ComponentDescriptor component,
+ ImmutableSetMultimap<ComponentDescriptor, ModuleDescriptor> repeatedModules) {
+ StringBuilder error =
+ new StringBuilder()
+ .append(component.typeElement().getQualifiedName())
+ .append(" repeats modules with scoped bindings or declarations:");
+
+ repeatedModules
+ .asMap()
+ .forEach(
+ (conflictingComponent, conflictingModules) -> {
+ error
+ .append("\n - ")
+ .append(conflictingComponent.typeElement().getQualifiedName())
+ .append(" also includes:");
+ for (ModuleDescriptor conflictingModule : conflictingModules) {
+ error
+ .append("\n - ")
+ .append(conflictingModule.moduleElement().getQualifiedName())
+ .append(" with scopes: ")
+ .append(COMMA_SEPARATED_JOINER.join(moduleScopes(conflictingModule)));
+ }
+ });
+ return error.toString();
+ }
+
+ private ImmutableSet<Scope> moduleScopes(ModuleDescriptor module) {
+ return FluentIterable.concat(module.allBindingDeclarations())
+ .transform(declaration -> uniqueScopeOf(declaration.bindingElement().get()))
+ .filter(scope -> scope.isPresent() && !scope.get().isReusable())
+ .transform(scope -> scope.get())
+ .toSet();
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/ComponentValidator.java b/java/dagger/internal/codegen/validation/ComponentValidator.java
new file mode 100644
index 000000000..b3322e5b2
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/ComponentValidator.java
@@ -0,0 +1,558 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.google.auto.common.MoreElements.asType;
+import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.auto.common.MoreTypes.asDeclared;
+import static com.google.auto.common.MoreTypes.asExecutable;
+import static com.google.auto.common.MoreTypes.asTypeElement;
+import static com.google.common.base.Verify.verify;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.common.collect.Multimaps.asMap;
+import static com.google.common.collect.Sets.intersection;
+import static dagger.internal.codegen.base.ComponentAnnotation.anyComponentAnnotation;
+import static dagger.internal.codegen.base.ModuleAnnotation.moduleAnnotation;
+import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.creatorAnnotationsFor;
+import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.productionCreatorAnnotations;
+import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.subcomponentCreatorAnnotations;
+import static dagger.internal.codegen.binding.ComponentKind.annotationsFor;
+import static dagger.internal.codegen.binding.ConfigurationAnnotations.enclosedAnnotatedTypes;
+import static dagger.internal.codegen.binding.ConfigurationAnnotations.getTransitiveModules;
+import static dagger.internal.codegen.binding.ErrorMessages.ComponentCreatorMessages.builderMethodRequiresNoArgs;
+import static dagger.internal.codegen.binding.ErrorMessages.ComponentCreatorMessages.moreThanOneRefToSubcomponent;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnyAnnotation;
+import static java.util.Comparator.comparing;
+import static javax.lang.model.element.ElementKind.CLASS;
+import static javax.lang.model.element.ElementKind.INTERFACE;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.type.TypeKind.VOID;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Sets;
+import dagger.Component;
+import dagger.Reusable;
+import dagger.internal.codegen.base.ClearableCache;
+import dagger.internal.codegen.base.ComponentAnnotation;
+import dagger.internal.codegen.binding.ComponentKind;
+import dagger.internal.codegen.binding.DependencyRequestFactory;
+import dagger.internal.codegen.binding.ErrorMessages;
+import dagger.internal.codegen.binding.MethodSignatureFormatter;
+import dagger.internal.codegen.binding.ModuleKind;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.producers.CancellationPolicy;
+import dagger.producers.ProductionComponent;
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVisitor;
+import javax.lang.model.util.SimpleTypeVisitor8;
+
+/**
+ * Performs superficial validation of the contract of the {@link Component} and {@link
+ * ProductionComponent} annotations.
+ */
+@Singleton
+public final class ComponentValidator implements ClearableCache {
+ private final DaggerElements elements;
+ private final DaggerTypes types;
+ private final ModuleValidator moduleValidator;
+ private final ComponentCreatorValidator creatorValidator;
+ private final DependencyRequestValidator dependencyRequestValidator;
+ private final MembersInjectionValidator membersInjectionValidator;
+ private final MethodSignatureFormatter methodSignatureFormatter;
+ private final DependencyRequestFactory dependencyRequestFactory;
+ private final Map<TypeElement, ValidationReport<TypeElement>> reports = new HashMap<>();
+
+ @Inject
+ ComponentValidator(
+ DaggerElements elements,
+ DaggerTypes types,
+ ModuleValidator moduleValidator,
+ ComponentCreatorValidator creatorValidator,
+ DependencyRequestValidator dependencyRequestValidator,
+ MembersInjectionValidator membersInjectionValidator,
+ MethodSignatureFormatter methodSignatureFormatter,
+ DependencyRequestFactory dependencyRequestFactory) {
+ this.elements = elements;
+ this.types = types;
+ this.moduleValidator = moduleValidator;
+ this.creatorValidator = creatorValidator;
+ this.dependencyRequestValidator = dependencyRequestValidator;
+ this.membersInjectionValidator = membersInjectionValidator;
+ this.methodSignatureFormatter = methodSignatureFormatter;
+ this.dependencyRequestFactory = dependencyRequestFactory;
+ }
+
+ @Override
+ public void clearCache() {
+ reports.clear();
+ }
+
+ /** Validates the given component. */
+ public ValidationReport<TypeElement> validate(TypeElement component) {
+ return reentrantComputeIfAbsent(reports, component, this::validateUncached);
+ }
+
+ private ValidationReport<TypeElement> validateUncached(TypeElement component) {
+ return new ElementValidator(component).validateElement();
+ }
+
+ private class ElementValidator {
+ private final TypeElement component;
+ private final ValidationReport.Builder<TypeElement> report;
+ private final ImmutableSet<ComponentKind> componentKinds;
+
+ // Populated by ComponentMethodValidators
+ private final SetMultimap<Element, ExecutableElement> referencedSubcomponents =
+ LinkedHashMultimap.create();
+
+ ElementValidator(TypeElement component) {
+ this.component = component;
+ this.report = ValidationReport.about(component);
+ this.componentKinds = ComponentKind.getComponentKinds(component);
+ }
+
+ private ComponentKind componentKind() {
+ return getOnlyElement(componentKinds);
+ }
+
+ private ComponentAnnotation componentAnnotation() {
+ return anyComponentAnnotation(component).get();
+ }
+
+ private DeclaredType componentType() {
+ return asDeclared(component.asType());
+ }
+
+ ValidationReport<TypeElement> validateElement() {
+ if (componentKinds.size() > 1) {
+ return moreThanOneComponentAnnotation();
+ }
+
+ validateUseOfCancellationPolicy();
+ validateIsAbstractType();
+ validateCreators();
+ validateNoReusableAnnotation();
+ validateComponentMethods();
+ validateNoConflictingEntryPoints();
+ validateSubcomponentReferences();
+ validateComponentDependencies();
+ validateReferencedModules();
+ validateSubcomponents();
+
+ return report.build();
+ }
+
+ private ValidationReport<TypeElement> moreThanOneComponentAnnotation() {
+ String error =
+ "Components may not be annotated with more than one component annotation: found "
+ + annotationsFor(componentKinds);
+ report.addError(error, component);
+ return report.build();
+ }
+
+ private void validateUseOfCancellationPolicy() {
+ if (isAnnotationPresent(component, CancellationPolicy.class)
+ && !componentKind().isProducer()) {
+ report.addError(
+ "@CancellationPolicy may only be applied to production components and subcomponents",
+ component);
+ }
+ }
+
+ private void validateIsAbstractType() {
+ if (!component.getKind().equals(INTERFACE)
+ && !(component.getKind().equals(CLASS) && component.getModifiers().contains(ABSTRACT))) {
+ report.addError(
+ String.format(
+ "@%s may only be applied to an interface or abstract class",
+ componentKind().annotation().getSimpleName()),
+ component);
+ }
+ }
+
+ private void validateCreators() {
+ ImmutableList<DeclaredType> creators =
+ creatorAnnotationsFor(componentAnnotation()).stream()
+ .flatMap(annotation -> enclosedAnnotatedTypes(component, annotation).stream())
+ .collect(toImmutableList());
+ creators.forEach(
+ creator -> report.addSubreport(creatorValidator.validate(asTypeElement(creator))));
+ if (creators.size() > 1) {
+ report.addError(
+ String.format(
+ ErrorMessages.componentMessagesFor(componentKind()).moreThanOne(), creators),
+ component);
+ }
+ }
+
+ private void validateNoReusableAnnotation() {
+ Optional<AnnotationMirror> reusableAnnotation =
+ getAnnotationMirror(component, Reusable.class);
+ if (reusableAnnotation.isPresent()) {
+ report.addError(
+ "@Reusable cannot be applied to components or subcomponents",
+ component,
+ reusableAnnotation.get());
+ }
+ }
+
+ private void validateComponentMethods() {
+ getLocalAndInheritedMethods(component, types, elements).stream()
+ .filter(method -> method.getModifiers().contains(ABSTRACT))
+ .map(ComponentMethodValidator::new)
+ .forEachOrdered(ComponentMethodValidator::validateMethod);
+ }
+
+ private class ComponentMethodValidator {
+ private final ExecutableElement method;
+ private final ExecutableType resolvedMethod;
+ private final List<? extends TypeMirror> parameterTypes;
+ private final List<? extends VariableElement> parameters;
+ private final TypeMirror returnType;
+
+ ComponentMethodValidator(ExecutableElement method) {
+ this.method = method;
+ this.resolvedMethod = asExecutable(types.asMemberOf(componentType(), method));
+ this.parameterTypes = resolvedMethod.getParameterTypes();
+ this.parameters = method.getParameters();
+ this.returnType = resolvedMethod.getReturnType();
+ }
+
+ void validateMethod() {
+ validateNoTypeVariables();
+
+ // abstract methods are ones we have to implement, so they each need to be validated
+ // first, check the return type. if it's a subcomponent, validate that method as
+ // such.
+ Optional<AnnotationMirror> subcomponentAnnotation = subcomponentAnnotation();
+ if (subcomponentAnnotation.isPresent()) {
+ validateSubcomponentFactoryMethod(subcomponentAnnotation.get());
+ } else if (subcomponentCreatorAnnotation().isPresent()) {
+ validateSubcomponentCreatorMethod();
+ } else {
+ // if it's not a subcomponent...
+ switch (parameters.size()) {
+ case 0:
+ validateProvisionMethod();
+ break;
+ case 1:
+ validateMembersInjectionMethod();
+ break;
+ default:
+ reportInvalidMethod();
+ break;
+ }
+ }
+ }
+
+ private void validateNoTypeVariables() {
+ if (!resolvedMethod.getTypeVariables().isEmpty()) {
+ report.addError("Component methods cannot have type variables", method);
+ }
+ }
+
+ private Optional<AnnotationMirror> subcomponentAnnotation() {
+ return checkForAnnotations(
+ returnType,
+ componentKind().legalSubcomponentKinds().stream()
+ .map(ComponentKind::annotation)
+ .collect(toImmutableSet()));
+ }
+
+ private Optional<AnnotationMirror> subcomponentCreatorAnnotation() {
+ return checkForAnnotations(
+ returnType,
+ componentAnnotation().isProduction()
+ ? intersection(subcomponentCreatorAnnotations(), productionCreatorAnnotations())
+ : subcomponentCreatorAnnotations());
+ }
+
+ private void validateSubcomponentFactoryMethod(AnnotationMirror subcomponentAnnotation) {
+ referencedSubcomponents.put(MoreTypes.asElement(returnType), method);
+
+ ComponentKind subcomponentKind =
+ ComponentKind.forAnnotatedElement(MoreTypes.asTypeElement(returnType)).get();
+ ImmutableSet<TypeElement> moduleTypes =
+ ComponentAnnotation.componentAnnotation(subcomponentAnnotation).modules();
+
+ // TODO(gak): This logic maybe/probably shouldn't live here as it requires us to traverse
+ // subcomponents and their modules separately from how it is done in ComponentDescriptor and
+ // ModuleDescriptor
+ @SuppressWarnings("deprecation")
+ ImmutableSet<TypeElement> transitiveModules =
+ getTransitiveModules(types, elements, moduleTypes);
+
+ Set<TypeElement> variableTypes = Sets.newHashSet();
+
+ for (int i = 0; i < parameterTypes.size(); i++) {
+ VariableElement parameter = parameters.get(i);
+ TypeMirror parameterType = parameterTypes.get(i);
+ Optional<TypeElement> moduleType =
+ parameterType.accept(
+ new SimpleTypeVisitor8<Optional<TypeElement>, Void>() {
+ @Override
+ protected Optional<TypeElement> defaultAction(TypeMirror e, Void p) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional<TypeElement> visitDeclared(DeclaredType t, Void p) {
+ for (ModuleKind moduleKind : subcomponentKind.legalModuleKinds()) {
+ if (isAnnotationPresent(t.asElement(), moduleKind.annotation())) {
+ return Optional.of(MoreTypes.asTypeElement(t));
+ }
+ }
+ return Optional.empty();
+ }
+ },
+ null);
+ if (moduleType.isPresent()) {
+ if (variableTypes.contains(moduleType.get())) {
+ report.addError(
+ String.format(
+ "A module may only occur once an an argument in a Subcomponent factory "
+ + "method, but %s was already passed.",
+ moduleType.get().getQualifiedName()),
+ parameter);
+ }
+ if (!transitiveModules.contains(moduleType.get())) {
+ report.addError(
+ String.format(
+ "%s is present as an argument to the %s factory method, but is not one of the"
+ + " modules used to implement the subcomponent.",
+ moduleType.get().getQualifiedName(),
+ MoreTypes.asTypeElement(returnType).getQualifiedName()),
+ method);
+ }
+ variableTypes.add(moduleType.get());
+ } else {
+ report.addError(
+ String.format(
+ "Subcomponent factory methods may only accept modules, but %s is not.",
+ parameterType),
+ parameter);
+ }
+ }
+ }
+
+ private void validateSubcomponentCreatorMethod() {
+ referencedSubcomponents.put(MoreTypes.asElement(returnType).getEnclosingElement(), method);
+
+ if (!parameters.isEmpty()) {
+ report.addError(builderMethodRequiresNoArgs(), method);
+ }
+
+ TypeElement creatorElement = MoreTypes.asTypeElement(returnType);
+ // TODO(sameb): The creator validator right now assumes the element is being compiled
+ // in this pass, which isn't true here. We should change error messages to spit out
+ // this method as the subject and add the original subject to the message output.
+ report.addSubreport(creatorValidator.validate(creatorElement));
+ }
+
+ private void validateProvisionMethod() {
+ dependencyRequestValidator.validateDependencyRequest(report, method, returnType);
+ }
+
+ private void validateMembersInjectionMethod() {
+ TypeMirror parameterType = getOnlyElement(parameterTypes);
+ report.addSubreport(
+ membersInjectionValidator.validateMembersInjectionMethod(method, parameterType));
+ if (!(returnType.getKind().equals(VOID) || types.isSameType(returnType, parameterType))) {
+ report.addError(
+ "Members injection methods may only return the injected type or void.", method);
+ }
+ }
+
+ private void reportInvalidMethod() {
+ report.addError(
+ "This method isn't a valid provision method, members injection method or "
+ + "subcomponent factory method. Dagger cannot implement this method",
+ method);
+ }
+ }
+
+ private void validateNoConflictingEntryPoints() {
+ // Collect entry point methods that are not overridden by others. If the "same" method is
+ // inherited from more than one supertype, each will be in the multimap.
+ SetMultimap<String, ExecutableElement> entryPointMethods = HashMultimap.create();
+
+ methodsIn(elements.getAllMembers(component)).stream()
+ .filter(
+ method ->
+ isEntryPoint(method, asExecutable(types.asMemberOf(componentType(), method))))
+ .forEach(
+ method ->
+ addMethodUnlessOverridden(
+ method, entryPointMethods.get(method.getSimpleName().toString())));
+
+ for (Set<ExecutableElement> methods : asMap(entryPointMethods).values()) {
+ if (distinctKeys(methods).size() > 1) {
+ reportConflictingEntryPoints(methods);
+ }
+ }
+ }
+
+ private void reportConflictingEntryPoints(Collection<ExecutableElement> methods) {
+ verify(
+ methods.stream().map(ExecutableElement::getEnclosingElement).distinct().count()
+ == methods.size(),
+ "expected each method to be declared on a different type: %s",
+ methods);
+ StringBuilder message = new StringBuilder("conflicting entry point declarations:");
+ methodSignatureFormatter
+ .typedFormatter(componentType())
+ .formatIndentedList(
+ message,
+ ImmutableList.sortedCopyOf(
+ comparing(
+ method -> asType(method.getEnclosingElement()).getQualifiedName().toString()),
+ methods),
+ 1);
+ report.addError(message.toString());
+ }
+
+ private void validateSubcomponentReferences() {
+ Maps.filterValues(referencedSubcomponents.asMap(), methods -> methods.size() > 1)
+ .forEach(
+ (subcomponent, methods) ->
+ report.addError(
+ String.format(moreThanOneRefToSubcomponent(), subcomponent, methods),
+ component));
+ }
+
+ private void validateComponentDependencies() {
+ for (TypeMirror type : componentAnnotation().dependencyTypes()) {
+ type.accept(CHECK_DEPENDENCY_TYPES, report);
+ }
+ }
+
+ private void validateReferencedModules() {
+ report.addSubreport(
+ moduleValidator.validateReferencedModules(
+ component,
+ componentAnnotation().annotation(),
+ componentKind().legalModuleKinds(),
+ new HashSet<>()));
+ }
+
+ private void validateSubcomponents() {
+ // Make sure we validate any subcomponents we're referencing.
+ for (Element subcomponent : referencedSubcomponents.keySet()) {
+ ValidationReport<TypeElement> subreport = validate(asType(subcomponent));
+ report.addSubreport(subreport);
+ }
+ }
+
+ private ImmutableSet<Key> distinctKeys(Set<ExecutableElement> methods) {
+ return methods.stream()
+ .map(this::dependencyRequest)
+ .map(DependencyRequest::key)
+ .collect(toImmutableSet());
+ }
+
+ private DependencyRequest dependencyRequest(ExecutableElement method) {
+ ExecutableType methodType = asExecutable(types.asMemberOf(componentType(), method));
+ return ComponentKind.forAnnotatedElement(component).get().isProducer()
+ ? dependencyRequestFactory.forComponentProductionMethod(method, methodType)
+ : dependencyRequestFactory.forComponentProvisionMethod(method, methodType);
+ }
+ }
+
+ private static boolean isEntryPoint(ExecutableElement method, ExecutableType methodType) {
+ return method.getModifiers().contains(ABSTRACT)
+ && method.getParameters().isEmpty()
+ && !methodType.getReturnType().getKind().equals(VOID)
+ && methodType.getTypeVariables().isEmpty();
+ }
+
+ private void addMethodUnlessOverridden(ExecutableElement method, Set<ExecutableElement> methods) {
+ if (methods.stream().noneMatch(existingMethod -> overridesAsDeclared(existingMethod, method))) {
+ methods.removeIf(existingMethod -> overridesAsDeclared(method, existingMethod));
+ methods.add(method);
+ }
+ }
+
+ /**
+ * Returns {@code true} if {@code overrider} overrides {@code overridden} considered from within
+ * the type that declares {@code overrider}.
+ */
+ // TODO(dpb): Does this break for ECJ?
+ private boolean overridesAsDeclared(ExecutableElement overrider, ExecutableElement overridden) {
+ return elements.overrides(overrider, overridden, asType(overrider.getEnclosingElement()));
+ }
+
+ private static final TypeVisitor<Void, ValidationReport.Builder<?>> CHECK_DEPENDENCY_TYPES =
+ new SimpleTypeVisitor8<Void, ValidationReport.Builder<?>>() {
+ @Override
+ protected Void defaultAction(TypeMirror type, ValidationReport.Builder<?> report) {
+ report.addError(type + " is not a valid component dependency type");
+ return null;
+ }
+
+ @Override
+ public Void visitDeclared(DeclaredType type, ValidationReport.Builder<?> report) {
+ if (moduleAnnotation(MoreTypes.asTypeElement(type)).isPresent()) {
+ report.addError(type + " is a module, which cannot be a component dependency");
+ }
+ return null;
+ }
+ };
+
+ private static Optional<AnnotationMirror> checkForAnnotations(
+ TypeMirror type, final Set<? extends Class<? extends Annotation>> annotations) {
+ return type.accept(
+ new SimpleTypeVisitor8<Optional<AnnotationMirror>, Void>(Optional.empty()) {
+ @Override
+ public Optional<AnnotationMirror> visitDeclared(DeclaredType t, Void p) {
+ return getAnyAnnotation(t.asElement(), annotations);
+ }
+ },
+ null);
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/CompositeBindingGraphPlugin.java b/java/dagger/internal/codegen/validation/CompositeBindingGraphPlugin.java
new file mode 100644
index 000000000..a9c650a1b
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/CompositeBindingGraphPlugin.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Lists.asList;
+import static dagger.internal.codegen.base.ElementFormatter.elementToString;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.langmodel.DaggerElements.elementEncloses;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.errorprone.annotations.FormatMethod;
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.ChildFactoryMethodEdge;
+import dagger.model.BindingGraph.ComponentNode;
+import dagger.model.BindingGraph.DependencyEdge;
+import dagger.model.BindingGraph.MaybeBinding;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import javax.annotation.processing.Filer;
+import javax.inject.Inject;
+import javax.lang.model.util.Elements; // ALLOW_TYPES_ELEMENTS because of interface dependencies
+import javax.lang.model.util.Types; // ALLOW_TYPES_ELEMENTS because of interface dependencies
+import javax.tools.Diagnostic;
+
+/**
+ * Combines many {@link BindingGraphPlugin} implementations. This helps reduce spam by combining
+ * all of the messages that are reported on the root component.
+ */
+public final class CompositeBindingGraphPlugin implements BindingGraphPlugin {
+
+ private final ImmutableSet<BindingGraphPlugin> plugins;
+ private final String pluginName;
+ private final DiagnosticMessageGenerator.Factory messageGeneratorFactory;
+
+ /** Factory class for {@link CompositeBindingGraphPlugin}. */
+ public static final class Factory {
+ private final DiagnosticMessageGenerator.Factory messageGeneratorFactory;
+
+ @Inject Factory(DiagnosticMessageGenerator.Factory messageGeneratorFactory) {
+ this.messageGeneratorFactory = messageGeneratorFactory;
+ }
+
+ public CompositeBindingGraphPlugin create(
+ ImmutableSet<BindingGraphPlugin> plugins, String pluginName) {
+ return new CompositeBindingGraphPlugin(plugins, pluginName, messageGeneratorFactory);
+ }
+ }
+
+ private CompositeBindingGraphPlugin(
+ ImmutableSet<BindingGraphPlugin> plugins,
+ String pluginName,
+ DiagnosticMessageGenerator.Factory messageGeneratorFactory) {
+ this.plugins = plugins;
+ this.pluginName = pluginName;
+ this.messageGeneratorFactory = messageGeneratorFactory;
+ }
+
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ AggregatingDiagnosticReporter aggregatingDiagnosticReporter = new AggregatingDiagnosticReporter(
+ bindingGraph, diagnosticReporter, messageGeneratorFactory.create(bindingGraph));
+ plugins.forEach(plugin -> {
+ aggregatingDiagnosticReporter.setCurrentPlugin(plugin.pluginName());
+ plugin.visitGraph(bindingGraph, aggregatingDiagnosticReporter);
+ });
+ aggregatingDiagnosticReporter.report();
+ }
+
+ @Override
+ public void initFiler(Filer filer) {
+ plugins.forEach(plugin -> plugin.initFiler(filer));
+ }
+
+ @Override
+ public void initTypes(Types types) {
+ plugins.forEach(plugin -> plugin.initTypes(types));
+ }
+
+ @Override
+ public void initElements(Elements elements) {
+ plugins.forEach(plugin -> plugin.initElements(elements));
+ }
+
+ @Override
+ public void initOptions(Map<String, String> options) {
+ plugins.forEach(plugin -> plugin.initOptions(options));
+ }
+
+ @Override
+ public Set<String> supportedOptions() {
+ return plugins.stream().flatMap(
+ plugin -> plugin.supportedOptions().stream()).collect(toImmutableSet());
+ }
+
+ @Override
+ public String pluginName() {
+ return pluginName;
+ }
+
+ // TODO(erichang): This kind of breaks some of the encapsulation by relying on or repeating
+ // logic within DiagnosticReporterImpl. Hopefully if the experiment for aggregated messages
+ // goes well though this can be merged with that implementation.
+ private static final class AggregatingDiagnosticReporter implements DiagnosticReporter {
+ private final DiagnosticReporter delegate;
+ private final BindingGraph graph;
+ // Initialize with a new line so the first message appears below the reported component
+ private final StringBuilder messageBuilder = new StringBuilder("\n");
+ private final DiagnosticMessageGenerator messageGenerator;
+ private Optional<Diagnostic.Kind> mergedDiagnosticKind = Optional.empty();
+ private String currentPluginName = null;
+
+ AggregatingDiagnosticReporter(
+ BindingGraph graph,
+ DiagnosticReporter delegate,
+ DiagnosticMessageGenerator messageGenerator) {
+ this.graph = graph;
+ this.delegate = delegate;
+ this.messageGenerator = messageGenerator;
+ }
+
+ /** Sets the currently running aggregated plugin. Used to add a diagnostic prefix. */
+ void setCurrentPlugin(String pluginName) {
+ currentPluginName = pluginName;
+ }
+
+ /** Reports all of the stored diagnostics. */
+ void report() {
+ if (mergedDiagnosticKind.isPresent()) {
+ delegate.reportComponent(
+ mergedDiagnosticKind.get(),
+ graph.rootComponentNode(),
+ PackageNameCompressor.compressPackagesInMessage(messageBuilder.toString()));
+ }
+ }
+
+ @Override
+ public void reportComponent(Diagnostic.Kind diagnosticKind, ComponentNode componentNode,
+ String message) {
+ addMessage(diagnosticKind, message);
+ messageGenerator.appendComponentPathUnlessAtRoot(messageBuilder, componentNode);
+ }
+
+ @Override
+ @FormatMethod
+ public void reportComponent(
+ Diagnostic.Kind diagnosticKind,
+ ComponentNode componentNode,
+ String messageFormat,
+ Object firstArg,
+ Object... moreArgs) {
+ reportComponent(
+ diagnosticKind, componentNode, formatMessage(messageFormat, firstArg, moreArgs));
+ }
+
+ @Override
+ public void reportBinding(Diagnostic.Kind diagnosticKind, MaybeBinding binding,
+ String message) {
+ addMessage(diagnosticKind,
+ String.format("%s%s", message, messageGenerator.getMessage(binding)));
+ }
+
+ @Override
+ @FormatMethod
+ public void reportBinding(
+ Diagnostic.Kind diagnosticKind,
+ MaybeBinding binding,
+ String messageFormat,
+ Object firstArg,
+ Object... moreArgs) {
+ reportBinding(diagnosticKind, binding, formatMessage(messageFormat, firstArg, moreArgs));
+ }
+
+ @Override
+ public void reportDependency(
+ Diagnostic.Kind diagnosticKind, DependencyEdge dependencyEdge, String message) {
+ addMessage(diagnosticKind,
+ String.format("%s%s", message, messageGenerator.getMessage(dependencyEdge)));
+ }
+
+ @Override
+ @FormatMethod
+ public void reportDependency(
+ Diagnostic.Kind diagnosticKind,
+ DependencyEdge dependencyEdge,
+ String messageFormat,
+ Object firstArg,
+ Object... moreArgs) {
+ reportDependency(
+ diagnosticKind, dependencyEdge, formatMessage(messageFormat, firstArg, moreArgs));
+ }
+
+ @Override
+ public void reportSubcomponentFactoryMethod(
+ Diagnostic.Kind diagnosticKind,
+ ChildFactoryMethodEdge childFactoryMethodEdge,
+ String message) {
+ // TODO(erichang): This repeats some of the logic in DiagnosticReporterImpl. Remove when
+ // merged.
+ if (elementEncloses(
+ graph.rootComponentNode().componentPath().currentComponent(),
+ childFactoryMethodEdge.factoryMethod())) {
+ // Let this pass through since it is not an error reported on the root component
+ delegate.reportSubcomponentFactoryMethod(diagnosticKind, childFactoryMethodEdge, message);
+ } else {
+ addMessage(
+ diagnosticKind,
+ String.format(
+ "[%s] %s", elementToString(childFactoryMethodEdge.factoryMethod()), message));
+ }
+ }
+
+ @Override
+ @FormatMethod
+ public void reportSubcomponentFactoryMethod(
+ Diagnostic.Kind diagnosticKind,
+ ChildFactoryMethodEdge childFactoryMethodEdge,
+ String messageFormat,
+ Object firstArg,
+ Object... moreArgs) {
+ reportSubcomponentFactoryMethod(
+ diagnosticKind, childFactoryMethodEdge, formatMessage(messageFormat, firstArg, moreArgs));
+ }
+
+ /** Adds a message to the stored aggregated message. */
+ private void addMessage(Diagnostic.Kind diagnosticKind, String message) {
+ checkNotNull(diagnosticKind);
+ checkNotNull(message);
+ checkState(currentPluginName != null);
+
+ // Add a separator if this isn't the first message
+ if (mergedDiagnosticKind.isPresent()) {
+ messageBuilder.append("\n\n");
+ }
+
+ mergeDiagnosticKind(diagnosticKind);
+ // Adds brackets as well as special color strings to make the string red and bold.
+ messageBuilder.append(String.format("\033[1;31m[%s]\033[0m ", currentPluginName));
+ messageBuilder.append(message);
+ }
+
+ private static String formatMessage(String messageFormat, Object firstArg, Object[] moreArgs) {
+ return String.format(messageFormat, asList(firstArg, moreArgs).toArray());
+ }
+
+ private void mergeDiagnosticKind(Diagnostic.Kind diagnosticKind) {
+ checkArgument(diagnosticKind != Diagnostic.Kind.MANDATORY_WARNING,
+ "Dagger plugins should not be issuing mandatory warnings");
+ if (!mergedDiagnosticKind.isPresent()) {
+ mergedDiagnosticKind = Optional.of(diagnosticKind);
+ return;
+ }
+ Diagnostic.Kind current = mergedDiagnosticKind.get();
+ if (current == Diagnostic.Kind.ERROR || diagnosticKind == Diagnostic.Kind.ERROR) {
+ mergedDiagnosticKind = Optional.of(Diagnostic.Kind.ERROR);
+ } else if (current == Diagnostic.Kind.WARNING || diagnosticKind == Diagnostic.Kind.WARNING) {
+ mergedDiagnosticKind = Optional.of(Diagnostic.Kind.WARNING);
+ } else if (current == Diagnostic.Kind.NOTE || diagnosticKind == Diagnostic.Kind.NOTE) {
+ mergedDiagnosticKind = Optional.of(Diagnostic.Kind.NOTE);
+ } else {
+ mergedDiagnosticKind = Optional.of(Diagnostic.Kind.OTHER);
+ }
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/DependencyRequestValidator.java b/java/dagger/internal/codegen/validation/DependencyRequestValidator.java
new file mode 100644
index 000000000..68c960478
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/DependencyRequestValidator.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.google.auto.common.MoreElements.asType;
+import static com.google.auto.common.MoreElements.asVariable;
+import static com.google.auto.common.MoreTypes.asTypeElement;
+import static dagger.internal.codegen.base.RequestKinds.extractKeyType;
+import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.isAssistedFactoryType;
+import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.isAssistedInjectionType;
+import static dagger.internal.codegen.binding.SourceFiles.membersInjectorNameForType;
+import static javax.lang.model.element.Modifier.STATIC;
+import static javax.lang.model.type.TypeKind.WILDCARD;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableCollection;
+import dagger.MembersInjector;
+import dagger.assisted.Assisted;
+import dagger.internal.codegen.base.FrameworkTypes;
+import dagger.internal.codegen.base.RequestKinds;
+import dagger.internal.codegen.binding.InjectionAnnotations;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.model.RequestKind;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+/** Validation for dependency requests. */
+final class DependencyRequestValidator {
+ private final MembersInjectionValidator membersInjectionValidator;
+ private final InjectionAnnotations injectionAnnotations;
+ private final KotlinMetadataUtil metadataUtil;
+ private final DaggerElements elements;
+
+ @Inject
+ DependencyRequestValidator(
+ MembersInjectionValidator membersInjectionValidator,
+ InjectionAnnotations injectionAnnotations,
+ KotlinMetadataUtil metadataUtil,
+ DaggerElements elements) {
+ this.membersInjectionValidator = membersInjectionValidator;
+ this.injectionAnnotations = injectionAnnotations;
+ this.metadataUtil = metadataUtil;
+ this.elements = elements;
+ }
+
+ /**
+ * Adds an error if the given dependency request has more than one qualifier annotation or is a
+ * non-instance request with a wildcard type.
+ */
+ void validateDependencyRequest(
+ ValidationReport.Builder<?> report, Element requestElement, TypeMirror requestType) {
+ if (MoreElements.isAnnotationPresent(requestElement, Assisted.class)) {
+ // Don't validate assisted parameters. These are not dependency requests.
+ return;
+ }
+ checkQualifiers(report, requestElement);
+ checkType(report, requestElement, requestType);
+ }
+
+ private void checkQualifiers(ValidationReport.Builder<?> report, Element requestElement) {
+ if (requestElement.getKind() == ElementKind.FIELD
+ // static injected fields are not supported, no need to get qualifier from kotlin metadata
+ && !requestElement.getModifiers().contains(STATIC)
+ && metadataUtil.hasMetadata(requestElement)
+ && metadataUtil.isMissingSyntheticPropertyForAnnotations(asVariable(requestElement))) {
+ Optional<TypeElement> membersInjector =
+ Optional.ofNullable(
+ elements.getTypeElement(
+ membersInjectorNameForType(asType(requestElement.getEnclosingElement()))));
+ if (!membersInjector.isPresent()) {
+ report.addError(
+ "Unable to read annotations on an injected Kotlin property. The Dagger compiler must"
+ + " also be applied to any project containing @Inject properties.",
+ requestElement);
+ return; // finish checking qualifiers since current information is unreliable.
+ }
+ }
+
+ ImmutableCollection<? extends AnnotationMirror> qualifiers =
+ injectionAnnotations.getQualifiers(requestElement);
+ if (qualifiers.size() > 1) {
+ for (AnnotationMirror qualifier : qualifiers) {
+ report.addError(
+ "A single dependency request may not use more than one @Qualifier",
+ requestElement,
+ qualifier);
+ }
+ }
+ }
+
+ private void checkType(
+ ValidationReport.Builder<?> report, Element requestElement, TypeMirror requestType) {
+ TypeMirror keyType = extractKeyType(requestType);
+ RequestKind requestKind = RequestKinds.getRequestKind(requestType);
+ if (keyType.getKind() == TypeKind.DECLARED) {
+ TypeElement typeElement = asTypeElement(keyType);
+ if (isAssistedInjectionType(typeElement)) {
+ report.addError(
+ "Dagger does not support injecting @AssistedInject type, "
+ + requestType
+ + ". Did you mean to inject its assisted factory type instead?",
+ requestElement);
+ }
+ if (requestKind != RequestKind.INSTANCE && isAssistedFactoryType(typeElement)) {
+ report.addError(
+ "Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, "
+ + "or Produced<T> when T is an @AssistedFactory-annotated type such as "
+ + keyType,
+ requestElement);
+ }
+ }
+ if (keyType.getKind().equals(WILDCARD)) {
+ // TODO(ronshapiro): Explore creating this message using RequestKinds.
+ report.addError(
+ "Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, "
+ + "or Produced<T> when T is a wildcard type such as "
+ + keyType,
+ requestElement);
+ }
+ if (MoreTypes.isType(keyType) && MoreTypes.isTypeOf(MembersInjector.class, keyType)) {
+ DeclaredType membersInjectorType = MoreTypes.asDeclared(keyType);
+ if (membersInjectorType.getTypeArguments().isEmpty()) {
+ report.addError("Cannot inject a raw MembersInjector", requestElement);
+ } else {
+ report.addSubreport(
+ membersInjectionValidator.validateMembersInjectionRequest(
+ requestElement, membersInjectorType.getTypeArguments().get(0)));
+ }
+ }
+ }
+
+ /**
+ * Adds an error if the given dependency request is for a {@link dagger.producers.Producer} or
+ * {@link dagger.producers.Produced}.
+ *
+ * <p>Only call this when processing a provision binding.
+ */
+ // TODO(dpb): Should we disallow Producer entry points in non-production components?
+ void checkNotProducer(ValidationReport.Builder<?> report, VariableElement requestElement) {
+ TypeMirror requestType = requestElement.asType();
+ if (FrameworkTypes.isProducerType(requestType)) {
+ report.addError(
+ String.format(
+ "%s may only be injected in @Produces methods",
+ MoreTypes.asTypeElement(requestType).getSimpleName()),
+ requestElement);
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/DiagnosticMessageGenerator.java b/java/dagger/internal/codegen/validation/DiagnosticMessageGenerator.java
new file mode 100644
index 000000000..7d7b9e3a9
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/DiagnosticMessageGenerator.java
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.google.common.base.Predicates.equalTo;
+import static com.google.common.base.Verify.verify;
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.getLast;
+import static com.google.common.collect.Iterables.indexOf;
+import static com.google.common.collect.Iterables.transform;
+import static dagger.internal.codegen.base.ElementFormatter.elementToString;
+import static dagger.internal.codegen.extension.DaggerGraphs.shortestPath;
+import static dagger.internal.codegen.extension.DaggerStreams.instancesOf;
+import static dagger.internal.codegen.extension.DaggerStreams.presentValues;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.langmodel.DaggerElements.DECLARATION_ORDER;
+import static dagger.internal.codegen.langmodel.DaggerElements.closestEnclosingTypeElement;
+import static java.util.Collections.min;
+import static java.util.Comparator.comparing;
+import static java.util.Comparator.comparingInt;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Table;
+import dagger.internal.codegen.base.ElementFormatter;
+import dagger.internal.codegen.base.Formatter;
+import dagger.internal.codegen.binding.DependencyRequestFormatter;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.DependencyEdge;
+import dagger.model.BindingGraph.Edge;
+import dagger.model.BindingGraph.MaybeBinding;
+import dagger.model.BindingGraph.Node;
+import dagger.model.ComponentPath;
+import java.util.Comparator;
+import java.util.Set;
+import java.util.function.Function;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+/** Helper class for generating diagnostic messages. */
+public final class DiagnosticMessageGenerator {
+
+ /** Injectable factory for {@code DiagnosticMessageGenerator}. */
+ public static final class Factory {
+ private final DaggerTypes types;
+ private final DependencyRequestFormatter dependencyRequestFormatter;
+ private final ElementFormatter elementFormatter;
+
+ @Inject
+ Factory(
+ DaggerTypes types,
+ DependencyRequestFormatter dependencyRequestFormatter,
+ ElementFormatter elementFormatter) {
+ this.types = types;
+ this.dependencyRequestFormatter = dependencyRequestFormatter;
+ this.elementFormatter = elementFormatter;
+ }
+
+ /** Creates a {@code DiagnosticMessageGenerator} for the given binding graph. */
+ public DiagnosticMessageGenerator create(BindingGraph graph) {
+ return new DiagnosticMessageGenerator(
+ graph, types, dependencyRequestFormatter, elementFormatter);
+ }
+ }
+
+ private final BindingGraph graph;
+ private final DependencyRequestFormatter dependencyRequestFormatter;
+ private final ElementFormatter elementFormatter;
+
+ /** A cached function from type to all of its supertypes in breadth-first order. */
+ private final Function<TypeElement, Iterable<TypeElement>> supertypes;
+
+ /** The shortest path (value) from an entry point (column) to a binding (row). */
+ private final Table<MaybeBinding, DependencyEdge, ImmutableList<Node>> shortestPaths =
+ HashBasedTable.create();
+
+ private static <K, V> Function<K, V> memoize(Function<K, V> uncached) {
+ // If Android Guava is on the processor path, then c.g.c.b.Function (which LoadingCache
+ // implements) does not extend j.u.f.Function.
+ // TODO(erichang): Fix current breakages and try to remove this to enforce not having this on
+ // processor path.
+
+ // First, explicitly convert uncached to c.g.c.b.Function because CacheLoader.from() expects
+ // one.
+ com.google.common.base.Function<K, V> uncachedAsBaseFunction = uncached::apply;
+
+ LoadingCache<K, V> cache =
+ CacheBuilder.newBuilder().build(CacheLoader.from(uncachedAsBaseFunction));
+
+ // Second, explicitly convert LoadingCache to j.u.f.Function.
+ @SuppressWarnings("deprecation") // uncachedAsBaseFunction throws only unchecked exceptions
+ Function<K, V> memoized = cache::apply;
+
+ return memoized;
+ }
+
+ private DiagnosticMessageGenerator(
+ BindingGraph graph,
+ DaggerTypes types,
+ DependencyRequestFormatter dependencyRequestFormatter,
+ ElementFormatter elementFormatter) {
+ this.graph = graph;
+ this.dependencyRequestFormatter = dependencyRequestFormatter;
+ this.elementFormatter = elementFormatter;
+ supertypes =
+ memoize(
+ component -> transform(types.supertypes(component.asType()), MoreTypes::asTypeElement));
+ }
+
+ public String getMessage(MaybeBinding binding) {
+ ImmutableSet<DependencyEdge> entryPoints = graph.entryPointEdgesDependingOnBinding(binding);
+ ImmutableSet<DependencyEdge> requests = requests(binding);
+ ImmutableList<DependencyEdge> dependencyTrace = dependencyTrace(binding, entryPoints);
+
+ return getMessageInternal(dependencyTrace, requests, entryPoints);
+ }
+
+ public String getMessage(DependencyEdge dependencyEdge) {
+ ImmutableSet<DependencyEdge> requests = ImmutableSet.of(dependencyEdge);
+
+ ImmutableSet<DependencyEdge> entryPoints;
+ ImmutableList<DependencyEdge> dependencyTrace;
+ if (dependencyEdge.isEntryPoint()) {
+ entryPoints = ImmutableSet.of(dependencyEdge);
+ dependencyTrace = ImmutableList.of(dependencyEdge);
+ } else {
+ // It's not an entry point, so it's part of a binding
+ dagger.model.Binding binding = (dagger.model.Binding) source(dependencyEdge);
+ entryPoints = graph.entryPointEdgesDependingOnBinding(binding);
+ dependencyTrace =
+ ImmutableList.<DependencyEdge>builder()
+ .add(dependencyEdge)
+ .addAll(dependencyTrace(binding, entryPoints))
+ .build();
+ }
+
+ return getMessageInternal(dependencyTrace, requests, entryPoints);
+ }
+
+ private String getMessageInternal(
+ ImmutableList<DependencyEdge> dependencyTrace,
+ ImmutableSet<DependencyEdge> requests,
+ ImmutableSet<DependencyEdge> entryPoints) {
+ StringBuilder message =
+ graph.isFullBindingGraph()
+ ? new StringBuilder()
+ : new StringBuilder(dependencyTrace.size() * 100 /* a guess heuristic */);
+
+ // Print the dependency trace unless it's a full binding graph
+ if (!graph.isFullBindingGraph()) {
+ dependencyTrace.forEach(
+ edge -> dependencyRequestFormatter.appendFormatLine(message, edge.dependencyRequest()));
+ if (!dependencyTrace.isEmpty()) {
+ appendComponentPathUnlessAtRoot(message, source(getLast(dependencyTrace)));
+ }
+ }
+
+ // Print any dependency requests that aren't shown as part of the dependency trace.
+ ImmutableSet<Element> requestsToPrint =
+ requests.stream()
+ // if printing entry points, skip entry points and the traced request
+ .filter(
+ request ->
+ graph.isFullBindingGraph()
+ || (!request.isEntryPoint() && !isTracedRequest(dependencyTrace, request)))
+ .map(request -> request.dependencyRequest().requestElement())
+ .flatMap(presentValues())
+ .collect(toImmutableSet());
+ if (!requestsToPrint.isEmpty()) {
+ message
+ .append("\nIt is")
+ .append(graph.isFullBindingGraph() ? " " : " also ")
+ .append("requested at:");
+ elementFormatter.formatIndentedList(message, requestsToPrint, 1);
+ }
+
+ // Print the remaining entry points, showing which component they're in, unless it's a full
+ // binding graph
+ if (!graph.isFullBindingGraph() && entryPoints.size() > 1) {
+ message.append("\nThe following other entry points also depend on it:");
+ entryPointFormatter.formatIndentedList(
+ message,
+ entryPoints.stream()
+ .filter(entryPoint -> !entryPoint.equals(getLast(dependencyTrace)))
+ .sorted(
+ // 1. List entry points in components closest to the root first.
+ // 2. List entry points declared in a component before those in a supertype.
+ // 3. List entry points in declaration order in their declaring type.
+ rootComponentFirst()
+ .thenComparing(nearestComponentSupertypeFirst())
+ .thenComparing(requestElementDeclarationOrder()))
+ .collect(toImmutableList()),
+ 1);
+ }
+ return message.toString();
+ }
+
+ public void appendComponentPathUnlessAtRoot(StringBuilder message, Node node) {
+ if (!node.componentPath().equals(graph.rootComponentNode().componentPath())) {
+ message.append(String.format(" [%s]", node.componentPath()));
+ }
+ }
+
+ private final Formatter<DependencyEdge> entryPointFormatter =
+ new Formatter<DependencyEdge>() {
+ @Override
+ public String format(DependencyEdge object) {
+ Element requestElement = object.dependencyRequest().requestElement().get();
+ StringBuilder element = new StringBuilder(elementToString(requestElement));
+
+ // For entry points declared in subcomponents or supertypes of the root component,
+ // append the component path to make clear to the user which component it's in.
+ ComponentPath componentPath = source(object).componentPath();
+ if (!componentPath.atRoot()
+ || !requestElement.getEnclosingElement().equals(componentPath.rootComponent())) {
+ element.append(String.format(" [%s]", componentPath));
+ }
+ return element.toString();
+ }
+ };
+
+ private static boolean isTracedRequest(
+ ImmutableList<DependencyEdge> dependencyTrace, DependencyEdge request) {
+ return !dependencyTrace.isEmpty() && request.equals(dependencyTrace.get(0));
+ }
+
+ /**
+ * Returns the dependency trace from one of the {@code entryPoints} to {@code binding} to {@code
+ * message} as a list <i>ending with</i> the entry point.
+ */
+ // TODO(ronshapiro): Adding a DependencyPath type to dagger.model could be useful, i.e.
+ // bindingGraph.shortestPathFromEntryPoint(DependencyEdge, MaybeBindingNode)
+ ImmutableList<DependencyEdge> dependencyTrace(
+ MaybeBinding binding, ImmutableSet<DependencyEdge> entryPoints) {
+ // Module binding graphs may have bindings unreachable from any entry points. If there are
+ // no entry points for this DiagnosticInfo, don't try to print a dependency trace.
+ if (entryPoints.isEmpty()) {
+ return ImmutableList.of();
+ }
+ // Show the full dependency trace for one entry point.
+ DependencyEdge entryPointForTrace =
+ min(
+ entryPoints,
+ // prefer entry points in components closest to the root
+ rootComponentFirst()
+ // then prefer entry points with a short dependency path to the error
+ .thenComparing(shortestDependencyPathFirst(binding))
+ // then prefer entry points declared in the component to those declared in a
+ // supertype
+ .thenComparing(nearestComponentSupertypeFirst())
+ // finally prefer entry points declared first in their enclosing type
+ .thenComparing(requestElementDeclarationOrder()));
+
+ ImmutableList<Node> shortestBindingPath =
+ shortestPathFromEntryPoint(entryPointForTrace, binding);
+ verify(
+ !shortestBindingPath.isEmpty(),
+ "no dependency path from %s to %s in %s",
+ entryPointForTrace,
+ binding,
+ graph);
+
+ ImmutableList.Builder<DependencyEdge> dependencyTrace = ImmutableList.builder();
+ dependencyTrace.add(entryPointForTrace);
+ for (int i = 0; i < shortestBindingPath.size() - 1; i++) {
+ Set<Edge> dependenciesBetween =
+ graph
+ .network()
+ .edgesConnecting(shortestBindingPath.get(i), shortestBindingPath.get(i + 1));
+ // If a binding requests a key more than once, any of them should be fine to get to the
+ // shortest path
+ dependencyTrace.add((DependencyEdge) Iterables.get(dependenciesBetween, 0));
+ }
+ return dependencyTrace.build().reverse();
+ }
+
+ /** Returns all the nonsynthetic dependency requests for a binding. */
+ ImmutableSet<DependencyEdge> requests(MaybeBinding binding) {
+ return graph.network().inEdges(binding).stream()
+ .flatMap(instancesOf(DependencyEdge.class))
+ .filter(edge -> edge.dependencyRequest().requestElement().isPresent())
+ .sorted(requestEnclosingTypeName().thenComparing(requestElementDeclarationOrder()))
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * Returns a comparator that sorts entry points in components whose paths from the root are
+ * shorter first.
+ */
+ Comparator<DependencyEdge> rootComponentFirst() {
+ return comparingInt(entryPoint -> source(entryPoint).componentPath().components().size());
+ }
+
+ /**
+ * Returns a comparator that puts entry points whose shortest dependency path to {@code binding}
+ * is shortest first.
+ */
+ Comparator<DependencyEdge> shortestDependencyPathFirst(MaybeBinding binding) {
+ return comparing(entryPoint -> shortestPathFromEntryPoint(entryPoint, binding).size());
+ }
+
+ ImmutableList<Node> shortestPathFromEntryPoint(DependencyEdge entryPoint, MaybeBinding binding) {
+ return shortestPaths
+ .row(binding)
+ .computeIfAbsent(
+ entryPoint,
+ ep ->
+ shortestPath(
+ node ->
+ filter(graph.network().successors(node), MaybeBinding.class::isInstance),
+ graph.network().incidentNodes(ep).target(),
+ binding));
+ }
+
+ /**
+ * Returns a comparator that sorts entry points in by the distance of the type that declares them
+ * from the type of the component that contains them.
+ *
+ * <p>For instance, an entry point declared directly in the component type would sort before one
+ * declared in a direct supertype, which would sort before one declared in a supertype of a
+ * supertype.
+ */
+ Comparator<DependencyEdge> nearestComponentSupertypeFirst() {
+ return comparingInt(
+ entryPoint ->
+ indexOf(
+ supertypes.apply(componentContainingEntryPoint(entryPoint)),
+ equalTo(typeDeclaringEntryPoint(entryPoint))));
+ }
+
+ TypeElement componentContainingEntryPoint(DependencyEdge entryPoint) {
+ return source(entryPoint).componentPath().currentComponent();
+ }
+
+ TypeElement typeDeclaringEntryPoint(DependencyEdge entryPoint) {
+ return MoreElements.asType(
+ entryPoint.dependencyRequest().requestElement().get().getEnclosingElement());
+ }
+
+ /**
+ * Returns a comparator that sorts dependency edges lexicographically by the qualified name of the
+ * type that contains them. Only appropriate for edges with request elements.
+ */
+ Comparator<DependencyEdge> requestEnclosingTypeName() {
+ return comparing(
+ edge ->
+ closestEnclosingTypeElement(edge.dependencyRequest().requestElement().get())
+ .getQualifiedName()
+ .toString());
+ }
+
+ /**
+ * Returns a comparator that sorts edges in the order in which their request elements were
+ * declared in their declaring type.
+ *
+ * <p>Only useful to compare edges whose request elements were declared in the same type.
+ */
+ Comparator<DependencyEdge> requestElementDeclarationOrder() {
+ return comparing(edge -> edge.dependencyRequest().requestElement().get(), DECLARATION_ORDER);
+ }
+
+ private Node source(Edge edge) {
+ return graph.network().incidentNodes(edge).source();
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/DiagnosticReporterFactory.java b/java/dagger/internal/codegen/validation/DiagnosticReporterFactory.java
new file mode 100644
index 000000000..35ae7c728
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/DiagnosticReporterFactory.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.google.common.collect.Lists.asList;
+import static dagger.internal.codegen.base.ElementFormatter.elementToString;
+import static dagger.internal.codegen.langmodel.DaggerElements.elementEncloses;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.errorprone.annotations.FormatMethod;
+import dagger.model.BindingGraph;
+import dagger.model.BindingGraph.ChildFactoryMethodEdge;
+import dagger.model.BindingGraph.ComponentNode;
+import dagger.model.BindingGraph.DependencyEdge;
+import dagger.model.BindingGraph.MaybeBinding;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import javax.annotation.processing.Messager;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic;
+import org.checkerframework.checker.nullness.compatqual.NullableDecl;
+
+/** A factory for {@link DiagnosticReporter}s. */
+// TODO(ronshapiro): If multiple plugins print errors on the same node/edge, should we condense the
+// messages and only print the dependency trace once?
+final class DiagnosticReporterFactory {
+ private final Messager messager;
+ private final DiagnosticMessageGenerator.Factory diagnosticMessageGeneratorFactory;
+
+ @Inject
+ DiagnosticReporterFactory(
+ Messager messager, DiagnosticMessageGenerator.Factory diagnosticMessageGeneratorFactory) {
+ this.messager = messager;
+ this.diagnosticMessageGeneratorFactory = diagnosticMessageGeneratorFactory;
+ }
+
+ /** Creates a reporter for a binding graph and a plugin. */
+ DiagnosticReporterImpl reporter(
+ BindingGraph graph, BindingGraphPlugin plugin, boolean reportErrorsAsWarnings) {
+ return new DiagnosticReporterImpl(graph, plugin.pluginName(), reportErrorsAsWarnings);
+ }
+
+ /**
+ * A {@link DiagnosticReporter} that keeps track of which {@linkplain Diagnostic.Kind kinds} of
+ * diagnostics were reported.
+ */
+ final class DiagnosticReporterImpl implements DiagnosticReporter {
+ private final String plugin;
+ private final TypeElement rootComponent;
+ private final boolean reportErrorsAsWarnings;
+ private final ImmutableSet.Builder<Diagnostic.Kind> reportedDiagnosticKinds =
+ ImmutableSet.builder();
+ private final DiagnosticMessageGenerator diagnosticMessageGenerator;
+
+ DiagnosticReporterImpl(BindingGraph graph, String plugin, boolean reportErrorsAsWarnings) {
+ this.plugin = plugin;
+ this.reportErrorsAsWarnings = reportErrorsAsWarnings;
+ this.rootComponent = graph.rootComponentNode().componentPath().currentComponent();
+ this.diagnosticMessageGenerator = diagnosticMessageGeneratorFactory.create(graph);
+ }
+
+ /** Returns which {@linkplain Diagnostic.Kind kinds} of diagnostics were reported. */
+ ImmutableSet<Diagnostic.Kind> reportedDiagnosticKinds() {
+ return reportedDiagnosticKinds.build();
+ }
+
+ @Override
+ public void reportComponent(
+ Diagnostic.Kind diagnosticKind, ComponentNode componentNode, String messageFormat) {
+ StringBuilder message = new StringBuilder(messageFormat);
+ diagnosticMessageGenerator.appendComponentPathUnlessAtRoot(message, componentNode);
+ // TODO(dpb): Report at the component node component.
+ printMessage(diagnosticKind, message, rootComponent);
+ }
+
+ @Override
+ @FormatMethod
+ public void reportComponent(
+ Diagnostic.Kind diagnosticKind,
+ ComponentNode componentNode,
+ String messageFormat,
+ Object firstArg,
+ Object... moreArgs) {
+ reportComponent(
+ diagnosticKind, componentNode, formatMessage(messageFormat, firstArg, moreArgs));
+ }
+
+ // TODO(ronshapiro): should this also include the binding element?
+ @Override
+ public void reportBinding(
+ Diagnostic.Kind diagnosticKind, MaybeBinding binding, String message) {
+ printMessage(
+ diagnosticKind, message + diagnosticMessageGenerator.getMessage(binding), rootComponent);
+ }
+
+ @Override
+ public void reportBinding(
+ Diagnostic.Kind diagnosticKind,
+ MaybeBinding binding,
+ String messageFormat,
+ Object firstArg,
+ Object... moreArgs) {
+ reportBinding(diagnosticKind, binding, formatMessage(messageFormat, firstArg, moreArgs));
+ }
+
+ @Override
+ public void reportDependency(
+ Diagnostic.Kind diagnosticKind, DependencyEdge dependencyEdge, String message) {
+ printMessage(
+ diagnosticKind,
+ message + diagnosticMessageGenerator.getMessage(dependencyEdge),
+ rootComponent);
+ }
+
+ @Override
+ public void reportDependency(
+ Diagnostic.Kind diagnosticKind,
+ DependencyEdge dependencyEdge,
+ String messageFormat,
+ Object firstArg,
+ Object... moreArgs) {
+ reportDependency(
+ diagnosticKind, dependencyEdge, formatMessage(messageFormat, firstArg, moreArgs));
+ }
+
+ @Override
+ public void reportSubcomponentFactoryMethod(
+ Diagnostic.Kind diagnosticKind,
+ ChildFactoryMethodEdge childFactoryMethodEdge,
+ String message) {
+ printMessage(diagnosticKind, message, childFactoryMethodEdge.factoryMethod());
+ }
+
+ @Override
+ public void reportSubcomponentFactoryMethod(
+ Diagnostic.Kind diagnosticKind,
+ ChildFactoryMethodEdge childFactoryMethodEdge,
+ String messageFormat,
+ Object firstArg,
+ Object... moreArgs) {
+ reportSubcomponentFactoryMethod(
+ diagnosticKind, childFactoryMethodEdge, formatMessage(messageFormat, firstArg, moreArgs));
+ }
+
+ private String formatMessage(String messageFormat, Object firstArg, Object[] moreArgs) {
+ return String.format(messageFormat, asList(firstArg, moreArgs).toArray());
+ }
+
+ void printMessage(
+ Diagnostic.Kind diagnosticKind,
+ CharSequence message,
+ @NullableDecl Element elementToReport) {
+ if (diagnosticKind.equals(ERROR) && reportErrorsAsWarnings) {
+ diagnosticKind = Diagnostic.Kind.WARNING;
+ }
+ reportedDiagnosticKinds.add(diagnosticKind);
+ StringBuilder fullMessage = new StringBuilder();
+ appendBracketPrefix(fullMessage, plugin);
+
+ // TODO(ronshapiro): should we create a HashSet out of elementEncloses() so we don't
+ // need to do an O(n) contains() each time?
+ if (elementToReport != null && !elementEncloses(rootComponent, elementToReport)) {
+ appendBracketPrefix(fullMessage, elementToString(elementToReport));
+ elementToReport = rootComponent;
+ }
+
+ messager.printMessage(diagnosticKind, fullMessage.append(message), elementToReport);
+ }
+
+ private void appendBracketPrefix(StringBuilder message, String prefix) {
+ message.append(String.format("[%s] ", prefix));
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java b/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java
new file mode 100644
index 000000000..06f68f68d
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static dagger.internal.codegen.base.Keys.isValidImplicitProvisionKey;
+import static dagger.internal.codegen.base.Keys.isValidMembersInjectionKey;
+import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.assistedInjectedConstructors;
+import static dagger.internal.codegen.binding.InjectionAnnotations.injectedConstructors;
+import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import dagger.Component;
+import dagger.MembersInjector;
+import dagger.Provides;
+import dagger.internal.codegen.base.SourceFileGenerationException;
+import dagger.internal.codegen.base.SourceFileGenerator;
+import dagger.internal.codegen.binding.Binding;
+import dagger.internal.codegen.binding.BindingFactory;
+import dagger.internal.codegen.binding.InjectBindingRegistry;
+import dagger.internal.codegen.binding.KeyFactory;
+import dagger.internal.codegen.binding.MembersInjectionBinding;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Key;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import javax.annotation.processing.Messager;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.tools.Diagnostic.Kind;
+
+/**
+ * Maintains the collection of provision bindings from {@link Inject} constructors and members
+ * injection bindings from {@link Inject} fields and methods known to the annotation processor.
+ * Note that this registry <b>does not</b> handle any explicit bindings (those from {@link Provides}
+ * methods, {@link Component} dependencies, etc.).
+ */
+@Singleton
+final class InjectBindingRegistryImpl implements InjectBindingRegistry {
+ private final DaggerElements elements;
+ private final DaggerTypes types;
+ private final Messager messager;
+ private final InjectValidator injectValidator;
+ private final InjectValidator injectValidatorWhenGeneratingCode;
+ private final KeyFactory keyFactory;
+ private final BindingFactory bindingFactory;
+ private final CompilerOptions compilerOptions;
+
+ final class BindingsCollection<B extends Binding> {
+ private final Class<?> factoryClass;
+ private final Map<Key, B> bindingsByKey = Maps.newLinkedHashMap();
+ private final Deque<B> bindingsRequiringGeneration = new ArrayDeque<>();
+ private final Set<Key> materializedBindingKeys = Sets.newLinkedHashSet();
+
+ BindingsCollection(Class<?> factoryClass) {
+ this.factoryClass = factoryClass;
+ }
+
+ void generateBindings(SourceFileGenerator<B> generator) throws SourceFileGenerationException {
+ for (B binding = bindingsRequiringGeneration.poll();
+ binding != null;
+ binding = bindingsRequiringGeneration.poll()) {
+ checkState(!binding.unresolved().isPresent());
+ if (injectValidatorWhenGeneratingCode.isValidType(binding.key().type())) {
+ generator.generate(binding);
+ }
+ materializedBindingKeys.add(binding.key());
+ }
+ // Because Elements instantiated across processing rounds are not guaranteed to be equals() to
+ // the logically same element, clear the cache after generating
+ bindingsByKey.clear();
+ }
+
+ /** Returns a previously cached binding. */
+ B getBinding(Key key) {
+ return bindingsByKey.get(key);
+ }
+
+ /** Caches the binding and generates it if it needs generation. */
+ void tryRegisterBinding(B binding, boolean warnIfNotAlreadyGenerated) {
+ tryToCacheBinding(binding);
+ tryToGenerateBinding(binding, warnIfNotAlreadyGenerated);
+ }
+
+ /**
+ * Tries to generate a binding, not generating if it already is generated. For resolved
+ * bindings, this will try to generate the unresolved version of the binding.
+ */
+ void tryToGenerateBinding(B binding, boolean warnIfNotAlreadyGenerated) {
+ if (shouldGenerateBinding(binding)) {
+ bindingsRequiringGeneration.offer(binding);
+ if (compilerOptions.warnIfInjectionFactoryNotGeneratedUpstream()
+ && warnIfNotAlreadyGenerated) {
+ messager.printMessage(
+ Kind.NOTE,
+ String.format(
+ "Generating a %s for %s. "
+ + "Prefer to run the dagger processor over that class instead.",
+ factoryClass.getSimpleName(),
+ types.erasure(binding.key().type()))); // erasure to strip <T> from msgs.
+ }
+ }
+ }
+
+ /** Returns true if the binding needs to be generated. */
+ private boolean shouldGenerateBinding(B binding) {
+ return !binding.unresolved().isPresent()
+ && !materializedBindingKeys.contains(binding.key())
+ && !bindingsRequiringGeneration.contains(binding)
+ && elements.getTypeElement(generatedClassNameForBinding(binding)) == null;
+ }
+
+ /** Caches the binding for future lookups by key. */
+ private void tryToCacheBinding(B binding) {
+ // We only cache resolved bindings or unresolved bindings w/o type arguments.
+ // Unresolved bindings w/ type arguments aren't valid for the object graph.
+ if (binding.unresolved().isPresent()
+ || binding.bindingTypeElement().get().getTypeParameters().isEmpty()) {
+ Key key = binding.key();
+ Binding previousValue = bindingsByKey.put(key, binding);
+ checkState(previousValue == null || binding.equals(previousValue),
+ "couldn't register %s. %s was already registered for %s",
+ binding, previousValue, key);
+ }
+ }
+ }
+
+ private final BindingsCollection<ProvisionBinding> provisionBindings =
+ new BindingsCollection<>(Provider.class);
+ private final BindingsCollection<MembersInjectionBinding> membersInjectionBindings =
+ new BindingsCollection<>(MembersInjector.class);
+
+ @Inject
+ InjectBindingRegistryImpl(
+ DaggerElements elements,
+ DaggerTypes types,
+ Messager messager,
+ InjectValidator injectValidator,
+ KeyFactory keyFactory,
+ BindingFactory bindingFactory,
+ CompilerOptions compilerOptions) {
+ this.elements = elements;
+ this.types = types;
+ this.messager = messager;
+ this.injectValidator = injectValidator;
+ this.injectValidatorWhenGeneratingCode = injectValidator.whenGeneratingCode();
+ this.keyFactory = keyFactory;
+ this.bindingFactory = bindingFactory;
+ this.compilerOptions = compilerOptions;
+ }
+
+
+ // TODO(dpb): make the SourceFileGenerators fields so they don't have to be passed in
+ @Override
+ public void generateSourcesForRequiredBindings(
+ SourceFileGenerator<ProvisionBinding> factoryGenerator,
+ SourceFileGenerator<MembersInjectionBinding> membersInjectorGenerator)
+ throws SourceFileGenerationException {
+ provisionBindings.generateBindings(factoryGenerator);
+ membersInjectionBindings.generateBindings(membersInjectorGenerator);
+ }
+
+ /**
+ * Registers the binding for generation and later lookup. If the binding is resolved, we also
+ * attempt to register an unresolved version of it.
+ */
+ private void registerBinding(ProvisionBinding binding, boolean warnIfNotAlreadyGenerated) {
+ provisionBindings.tryRegisterBinding(binding, warnIfNotAlreadyGenerated);
+ if (binding.unresolved().isPresent()) {
+ provisionBindings.tryToGenerateBinding(binding.unresolved().get(), warnIfNotAlreadyGenerated);
+ }
+ }
+
+ /**
+ * Registers the binding for generation and later lookup. If the binding is resolved, we also
+ * attempt to register an unresolved version of it.
+ */
+ private void registerBinding(MembersInjectionBinding binding, boolean warnIfNotAlreadyGenerated) {
+ /*
+ * We generate MembersInjector classes for types with @Inject constructors only if they have any
+ * injection sites.
+ *
+ * We generate MembersInjector classes for types without @Inject constructors only if they have
+ * local (non-inherited) injection sites.
+ *
+ * Warn only when registering bindings post-hoc for those types.
+ */
+ if (warnIfNotAlreadyGenerated) {
+ boolean hasInjectConstructor =
+ !(injectedConstructors(binding.membersInjectedType()).isEmpty()
+ && assistedInjectedConstructors(binding.membersInjectedType()).isEmpty());
+ warnIfNotAlreadyGenerated =
+ hasInjectConstructor
+ ? !binding.injectionSites().isEmpty()
+ : binding.hasLocalInjectionSites();
+ }
+
+ membersInjectionBindings.tryRegisterBinding(binding, warnIfNotAlreadyGenerated);
+ if (binding.unresolved().isPresent()) {
+ membersInjectionBindings.tryToGenerateBinding(
+ binding.unresolved().get(), warnIfNotAlreadyGenerated);
+ }
+ }
+
+ @Override
+ public Optional<ProvisionBinding> tryRegisterConstructor(ExecutableElement constructorElement) {
+ return tryRegisterConstructor(constructorElement, Optional.empty(), false);
+ }
+
+ @CanIgnoreReturnValue
+ private Optional<ProvisionBinding> tryRegisterConstructor(
+ ExecutableElement constructorElement,
+ Optional<TypeMirror> resolvedType,
+ boolean warnIfNotAlreadyGenerated) {
+ TypeElement typeElement = MoreElements.asType(constructorElement.getEnclosingElement());
+ DeclaredType type = MoreTypes.asDeclared(typeElement.asType());
+ Key key = keyFactory.forInjectConstructorWithResolvedType(type);
+ ProvisionBinding cachedBinding = provisionBindings.getBinding(key);
+ if (cachedBinding != null) {
+ return Optional.of(cachedBinding);
+ }
+
+ ValidationReport<TypeElement> report = injectValidator.validateConstructor(constructorElement);
+ report.printMessagesTo(messager);
+ if (report.isClean()) {
+ ProvisionBinding binding = bindingFactory.injectionBinding(constructorElement, resolvedType);
+ registerBinding(binding, warnIfNotAlreadyGenerated);
+ if (!binding.injectionSites().isEmpty()) {
+ tryRegisterMembersInjectedType(typeElement, resolvedType, warnIfNotAlreadyGenerated);
+ }
+ return Optional.of(binding);
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional<MembersInjectionBinding> tryRegisterMembersInjectedType(TypeElement typeElement) {
+ return tryRegisterMembersInjectedType(typeElement, Optional.empty(), false);
+ }
+
+ @CanIgnoreReturnValue
+ private Optional<MembersInjectionBinding> tryRegisterMembersInjectedType(
+ TypeElement typeElement,
+ Optional<TypeMirror> resolvedType,
+ boolean warnIfNotAlreadyGenerated) {
+ DeclaredType type = MoreTypes.asDeclared(typeElement.asType());
+ Key key = keyFactory.forInjectConstructorWithResolvedType(type);
+ MembersInjectionBinding cachedBinding = membersInjectionBindings.getBinding(key);
+ if (cachedBinding != null) {
+ return Optional.of(cachedBinding);
+ }
+
+ ValidationReport<TypeElement> report =
+ injectValidator.validateMembersInjectionType(typeElement);
+ report.printMessagesTo(messager);
+ if (report.isClean()) {
+ MembersInjectionBinding binding = bindingFactory.membersInjectionBinding(type, resolvedType);
+ registerBinding(binding, warnIfNotAlreadyGenerated);
+ for (Optional<DeclaredType> supertype = types.nonObjectSuperclass(type);
+ supertype.isPresent();
+ supertype = types.nonObjectSuperclass(supertype.get())) {
+ getOrFindMembersInjectionBinding(keyFactory.forMembersInjectedType(supertype.get()));
+ }
+ return Optional.of(binding);
+ }
+ return Optional.empty();
+ }
+
+ @CanIgnoreReturnValue
+ @Override
+ public Optional<ProvisionBinding> getOrFindProvisionBinding(Key key) {
+ checkNotNull(key);
+ if (!isValidImplicitProvisionKey(key, types)) {
+ return Optional.empty();
+ }
+ ProvisionBinding binding = provisionBindings.getBinding(key);
+ if (binding != null) {
+ return Optional.of(binding);
+ }
+
+ // ok, let's see if we can find an @Inject constructor
+ TypeElement element = MoreElements.asType(types.asElement(key.type()));
+ ImmutableSet<ExecutableElement> injectConstructors =
+ ImmutableSet.<ExecutableElement>builder()
+ .addAll(injectedConstructors(element))
+ .addAll(assistedInjectedConstructors(element))
+ .build();
+ switch (injectConstructors.size()) {
+ case 0:
+ // No constructor found.
+ return Optional.empty();
+ case 1:
+ return tryRegisterConstructor(
+ Iterables.getOnlyElement(injectConstructors), Optional.of(key.type()), true);
+ default:
+ throw new IllegalStateException("Found multiple @Inject constructors: "
+ + injectConstructors);
+ }
+ }
+
+ @CanIgnoreReturnValue
+ @Override
+ public Optional<MembersInjectionBinding> getOrFindMembersInjectionBinding(Key key) {
+ checkNotNull(key);
+ // TODO(gak): is checking the kind enough?
+ checkArgument(isValidMembersInjectionKey(key));
+ MembersInjectionBinding binding = membersInjectionBindings.getBinding(key);
+ if (binding != null) {
+ return Optional.of(binding);
+ }
+ Optional<MembersInjectionBinding> newBinding =
+ tryRegisterMembersInjectedType(
+ MoreTypes.asTypeElement(key.type()), Optional.of(key.type()), true);
+ return newBinding;
+ }
+
+ @Override
+ public Optional<ProvisionBinding> getOrFindMembersInjectorProvisionBinding(Key key) {
+ if (!isValidMembersInjectionKey(key)) {
+ return Optional.empty();
+ }
+ Key membersInjectionKey = keyFactory.forMembersInjectedType(types.unwrapType(key.type()));
+ return getOrFindMembersInjectionBinding(membersInjectionKey)
+ .map(binding -> bindingFactory.membersInjectorBinding(key, binding));
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/InjectBindingRegistryModule.java b/java/dagger/internal/codegen/validation/InjectBindingRegistryModule.java
new file mode 100644
index 000000000..3a164ca8a
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/InjectBindingRegistryModule.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.internal.codegen.binding.InjectBindingRegistry;
+
+/** Binds the {@link InjectBindingRegistry} implementation. */
+@Module
+public interface InjectBindingRegistryModule {
+ @Binds InjectBindingRegistry injectBindingRegistry(InjectBindingRegistryImpl impl);
+}
diff --git a/java/dagger/internal/codegen/validation/InjectValidator.java b/java/dagger/internal/codegen/validation/InjectValidator.java
new file mode 100644
index 000000000..240f9d099
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/InjectValidator.java
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.google.auto.common.MoreElements.asType;
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static dagger.internal.codegen.base.Scopes.scopesOf;
+import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.assistedInjectedConstructors;
+import static dagger.internal.codegen.binding.InjectionAnnotations.injectedConstructors;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.STATIC;
+import static javax.lang.model.type.TypeKind.DECLARED;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSet;
+import dagger.assisted.AssistedInject;
+import dagger.internal.codegen.base.ClearableCache;
+import dagger.internal.codegen.binding.InjectionAnnotations;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.Accessibility;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Scope;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.tools.Diagnostic;
+import javax.tools.Diagnostic.Kind;
+
+/**
+ * A {@linkplain ValidationReport validator} for {@link Inject}-annotated elements and the types
+ * that contain them.
+ */
+@Singleton
+public final class InjectValidator implements ClearableCache {
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+ private final CompilerOptions compilerOptions;
+ private final DependencyRequestValidator dependencyRequestValidator;
+ private final Optional<Diagnostic.Kind> privateAndStaticInjectionDiagnosticKind;
+ private final InjectionAnnotations injectionAnnotations;
+ private final KotlinMetadataUtil metadataUtil;
+ private final Map<ExecutableElement, ValidationReport<TypeElement>> reports = new HashMap<>();
+
+ @Inject
+ InjectValidator(
+ DaggerTypes types,
+ DaggerElements elements,
+ DependencyRequestValidator dependencyRequestValidator,
+ CompilerOptions compilerOptions,
+ InjectionAnnotations injectionAnnotations,
+ KotlinMetadataUtil metadataUtil) {
+ this(
+ types,
+ elements,
+ compilerOptions,
+ dependencyRequestValidator,
+ Optional.empty(),
+ injectionAnnotations,
+ metadataUtil);
+ }
+
+ private InjectValidator(
+ DaggerTypes types,
+ DaggerElements elements,
+ CompilerOptions compilerOptions,
+ DependencyRequestValidator dependencyRequestValidator,
+ Optional<Kind> privateAndStaticInjectionDiagnosticKind,
+ InjectionAnnotations injectionAnnotations,
+ KotlinMetadataUtil metadataUtil) {
+ this.types = types;
+ this.elements = elements;
+ this.compilerOptions = compilerOptions;
+ this.dependencyRequestValidator = dependencyRequestValidator;
+ this.privateAndStaticInjectionDiagnosticKind = privateAndStaticInjectionDiagnosticKind;
+ this.injectionAnnotations = injectionAnnotations;
+ this.metadataUtil = metadataUtil;
+ }
+
+ @Override
+ public void clearCache() {
+ reports.clear();
+ }
+
+ /**
+ * Returns a new validator that performs the same validation as this one, but is strict about
+ * rejecting optionally-specified JSR 330 behavior that Dagger doesn't support (unless {@code
+ * -Adagger.ignorePrivateAndStaticInjectionForComponent=enabled} was set in the javac options).
+ */
+ public InjectValidator whenGeneratingCode() {
+ return compilerOptions.ignorePrivateAndStaticInjectionForComponent()
+ ? this
+ : new InjectValidator(
+ types,
+ elements,
+ compilerOptions,
+ dependencyRequestValidator,
+ Optional.of(Diagnostic.Kind.ERROR),
+ injectionAnnotations,
+ metadataUtil);
+ }
+
+ public ValidationReport<TypeElement> validateConstructor(ExecutableElement constructorElement) {
+ return reentrantComputeIfAbsent(reports, constructorElement, this::validateConstructorUncached);
+ }
+
+ private ValidationReport<TypeElement> validateConstructorUncached(
+ ExecutableElement constructorElement) {
+ ValidationReport.Builder<TypeElement> builder =
+ ValidationReport.about(asType(constructorElement.getEnclosingElement()));
+
+ if (isAnnotationPresent(constructorElement, Inject.class)
+ && isAnnotationPresent(constructorElement, AssistedInject.class)) {
+ builder.addError("Constructors cannot be annotated with both @Inject and @AssistedInject");
+ }
+
+ Class<?> injectAnnotation =
+ isAnnotationPresent(constructorElement, Inject.class) ? Inject.class : AssistedInject.class;
+
+ if (constructorElement.getModifiers().contains(PRIVATE)) {
+ builder.addError(
+ "Dagger does not support injection into private constructors", constructorElement);
+ }
+
+ for (AnnotationMirror qualifier : injectionAnnotations.getQualifiers(constructorElement)) {
+ builder.addError(
+ String.format(
+ "@Qualifier annotations are not allowed on @%s constructors",
+ injectAnnotation.getSimpleName()),
+ constructorElement,
+ qualifier);
+ }
+
+ String scopeErrorMsg =
+ String.format(
+ "@Scope annotations are not allowed on @%s constructors",
+ injectAnnotation.getSimpleName());
+
+ if (injectAnnotation == Inject.class) {
+ scopeErrorMsg += "; annotate the class instead";
+ }
+
+ for (Scope scope : scopesOf(constructorElement)) {
+ builder.addError(scopeErrorMsg, constructorElement, scope.scopeAnnotation());
+ }
+
+ for (VariableElement parameter : constructorElement.getParameters()) {
+ validateDependencyRequest(builder, parameter);
+ }
+
+ if (throwsCheckedExceptions(constructorElement)) {
+ builder.addItem(
+ String.format(
+ "Dagger does not support checked exceptions on @%s constructors",
+ injectAnnotation.getSimpleName()),
+ privateMemberDiagnosticKind(),
+ constructorElement);
+ }
+
+ checkInjectIntoPrivateClass(constructorElement, builder);
+
+ TypeElement enclosingElement =
+ MoreElements.asType(constructorElement.getEnclosingElement());
+
+ Set<Modifier> typeModifiers = enclosingElement.getModifiers();
+ if (typeModifiers.contains(ABSTRACT)) {
+ builder.addError(
+ String.format(
+ "@%s is nonsense on the constructor of an abstract class",
+ injectAnnotation.getSimpleName()),
+ constructorElement);
+ }
+
+ if (enclosingElement.getNestingKind().isNested()
+ && !typeModifiers.contains(STATIC)) {
+ builder.addError(
+ String.format(
+ "@%s constructors are invalid on inner classes. "
+ + "Did you mean to make the class static?",
+ injectAnnotation.getSimpleName()),
+ constructorElement);
+ }
+
+ // This is computationally expensive, but probably preferable to a giant index
+ ImmutableSet<ExecutableElement> injectConstructors =
+ ImmutableSet.<ExecutableElement>builder()
+ .addAll(injectedConstructors(enclosingElement))
+ .addAll(assistedInjectedConstructors(enclosingElement))
+ .build();
+
+ if (injectConstructors.size() > 1) {
+ builder.addError("Types may only contain one injected constructor", constructorElement);
+ }
+
+ ImmutableSet<Scope> scopes = scopesOf(enclosingElement);
+ if (injectAnnotation == AssistedInject.class) {
+ for (Scope scope : scopes) {
+ builder.addError(
+ "A type with an @AssistedInject-annotated constructor cannot be scoped",
+ enclosingElement,
+ scope.scopeAnnotation());
+ }
+ } else if (scopes.size() > 1) {
+ for (Scope scope : scopes) {
+ builder.addError(
+ "A single binding may not declare more than one @Scope",
+ enclosingElement,
+ scope.scopeAnnotation());
+ }
+ }
+
+ return builder.build();
+ }
+
+ private ValidationReport<VariableElement> validateField(VariableElement fieldElement) {
+ ValidationReport.Builder<VariableElement> builder = ValidationReport.about(fieldElement);
+ Set<Modifier> modifiers = fieldElement.getModifiers();
+ if (modifiers.contains(FINAL)) {
+ builder.addError("@Inject fields may not be final", fieldElement);
+ }
+
+ if (modifiers.contains(PRIVATE)) {
+ builder.addItem(
+ "Dagger does not support injection into private fields",
+ privateMemberDiagnosticKind(),
+ fieldElement);
+ }
+
+ if (modifiers.contains(STATIC)) {
+ builder.addItem(
+ "Dagger does not support injection into static fields",
+ staticMemberDiagnosticKind(),
+ fieldElement);
+ }
+
+ validateDependencyRequest(builder, fieldElement);
+
+ return builder.build();
+ }
+
+ private ValidationReport<ExecutableElement> validateMethod(ExecutableElement methodElement) {
+ ValidationReport.Builder<ExecutableElement> builder = ValidationReport.about(methodElement);
+ Set<Modifier> modifiers = methodElement.getModifiers();
+ if (modifiers.contains(ABSTRACT)) {
+ builder.addError("Methods with @Inject may not be abstract", methodElement);
+ }
+
+ if (modifiers.contains(PRIVATE)) {
+ builder.addItem(
+ "Dagger does not support injection into private methods",
+ privateMemberDiagnosticKind(),
+ methodElement);
+ }
+
+ if (modifiers.contains(STATIC)) {
+ builder.addItem(
+ "Dagger does not support injection into static methods",
+ staticMemberDiagnosticKind(),
+ methodElement);
+ }
+
+ if (!methodElement.getTypeParameters().isEmpty()) {
+ builder.addError("Methods with @Inject may not declare type parameters", methodElement);
+ }
+
+ if (!methodElement.getThrownTypes().isEmpty()) {
+ builder.addError("Methods with @Inject may not throw checked exceptions. "
+ + "Please wrap your exceptions in a RuntimeException instead.", methodElement);
+ }
+
+ for (VariableElement parameter : methodElement.getParameters()) {
+ validateDependencyRequest(builder, parameter);
+ }
+
+ return builder.build();
+ }
+
+ private void validateDependencyRequest(
+ ValidationReport.Builder<?> builder, VariableElement parameter) {
+ dependencyRequestValidator.validateDependencyRequest(builder, parameter, parameter.asType());
+ dependencyRequestValidator.checkNotProducer(builder, parameter);
+ }
+
+ public ValidationReport<TypeElement> validateMembersInjectionType(TypeElement typeElement) {
+ // TODO(beder): This element might not be currently compiled, so this error message could be
+ // left in limbo. Find an appropriate way to display the error message in that case.
+ ValidationReport.Builder<TypeElement> builder = ValidationReport.about(typeElement);
+ boolean hasInjectedMembers = false;
+ for (VariableElement element : ElementFilter.fieldsIn(typeElement.getEnclosedElements())) {
+ if (MoreElements.isAnnotationPresent(element, Inject.class)) {
+ hasInjectedMembers = true;
+ ValidationReport<VariableElement> report = validateField(element);
+ if (!report.isClean()) {
+ builder.addSubreport(report);
+ }
+ }
+ }
+ for (ExecutableElement element : ElementFilter.methodsIn(typeElement.getEnclosedElements())) {
+ if (MoreElements.isAnnotationPresent(element, Inject.class)) {
+ hasInjectedMembers = true;
+ ValidationReport<ExecutableElement> report = validateMethod(element);
+ if (!report.isClean()) {
+ builder.addSubreport(report);
+ }
+ }
+ }
+
+ if (hasInjectedMembers) {
+ checkInjectIntoPrivateClass(typeElement, builder);
+ checkInjectIntoKotlinObject(typeElement, builder);
+ }
+ TypeMirror superclass = typeElement.getSuperclass();
+ if (!superclass.getKind().equals(TypeKind.NONE)) {
+ ValidationReport<TypeElement> report = validateType(MoreTypes.asTypeElement(superclass));
+ if (!report.isClean()) {
+ builder.addSubreport(report);
+ }
+ }
+ return builder.build();
+ }
+
+ public ValidationReport<TypeElement> validateType(TypeElement typeElement) {
+ ValidationReport.Builder<TypeElement> builder = ValidationReport.about(typeElement);
+ ValidationReport<TypeElement> membersInjectionReport =
+ validateMembersInjectionType(typeElement);
+ if (!membersInjectionReport.isClean()) {
+ builder.addSubreport(membersInjectionReport);
+ }
+ for (ExecutableElement element :
+ ElementFilter.constructorsIn(typeElement.getEnclosedElements())) {
+ if (isAnnotationPresent(element, Inject.class)
+ || isAnnotationPresent(element, AssistedInject.class)) {
+ ValidationReport<TypeElement> report = validateConstructor(element);
+ if (!report.isClean()) {
+ builder.addSubreport(report);
+ }
+ }
+ }
+ return builder.build();
+ }
+
+ public boolean isValidType(TypeMirror type) {
+ if (!type.getKind().equals(DECLARED)) {
+ return true;
+ }
+ return validateType(MoreTypes.asTypeElement(type)).isClean();
+ }
+
+ /** Returns true if the given method element declares a checked exception. */
+ private boolean throwsCheckedExceptions(ExecutableElement methodElement) {
+ TypeMirror runtimeExceptionType = elements.getTypeElement(RuntimeException.class).asType();
+ TypeMirror errorType = elements.getTypeElement(Error.class).asType();
+ for (TypeMirror thrownType : methodElement.getThrownTypes()) {
+ if (!types.isSubtype(thrownType, runtimeExceptionType)
+ && !types.isSubtype(thrownType, errorType)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void checkInjectIntoPrivateClass(
+ Element element, ValidationReport.Builder<TypeElement> builder) {
+ if (!Accessibility.isElementAccessibleFromOwnPackage(
+ DaggerElements.closestEnclosingTypeElement(element))) {
+ builder.addItem(
+ "Dagger does not support injection into private classes",
+ privateMemberDiagnosticKind(),
+ element);
+ }
+ }
+
+ private void checkInjectIntoKotlinObject(
+ TypeElement element, ValidationReport.Builder<TypeElement> builder) {
+ if (metadataUtil.isObjectClass(element) || metadataUtil.isCompanionObjectClass(element)) {
+ builder.addError("Dagger does not support injection into Kotlin objects", element);
+ }
+ }
+
+ private Diagnostic.Kind privateMemberDiagnosticKind() {
+ return privateAndStaticInjectionDiagnosticKind.orElse(
+ compilerOptions.privateMemberValidationKind());
+ }
+
+ private Diagnostic.Kind staticMemberDiagnosticKind() {
+ return privateAndStaticInjectionDiagnosticKind.orElse(
+ compilerOptions.staticMemberValidationKind());
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/MapKeyValidator.java b/java/dagger/internal/codegen/validation/MapKeyValidator.java
new file mode 100644
index 000000000..6aa514710
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/MapKeyValidator.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import dagger.MapKey;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import java.util.List;
+import javax.inject.Inject;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeKind;
+
+/**
+ * A validator for {@link MapKey} annotations.
+ */
+// TODO(dpb,gak): Should unwrapped MapKeys be required to have their single member be named "value"?
+public final class MapKeyValidator {
+ private final DaggerElements elements;
+
+ @Inject
+ MapKeyValidator(DaggerElements elements) {
+ this.elements = elements;
+ }
+
+ public ValidationReport<Element> validate(Element element) {
+ ValidationReport.Builder<Element> builder = ValidationReport.about(element);
+ List<ExecutableElement> members = methodsIn(((TypeElement) element).getEnclosedElements());
+ if (members.isEmpty()) {
+ builder.addError("Map key annotations must have members", element);
+ } else if (element.getAnnotation(MapKey.class).unwrapValue()) {
+ if (members.size() > 1) {
+ builder.addError(
+ "Map key annotations with unwrapped values must have exactly one member", element);
+ } else if (members.get(0).getReturnType().getKind() == TypeKind.ARRAY) {
+ builder.addError("Map key annotations with unwrapped values cannot use arrays", element);
+ }
+ } else if (autoAnnotationIsMissing()) {
+ builder.addError(
+ "@AutoAnnotation is a necessary dependency if @MapKey(unwrapValue = false). Add a "
+ + "dependency on com.google.auto.value:auto-value:<current version>");
+ }
+ return builder.build();
+ }
+
+ private boolean autoAnnotationIsMissing() {
+ return elements.getTypeElement("com.google.auto.value.AutoAnnotation") == null;
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/MembersInjectionValidator.java b/java/dagger/internal/codegen/validation/MembersInjectionValidator.java
new file mode 100644
index 000000000..afa6270f4
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/MembersInjectionValidator.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.auto.common.MoreElements;
+import dagger.internal.codegen.binding.InjectionAnnotations;
+import javax.inject.Inject;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVisitor;
+import javax.lang.model.util.SimpleTypeVisitor8;
+
+/**
+ * Validates members injection requests (members injection methods on components and requests for
+ * {@code MembersInjector<Foo>}).
+ */
+final class MembersInjectionValidator {
+ private final InjectionAnnotations injectionAnnotations;
+
+ @Inject
+ MembersInjectionValidator(InjectionAnnotations injectionAnnotations) {
+ this.injectionAnnotations = injectionAnnotations;
+ }
+
+ /** Reports errors if a request for a {@code MembersInjector<Foo>}) is invalid. */
+ ValidationReport<Element> validateMembersInjectionRequest(
+ Element requestElement, TypeMirror membersInjectedType) {
+ ValidationReport.Builder<Element> report = ValidationReport.about(requestElement);
+ checkQualifiers(report, requestElement);
+ membersInjectedType.accept(VALIDATE_MEMBERS_INJECTED_TYPE, report);
+ return report.build();
+ }
+
+ /**
+ * Reports errors if a members injection method on a component is invalid.
+ *
+ * @throws IllegalArgumentException if the method doesn't have exactly one parameter
+ */
+ ValidationReport<ExecutableElement> validateMembersInjectionMethod(
+ ExecutableElement method, TypeMirror membersInjectedType) {
+ checkArgument(
+ method.getParameters().size() == 1, "expected a method with one parameter: %s", method);
+
+ ValidationReport.Builder<ExecutableElement> report = ValidationReport.about(method);
+ checkQualifiers(report, method);
+ checkQualifiers(report, method.getParameters().get(0));
+ membersInjectedType.accept(VALIDATE_MEMBERS_INJECTED_TYPE, report);
+ return report.build();
+ }
+
+ private void checkQualifiers(ValidationReport.Builder<?> report, Element element) {
+ for (AnnotationMirror qualifier : injectionAnnotations.getQualifiers(element)) {
+ report.addError("Cannot inject members into qualified types", element, qualifier);
+ break; // just report on the first qualifier, in case there is more than one
+ }
+ }
+
+ private static final TypeVisitor<Void, ValidationReport.Builder<?>>
+ VALIDATE_MEMBERS_INJECTED_TYPE =
+ new SimpleTypeVisitor8<Void, ValidationReport.Builder<?>>() {
+ // Only declared types can be members-injected.
+ @Override
+ protected Void defaultAction(TypeMirror type, ValidationReport.Builder<?> report) {
+ report.addError("Cannot inject members into " + type);
+ return null;
+ }
+
+ @Override
+ public Void visitDeclared(DeclaredType type, ValidationReport.Builder<?> report) {
+ if (type.getTypeArguments().isEmpty()) {
+ // If the type is the erasure of a generic type, that means the user referred to
+ // Foo<T> as just 'Foo', which we don't allow. (This is a judgement call; we
+ // *could* allow it and instantiate the type bounds, but we don't.)
+ if (!MoreElements.asType(type.asElement()).getTypeParameters().isEmpty()) {
+ report.addError("Cannot inject members into raw type " + type);
+ }
+ } else {
+ // If the type has arguments, validate that each type argument is declared.
+ // Otherwise the type argument may be a wildcard (or other type), and we can't
+ // resolve that to actual types. For array type arguments, validate the type of the
+ // array.
+ for (TypeMirror arg : type.getTypeArguments()) {
+ if (!arg.accept(DECLARED_OR_ARRAY, null)) {
+ report.addError(
+ "Cannot inject members into types with unbounded type arguments: " + type);
+ }
+ }
+ }
+ return null;
+ }
+ };
+
+ // TODO(dpb): Can this be inverted so it explicitly rejects wildcards or type variables?
+ // This logic is hard to describe.
+ private static final TypeVisitor<Boolean, Void> DECLARED_OR_ARRAY =
+ new SimpleTypeVisitor8<Boolean, Void>(false) {
+ @Override
+ public Boolean visitArray(ArrayType arrayType, Void p) {
+ return arrayType
+ .getComponentType()
+ .accept(
+ new SimpleTypeVisitor8<Boolean, Void>(false) {
+ @Override
+ public Boolean visitDeclared(DeclaredType declaredType, Void p) {
+ for (TypeMirror arg : declaredType.getTypeArguments()) {
+ if (!arg.accept(this, null)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public Boolean visitArray(ArrayType arrayType, Void p) {
+ return arrayType.getComponentType().accept(this, null);
+ }
+
+ @Override
+ public Boolean visitPrimitive(PrimitiveType primitiveType, Void p) {
+ return true;
+ }
+ },
+ null);
+ }
+
+ @Override
+ public Boolean visitDeclared(DeclaredType t, Void p) {
+ return true;
+ }
+ };
+}
diff --git a/java/dagger/internal/codegen/validation/ModuleValidator.java b/java/dagger/internal/codegen/validation/ModuleValidator.java
new file mode 100644
index 000000000..02ac06a25
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/ModuleValidator.java
@@ -0,0 +1,712 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.auto.common.MoreTypes.asTypeElement;
+import static com.google.auto.common.Visibility.PRIVATE;
+import static com.google.auto.common.Visibility.PUBLIC;
+import static com.google.auto.common.Visibility.effectiveVisibilityOfElement;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.base.ComponentAnnotation.componentAnnotation;
+import static dagger.internal.codegen.base.ComponentAnnotation.isComponentAnnotation;
+import static dagger.internal.codegen.base.ComponentAnnotation.subcomponentAnnotation;
+import static dagger.internal.codegen.base.ModuleAnnotation.isModuleAnnotation;
+import static dagger.internal.codegen.base.ModuleAnnotation.moduleAnnotation;
+import static dagger.internal.codegen.base.MoreAnnotationMirrors.simpleName;
+import static dagger.internal.codegen.base.MoreAnnotationValues.asType;
+import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.getCreatorAnnotations;
+import static dagger.internal.codegen.binding.ConfigurationAnnotations.getSubcomponentCreator;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
+import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
+import static java.util.stream.Collectors.joining;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.STATIC;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.auto.common.Visibility;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.MultimapBuilder;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.Sets;
+import com.google.errorprone.annotations.FormatMethod;
+import dagger.Module;
+import dagger.Subcomponent;
+import dagger.internal.codegen.base.ModuleAnnotation;
+import dagger.internal.codegen.binding.BindingGraphFactory;
+import dagger.internal.codegen.binding.ComponentCreatorAnnotation;
+import dagger.internal.codegen.binding.ComponentDescriptorFactory;
+import dagger.internal.codegen.binding.MethodSignatureFormatter;
+import dagger.internal.codegen.binding.ModuleKind;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.BindingGraph;
+import dagger.producers.ProducerModule;
+import dagger.producers.ProductionSubcomponent;
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Scope;
+import javax.inject.Singleton;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleAnnotationValueVisitor8;
+import javax.lang.model.util.SimpleTypeVisitor8;
+
+/** A {@linkplain ValidationReport validator} for {@link Module}s or {@link ProducerModule}s. */
+@Singleton
+public final class ModuleValidator {
+ private static final ImmutableSet<Class<? extends Annotation>> SUBCOMPONENT_TYPES =
+ ImmutableSet.of(Subcomponent.class, ProductionSubcomponent.class);
+ private static final ImmutableSet<Class<? extends Annotation>> SUBCOMPONENT_CREATOR_TYPES =
+ ImmutableSet.of(
+ Subcomponent.Builder.class,
+ Subcomponent.Factory.class,
+ ProductionSubcomponent.Builder.class,
+ ProductionSubcomponent.Factory.class);
+ private static final Optional<Class<?>> ANDROID_PROCESSOR;
+ private static final String CONTRIBUTES_ANDROID_INJECTOR_NAME =
+ "dagger.android.ContributesAndroidInjector";
+ private static final String ANDROID_PROCESSOR_NAME = "dagger.android.processor.AndroidProcessor";
+
+ static {
+ Class<?> clazz;
+ try {
+ clazz = Class.forName(ANDROID_PROCESSOR_NAME, false, ModuleValidator.class.getClassLoader());
+ } catch (ClassNotFoundException ignored) {
+ clazz = null;
+ }
+ ANDROID_PROCESSOR = Optional.ofNullable(clazz);
+ }
+
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+ private final AnyBindingMethodValidator anyBindingMethodValidator;
+ private final MethodSignatureFormatter methodSignatureFormatter;
+ private final ComponentDescriptorFactory componentDescriptorFactory;
+ private final BindingGraphFactory bindingGraphFactory;
+ private final BindingGraphValidator bindingGraphValidator;
+ private final KotlinMetadataUtil metadataUtil;
+ private final Map<TypeElement, ValidationReport<TypeElement>> cache = new HashMap<>();
+ private final Set<TypeElement> knownModules = new HashSet<>();
+
+ @Inject
+ ModuleValidator(
+ DaggerTypes types,
+ DaggerElements elements,
+ AnyBindingMethodValidator anyBindingMethodValidator,
+ MethodSignatureFormatter methodSignatureFormatter,
+ ComponentDescriptorFactory componentDescriptorFactory,
+ BindingGraphFactory bindingGraphFactory,
+ BindingGraphValidator bindingGraphValidator,
+ KotlinMetadataUtil metadataUtil) {
+ this.types = types;
+ this.elements = elements;
+ this.anyBindingMethodValidator = anyBindingMethodValidator;
+ this.methodSignatureFormatter = methodSignatureFormatter;
+ this.componentDescriptorFactory = componentDescriptorFactory;
+ this.bindingGraphFactory = bindingGraphFactory;
+ this.bindingGraphValidator = bindingGraphValidator;
+ this.metadataUtil = metadataUtil;
+ }
+
+ /**
+ * Adds {@code modules} to the set of module types that will be validated during this compilation
+ * step. If a component or module includes a module that is not in this set, that included module
+ * is assumed to be valid because it was processed in a previous compilation step. If it were
+ * invalid, that previous compilation step would have failed and blocked this one.
+ *
+ * <p>This logic depends on this method being called before {@linkplain #validate(TypeElement)
+ * validating} any module or {@linkplain #validateReferencedModules(TypeElement, AnnotationMirror,
+ * ImmutableSet, Set) component}.
+ */
+ public void addKnownModules(Collection<TypeElement> modules) {
+ knownModules.addAll(modules);
+ }
+
+ /** Returns a validation report for a module type. */
+ public ValidationReport<TypeElement> validate(TypeElement module) {
+ return validate(module, new HashSet<>());
+ }
+
+ private ValidationReport<TypeElement> validate(
+ TypeElement module, Set<TypeElement> visitedModules) {
+ if (visitedModules.add(module)) {
+ return reentrantComputeIfAbsent(cache, module, m -> validateUncached(module, visitedModules));
+ }
+ return ValidationReport.about(module).build();
+ }
+
+ private ValidationReport<TypeElement> validateUncached(
+ TypeElement module, Set<TypeElement> visitedModules) {
+ ValidationReport.Builder<TypeElement> builder = ValidationReport.about(module);
+ ModuleKind moduleKind = ModuleKind.forAnnotatedElement(module).get();
+ TypeElement contributesAndroidInjectorElement =
+ elements.getTypeElement(CONTRIBUTES_ANDROID_INJECTOR_NAME);
+ TypeMirror contributesAndroidInjector =
+ contributesAndroidInjectorElement != null
+ ? contributesAndroidInjectorElement.asType()
+ : null;
+ List<ExecutableElement> moduleMethods = methodsIn(module.getEnclosedElements());
+ List<ExecutableElement> bindingMethods = new ArrayList<>();
+ for (ExecutableElement moduleMethod : moduleMethods) {
+ if (anyBindingMethodValidator.isBindingMethod(moduleMethod)) {
+ builder.addSubreport(anyBindingMethodValidator.validate(moduleMethod));
+ bindingMethods.add(moduleMethod);
+ }
+
+ for (AnnotationMirror annotation : moduleMethod.getAnnotationMirrors()) {
+ if (!ANDROID_PROCESSOR.isPresent()
+ && MoreTypes.equivalence()
+ .equivalent(contributesAndroidInjector, annotation.getAnnotationType())) {
+ builder.addSubreport(
+ ValidationReport.about(moduleMethod)
+ .addError(
+ String.format(
+ "@%s was used, but %s was not found on the processor path",
+ CONTRIBUTES_ANDROID_INJECTOR_NAME, ANDROID_PROCESSOR_NAME))
+ .build());
+ break;
+ }
+ }
+ }
+
+ if (bindingMethods.stream()
+ .map(ModuleMethodKind::ofMethod)
+ .collect(toImmutableSet())
+ .containsAll(
+ EnumSet.of(ModuleMethodKind.ABSTRACT_DECLARATION, ModuleMethodKind.INSTANCE_BINDING))) {
+ builder.addError(
+ String.format(
+ "A @%s may not contain both non-static and abstract binding methods",
+ moduleKind.annotation().getSimpleName()));
+ }
+
+ validateModuleVisibility(module, moduleKind, builder);
+
+ ImmutableListMultimap<Name, ExecutableElement> bindingMethodsByName =
+ Multimaps.index(bindingMethods, ExecutableElement::getSimpleName);
+
+ validateMethodsWithSameName(builder, bindingMethodsByName);
+ if (module.getKind() != ElementKind.INTERFACE) {
+ validateBindingMethodOverrides(
+ module,
+ builder,
+ Multimaps.index(moduleMethods, ExecutableElement::getSimpleName),
+ bindingMethodsByName);
+ }
+ validateModifiers(module, builder);
+ validateReferencedModules(module, moduleKind, visitedModules, builder);
+ validateReferencedSubcomponents(module, moduleKind, builder);
+ validateNoScopeAnnotationsOnModuleElement(module, moduleKind, builder);
+ validateSelfCycles(module, builder);
+ if (metadataUtil.hasEnclosedCompanionObject(module)) {
+ validateCompanionModule(module, builder);
+ }
+
+ if (builder.build().isClean()
+ && bindingGraphValidator.shouldDoFullBindingGraphValidation(module)) {
+ validateModuleBindings(module, builder);
+ }
+
+ return builder.build();
+ }
+
+ private void validateReferencedSubcomponents(
+ final TypeElement subject,
+ ModuleKind moduleKind,
+ final ValidationReport.Builder<TypeElement> builder) {
+ // TODO(ronshapiro): use validateTypesAreDeclared when it is checked in
+ ModuleAnnotation moduleAnnotation = moduleAnnotation(moduleKind.getModuleAnnotation(subject));
+ for (AnnotationValue subcomponentAttribute :
+ moduleAnnotation.subcomponentsAsAnnotationValues()) {
+ asType(subcomponentAttribute)
+ .accept(
+ new SimpleTypeVisitor8<Void, Void>() {
+ @Override
+ protected Void defaultAction(TypeMirror e, Void aVoid) {
+ builder.addError(
+ e + " is not a valid subcomponent type",
+ subject,
+ moduleAnnotation.annotation(),
+ subcomponentAttribute);
+ return null;
+ }
+
+ @Override
+ public Void visitDeclared(DeclaredType declaredType, Void aVoid) {
+ TypeElement attributeType = asTypeElement(declaredType);
+ if (isAnyAnnotationPresent(attributeType, SUBCOMPONENT_TYPES)) {
+ validateSubcomponentHasBuilder(
+ attributeType, moduleAnnotation.annotation(), builder);
+ } else {
+ builder.addError(
+ isAnyAnnotationPresent(attributeType, SUBCOMPONENT_CREATOR_TYPES)
+ ? moduleSubcomponentsIncludesCreator(attributeType)
+ : moduleSubcomponentsIncludesNonSubcomponent(attributeType),
+ subject,
+ moduleAnnotation.annotation(),
+ subcomponentAttribute);
+ }
+
+ return null;
+ }
+ },
+ null);
+ }
+ }
+
+ private static String moduleSubcomponentsIncludesNonSubcomponent(TypeElement notSubcomponent) {
+ return notSubcomponent.getQualifiedName()
+ + " is not a @Subcomponent or @ProductionSubcomponent";
+ }
+
+ private static String moduleSubcomponentsIncludesCreator(
+ TypeElement moduleSubcomponentsAttribute) {
+ TypeElement subcomponentType =
+ MoreElements.asType(moduleSubcomponentsAttribute.getEnclosingElement());
+ ComponentCreatorAnnotation creatorAnnotation =
+ getOnlyElement(getCreatorAnnotations(moduleSubcomponentsAttribute));
+ return String.format(
+ "%s is a @%s.%s. Did you mean to use %s?",
+ moduleSubcomponentsAttribute.getQualifiedName(),
+ subcomponentAnnotation(subcomponentType).get().simpleName(),
+ creatorAnnotation.creatorKind().typeName(),
+ subcomponentType.getQualifiedName());
+ }
+
+ private static void validateSubcomponentHasBuilder(
+ TypeElement subcomponentAttribute,
+ AnnotationMirror moduleAnnotation,
+ ValidationReport.Builder<TypeElement> builder) {
+ if (getSubcomponentCreator(subcomponentAttribute).isPresent()) {
+ return;
+ }
+ builder.addError(
+ moduleSubcomponentsDoesntHaveCreator(subcomponentAttribute, moduleAnnotation),
+ builder.getSubject(),
+ moduleAnnotation);
+ }
+
+ private static String moduleSubcomponentsDoesntHaveCreator(
+ TypeElement subcomponent, AnnotationMirror moduleAnnotation) {
+ return String.format(
+ "%1$s doesn't have a @%2$s.Builder or @%2$s.Factory, which is required when used with "
+ + "@%3$s.subcomponents",
+ subcomponent.getQualifiedName(),
+ subcomponentAnnotation(subcomponent).get().simpleName(),
+ simpleName(moduleAnnotation));
+ }
+
+ enum ModuleMethodKind {
+ ABSTRACT_DECLARATION,
+ INSTANCE_BINDING,
+ STATIC_BINDING,
+ ;
+
+ static ModuleMethodKind ofMethod(ExecutableElement moduleMethod) {
+ if (moduleMethod.getModifiers().contains(STATIC)) {
+ return STATIC_BINDING;
+ } else if (moduleMethod.getModifiers().contains(ABSTRACT)) {
+ return ABSTRACT_DECLARATION;
+ } else {
+ return INSTANCE_BINDING;
+ }
+ }
+ }
+
+ private void validateModifiers(
+ TypeElement subject, ValidationReport.Builder<TypeElement> builder) {
+ // This coupled with the check for abstract modules in ComponentValidator guarantees that
+ // only modules without type parameters are referenced from @Component(modules={...}).
+ if (!subject.getTypeParameters().isEmpty() && !subject.getModifiers().contains(ABSTRACT)) {
+ builder.addError("Modules with type parameters must be abstract", subject);
+ }
+ }
+
+ private void validateMethodsWithSameName(
+ ValidationReport.Builder<TypeElement> builder,
+ ListMultimap<Name, ExecutableElement> bindingMethodsByName) {
+ for (Entry<Name, Collection<ExecutableElement>> entry :
+ bindingMethodsByName.asMap().entrySet()) {
+ if (entry.getValue().size() > 1) {
+ for (ExecutableElement offendingMethod : entry.getValue()) {
+ builder.addError(
+ String.format(
+ "Cannot have more than one binding method with the same name in a single module"),
+ offendingMethod);
+ }
+ }
+ }
+ }
+
+ private void validateReferencedModules(
+ TypeElement subject,
+ ModuleKind moduleKind,
+ Set<TypeElement> visitedModules,
+ ValidationReport.Builder<TypeElement> builder) {
+ // Validate that all the modules we include are valid for inclusion.
+ AnnotationMirror mirror = moduleKind.getModuleAnnotation(subject);
+ builder.addSubreport(
+ validateReferencedModules(
+ subject, mirror, moduleKind.legalIncludedModuleKinds(), visitedModules));
+ }
+
+ /**
+ * Validates modules included in a given module or installed in a given component.
+ *
+ * <p>Checks that the referenced modules are non-generic types annotated with {@code @Module} or
+ * {@code @ProducerModule}.
+ *
+ * <p>If the referenced module is in the {@linkplain #addKnownModules(Collection) known modules
+ * set} and has errors, reports an error at that module's inclusion.
+ *
+ * @param annotatedType the annotated module or component
+ * @param annotation the annotation specifying the referenced modules ({@code @Component},
+ * {@code @ProductionComponent}, {@code @Subcomponent}, {@code @ProductionSubcomponent},
+ * {@code @Module}, or {@code @ProducerModule})
+ * @param validModuleKinds the module kinds that the annotated type is permitted to include
+ */
+ ValidationReport<TypeElement> validateReferencedModules(
+ TypeElement annotatedType,
+ AnnotationMirror annotation,
+ ImmutableSet<ModuleKind> validModuleKinds,
+ Set<TypeElement> visitedModules) {
+ ValidationReport.Builder<TypeElement> subreport = ValidationReport.about(annotatedType);
+ ImmutableSet<? extends Class<? extends Annotation>> validModuleAnnotations =
+ validModuleKinds.stream().map(ModuleKind::annotation).collect(toImmutableSet());
+
+ for (AnnotationValue includedModule : getModules(annotation)) {
+ asType(includedModule)
+ .accept(
+ new SimpleTypeVisitor8<Void, Void>() {
+ @Override
+ protected Void defaultAction(TypeMirror mirror, Void p) {
+ reportError("%s is not a valid module type.", mirror);
+ return null;
+ }
+
+ @Override
+ public Void visitDeclared(DeclaredType t, Void p) {
+ TypeElement module = MoreElements.asType(t.asElement());
+ if (!t.getTypeArguments().isEmpty()) {
+ reportError(
+ "%s is listed as a module, but has type parameters",
+ module.getQualifiedName());
+ }
+ if (!isAnyAnnotationPresent(module, validModuleAnnotations)) {
+ reportError(
+ "%s is listed as a module, but is not annotated with %s",
+ module.getQualifiedName(),
+ (validModuleAnnotations.size() > 1 ? "one of " : "")
+ + validModuleAnnotations.stream()
+ .map(otherClass -> "@" + otherClass.getSimpleName())
+ .collect(joining(", ")));
+ } else if (knownModules.contains(module)
+ && !validate(module, visitedModules).isClean()) {
+ reportError("%s has errors", module.getQualifiedName());
+ }
+ if (metadataUtil.isCompanionObjectClass(module)) {
+ reportError(
+ "%s is listed as a module, but it is a companion object class. "
+ + "Add @Module to the enclosing class and reference that instead.",
+ module.getQualifiedName());
+ }
+ return null;
+ }
+
+ @FormatMethod
+ private void reportError(String format, Object... args) {
+ subreport.addError(
+ String.format(format, args), annotatedType, annotation, includedModule);
+ }
+ },
+ null);
+ }
+ return subreport.build();
+ }
+
+ private static ImmutableList<AnnotationValue> getModules(AnnotationMirror annotation) {
+ if (isModuleAnnotation(annotation)) {
+ return moduleAnnotation(annotation).includesAsAnnotationValues();
+ }
+ if (isComponentAnnotation(annotation)) {
+ return componentAnnotation(annotation).moduleValues();
+ }
+ throw new IllegalArgumentException(String.format("unsupported annotation: %s", annotation));
+ }
+
+ private void validateBindingMethodOverrides(
+ TypeElement subject,
+ ValidationReport.Builder<TypeElement> builder,
+ ImmutableListMultimap<Name, ExecutableElement> moduleMethodsByName,
+ ImmutableListMultimap<Name, ExecutableElement> bindingMethodsByName) {
+ // For every binding method, confirm it overrides nothing *and* nothing overrides it.
+ // Consider the following hierarchy:
+ // class Parent {
+ // @Provides Foo a() {}
+ // @Provides Foo b() {}
+ // Foo c() {}
+ // }
+ // class Child extends Parent {
+ // @Provides Foo a() {}
+ // Foo b() {}
+ // @Provides Foo c() {}
+ // }
+ // In each of those cases, we want to fail. "a" is clear, "b" because Child is overriding
+ // a binding method in Parent, and "c" because Child is defining a binding method that overrides
+ // Parent.
+ TypeElement currentClass = subject;
+ TypeMirror objectType = elements.getTypeElement(Object.class).asType();
+ // We keep track of methods that failed so we don't spam with multiple failures.
+ Set<ExecutableElement> failedMethods = Sets.newHashSet();
+ ListMultimap<Name, ExecutableElement> allMethodsByName =
+ MultimapBuilder.hashKeys().arrayListValues().build(moduleMethodsByName);
+
+ while (!types.isSameType(currentClass.getSuperclass(), objectType)) {
+ currentClass = MoreElements.asType(types.asElement(currentClass.getSuperclass()));
+ List<ExecutableElement> superclassMethods = methodsIn(currentClass.getEnclosedElements());
+ for (ExecutableElement superclassMethod : superclassMethods) {
+ Name name = superclassMethod.getSimpleName();
+ // For each method in the superclass, confirm our binding methods don't override it
+ for (ExecutableElement bindingMethod : bindingMethodsByName.get(name)) {
+ if (failedMethods.add(bindingMethod)
+ && elements.overrides(bindingMethod, superclassMethod, subject)) {
+ builder.addError(
+ String.format(
+ "Binding methods may not override another method. Overrides: %s",
+ methodSignatureFormatter.format(superclassMethod)),
+ bindingMethod);
+ }
+ }
+ // For each binding method in superclass, confirm our methods don't override it.
+ if (anyBindingMethodValidator.isBindingMethod(superclassMethod)) {
+ for (ExecutableElement method : allMethodsByName.get(name)) {
+ if (failedMethods.add(method)
+ && elements.overrides(method, superclassMethod, subject)) {
+ builder.addError(
+ String.format(
+ "Binding methods may not be overridden in modules. Overrides: %s",
+ methodSignatureFormatter.format(superclassMethod)),
+ method);
+ }
+ }
+ }
+ allMethodsByName.put(superclassMethod.getSimpleName(), superclassMethod);
+ }
+ }
+ }
+
+ private void validateModuleVisibility(
+ final TypeElement moduleElement,
+ ModuleKind moduleKind,
+ final ValidationReport.Builder<?> reportBuilder) {
+ ModuleAnnotation moduleAnnotation =
+ moduleAnnotation(getAnnotationMirror(moduleElement, moduleKind.annotation()).get());
+ Visibility moduleVisibility = Visibility.ofElement(moduleElement);
+ Visibility moduleEffectiveVisibility = effectiveVisibilityOfElement(moduleElement);
+ if (moduleVisibility.equals(PRIVATE)) {
+ reportBuilder.addError("Modules cannot be private.", moduleElement);
+ } else if (moduleEffectiveVisibility.equals(PRIVATE)) {
+ reportBuilder.addError("Modules cannot be enclosed in private types.", moduleElement);
+ }
+
+ switch (moduleElement.getNestingKind()) {
+ case ANONYMOUS:
+ throw new IllegalStateException("Can't apply @Module to an anonymous class");
+ case LOCAL:
+ throw new IllegalStateException("Local classes shouldn't show up in the processor");
+ case MEMBER:
+ case TOP_LEVEL:
+ if (moduleEffectiveVisibility.equals(PUBLIC)) {
+ ImmutableSet<TypeElement> invalidVisibilityIncludes =
+ getModuleIncludesWithInvalidVisibility(moduleAnnotation);
+ if (!invalidVisibilityIncludes.isEmpty()) {
+ reportBuilder.addError(
+ String.format(
+ "This module is public, but it includes non-public (or effectively non-public) "
+ + "modules (%s) that have non-static, non-abstract binding methods. Either "
+ + "reduce the visibility of this module, make the included modules "
+ + "public, or make all of the binding methods on the included modules "
+ + "abstract or static.",
+ formatListForErrorMessage(invalidVisibilityIncludes.asList())),
+ moduleElement);
+ }
+ }
+ }
+ }
+
+ private ImmutableSet<TypeElement> getModuleIncludesWithInvalidVisibility(
+ ModuleAnnotation moduleAnnotation) {
+ return moduleAnnotation.includes().stream()
+ .filter(include -> !effectiveVisibilityOfElement(include).equals(PUBLIC))
+ .filter(this::requiresModuleInstance)
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * Returns {@code true} if a module instance is needed for any of the binding methods on the given
+ * {@code module}. This is the case when the module has any binding methods that are neither
+ * {@code abstract} nor {@code static}. Alternatively, if the module is a Kotlin Object then the
+ * binding methods are considered {@code static}, requiring no module instance.
+ */
+ private boolean requiresModuleInstance(TypeElement module) {
+ // Note elements.getAllMembers(module) rather than module.getEnclosedElements() here: we need to
+ // include binding methods declared in supertypes because unlike most other validations being
+ // done in this class, which assume that supertype binding methods will be validated in a
+ // separate call to the validator since the supertype itself must be a @Module, we need to look
+ // at all the binding methods in the module's type hierarchy here.
+ boolean isKotlinObject =
+ metadataUtil.isObjectClass(module) || metadataUtil.isCompanionObjectClass(module);
+ if (isKotlinObject) {
+ return false;
+ }
+ return methodsIn(elements.getAllMembers(module)).stream()
+ .filter(anyBindingMethodValidator::isBindingMethod)
+ .map(ExecutableElement::getModifiers)
+ .anyMatch(modifiers -> !modifiers.contains(ABSTRACT) && !modifiers.contains(STATIC));
+ }
+
+ private void validateNoScopeAnnotationsOnModuleElement(
+ TypeElement module, ModuleKind moduleKind, ValidationReport.Builder<TypeElement> report) {
+ for (AnnotationMirror scope : getAnnotatedAnnotations(module, Scope.class)) {
+ report.addError(
+ String.format(
+ "@%ss cannot be scoped. Did you mean to scope a method instead?",
+ moduleKind.annotation().getSimpleName()),
+ module,
+ scope);
+ }
+ }
+
+ private void validateSelfCycles(
+ TypeElement module, ValidationReport.Builder<TypeElement> builder) {
+ ModuleAnnotation moduleAnnotation = moduleAnnotation(module).get();
+ moduleAnnotation
+ .includesAsAnnotationValues()
+ .forEach(
+ value ->
+ value.accept(
+ new SimpleAnnotationValueVisitor8<Void, Void>() {
+ @Override
+ public Void visitType(TypeMirror includedModule, Void aVoid) {
+ if (MoreTypes.equivalence().equivalent(module.asType(), includedModule)) {
+ String moduleKind = moduleAnnotation.annotationName();
+ builder.addError(
+ String.format("@%s cannot include themselves.", moduleKind),
+ module,
+ moduleAnnotation.annotation(),
+ value);
+ }
+ return null;
+ }
+ },
+ null));
+ }
+
+ private void validateCompanionModule(
+ TypeElement module, ValidationReport.Builder<TypeElement> builder) {
+ checkArgument(metadataUtil.hasEnclosedCompanionObject(module));
+ TypeElement companionModule = metadataUtil.getEnclosedCompanionObject(module);
+ List<ExecutableElement> companionModuleMethods =
+ methodsIn(companionModule.getEnclosedElements());
+ List<ExecutableElement> companionBindingMethods = new ArrayList<>();
+ for (ExecutableElement companionModuleMethod : companionModuleMethods) {
+ if (anyBindingMethodValidator.isBindingMethod(companionModuleMethod)) {
+ builder.addSubreport(anyBindingMethodValidator.validate(companionModuleMethod));
+ companionBindingMethods.add(companionModuleMethod);
+ }
+
+ // On normal modules only overriding other binding methods is disallowed, but for companion
+ // objects we are prohibiting any override. For this can rely on checking the @Override
+ // annotation since the Kotlin compiler will always produce them for overriding methods.
+ if (isAnnotationPresent(companionModuleMethod, Override.class)) {
+ builder.addError(
+ "Binding method in companion object may not override another method.",
+ companionModuleMethod);
+ }
+
+ // TODO(danysantiago): Be strict about the usage of @JvmStatic, i.e. tell user to remove it.
+ }
+
+ ImmutableListMultimap<Name, ExecutableElement> bindingMethodsByName =
+ Multimaps.index(companionBindingMethods, ExecutableElement::getSimpleName);
+ validateMethodsWithSameName(builder, bindingMethodsByName);
+
+ // If there are provision methods, then check the visibility. Companion objects are composed by
+ // an inner class and a static field, it is not enough to check the visibility on the type
+ // element or the field, therefore we check the metadata.
+ if (!companionBindingMethods.isEmpty() && metadataUtil.isVisibilityPrivate(companionModule)) {
+ builder.addError(
+ "A Companion Module with binding methods cannot be private.", companionModule);
+ }
+ }
+
+ private void validateModuleBindings(
+ TypeElement module, ValidationReport.Builder<TypeElement> report) {
+ BindingGraph bindingGraph =
+ bindingGraphFactory.create(
+ componentDescriptorFactory.moduleComponentDescriptor(module), true)
+ .topLevelBindingGraph();
+ if (!bindingGraphValidator.isValid(bindingGraph)) {
+ // Since the validator uses a DiagnosticReporter to report errors, the ValdiationReport won't
+ // have any Items for them. We have to tell the ValidationReport that some errors were
+ // reported for the subject.
+ report.markDirty();
+ }
+ }
+
+ private static String formatListForErrorMessage(List<?> things) {
+ switch (things.size()) {
+ case 0:
+ return "";
+ case 1:
+ return things.get(0).toString();
+ default:
+ StringBuilder output = new StringBuilder();
+ Joiner.on(", ").appendTo(output, things.subList(0, things.size() - 1));
+ output.append(" and ").append(things.get(things.size() - 1));
+ return output.toString();
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/MonitoringModuleGenerator.java b/java/dagger/internal/codegen/validation/MonitoringModuleGenerator.java
new file mode 100644
index 000000000..2c72e5f0d
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/MonitoringModuleGenerator.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.javapoet.TypeNames.PRODUCTION_COMPONENT_MONITOR_FACTORY;
+import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
+import static dagger.internal.codegen.javapoet.TypeNames.setOf;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeSpec;
+import dagger.Module;
+import dagger.Provides;
+import dagger.internal.codegen.base.SourceFileGenerator;
+import dagger.internal.codegen.binding.SourceFiles;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.multibindings.Multibinds;
+import dagger.producers.ProductionScope;
+import dagger.producers.monitoring.ProductionComponentMonitor;
+import dagger.producers.monitoring.internal.Monitors;
+import java.util.Optional;
+import javax.annotation.processing.Filer;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+/** Generates a monitoring module for use with production components. */
+final class MonitoringModuleGenerator extends SourceFileGenerator<TypeElement> {
+
+ @Inject
+ MonitoringModuleGenerator(Filer filer, DaggerElements elements, SourceVersion sourceVersion) {
+ super(filer, elements, sourceVersion);
+ }
+
+ @Override
+ public ClassName nameGeneratedType(TypeElement componentElement) {
+ return SourceFiles.generatedMonitoringModuleName(componentElement);
+ }
+
+ @Override
+ public Element originatingElement(TypeElement componentElement) {
+ return componentElement;
+ }
+
+ @Override
+ public Optional<TypeSpec.Builder> write(TypeElement componentElement) {
+ return Optional.of(
+ classBuilder(nameGeneratedType(componentElement))
+ .addAnnotation(Module.class)
+ .addModifiers(ABSTRACT)
+ .addMethod(privateConstructor())
+ .addMethod(setOfFactories())
+ .addMethod(monitor(componentElement)));
+ }
+
+ private MethodSpec privateConstructor() {
+ return constructorBuilder().addModifiers(PRIVATE).build();
+ }
+
+ private MethodSpec setOfFactories() {
+ return methodBuilder("setOfFactories")
+ .addAnnotation(Multibinds.class)
+ .addModifiers(ABSTRACT)
+ .returns(setOf(PRODUCTION_COMPONENT_MONITOR_FACTORY))
+ .build();
+ }
+
+ private MethodSpec monitor(TypeElement componentElement) {
+ return methodBuilder("monitor")
+ .returns(ProductionComponentMonitor.class)
+ .addModifiers(STATIC)
+ .addAnnotation(Provides.class)
+ .addAnnotation(ProductionScope.class)
+ .addParameter(providerOf(ClassName.get(componentElement.asType())), "component")
+ .addParameter(
+ providerOf(setOf(PRODUCTION_COMPONENT_MONITOR_FACTORY)), "factories")
+ .addStatement(
+ "return $T.createMonitorForComponent(component, factories)", Monitors.class)
+ .build();
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/MonitoringModuleProcessingStep.java b/java/dagger/internal/codegen/validation/MonitoringModuleProcessingStep.java
new file mode 100644
index 000000000..5d4c64e39
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/MonitoringModuleProcessingStep.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableSet;
+import dagger.producers.ProductionComponent;
+import dagger.producers.ProductionSubcomponent;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+import javax.annotation.processing.Messager;
+import javax.inject.Inject;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * A processing step that is responsible for generating a special module for a {@link
+ * ProductionComponent} or {@link ProductionSubcomponent}.
+ */
+public final class MonitoringModuleProcessingStep extends TypeCheckingProcessingStep<TypeElement> {
+ private final Messager messager;
+ private final MonitoringModuleGenerator monitoringModuleGenerator;
+
+ @Inject
+ MonitoringModuleProcessingStep(
+ Messager messager, MonitoringModuleGenerator monitoringModuleGenerator) {
+ super(MoreElements::asType);
+ this.messager = messager;
+ this.monitoringModuleGenerator = monitoringModuleGenerator;
+ }
+
+ @Override
+ public Set<? extends Class<? extends Annotation>> annotations() {
+ return ImmutableSet.of(ProductionComponent.class, ProductionSubcomponent.class);
+ }
+
+ @Override
+ protected void process(
+ TypeElement element, ImmutableSet<Class<? extends Annotation>> annotations) {
+ monitoringModuleGenerator.generate(MoreElements.asType(element), messager);
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/MultibindingAnnotationsProcessingStep.java b/java/dagger/internal/codegen/validation/MultibindingAnnotationsProcessingStep.java
new file mode 100644
index 000000000..fd75eac24
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/MultibindingAnnotationsProcessingStep.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableSet;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.multibindings.IntoMap;
+import dagger.multibindings.IntoSet;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+import javax.annotation.processing.Messager;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+
+/**
+ * Processing step that verifies that {@link IntoSet}, {@link ElementsIntoSet} and {@link IntoMap}
+ * are not present on non-binding methods.
+ */
+public final class MultibindingAnnotationsProcessingStep
+ extends TypeCheckingProcessingStep<ExecutableElement> {
+ private final AnyBindingMethodValidator anyBindingMethodValidator;
+ private final Messager messager;
+
+ @Inject
+ MultibindingAnnotationsProcessingStep(
+ AnyBindingMethodValidator anyBindingMethodValidator, Messager messager) {
+ super(MoreElements::asExecutable);
+ this.anyBindingMethodValidator = anyBindingMethodValidator;
+ this.messager = messager;
+ }
+
+ @Override
+ public Set<? extends Class<? extends Annotation>> annotations() {
+ return ImmutableSet.of(IntoSet.class, ElementsIntoSet.class, IntoMap.class);
+ }
+
+ @Override
+ protected void process(
+ ExecutableElement method, ImmutableSet<Class<? extends Annotation>> annotations) {
+ if (!anyBindingMethodValidator.isBindingMethod(method)) {
+ annotations.forEach(
+ annotation ->
+ messager.printMessage(
+ ERROR,
+ "Multibinding annotations may only be on @Provides, @Produces, or @Binds methods",
+ method,
+ getAnnotationMirror(method, annotation).get()));
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/MultibindsMethodValidator.java b/java/dagger/internal/codegen/validation/MultibindsMethodValidator.java
new file mode 100644
index 000000000..8829667b2
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/MultibindsMethodValidator.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static dagger.internal.codegen.base.FrameworkTypes.isFrameworkType;
+import static dagger.internal.codegen.validation.BindingElementValidator.AllowsMultibindings.NO_MULTIBINDINGS;
+import static dagger.internal.codegen.validation.BindingElementValidator.AllowsScoping.NO_SCOPING;
+import static dagger.internal.codegen.validation.BindingMethodValidator.Abstractness.MUST_BE_ABSTRACT;
+import static dagger.internal.codegen.validation.BindingMethodValidator.ExceptionSuperclass.NO_EXCEPTIONS;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableSet;
+import dagger.Module;
+import dagger.internal.codegen.base.MapType;
+import dagger.internal.codegen.base.SetType;
+import dagger.internal.codegen.binding.InjectionAnnotations;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.multibindings.Multibinds;
+import dagger.producers.ProducerModule;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.TypeMirror;
+
+/** A validator for {@link Multibinds} methods. */
+class MultibindsMethodValidator extends BindingMethodValidator {
+
+ /** Creates a validator for {@link Multibinds @Multibinds} methods. */
+ @Inject
+ MultibindsMethodValidator(
+ DaggerElements elements,
+ DaggerTypes types,
+ KotlinMetadataUtil kotlinMetadataUtil,
+ DependencyRequestValidator dependencyRequestValidator,
+ InjectionAnnotations injectionAnnotations) {
+ super(
+ elements,
+ types,
+ kotlinMetadataUtil,
+ Multibinds.class,
+ ImmutableSet.of(Module.class, ProducerModule.class),
+ dependencyRequestValidator,
+ MUST_BE_ABSTRACT,
+ NO_EXCEPTIONS,
+ NO_MULTIBINDINGS,
+ NO_SCOPING,
+ injectionAnnotations);
+ }
+
+ @Override
+ protected ElementValidator elementValidator(ExecutableElement element) {
+ return new Validator(element);
+ }
+
+ private class Validator extends MethodValidator {
+ Validator(ExecutableElement element) {
+ super(element);
+ }
+
+ @Override
+ protected void checkParameters() {
+ if (!element.getParameters().isEmpty()) {
+ report.addError(bindingMethods("cannot have parameters"));
+ }
+ }
+
+ /** Adds an error unless the method returns a {@code Map<K, V>} or {@code Set<T>}. */
+ @Override
+ protected void checkType() {
+ if (!isPlainMap(element.getReturnType())
+ && !isPlainSet(element.getReturnType())) {
+ report.addError(bindingMethods("must return Map<K, V> or Set<T>"));
+ }
+ }
+
+ private boolean isPlainMap(TypeMirror returnType) {
+ if (!MapType.isMap(returnType)) {
+ return false;
+ }
+ MapType mapType = MapType.from(returnType);
+ return !mapType.isRawType()
+ && MoreTypes.isType(mapType.valueType()) // No wildcards.
+ && !isFrameworkType(mapType.valueType());
+ }
+
+ private boolean isPlainSet(TypeMirror returnType) {
+ if (!SetType.isSet(returnType)) {
+ return false;
+ }
+ SetType setType = SetType.from(returnType);
+ return !setType.isRawType()
+ && MoreTypes.isType(setType.elementType()) // No wildcards.
+ && !isFrameworkType(setType.elementType());
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/PackageNameCompressor.java b/java/dagger/internal/codegen/validation/PackageNameCompressor.java
new file mode 100644
index 000000000..6b7c720b2
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/PackageNameCompressor.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static java.util.Comparator.comparing;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Munges an error message to remove/shorten package names and adds a legend at the end.
+ */
+final class PackageNameCompressor {
+
+ static final String LEGEND_HEADER =
+ "\n\n======================\nFull classname legend:\n======================\n";
+ static final String LEGEND_FOOTER =
+ "========================\nEnd of classname legend:\n========================\n";
+
+ private static final ImmutableSet<String> PACKAGES_SKIPPED_IN_LEGEND = ImmutableSet.of(
+ "java.lang.",
+ "java.util.");
+
+ private static final Splitter PACKAGE_SPLITTER = Splitter.on('.');
+
+ private static final Joiner PACKAGE_JOINER = Joiner.on('.');
+
+ // TODO(erichang): Consider validating this regex by also passing in all of the known types from
+ // keys, module names, component names, etc and checking against that list. This may have some
+ // extra complications with taking apart types like List<Foo> to get the inner class names.
+ private static final Pattern CLASSNAME_PATTERN =
+ // Match lowercase package names with trailing dots. Start with a non-word character so we
+ // don't match substrings in like Bar.Foo and match the ar.Foo. Start a group to not include
+ // the non-word character.
+ Pattern.compile("[\\W](([a-z_0-9]++[.])++"
+ // Then match a name starting with an uppercase letter. This is the outer class name.
+ + "[A-Z][\\w$]++)");
+
+ /**
+ * Compresses an error message by stripping the packages out of class names and adding them
+ * to a legend at the bottom of the error.
+ */
+ static String compressPackagesInMessage(String input) {
+ Matcher matcher = CLASSNAME_PATTERN.matcher(input);
+
+ Set<String> names = new HashSet<>();
+ // Find all classnames in the error. Note that if our regex isn't complete, it just means the
+ // classname is left in the full form, which is a fine fallback.
+ while (matcher.find()) {
+ String name = matcher.group(1);
+ names.add(name);
+ }
+ // Now dedupe any conflicts. Use a TreeMap since we're going to need the legend sorted anyway.
+ // This map is from short name to full name.
+ Map<String, String> replacementMap = shortenNames(names);
+
+ // If we have nothing to replace, just return the original.
+ if (replacementMap.isEmpty()) {
+ return input;
+ }
+
+ // Find the longest key for building the legend
+ int longestKey = replacementMap.keySet().stream().max(comparing(String::length)).get().length();
+
+ String replacedString = input;
+ StringBuilder legendBuilder = new StringBuilder();
+ for (Map.Entry<String, String> entry : replacementMap.entrySet()) {
+ String shortName = entry.getKey();
+ String fullName = entry.getValue();
+ // Do the replacements in the message
+ replacedString = replacedString.replace(fullName, shortName);
+
+ // Skip certain prefixes. We need to check the shortName for a . though in case
+ // there was some type of conflict like java.util.concurrent.Future and
+ // java.util.foo.Future that got shortened to concurrent.Future and foo.Future.
+ // In those cases we do not want to skip the legend. We only skip if the class
+ // is directly in that package.
+ String prefix = fullName.substring(0, fullName.length() - shortName.length());
+ if (PACKAGES_SKIPPED_IN_LEGEND.contains(prefix) && !shortName.contains(".")) {
+ continue;
+ }
+
+ // Add to the legend
+ legendBuilder
+ .append(shortName)
+ .append(": ")
+ // Add enough spaces to adjust the columns
+ .append(Strings.repeat(" ", longestKey - shortName.length()))
+ .append(fullName)
+ .append("\n");
+ }
+
+ return legendBuilder.length() == 0 ? replacedString
+ : replacedString + LEGEND_HEADER + legendBuilder + LEGEND_FOOTER;
+ }
+
+ /**
+ * Returns a map from short name to full name after resolving conflicts. This resolves conflicts
+ * by adding on segments of the package name until they are unique. For example, com.foo.Baz and
+ * com.bar.Baz will conflict on Baz and then resolve with foo.Baz and bar.Baz as replacements.
+ */
+ private static Map<String, String> shortenNames(Collection<String> names) {
+ HashMultimap<String, List<String>> shortNameToPartsMap = HashMultimap.create();
+ for (String name : names) {
+ List<String> parts = new ArrayList<>(PACKAGE_SPLITTER.splitToList(name));
+ // Start with the just the class name as the simple name
+ String className = parts.remove(parts.size() - 1);
+ shortNameToPartsMap.put(className, parts);
+ }
+
+ // Iterate through looking for conflicts adding the next part of the package until there are no
+ // more conflicts
+ while (true) {
+ // Save the keys with conflicts to avoid concurrent modification issues
+ List<String> conflictingShortNames = new ArrayList<>();
+ for (Map.Entry<String, Collection<List<String>>> entry
+ : shortNameToPartsMap.asMap().entrySet()) {
+ if (entry.getValue().size() > 1) {
+ conflictingShortNames.add(entry.getKey());
+ }
+ }
+
+ if (conflictingShortNames.isEmpty()) {
+ break;
+ }
+
+ // For all conflicts, add in the next part of the package
+ for (String conflictingShortName : conflictingShortNames) {
+ Set<List<String>> partsCollection = shortNameToPartsMap.removeAll(conflictingShortName);
+ for (List<String> parts : partsCollection) {
+ String newShortName = parts.remove(parts.size() - 1) + "." + conflictingShortName;
+ // If we've removed the last part of the package, then just skip it entirely because
+ // now we're not shortening it at all.
+ if (!parts.isEmpty()) {
+ shortNameToPartsMap.put(newShortName, parts);
+ }
+ }
+ }
+ }
+
+ // Turn the multimap into a regular map now that conflicts have been resolved. Use a TreeMap
+ // since we're going to need the legend sorted anyway. This map is from short name to full name.
+ Map<String, String> replacementMap = new TreeMap<>();
+ for (Map.Entry<String, Collection<List<String>>> entry
+ : shortNameToPartsMap.asMap().entrySet()) {
+ replacementMap.put(
+ entry.getKey(),
+ PACKAGE_JOINER.join(Iterables.getOnlyElement(entry.getValue())) + "." + entry.getKey());
+ }
+ return replacementMap;
+ }
+
+ private PackageNameCompressor() {}
+}
diff --git a/java/dagger/internal/codegen/validation/ProducesMethodValidator.java b/java/dagger/internal/codegen/validation/ProducesMethodValidator.java
new file mode 100644
index 000000000..1606ebec0
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/ProducesMethodValidator.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.validation.BindingElementValidator.AllowsMultibindings.ALLOWS_MULTIBINDINGS;
+import static dagger.internal.codegen.validation.BindingElementValidator.AllowsScoping.NO_SCOPING;
+import static dagger.internal.codegen.validation.BindingMethodValidator.Abstractness.MUST_BE_CONCRETE;
+import static dagger.internal.codegen.validation.BindingMethodValidator.ExceptionSuperclass.EXCEPTION;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.internal.codegen.binding.ConfigurationAnnotations;
+import dagger.internal.codegen.binding.InjectionAnnotations;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.producers.ProducerModule;
+import dagger.producers.Produces;
+import java.util.Optional;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/** A validator for {@link Produces} methods. */
+final class ProducesMethodValidator extends BindingMethodValidator {
+
+ @Inject
+ ProducesMethodValidator(
+ DaggerElements elements,
+ DaggerTypes types,
+ KotlinMetadataUtil kotlinMetadataUtil,
+ DependencyRequestValidator dependencyRequestValidator,
+ InjectionAnnotations injectionAnnotations) {
+ super(
+ elements,
+ types,
+ kotlinMetadataUtil,
+ dependencyRequestValidator,
+ Produces.class,
+ ProducerModule.class,
+ MUST_BE_CONCRETE,
+ EXCEPTION,
+ ALLOWS_MULTIBINDINGS,
+ NO_SCOPING,
+ injectionAnnotations);
+ }
+
+ @Override
+ protected String elementsIntoSetNotASetMessage() {
+ return "@Produces methods of type set values must return a Set or ListenableFuture of Set";
+ }
+
+ @Override
+ protected String badTypeMessage() {
+ return "@Produces methods can return only a primitive, an array, a type variable, "
+ + "a declared type, or a ListenableFuture of one of those types";
+ }
+
+ @Override
+ protected ElementValidator elementValidator(ExecutableElement element) {
+ return new Validator(element);
+ }
+
+ private class Validator extends MethodValidator {
+ Validator(ExecutableElement element) {
+ super(element);
+ }
+
+ @Override
+ protected void checkAdditionalMethodProperties() {
+ checkNullable();
+ }
+
+ /** Adds a warning if a {@link Produces @Produces} method is declared nullable. */
+ // TODO(beder): Properly handle nullable with producer methods.
+ private void checkNullable() {
+ if (ConfigurationAnnotations.getNullableType(element).isPresent()) {
+ report.addWarning("@Nullable on @Produces methods does not do anything");
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Allows {@code keyType} to be a {@link ListenableFuture} of an otherwise-valid key type.
+ */
+ @Override
+ protected void checkKeyType(TypeMirror keyType) {
+ Optional<TypeMirror> typeToCheck = unwrapListenableFuture(keyType);
+ if (typeToCheck.isPresent()) {
+ super.checkKeyType(typeToCheck.get());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Allows an {@link ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES} method to return
+ * a {@link ListenableFuture} of a {@link Set} as well.
+ */
+ @Override
+ protected void checkSetValuesType() {
+ Optional<TypeMirror> typeToCheck = unwrapListenableFuture(element.getReturnType());
+ if (typeToCheck.isPresent()) {
+ checkSetValuesType(typeToCheck.get());
+ }
+ }
+
+ private Optional<TypeMirror> unwrapListenableFuture(TypeMirror type) {
+ if (MoreTypes.isType(type) && MoreTypes.isTypeOf(ListenableFuture.class, type)) {
+ DeclaredType declaredType = MoreTypes.asDeclared(type);
+ if (declaredType.getTypeArguments().isEmpty()) {
+ report.addError("@Produces methods cannot return a raw ListenableFuture");
+ return Optional.empty();
+ } else {
+ return Optional.of((TypeMirror) getOnlyElement(declaredType.getTypeArguments()));
+ }
+ }
+ return Optional.of(type);
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/ProvidesMethodValidator.java b/java/dagger/internal/codegen/validation/ProvidesMethodValidator.java
new file mode 100644
index 000000000..6b4f30387
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/ProvidesMethodValidator.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static dagger.internal.codegen.validation.BindingElementValidator.AllowsMultibindings.ALLOWS_MULTIBINDINGS;
+import static dagger.internal.codegen.validation.BindingElementValidator.AllowsScoping.ALLOWS_SCOPING;
+import static dagger.internal.codegen.validation.BindingMethodValidator.Abstractness.MUST_BE_CONCRETE;
+import static dagger.internal.codegen.validation.BindingMethodValidator.ExceptionSuperclass.RUNTIME_EXCEPTION;
+
+import com.google.common.collect.ImmutableSet;
+import dagger.Module;
+import dagger.Provides;
+import dagger.internal.codegen.binding.InjectionAnnotations;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.producers.ProducerModule;
+import javax.inject.Inject;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+
+/** A validator for {@link Provides} methods. */
+final class ProvidesMethodValidator extends BindingMethodValidator {
+
+ private final DependencyRequestValidator dependencyRequestValidator;
+
+ @Inject
+ ProvidesMethodValidator(
+ DaggerElements elements,
+ DaggerTypes types,
+ KotlinMetadataUtil kotlinMetadataUtil,
+ DependencyRequestValidator dependencyRequestValidator,
+ InjectionAnnotations injectionAnnotations) {
+ super(
+ elements,
+ types,
+ kotlinMetadataUtil,
+ Provides.class,
+ ImmutableSet.of(Module.class, ProducerModule.class),
+ dependencyRequestValidator,
+ MUST_BE_CONCRETE,
+ RUNTIME_EXCEPTION,
+ ALLOWS_MULTIBINDINGS,
+ ALLOWS_SCOPING,
+ injectionAnnotations);
+ this.dependencyRequestValidator = dependencyRequestValidator;
+ }
+
+ @Override
+ protected ElementValidator elementValidator(ExecutableElement element) {
+ return new Validator(element);
+ }
+
+ private class Validator extends MethodValidator {
+ Validator(ExecutableElement element) {
+ super(element);
+ }
+
+ @Override
+ protected void checkAdditionalMethodProperties() {
+ }
+
+ /** Adds an error if a {@link Provides @Provides} method depends on a producer type. */
+ @Override
+ protected void checkParameter(VariableElement parameter) {
+ super.checkParameter(parameter);
+ dependencyRequestValidator.checkNotProducer(report, parameter);
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/TypeCheckingProcessingStep.java b/java/dagger/internal/codegen/validation/TypeCheckingProcessingStep.java
new file mode 100644
index 000000000..57867f13b
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/TypeCheckingProcessingStep.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.SetMultimap;
+import java.lang.annotation.Annotation;
+import java.util.function.Function;
+import javax.lang.model.element.Element;
+
+/**
+ * A {@link ProcessingStep} that processes one element at a time and defers any for which {@link
+ * TypeNotPresentException} is thrown.
+ */
+// TODO(dpb): Contribute to auto-common.
+public abstract class TypeCheckingProcessingStep<E extends Element> implements ProcessingStep {
+ private final Function<Element, E> downcaster;
+
+ protected TypeCheckingProcessingStep(Function<Element, E> downcaster) {
+ this.downcaster = checkNotNull(downcaster);
+ }
+
+ @Override
+ public ImmutableSet<Element> process(
+ SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
+ ImmutableSet.Builder<Element> deferredElements = ImmutableSet.builder();
+ ImmutableSetMultimap.copyOf(elementsByAnnotation)
+ .inverse()
+ .asMap()
+ .forEach(
+ (element, annotations) -> {
+ try {
+ process(downcaster.apply(element), ImmutableSet.copyOf(annotations));
+ } catch (TypeNotPresentException e) {
+ deferredElements.add(element);
+ }
+ });
+ return deferredElements.build();
+ }
+
+ /**
+ * Processes one element. If this method throws {@link TypeNotPresentException}, the element will
+ * be deferred until the next round of processing.
+ *
+ * @param annotations the subset of {@link ProcessingStep#annotations()} that annotate {@code
+ * element}
+ */
+ protected abstract void process(E element, ImmutableSet<Class<? extends Annotation>> annotations);
+}
diff --git a/java/dagger/internal/codegen/validation/TypeHierarchyValidator.java b/java/dagger/internal/codegen/validation/TypeHierarchyValidator.java
new file mode 100644
index 000000000..5fa02707a
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/TypeHierarchyValidator.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import com.google.auto.common.MoreTypes;
+import com.google.auto.common.SuperficialValidation;
+import com.google.common.base.Equivalence;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.util.ArrayDeque;
+import java.util.HashSet;
+import java.util.Queue;
+import java.util.Set;
+import javax.lang.model.type.TypeMirror;
+
+/** Utility methods for validating the type hierarchy of a given type. */
+final class TypeHierarchyValidator {
+ private TypeHierarchyValidator() {}
+
+ /**
+ * Validate the type hierarchy of the given type including all super classes, interfaces, and
+ * type parameters.
+ *
+ * @throws TypeNotPresentException if an type in the hierarchy is not valid.
+ */
+ public static void validateTypeHierarchy(TypeMirror type, DaggerTypes types) {
+ Queue<TypeMirror> queue = new ArrayDeque<>();
+ Set<Equivalence.Wrapper<TypeMirror>> queued = new HashSet<>();
+ queue.add(type);
+ queued.add(MoreTypes.equivalence().wrap(type));
+ while (!queue.isEmpty()) {
+ TypeMirror currType = queue.remove();
+ if (!SuperficialValidation.validateType(currType)) {
+ throw new TypeNotPresentException(currType.toString(), null);
+ }
+ for (TypeMirror superType : types.directSupertypes(currType)) {
+ if (queued.add(MoreTypes.equivalence().wrap(superType))) {
+ queue.add(superType);
+ }
+ }
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/validation/Validation.java b/java/dagger/internal/codegen/validation/Validation.java
new file mode 100644
index 000000000..620b0f041
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/Validation.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import javax.inject.Qualifier;
+
+/**
+ * Qualifier annotation for the {@link dagger.spi.BindingGraphPlugin}s that are used to implement
+ * core Dagger validation.
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Qualifier
+public @interface Validation {}
diff --git a/java/dagger/internal/codegen/validation/ValidationReport.java b/java/dagger/internal/codegen/validation/ValidationReport.java
new file mode 100644
index 000000000..7f3737595
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/ValidationReport.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.validation;
+
+import static dagger.internal.codegen.base.ElementFormatter.elementToString;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static javax.tools.Diagnostic.Kind.ERROR;
+import static javax.tools.Diagnostic.Kind.NOTE;
+import static javax.tools.Diagnostic.Kind.WARNING;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.graph.Traverser;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.google.errorprone.annotations.CheckReturnValue;
+import java.util.Optional;
+import javax.annotation.processing.Messager;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.tools.Diagnostic;
+import javax.tools.Diagnostic.Kind;
+
+/** A collection of issues to report for source code. */
+public final class ValidationReport<T extends Element> {
+ private static final Traverser<ValidationReport<?>> SUBREPORTS =
+ Traverser.forTree(report -> report.subreports);
+
+ private final T subject;
+ private final ImmutableSet<Item> items;
+ private final ImmutableSet<ValidationReport<?>> subreports;
+ private final boolean markedDirty;
+ private boolean hasPrintedErrors;
+
+ private ValidationReport(
+ T subject,
+ ImmutableSet<Item> items,
+ ImmutableSet<ValidationReport<?>> subreports,
+ boolean markedDirty) {
+ this.subject = subject;
+ this.items = items;
+ this.subreports = subreports;
+ this.markedDirty = markedDirty;
+ }
+
+ /** Returns the items from this report and all transitive subreports. */
+ public ImmutableSet<Item> allItems() {
+ return ImmutableSet.copyOf(SUBREPORTS.depthFirstPreOrder(this))
+ .stream()
+ .flatMap(report -> report.items.stream())
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * Returns {@code true} if there are no errors in this report or any subreports and markedDirty is
+ * {@code false}.
+ */
+ public boolean isClean() {
+ if (markedDirty) {
+ return false;
+ }
+ for (Item item : items) {
+ switch (item.kind()) {
+ case ERROR:
+ return false;
+ default:
+ break;
+ }
+ }
+ for (ValidationReport<?> subreport : subreports) {
+ if (!subreport.isClean()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Prints all messages to {@code messager} (and recurs for subreports). If a
+ * message's {@linkplain Item#element() element} is contained within the report's subject,
+ * associates the message with the message's element. Otherwise, since {@link Diagnostic}
+ * reporting is expected to be associated with elements that are currently being compiled,
+ * associates the message with the subject itself and prepends a reference to the item's element.
+ */
+ public void printMessagesTo(Messager messager) {
+ if (hasPrintedErrors) {
+ // Avoid printing the errors from this validation report more than once.
+ return;
+ }
+ hasPrintedErrors = true;
+ for (Item item : items) {
+ if (isEnclosedIn(subject, item.element())) {
+ if (item.annotation().isPresent()) {
+ if (item.annotationValue().isPresent()) {
+ messager.printMessage(
+ item.kind(),
+ item.message(),
+ item.element(),
+ item.annotation().get(),
+ item.annotationValue().get());
+ } else {
+ messager.printMessage(
+ item.kind(), item.message(), item.element(), item.annotation().get());
+ }
+ } else {
+ messager.printMessage(item.kind(), item.message(), item.element());
+ }
+ } else {
+ String message = String.format("[%s] %s", elementToString(item.element()), item.message());
+ messager.printMessage(item.kind(), message, subject);
+ }
+ }
+ for (ValidationReport<?> subreport : subreports) {
+ subreport.printMessagesTo(messager);
+ }
+ }
+
+ private static boolean isEnclosedIn(Element parent, Element child) {
+ Element current = child;
+ while (current != null) {
+ if (current.equals(parent)) {
+ return true;
+ }
+ current = current.getEnclosingElement();
+ }
+ return false;
+ }
+
+ /** Metadata about a {@link ValidationReport} item. */
+ @AutoValue
+ public abstract static class Item {
+ public abstract String message();
+ public abstract Kind kind();
+ public abstract Element element();
+ public abstract Optional<AnnotationMirror> annotation();
+ abstract Optional<AnnotationValue> annotationValue();
+ }
+
+ public static <T extends Element> Builder<T> about(T subject) {
+ return new Builder<>(subject);
+ }
+
+ /** A {@link ValidationReport} builder. */
+ @CanIgnoreReturnValue
+ public static final class Builder<T extends Element> {
+ private final T subject;
+ private final ImmutableSet.Builder<Item> items = ImmutableSet.builder();
+ private final ImmutableSet.Builder<ValidationReport<?>> subreports = ImmutableSet.builder();
+ private boolean markedDirty;
+
+ private Builder(T subject) {
+ this.subject = subject;
+ }
+
+ @CheckReturnValue
+ T getSubject() {
+ return subject;
+ }
+
+ Builder<T> addItems(Iterable<Item> newItems) {
+ items.addAll(newItems);
+ return this;
+ }
+
+ public Builder<T> addError(String message) {
+ return addError(message, subject);
+ }
+
+ public Builder<T> addError(String message, Element element) {
+ return addItem(message, ERROR, element);
+ }
+
+ public Builder<T> addError(String message, Element element, AnnotationMirror annotation) {
+ return addItem(message, ERROR, element, annotation);
+ }
+
+ public Builder<T> addError(
+ String message,
+ Element element,
+ AnnotationMirror annotation,
+ AnnotationValue annotationValue) {
+ return addItem(message, ERROR, element, annotation, annotationValue);
+ }
+
+ Builder<T> addWarning(String message) {
+ return addWarning(message, subject);
+ }
+
+ Builder<T> addWarning(String message, Element element) {
+ return addItem(message, WARNING, element);
+ }
+
+ Builder<T> addWarning(String message, Element element, AnnotationMirror annotation) {
+ return addItem(message, WARNING, element, annotation);
+ }
+
+ Builder<T> addWarning(
+ String message,
+ Element element,
+ AnnotationMirror annotation,
+ AnnotationValue annotationValue) {
+ return addItem(message, WARNING, element, annotation, annotationValue);
+ }
+
+ Builder<T> addNote(String message) {
+ return addNote(message, subject);
+ }
+
+ Builder<T> addNote(String message, Element element) {
+ return addItem(message, NOTE, element);
+ }
+
+ Builder<T> addNote(String message, Element element, AnnotationMirror annotation) {
+ return addItem(message, NOTE, element, annotation);
+ }
+
+ Builder<T> addNote(
+ String message,
+ Element element,
+ AnnotationMirror annotation,
+ AnnotationValue annotationValue) {
+ return addItem(message, NOTE, element, annotation, annotationValue);
+ }
+
+ Builder<T> addItem(String message, Kind kind, Element element) {
+ return addItem(message, kind, element, Optional.empty(), Optional.empty());
+ }
+
+ Builder<T> addItem(String message, Kind kind, Element element, AnnotationMirror annotation) {
+ return addItem(message, kind, element, Optional.of(annotation), Optional.empty());
+ }
+
+ Builder<T> addItem(
+ String message,
+ Kind kind,
+ Element element,
+ AnnotationMirror annotation,
+ AnnotationValue annotationValue) {
+ return addItem(message, kind, element, Optional.of(annotation), Optional.of(annotationValue));
+ }
+
+ private Builder<T> addItem(
+ String message,
+ Kind kind,
+ Element element,
+ Optional<AnnotationMirror> annotation,
+ Optional<AnnotationValue> annotationValue) {
+ items.add(
+ new AutoValue_ValidationReport_Item(message, kind, element, annotation, annotationValue));
+ return this;
+ }
+
+ /**
+ * If called, then {@link #isClean()} will return {@code false} even if there are no error items
+ * in the report.
+ */
+ void markDirty() {
+ this.markedDirty = true;
+ }
+
+ public Builder<T> addSubreport(ValidationReport<?> subreport) {
+ subreports.add(subreport);
+ return this;
+ }
+
+ @CheckReturnValue
+ public ValidationReport<T> build() {
+ return new ValidationReport<>(subject, items.build(), subreports.build(), markedDirty);
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/AnnotationCreatorGenerator.java b/java/dagger/internal/codegen/writing/AnnotationCreatorGenerator.java
new file mode 100644
index 000000000..96e6340af
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/AnnotationCreatorGenerator.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.binding.AnnotationExpression.createMethodName;
+import static dagger.internal.codegen.binding.AnnotationExpression.getAnnotationCreatorClassName;
+import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.base.SourceFileGenerator;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import java.util.LinkedHashSet;
+import java.util.Optional;
+import java.util.Set;
+import javax.annotation.processing.Filer;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.util.SimpleTypeVisitor6;
+
+/**
+ * Generates classes that create annotation instances for an annotation type. The generated class
+ * will have a private empty constructor, a static method that creates the annotation type itself,
+ * and a static method that creates each annotation type that is nested in the top-level annotation
+ * type.
+ *
+ * <p>So for an example annotation:
+ *
+ * <pre>
+ * {@literal @interface} Foo {
+ * String s();
+ * int i();
+ * Bar bar(); // an annotation defined elsewhere
+ * }
+ * </pre>
+ *
+ * the generated class will look like:
+ *
+ * <pre>
+ * public final class FooCreator {
+ * private FooCreator() {}
+ *
+ * public static Foo createFoo(String s, int i, Bar bar) { … }
+ * public static Bar createBar(…) { … }
+ * }
+ * </pre>
+ */
+public class AnnotationCreatorGenerator extends SourceFileGenerator<TypeElement> {
+ private static final ClassName AUTO_ANNOTATION =
+ ClassName.get("com.google.auto.value", "AutoAnnotation");
+
+ @Inject
+ AnnotationCreatorGenerator(Filer filer, DaggerElements elements, SourceVersion sourceVersion) {
+ super(filer, elements, sourceVersion);
+ }
+
+ @Override
+ public ClassName nameGeneratedType(TypeElement annotationType) {
+ return getAnnotationCreatorClassName(annotationType);
+ }
+
+ @Override
+ public Element originatingElement(TypeElement annotationType) {
+ return annotationType;
+ }
+
+ @Override
+ public Optional<TypeSpec.Builder> write(TypeElement annotationType) {
+ ClassName generatedTypeName = nameGeneratedType(annotationType);
+ TypeSpec.Builder annotationCreatorBuilder =
+ classBuilder(generatedTypeName)
+ .addModifiers(PUBLIC, FINAL)
+ .addMethod(constructorBuilder().addModifiers(PRIVATE).build());
+
+ for (TypeElement annotationElement : annotationsToCreate(annotationType)) {
+ annotationCreatorBuilder.addMethod(buildCreateMethod(generatedTypeName, annotationElement));
+ }
+
+ return Optional.of(annotationCreatorBuilder);
+ }
+
+ private MethodSpec buildCreateMethod(ClassName generatedTypeName, TypeElement annotationElement) {
+ String createMethodName = createMethodName(annotationElement);
+ MethodSpec.Builder createMethod =
+ methodBuilder(createMethodName)
+ .addAnnotation(AUTO_ANNOTATION)
+ .addModifiers(PUBLIC, STATIC)
+ .returns(TypeName.get(annotationElement.asType()));
+
+ ImmutableList.Builder<CodeBlock> parameters = ImmutableList.builder();
+ for (ExecutableElement annotationMember : methodsIn(annotationElement.getEnclosedElements())) {
+ String parameterName = annotationMember.getSimpleName().toString();
+ TypeName parameterType = TypeName.get(annotationMember.getReturnType());
+ createMethod.addParameter(parameterType, parameterName);
+ parameters.add(CodeBlock.of("$L", parameterName));
+ }
+
+ ClassName autoAnnotationClass =
+ generatedTypeName.peerClass(
+ "AutoAnnotation_" + generatedTypeName.simpleName() + "_" + createMethodName);
+ createMethod.addStatement(
+ "return new $T($L)", autoAnnotationClass, makeParametersCodeBlock(parameters.build()));
+ return createMethod.build();
+ }
+
+ /**
+ * Returns the annotation types for which {@code @AutoAnnotation static Foo createFoo(…)} methods
+ * should be written.
+ */
+ protected Set<TypeElement> annotationsToCreate(TypeElement annotationElement) {
+ return nestedAnnotationElements(annotationElement, new LinkedHashSet<>());
+ }
+
+ @CanIgnoreReturnValue
+ private static Set<TypeElement> nestedAnnotationElements(
+ TypeElement annotationElement, Set<TypeElement> annotationElements) {
+ if (annotationElements.add(annotationElement)) {
+ for (ExecutableElement method : methodsIn(annotationElement.getEnclosedElements())) {
+ TRAVERSE_NESTED_ANNOTATIONS.visit(method.getReturnType(), annotationElements);
+ }
+ }
+ return annotationElements;
+ }
+
+ private static final SimpleTypeVisitor6<Void, Set<TypeElement>> TRAVERSE_NESTED_ANNOTATIONS =
+ new SimpleTypeVisitor6<Void, Set<TypeElement>>() {
+ @Override
+ public Void visitDeclared(DeclaredType t, Set<TypeElement> p) {
+ TypeElement typeElement = MoreTypes.asTypeElement(t);
+ if (typeElement.getKind() == ElementKind.ANNOTATION_TYPE) {
+ nestedAnnotationElements(typeElement, p);
+ }
+ return null;
+ }
+ };
+}
diff --git a/java/dagger/internal/codegen/writing/AnonymousProviderCreationExpression.java b/java/dagger/internal/codegen/writing/AnonymousProviderCreationExpression.java
new file mode 100644
index 000000000..b1237ca75
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/AnonymousProviderCreationExpression.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
+import static dagger.internal.codegen.javapoet.CodeBlocks.anonymousProvider;
+import static dagger.model.RequestKind.INSTANCE;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.binding.BindingRequest;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+
+/**
+ * A {@link javax.inject.Provider} creation expression for an anonymous inner class whose
+ * {@code get()} method returns the expression for an instance binding request for its key.
+ */
+final class AnonymousProviderCreationExpression
+ implements FrameworkInstanceCreationExpression {
+ private final ContributionBinding binding;
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final ClassName requestingClass;
+
+ AnonymousProviderCreationExpression(
+ ContributionBinding binding,
+ ComponentBindingExpressions componentBindingExpressions,
+ ClassName requestingClass) {
+ this.binding = binding;
+ this.componentBindingExpressions = componentBindingExpressions;
+ this.requestingClass = requestingClass;
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ BindingRequest instanceExpressionRequest = bindingRequest(binding.key(), INSTANCE);
+ Expression instanceExpression =
+ componentBindingExpressions.getDependencyExpression(
+ instanceExpressionRequest,
+ // Not a real class name, but the actual requestingClass is an inner class within the
+ // given class, not that class itself.
+ requestingClass.nestedClass("Anonymous"));
+ return anonymousProvider(instanceExpression);
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/AssistedFactoryBindingExpression.java b/java/dagger/internal/codegen/writing/AssistedFactoryBindingExpression.java
new file mode 100644
index 000000000..d0c481c7a
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/AssistedFactoryBindingExpression.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.auto.common.MoreElements.asType;
+import static com.google.auto.common.MoreTypes.asDeclared;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.assistedFactoryMethod;
+import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.assistedFactoryParameterSpecs;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.binding.BindingRequest;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import dagger.model.RequestKind;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+
+/**
+ * A {@link dagger.internal.codegen.writing.BindingExpression} for {@link
+ * dagger.assisted.AssistedFactory} methods.
+ */
+final class AssistedFactoryBindingExpression extends SimpleInvocationBindingExpression {
+ private final ProvisionBinding binding;
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final DaggerElements elements;
+ private final DaggerTypes types;
+
+ AssistedFactoryBindingExpression(
+ ProvisionBinding binding,
+ ComponentBindingExpressions componentBindingExpressions,
+ DaggerTypes types,
+ DaggerElements elements) {
+ super(binding);
+ this.binding = checkNotNull(binding);
+ this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+ this.elements = checkNotNull(elements);
+ this.types = checkNotNull(types);
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ // An assisted factory binding should have a single request for an assisted injection type.
+ DependencyRequest assistedInjectionRequest = getOnlyElement(binding.provisionDependencies());
+ Expression assistedInjectionExpression =
+ componentBindingExpressions.getDependencyExpression(
+ BindingRequest.bindingRequest(assistedInjectionRequest.key(), RequestKind.INSTANCE),
+ // This is kind of gross because the anonymous class doesn't really have a name we can
+ // reference. The requesting class name is really only needed to determine if we need to
+ // append "OwningClass.this." to the method call or not.
+ // TODO(bcorso): We should probably use a non-anonymous class here instead so that we
+ // actually have a proper class name.
+ requestingClass.peerClass(""));
+ return Expression.create(
+ assistedInjectionExpression.type(),
+ CodeBlock.of("$L", anonymousfactoryImpl(assistedInjectionExpression)));
+ }
+
+ private TypeSpec anonymousfactoryImpl(Expression assistedInjectionExpression) {
+ TypeElement factory = asType(binding.bindingElement().get());
+ DeclaredType factoryType = asDeclared(binding.key().type());
+ ExecutableElement factoryMethod = assistedFactoryMethod(factory, elements, types);
+
+ // We can't use MethodSpec.overriding directly because we need to control the parameter names.
+ MethodSpec factoryOverride = MethodSpec.overriding(factoryMethod, factoryType, types).build();
+ TypeSpec.Builder builder =
+ TypeSpec.anonymousClassBuilder("")
+ .addMethod(
+ MethodSpec.methodBuilder(factoryMethod.getSimpleName().toString())
+ .addModifiers(factoryOverride.modifiers)
+ .addTypeVariables(factoryOverride.typeVariables)
+ .returns(factoryOverride.returnType)
+ .addAnnotations(factoryOverride.annotations)
+ .addExceptions(factoryOverride.exceptions)
+ .addParameters(assistedFactoryParameterSpecs(binding, elements, types))
+ .addStatement("return $L", assistedInjectionExpression.codeBlock())
+ .build());
+
+ if (factory.getKind() == ElementKind.INTERFACE) {
+ builder.addSuperinterface(TypeName.get(factoryType));
+ } else {
+ builder.superclass(TypeName.get(factoryType));
+ }
+
+ return builder.build();
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/BUILD b/java/dagger/internal/codegen/writing/BUILD
new file mode 100644
index 000000000..dcb88b387
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/BUILD
@@ -0,0 +1,47 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Classes that assemble the model of the generated code and write to the Filer
+
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "writing",
+ srcs = glob(["*.java"]),
+ plugins = ["//java/dagger/internal/codegen/bootstrap"],
+ tags = ["maven:merged"],
+ deps = [
+ "//java/dagger:core",
+ "//java/dagger/internal/codegen/base",
+ "//java/dagger/internal/codegen/binding",
+ "//java/dagger/internal/codegen/compileroption",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/codegen/javapoet",
+ "//java/dagger/internal/codegen/kotlin",
+ "//java/dagger/internal/codegen/langmodel",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:concurrent",
+ "//java/dagger/producers",
+ "//java/dagger/spi",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/error_prone:annotations",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
diff --git a/java/dagger/internal/codegen/writing/BindingExpression.java b/java/dagger/internal/codegen/writing/BindingExpression.java
new file mode 100644
index 000000000..bd45f603e
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/BindingExpression.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.javapoet.Expression;
+
+/** A factory of code expressions used to access a single request for a binding in a component. */
+// TODO(bcorso): Rename this to RequestExpression?
+abstract class BindingExpression {
+
+ /**
+ * Returns an expression that evaluates to the value of a request based on the given requesting
+ * class.
+ *
+ * @param requestingClass the class that will contain the expression
+ */
+ abstract Expression getDependencyExpression(ClassName requestingClass);
+
+ /**
+ * Equivalent to {@link #getDependencyExpression} that is used only when the request is for an
+ * implementation of a component method. By default, just delegates to {@link
+ * #getDependencyExpression}.
+ */
+ Expression getDependencyExpressionForComponentMethod(
+ ComponentMethodDescriptor componentMethod, ComponentImplementation component) {
+ return getDependencyExpression(component.name());
+ }
+
+ /** Returns {@code true} if this binding expression should be encapsulated in a method. */
+ boolean requiresMethodEncapsulation() {
+ return false;
+ }
+
+ /**
+ * Returns an expression for the implementation of a component method with the given request.
+ *
+ * @param component the component that will contain the implemented method
+ */
+ CodeBlock getComponentMethodImplementation(
+ ComponentMethodDescriptor componentMethod, ComponentImplementation component) {
+ // By default, just delegate to #getDependencyExpression().
+ return CodeBlock.of(
+ "return $L;",
+ getDependencyExpressionForComponentMethod(componentMethod, component).codeBlock());
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/ComponentBindingExpressions.java b/java/dagger/internal/codegen/writing/ComponentBindingExpressions.java
new file mode 100644
index 000000000..5f1f0bdd8
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ComponentBindingExpressions.java
@@ -0,0 +1,721 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Verify.verify;
+import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
+import static dagger.internal.codegen.extension.DaggerCollectors.toOptional;
+import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.DOUBLE_CHECK;
+import static dagger.internal.codegen.javapoet.TypeNames.SINGLE_CHECK;
+import static dagger.internal.codegen.langmodel.Accessibility.isRawTypeAccessible;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+import static dagger.internal.codegen.writing.DelegateBindingExpression.isBindsScopeStrongerThanDependencyScope;
+import static dagger.internal.codegen.writing.MemberSelect.staticFactoryCreation;
+import static dagger.model.BindingKind.DELEGATE;
+import static dagger.model.BindingKind.MULTIBOUND_MAP;
+import static dagger.model.BindingKind.MULTIBOUND_SET;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import dagger.internal.codegen.binding.Binding;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.BindingNode;
+import dagger.internal.codegen.binding.BindingRequest;
+import dagger.internal.codegen.binding.BindingType;
+import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.binding.ComponentRequirement;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.binding.FrameworkType;
+import dagger.internal.codegen.binding.FrameworkTypeMapper;
+import dagger.internal.codegen.binding.MembersInjectionBinding;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import dagger.internal.codegen.writing.MethodBindingExpression.MethodImplementationStrategy;
+import dagger.model.BindingKind;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.type.TypeMirror;
+
+/** A central repository of code expressions used to access any binding available to a component. */
+@PerComponentImplementation
+public final class ComponentBindingExpressions {
+ // TODO(dpb,ronshapiro): refactor this and ComponentRequirementExpressions into a
+ // HierarchicalComponentMap<K, V>, or perhaps this use a flattened ImmutableMap, built from its
+ // parents? If so, maybe make BindingExpression.Factory create it.
+
+ private final Optional<ComponentBindingExpressions> parent;
+ private final BindingGraph graph;
+ private final ComponentImplementation componentImplementation;
+ private final ComponentImplementation topLevelComponentImplementation;
+ private final ComponentRequirementExpressions componentRequirementExpressions;
+ private final OptionalFactories optionalFactories;
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+ private final SourceVersion sourceVersion;
+ private final CompilerOptions compilerOptions;
+ private final MembersInjectionMethods membersInjectionMethods;
+ private final InnerSwitchingProviders innerSwitchingProviders;
+ private final Map<BindingRequest, BindingExpression> expressions = new HashMap<>();
+ private final KotlinMetadataUtil metadataUtil;
+
+ @Inject
+ ComponentBindingExpressions(
+ @ParentComponent Optional<ComponentBindingExpressions> parent,
+ BindingGraph graph,
+ ComponentImplementation componentImplementation,
+ @TopLevel ComponentImplementation topLevelComponentImplementation,
+ ComponentRequirementExpressions componentRequirementExpressions,
+ OptionalFactories optionalFactories,
+ DaggerTypes types,
+ DaggerElements elements,
+ SourceVersion sourceVersion,
+ CompilerOptions compilerOptions,
+ KotlinMetadataUtil metadataUtil) {
+ this.parent = parent;
+ this.graph = graph;
+ this.componentImplementation = componentImplementation;
+ this.topLevelComponentImplementation = topLevelComponentImplementation;
+ this.componentRequirementExpressions = checkNotNull(componentRequirementExpressions);
+ this.optionalFactories = checkNotNull(optionalFactories);
+ this.types = checkNotNull(types);
+ this.elements = checkNotNull(elements);
+ this.sourceVersion = checkNotNull(sourceVersion);
+ this.compilerOptions = checkNotNull(compilerOptions);
+ this.membersInjectionMethods =
+ new MembersInjectionMethods(
+ componentImplementation, this, graph, elements, types, metadataUtil);
+ this.innerSwitchingProviders =
+ new InnerSwitchingProviders(componentImplementation, this, types);
+ this.metadataUtil = metadataUtil;
+ }
+
+ /**
+ * Returns an expression that evaluates to the value of a binding request for a binding owned by
+ * this component or an ancestor.
+ *
+ * @param requestingClass the class that will contain the expression
+ * @throws IllegalStateException if there is no binding expression that satisfies the request
+ */
+ public Expression getDependencyExpression(BindingRequest request, ClassName requestingClass) {
+ return getBindingExpression(request).getDependencyExpression(requestingClass);
+ }
+
+ /**
+ * Equivalent to {@link #getDependencyExpression(BindingRequest, ClassName)} that is used only
+ * when the request is for implementation of a component method.
+ *
+ * @throws IllegalStateException if there is no binding expression that satisfies the request
+ */
+ Expression getDependencyExpressionForComponentMethod(
+ BindingRequest request,
+ ComponentMethodDescriptor componentMethod,
+ ComponentImplementation componentImplementation) {
+ return getBindingExpression(request)
+ .getDependencyExpressionForComponentMethod(componentMethod, componentImplementation);
+ }
+
+ /**
+ * Returns the {@link CodeBlock} for the method arguments used with the factory {@code create()}
+ * method for the given {@link ContributionBinding binding}.
+ */
+ CodeBlock getCreateMethodArgumentsCodeBlock(ContributionBinding binding) {
+ return makeParametersCodeBlock(getCreateMethodArgumentsCodeBlocks(binding));
+ }
+
+ private ImmutableList<CodeBlock> getCreateMethodArgumentsCodeBlocks(ContributionBinding binding) {
+ ImmutableList.Builder<CodeBlock> arguments = ImmutableList.builder();
+
+ if (binding.requiresModuleInstance()) {
+ arguments.add(
+ componentRequirementExpressions.getExpressionDuringInitialization(
+ ComponentRequirement.forModule(binding.contributingModule().get().asType()),
+ componentImplementation.name()));
+ }
+
+ binding.dependencies().stream()
+ .map(dependency -> frameworkRequest(binding, dependency))
+ .map(request -> getDependencyExpression(request, componentImplementation.name()))
+ .map(Expression::codeBlock)
+ .forEach(arguments::add);
+
+ return arguments.build();
+ }
+
+ private static BindingRequest frameworkRequest(
+ ContributionBinding binding, DependencyRequest dependency) {
+ // TODO(bcorso): See if we can get rid of FrameworkTypeMatcher
+ FrameworkType frameworkType =
+ FrameworkTypeMapper.forBindingType(binding.bindingType())
+ .getFrameworkType(dependency.kind());
+ return BindingRequest.bindingRequest(dependency.key(), frameworkType);
+ }
+
+ /**
+ * Returns an expression that evaluates to the value of a dependency request, for passing to a
+ * binding method, an {@code @Inject}-annotated constructor or member, or a proxy for one.
+ *
+ * <p>If the method is a generated static {@link InjectionMethods injection method}, each
+ * parameter will be {@link Object} if the dependency's raw type is inaccessible. If that is the
+ * case for this dependency, the returned expression will use a cast to evaluate to the raw type.
+ *
+ * @param requestingClass the class that will contain the expression
+ */
+ Expression getDependencyArgumentExpression(
+ DependencyRequest dependencyRequest, ClassName requestingClass) {
+
+ TypeMirror dependencyType = dependencyRequest.key().type();
+ BindingRequest bindingRequest = bindingRequest(dependencyRequest);
+ Expression dependencyExpression = getDependencyExpression(bindingRequest, requestingClass);
+
+ if (dependencyRequest.kind().equals(RequestKind.INSTANCE)
+ && !isTypeAccessibleFrom(dependencyType, requestingClass.packageName())
+ && isRawTypeAccessible(dependencyType, requestingClass.packageName())) {
+ return dependencyExpression.castTo(types.erasure(dependencyType));
+ }
+
+ return dependencyExpression;
+ }
+
+ /** Returns the implementation of a component method. */
+ public MethodSpec getComponentMethod(ComponentMethodDescriptor componentMethod) {
+ checkArgument(componentMethod.dependencyRequest().isPresent());
+ BindingRequest request = bindingRequest(componentMethod.dependencyRequest().get());
+ MethodSpec.Builder method =
+ MethodSpec.overriding(
+ componentMethod.methodElement(),
+ MoreTypes.asDeclared(graph.componentTypeElement().asType()),
+ types);
+ // Even though this is not used if the method is abstract, we need to invoke the binding
+ // expression in order for the side of effect of the method being added to the
+ // ComponentImplementation
+ CodeBlock methodBody =
+ getBindingExpression(request)
+ .getComponentMethodImplementation(componentMethod, componentImplementation);
+
+ return method.addCode(methodBody).build();
+ }
+
+ /** Returns the {@link BindingExpression} for the given {@link BindingRequest}. */
+ BindingExpression getBindingExpression(BindingRequest request) {
+ if (expressions.containsKey(request)) {
+ return expressions.get(request);
+ }
+
+ Optional<Binding> optionalBinding =
+ graph.bindingNodes(request.key()).stream()
+ // Filter out nodes we don't own.
+ .filter(bindingNode -> bindingNode.componentPath().equals(graph.componentPath()))
+ // Filter out nodes that don't match the request kind
+ .filter(
+ bindingNode ->
+ // The binding used for the binding expression depends on the request:
+ // 1. MembersInjectionBinding: satisfies MEMBERS_INJECTION requests
+ // 2. ContributionBindings: satisfies all other requests.
+ request.isRequestKind(RequestKind.MEMBERS_INJECTION)
+ ? bindingNode.delegate().bindingType() == BindingType.MEMBERS_INJECTION
+ : bindingNode.delegate().bindingType() == BindingType.PROVISION
+ || bindingNode.delegate().bindingType() == BindingType.PRODUCTION)
+ .map(BindingNode::delegate)
+ // We expect at most one binding to match since this graph is already validated.
+ .collect(toOptional());
+
+ if (optionalBinding.isPresent()) {
+ BindingExpression expression = createBindingExpression(optionalBinding.get(), request);
+ expressions.put(request, expression);
+ return expression;
+ }
+
+ checkArgument(parent.isPresent(), "no expression found for %s", request);
+ return parent.get().getBindingExpression(request);
+ }
+
+ /** Creates a binding expression. */
+ private BindingExpression createBindingExpression(Binding binding, BindingRequest request) {
+ switch (binding.bindingType()) {
+ case MEMBERS_INJECTION:
+ checkArgument(request.isRequestKind(RequestKind.MEMBERS_INJECTION));
+ return new MembersInjectionBindingExpression(
+ (MembersInjectionBinding) binding, membersInjectionMethods);
+
+ case PROVISION:
+ return provisionBindingExpression((ContributionBinding) binding, request);
+
+ case PRODUCTION:
+ return productionBindingExpression((ContributionBinding) binding, request);
+ }
+ throw new AssertionError(binding);
+ }
+
+ /**
+ * Returns a binding expression that uses a {@link javax.inject.Provider} for provision bindings
+ * or a {@link dagger.producers.Producer} for production bindings.
+ */
+ private BindingExpression frameworkInstanceBindingExpression(ContributionBinding binding) {
+ // TODO(bcorso): Consider merging the static factory creation logic into CreationExpressions?
+ Optional<MemberSelect> staticMethod =
+ useStaticFactoryCreation(binding) ? staticFactoryCreation(binding) : Optional.empty();
+ FrameworkInstanceSupplier frameworkInstanceSupplier =
+ staticMethod.isPresent()
+ ? staticMethod::get
+ : new FrameworkFieldInitializer(
+ componentImplementation,
+ binding,
+ binding.scope().isPresent()
+ ? scope(binding, frameworkInstanceCreationExpression(binding))
+ : frameworkInstanceCreationExpression(binding));
+
+ switch (binding.bindingType()) {
+ case PROVISION:
+ return new ProviderInstanceBindingExpression(
+ binding, frameworkInstanceSupplier, types, elements);
+ case PRODUCTION:
+ return new ProducerNodeInstanceBindingExpression(
+ binding, frameworkInstanceSupplier, types, elements, componentImplementation);
+ default:
+ throw new AssertionError("invalid binding type: " + binding.bindingType());
+ }
+ }
+
+ private FrameworkInstanceCreationExpression scope(
+ ContributionBinding binding, FrameworkInstanceCreationExpression unscoped) {
+ return () ->
+ CodeBlock.of(
+ "$T.provider($L)",
+ binding.scope().get().isReusable() ? SINGLE_CHECK : DOUBLE_CHECK,
+ unscoped.creationExpression());
+ }
+
+ /**
+ * Returns a creation expression for a {@link javax.inject.Provider} for provision bindings or a
+ * {@link dagger.producers.Producer} for production bindings.
+ */
+ private FrameworkInstanceCreationExpression frameworkInstanceCreationExpression(
+ ContributionBinding binding) {
+ switch (binding.kind()) {
+ case COMPONENT:
+ // The cast can be removed when we drop java 7 source support
+ return new InstanceFactoryCreationExpression(
+ () -> CodeBlock.of("($T) this", binding.key().type()));
+
+ case BOUND_INSTANCE:
+ return instanceFactoryCreationExpression(
+ binding, ComponentRequirement.forBoundInstance(binding));
+
+ case COMPONENT_DEPENDENCY:
+ return instanceFactoryCreationExpression(
+ binding, ComponentRequirement.forDependency(binding.key().type()));
+
+ case COMPONENT_PROVISION:
+ return new DependencyMethodProviderCreationExpression(
+ binding,
+ componentImplementation,
+ componentRequirementExpressions,
+ compilerOptions,
+ graph);
+
+ case SUBCOMPONENT_CREATOR:
+ return new AnonymousProviderCreationExpression(
+ binding, this, componentImplementation.name());
+
+ case ASSISTED_FACTORY:
+ case ASSISTED_INJECTION:
+ case INJECTION:
+ case PROVISION:
+ return new InjectionOrProvisionProviderCreationExpression(binding, this);
+
+ case COMPONENT_PRODUCTION:
+ return new DependencyMethodProducerCreationExpression(
+ binding, componentImplementation, componentRequirementExpressions, graph);
+
+ case PRODUCTION:
+ return new ProducerCreationExpression(binding, this);
+
+ case MULTIBOUND_SET:
+ return new SetFactoryCreationExpression(binding, componentImplementation, this, graph);
+
+ case MULTIBOUND_MAP:
+ return new MapFactoryCreationExpression(
+ binding, componentImplementation, this, graph, elements);
+
+ case DELEGATE:
+ return new DelegatingFrameworkInstanceCreationExpression(
+ binding, componentImplementation, this);
+
+ case OPTIONAL:
+ return new OptionalFactoryInstanceCreationExpression(
+ optionalFactories, binding, componentImplementation, this);
+
+ case MEMBERS_INJECTOR:
+ return new MembersInjectorProviderCreationExpression((ProvisionBinding) binding, this);
+
+ default:
+ throw new AssertionError(binding);
+ }
+ }
+
+ private InstanceFactoryCreationExpression instanceFactoryCreationExpression(
+ ContributionBinding binding, ComponentRequirement componentRequirement) {
+ return new InstanceFactoryCreationExpression(
+ binding.nullableType().isPresent(),
+ () ->
+ componentRequirementExpressions.getExpressionDuringInitialization(
+ componentRequirement, componentImplementation.name()));
+ }
+
+ /** Returns a binding expression for a provision binding. */
+ private BindingExpression provisionBindingExpression(
+ ContributionBinding binding, BindingRequest request) {
+ if (!request.requestKind().isPresent()) {
+ verify(
+ request.frameworkType().get().equals(FrameworkType.PRODUCER_NODE),
+ "expected a PRODUCER_NODE: %s",
+ request);
+ return producerFromProviderBindingExpression(binding);
+ }
+ RequestKind requestKind = request.requestKind().get();
+ Key key = request.key();
+ switch (requestKind) {
+ case INSTANCE:
+ return instanceBindingExpression(binding);
+
+ case PROVIDER:
+ return providerBindingExpression(binding);
+
+ case LAZY:
+ case PRODUCED:
+ case PROVIDER_OF_LAZY:
+ return new DerivedFromFrameworkInstanceBindingExpression(
+ key, FrameworkType.PROVIDER, requestKind, this, types);
+
+ case PRODUCER:
+ return producerFromProviderBindingExpression(binding);
+
+ case FUTURE:
+ return new ImmediateFutureBindingExpression(key, this, types, sourceVersion);
+
+ case MEMBERS_INJECTION:
+ throw new IllegalArgumentException();
+ }
+
+ throw new AssertionError();
+ }
+
+ /** Returns a binding expression for a production binding. */
+ private BindingExpression productionBindingExpression(
+ ContributionBinding binding, BindingRequest request) {
+ if (request.frameworkType().isPresent()) {
+ return frameworkInstanceBindingExpression(binding);
+ } else {
+ // If no FrameworkType is present, a RequestKind is guaranteed to be present.
+ RequestKind requestKind = request.requestKind().get();
+ return new DerivedFromFrameworkInstanceBindingExpression(
+ request.key(), FrameworkType.PRODUCER_NODE, requestKind, this, types);
+ }
+ }
+
+ /**
+ * Returns a binding expression for {@link RequestKind#PROVIDER} requests.
+ *
+ * <p>{@code @Binds} bindings that don't {@linkplain #needsCaching(ContributionBinding) need to be
+ * cached} can use a {@link DelegateBindingExpression}.
+ *
+ * <p>In fastInit mode, use an {@link InnerSwitchingProviders inner switching provider} unless
+ * that provider's case statement will simply call {@code get()} on another {@link Provider} (in
+ * which case, just use that Provider directly).
+ *
+ * <p>Otherwise, return a {@link FrameworkInstanceBindingExpression}.
+ */
+ private BindingExpression providerBindingExpression(ContributionBinding binding) {
+ if (binding.kind().equals(DELEGATE) && !needsCaching(binding)) {
+ return new DelegateBindingExpression(binding, RequestKind.PROVIDER, this, types, elements);
+ } else if (compilerOptions.fastInit(
+ topLevelComponentImplementation.componentDescriptor().typeElement())
+ && frameworkInstanceCreationExpression(binding).useInnerSwitchingProvider()
+ && !(instanceBindingExpression(binding)
+ instanceof DerivedFromFrameworkInstanceBindingExpression)) {
+ return wrapInMethod(
+ binding,
+ bindingRequest(binding.key(), RequestKind.PROVIDER),
+ innerSwitchingProviders.newBindingExpression(binding));
+ }
+ return frameworkInstanceBindingExpression(binding);
+ }
+
+ /**
+ * Returns a binding expression that uses a {@link dagger.producers.Producer} field for a
+ * provision binding.
+ */
+ private FrameworkInstanceBindingExpression producerFromProviderBindingExpression(
+ ContributionBinding binding) {
+ checkArgument(binding.bindingType().equals(BindingType.PROVISION));
+ return new ProducerNodeInstanceBindingExpression(
+ binding,
+ new FrameworkFieldInitializer(
+ componentImplementation,
+ binding,
+ new ProducerFromProviderCreationExpression(binding, componentImplementation, this)),
+ types,
+ elements,
+ componentImplementation);
+ }
+
+ /**
+ * Returns a binding expression for {@link RequestKind#INSTANCE} requests.
+ *
+ * <p>If there is a direct expression (not calling {@link Provider#get()}) we can use for an
+ * instance of this binding, return it, wrapped in a method if the binding {@linkplain
+ * #needsCaching(ContributionBinding) needs to be cached} or the expression has dependencies.
+ *
+ * <p>In fastInit mode, we can use direct expressions unless the binding needs to be cached.
+ */
+ private BindingExpression instanceBindingExpression(ContributionBinding binding) {
+ Optional<BindingExpression> maybeDirectInstanceExpression =
+ unscopedDirectInstanceExpression(binding);
+ if (canUseDirectInstanceExpression(binding) && maybeDirectInstanceExpression.isPresent()) {
+ BindingExpression directInstanceExpression = maybeDirectInstanceExpression.get();
+ return directInstanceExpression.requiresMethodEncapsulation() || needsCaching(binding)
+ ? wrapInMethod(
+ binding,
+ bindingRequest(binding.key(), RequestKind.INSTANCE),
+ directInstanceExpression)
+ : directInstanceExpression;
+ }
+ return new DerivedFromFrameworkInstanceBindingExpression(
+ binding.key(), FrameworkType.PROVIDER, RequestKind.INSTANCE, this, types);
+ }
+
+ /**
+ * Returns an unscoped binding expression for an {@link RequestKind#INSTANCE} that does not call
+ * {@code get()} on its provider, if there is one.
+ */
+ private Optional<BindingExpression> unscopedDirectInstanceExpression(
+ ContributionBinding binding) {
+ switch (binding.kind()) {
+ case DELEGATE:
+ return Optional.of(
+ new DelegateBindingExpression(binding, RequestKind.INSTANCE, this, types, elements));
+
+ case COMPONENT:
+ return Optional.of(
+ new ComponentInstanceBindingExpression(binding, componentImplementation.name()));
+
+ case COMPONENT_DEPENDENCY:
+ return Optional.of(
+ new ComponentRequirementBindingExpression(
+ binding,
+ ComponentRequirement.forDependency(binding.key().type()),
+ componentRequirementExpressions));
+
+ case COMPONENT_PROVISION:
+ return Optional.of(
+ new ComponentProvisionBindingExpression(
+ (ProvisionBinding) binding,
+ graph,
+ componentRequirementExpressions,
+ compilerOptions));
+
+ case SUBCOMPONENT_CREATOR:
+ return Optional.of(
+ new SubcomponentCreatorBindingExpression(
+ binding, componentImplementation.getSubcomponentCreatorSimpleName(binding.key())));
+
+ case MULTIBOUND_SET:
+ return Optional.of(
+ new SetBindingExpression((ProvisionBinding) binding, graph, this, types, elements));
+
+ case MULTIBOUND_MAP:
+ return Optional.of(
+ new MapBindingExpression((ProvisionBinding) binding, graph, this, types, elements));
+
+ case OPTIONAL:
+ return Optional.of(
+ new OptionalBindingExpression((ProvisionBinding) binding, this, types, sourceVersion));
+
+ case BOUND_INSTANCE:
+ return Optional.of(
+ new ComponentRequirementBindingExpression(
+ binding,
+ ComponentRequirement.forBoundInstance(binding),
+ componentRequirementExpressions));
+
+ case ASSISTED_FACTORY:
+ return Optional.of(
+ new AssistedFactoryBindingExpression(
+ (ProvisionBinding) binding, this, types, elements));
+
+ case ASSISTED_INJECTION:
+ case INJECTION:
+ case PROVISION:
+ return Optional.of(
+ new SimpleMethodBindingExpression(
+ (ProvisionBinding) binding,
+ compilerOptions,
+ this,
+ membersInjectionMethods,
+ componentRequirementExpressions,
+ elements,
+ sourceVersion,
+ metadataUtil));
+
+ case MEMBERS_INJECTOR:
+ return Optional.empty();
+
+ case MEMBERS_INJECTION:
+ case COMPONENT_PRODUCTION:
+ case PRODUCTION:
+ throw new IllegalArgumentException(binding.kind().toString());
+ default:
+ throw new AssertionError("Unexpected binding kind: " + binding.kind());
+ }
+ }
+
+ /**
+ * Returns {@code true} if the binding should use the static factory creation strategy.
+ *
+ * <p>In default mode, we always use the static factory creation strategy. In fastInit mode, we
+ * prefer to use a SwitchingProvider instead of static factories in order to reduce class loading;
+ * however, we allow static factories that can reused across multiple bindings, e.g. {@code
+ * MapFactory} or {@code SetFactory}.
+ */
+ private boolean useStaticFactoryCreation(ContributionBinding binding) {
+ return !compilerOptions.fastInit(
+ topLevelComponentImplementation.componentDescriptor().typeElement())
+ || binding.kind().equals(MULTIBOUND_MAP)
+ || binding.kind().equals(MULTIBOUND_SET);
+ }
+
+ /**
+ * Returns {@code true} if we can use a direct (not {@code Provider.get()}) expression for this
+ * binding. If the binding doesn't {@linkplain #needsCaching(ContributionBinding) need to be
+ * cached} and the binding is not an {@link BindingKind.ASSISTED_FACTORY}, we can.
+ *
+ * <p>In fastInit mode, we can use a direct expression even if the binding {@linkplain
+ * #needsCaching(ContributionBinding) needs to be cached}.
+ */
+ private boolean canUseDirectInstanceExpression(ContributionBinding binding) {
+ return (!needsCaching(binding) && binding.kind() != BindingKind.ASSISTED_FACTORY)
+ || compilerOptions.fastInit(
+ topLevelComponentImplementation.componentDescriptor().typeElement());
+ }
+
+ /**
+ * Returns a binding expression that uses a given one as the body of a method that users call. If
+ * a component provision method matches it, it will be the method implemented. If it does not
+ * match a component provision method and the binding is modifiable, then a new public modifiable
+ * binding method will be written. If the binding doesn't match a component method and is not
+ * modifiable, then a new private method will be written.
+ */
+ BindingExpression wrapInMethod(
+ ContributionBinding binding, BindingRequest request, BindingExpression bindingExpression) {
+ // If we've already wrapped the expression, then use the delegate.
+ if (bindingExpression instanceof MethodBindingExpression) {
+ return bindingExpression;
+ }
+
+ MethodImplementationStrategy methodImplementationStrategy =
+ methodImplementationStrategy(binding, request);
+ Optional<ComponentMethodDescriptor> matchingComponentMethod =
+ graph.componentDescriptor().firstMatchingComponentMethod(request);
+
+ ComponentImplementation shard = componentImplementation.shardImplementation(binding.key());
+
+ // Consider the case of a request from a component method like:
+ //
+ // DaggerMyComponent extends MyComponent {
+ // @Overrides
+ // Foo getFoo() {
+ // <FOO_BINDING_REQUEST>
+ // }
+ // }
+ //
+ // Normally, in this case we would return a ComponentMethodBindingExpression rather than a
+ // PrivateMethodBindingExpression so that #getFoo() can inline the implementation rather than
+ // create an unnecessary private method and return that. However, with sharding we don't want to
+ // inline the implementation because that would defeat some of the class pool savings if those
+ // fields had to communicate across shards. Thus, when a key belongs to a separate shard use a
+ // PrivateMethodBindingExpression and put the private method in the shard.
+ if (matchingComponentMethod.isPresent() && componentImplementation == shard) {
+ ComponentMethodDescriptor componentMethod = matchingComponentMethod.get();
+ return new ComponentMethodBindingExpression(
+ request,
+ binding,
+ methodImplementationStrategy,
+ bindingExpression,
+ componentImplementation,
+ componentMethod,
+ types);
+ } else {
+ return new PrivateMethodBindingExpression(
+ request,
+ binding,
+ methodImplementationStrategy,
+ bindingExpression,
+ shard,
+ types,
+ compilerOptions);
+ }
+ }
+
+ private MethodImplementationStrategy methodImplementationStrategy(
+ ContributionBinding binding, BindingRequest request) {
+ if (compilerOptions.fastInit(
+ topLevelComponentImplementation.componentDescriptor().typeElement())) {
+ if (request.isRequestKind(RequestKind.PROVIDER)) {
+ return MethodImplementationStrategy.SINGLE_CHECK;
+ } else if (request.isRequestKind(RequestKind.INSTANCE) && needsCaching(binding)) {
+ return binding.scope().get().isReusable()
+ ? MethodImplementationStrategy.SINGLE_CHECK
+ : MethodImplementationStrategy.DOUBLE_CHECK;
+ }
+ }
+ return MethodImplementationStrategy.SIMPLE;
+ }
+
+ /**
+ * Returns {@code true} if the component needs to make sure the provided value is cached.
+ *
+ * <p>The component needs to cache the value for scoped bindings except for {@code @Binds}
+ * bindings whose scope is no stronger than their delegate's.
+ */
+ private boolean needsCaching(ContributionBinding binding) {
+ if (!binding.scope().isPresent()) {
+ return false;
+ }
+ if (binding.kind().equals(DELEGATE)) {
+ return isBindsScopeStrongerThanDependencyScope(binding, graph);
+ }
+ return true;
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/ComponentCreatorImplementation.java b/java/dagger/internal/codegen/writing/ComponentCreatorImplementation.java
new file mode 100644
index 000000000..f9d218b90
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ComponentCreatorImplementation.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableMap;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.binding.ComponentRequirement;
+
+/** The implementation of a component creator type. */
+@AutoValue
+public abstract class ComponentCreatorImplementation {
+
+ /** Creates a new {@link ComponentCreatorImplementation}. */
+ public static ComponentCreatorImplementation create(
+ TypeSpec spec, ClassName name, ImmutableMap<ComponentRequirement, FieldSpec> fields) {
+ return new AutoValue_ComponentCreatorImplementation(spec, name, fields);
+ }
+
+ /** The type spec for the creator implementation. */
+ public abstract TypeSpec spec();
+
+ /** The name of the creator implementation class. */
+ public abstract ClassName name();
+
+ /** All fields that are present in this implementation. */
+ abstract ImmutableMap<ComponentRequirement, FieldSpec> fields();
+}
diff --git a/java/dagger/internal/codegen/writing/ComponentImplementation.java b/java/dagger/internal/codegen/writing/ComponentImplementation.java
new file mode 100644
index 000000000..ef6912460
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ComponentImplementation.java
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.binding.ComponentCreatorKind.BUILDER;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.MultimapBuilder;
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.base.UniqueNameSet;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.BindingRequest;
+import dagger.internal.codegen.binding.ComponentCreatorDescriptor;
+import dagger.internal.codegen.binding.ComponentCreatorKind;
+import dagger.internal.codegen.binding.ComponentDescriptor;
+import dagger.internal.codegen.binding.ComponentRequirement;
+import dagger.internal.codegen.binding.KeyVariableNamer;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.javapoet.TypeSpecs;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+/** The implementation of a component type. */
+public final class ComponentImplementation {
+ /** A type of field that this component can contain. */
+ public enum FieldSpecKind {
+ /** A field for a component shard. */
+ COMPONENT_SHARD,
+
+ /** A field required by the component, e.g. module instances. */
+ COMPONENT_REQUIREMENT_FIELD,
+
+ /**
+ * A field for the lock and cached value for {@linkplain PrivateMethodBindingExpression
+ * private-method scoped bindings}.
+ */
+ PRIVATE_METHOD_SCOPED_FIELD,
+
+ /** A framework field for type T, e.g. {@code Provider<T>}. */
+ FRAMEWORK_FIELD,
+
+ /** A static field that always returns an absent {@code Optional} value for the binding. */
+ ABSENT_OPTIONAL_FIELD
+ }
+
+ /** A type of method that this component can contain. */
+ // TODO(bcorso, dpb): Change the oder to constructor, initialize, component, then private
+ // (including MIM and AOM—why treat those separately?).
+ public enum MethodSpecKind {
+ /** The component constructor. */
+ CONSTRUCTOR,
+
+ /** A builder method for the component. (Only used by the root component.) */
+ BUILDER_METHOD,
+
+ /** A private method that wraps dependency expressions. */
+ PRIVATE_METHOD,
+
+ /** An initialization method that initializes component requirements and framework types. */
+ INITIALIZE_METHOD,
+
+ /** An implementation of a component interface method. */
+ COMPONENT_METHOD,
+
+ /** A private method that encapsulates members injection logic for a binding. */
+ MEMBERS_INJECTION_METHOD,
+
+ /** A static method that always returns an absent {@code Optional} value for the binding. */
+ ABSENT_OPTIONAL_METHOD,
+
+ /**
+ * The {@link dagger.producers.internal.CancellationListener#onProducerFutureCancelled(boolean)}
+ * method for a production component.
+ */
+ CANCELLATION_LISTENER_METHOD,
+ ;
+ }
+
+ /** A type of nested class that this component can contain. */
+ public enum TypeSpecKind {
+ /** A factory class for a present optional binding. */
+ PRESENT_FACTORY,
+
+ /** A class for the component creator (only used by the root component.) */
+ COMPONENT_CREATOR,
+
+ /** A provider class for a component provision. */
+ COMPONENT_PROVISION_FACTORY,
+
+ /** A class for the subcomponent or subcomponent builder. */
+ SUBCOMPONENT
+ }
+
+ private ComponentImplementation currentShard = this;
+ private final Map<Key, ComponentImplementation> shardsByKey = new HashMap<>();
+ private final Optional<ComponentImplementation> shardOwner;
+ private final BindingGraph graph;
+ private final ClassName name;
+ private final TypeSpec.Builder component;
+ private final SubcomponentNames subcomponentNames;
+ private final CompilerOptions compilerOptions;
+ private final CodeBlock externalReferenceBlock;
+ private final UniqueNameSet componentFieldNames = new UniqueNameSet();
+ private final UniqueNameSet componentMethodNames = new UniqueNameSet();
+ private final List<CodeBlock> initializations = new ArrayList<>();
+ private final List<CodeBlock> componentRequirementInitializations = new ArrayList<>();
+ private final Map<ComponentRequirement, String> componentRequirementParameterNames =
+ new HashMap<>();
+ private final Set<Key> cancellableProducerKeys = new LinkedHashSet<>();
+ private final ListMultimap<FieldSpecKind, FieldSpec> fieldSpecsMap =
+ MultimapBuilder.enumKeys(FieldSpecKind.class).arrayListValues().build();
+ private final ListMultimap<MethodSpecKind, MethodSpec> methodSpecsMap =
+ MultimapBuilder.enumKeys(MethodSpecKind.class).arrayListValues().build();
+ private final ListMultimap<TypeSpecKind, TypeSpec> typeSpecsMap =
+ MultimapBuilder.enumKeys(TypeSpecKind.class).arrayListValues().build();
+ private final List<Supplier<TypeSpec>> typeSuppliers = new ArrayList<>();
+
+ private ComponentImplementation(
+ BindingGraph graph,
+ ClassName name,
+ SubcomponentNames subcomponentNames,
+ CompilerOptions compilerOptions) {
+ this.graph = graph;
+ this.name = name;
+ this.component = classBuilder(name);
+ this.subcomponentNames = subcomponentNames;
+ this.shardOwner = Optional.empty();
+ this.externalReferenceBlock = CodeBlock.of("$T.this", name);
+ this.compilerOptions = compilerOptions;
+ }
+
+ private ComponentImplementation(ComponentImplementation shardOwner, ClassName shardName) {
+ this.graph = shardOwner.graph;
+ this.name = shardName;
+ this.component = classBuilder(shardName);
+ this.subcomponentNames = shardOwner.subcomponentNames;
+ this.compilerOptions = shardOwner.compilerOptions;
+ this.shardOwner = Optional.of(shardOwner);
+ String fieldName = UPPER_CAMEL.to(LOWER_CAMEL, name.simpleName());
+ String uniqueFieldName = shardOwner.getUniqueFieldName(fieldName);
+ this.externalReferenceBlock = CodeBlock.of("$T.this.$N", shardOwner.name, uniqueFieldName);
+ shardOwner.addTypeSupplier(() -> generate().build());
+ shardOwner.addField(
+ FieldSpecKind.COMPONENT_SHARD,
+ FieldSpec.builder(name, uniqueFieldName, PRIVATE, FINAL)
+ .initializer("new $T()", name)
+ .build());
+ }
+
+ /** Returns a component implementation for a top-level component. */
+ public static ComponentImplementation topLevelComponentImplementation(
+ BindingGraph graph,
+ ClassName name,
+ SubcomponentNames subcomponentNames,
+ CompilerOptions compilerOptions) {
+ return new ComponentImplementation(graph, name, subcomponentNames, compilerOptions);
+ }
+
+ /** Returns a component implementation that is a child of the current implementation. */
+ public ComponentImplementation childComponentImplementation(BindingGraph graph) {
+ checkState(!shardOwner.isPresent(), "Shards cannot create child components.");
+ ClassName childName = getSubcomponentName(graph.componentDescriptor());
+ return new ComponentImplementation(graph, childName, subcomponentNames, compilerOptions);
+ }
+
+ /** Returns a component implementation that is a shard of the current implementation. */
+ public ComponentImplementation shardImplementation(Key key) {
+ checkState(!shardOwner.isPresent(), "Shards cannot create other shards.");
+ if (!shardsByKey.containsKey(key)) {
+ int keysPerShard = compilerOptions.keysPerComponentShard(graph.componentTypeElement());
+ if (!shardsByKey.isEmpty() && shardsByKey.size() % keysPerShard == 0) {
+ ClassName shardName = name.nestedClass("Shard" + shardsByKey.size() / keysPerShard);
+ currentShard = new ComponentImplementation(this, shardName);
+ }
+ shardsByKey.put(key, currentShard);
+ }
+ return shardsByKey.get(key);
+ }
+
+ /** Returns a reference to this compenent when called from a class nested in this component. */
+ public CodeBlock externalReferenceBlock() {
+ return externalReferenceBlock;
+ }
+
+ // TODO(ronshapiro): see if we can remove this method and instead inject it in the objects that
+ // need it.
+ /** Returns the binding graph for the component being generated. */
+ public BindingGraph graph() {
+ return graph;
+ }
+
+ /** Returns the descriptor for the component being generated. */
+ public ComponentDescriptor componentDescriptor() {
+ return graph.componentDescriptor();
+ }
+
+ /** Returns the name of the component. */
+ public ClassName name() {
+ return name;
+ }
+
+ /** Returns whether or not the implementation is nested within another class. */
+ public boolean isNested() {
+ return name.enclosingClassName() != null;
+ }
+
+ /**
+ * Returns the kind of this component's creator.
+ *
+ * @throws IllegalStateException if the component has no creator
+ */
+ private ComponentCreatorKind creatorKind() {
+ checkState(componentDescriptor().hasCreator());
+ return componentDescriptor()
+ .creatorDescriptor()
+ .map(ComponentCreatorDescriptor::kind)
+ .orElse(BUILDER);
+ }
+
+ /**
+ * Returns the name of the creator class for this component. It will be a sibling of this
+ * generated class unless this is a top-level component, in which case it will be nested.
+ */
+ public ClassName getCreatorName() {
+ return isNested()
+ ? name.peerClass(subcomponentNames.getCreatorName(componentDescriptor()))
+ : name.nestedClass(creatorKind().typeName());
+ }
+
+ /** Returns the name of the nested implementation class for a child component. */
+ private ClassName getSubcomponentName(ComponentDescriptor childDescriptor) {
+ checkArgument(
+ componentDescriptor().childComponents().contains(childDescriptor),
+ "%s is not a child component of %s",
+ childDescriptor.typeElement(),
+ componentDescriptor().typeElement());
+ return name.nestedClass(subcomponentNames.get(childDescriptor) + "Impl");
+ }
+
+ /**
+ * Returns the simple name of the creator implementation class for the given subcomponent creator
+ * {@link Key}.
+ */
+ String getSubcomponentCreatorSimpleName(Key key) {
+ return subcomponentNames.getCreatorName(key);
+ }
+
+ /** Returns {@code true} if {@code type} is accessible from the generated component. */
+ boolean isTypeAccessible(TypeMirror type) {
+ return isTypeAccessibleFrom(type, name.packageName());
+ }
+
+ /** Adds the given super type to the component. */
+ public void addSupertype(TypeElement supertype) {
+ TypeSpecs.addSupertype(component, supertype);
+ }
+
+ // TODO(dpb): Consider taking FieldSpec, and returning identical FieldSpec with unique name?
+ /** Adds the given field to the component. */
+ public void addField(FieldSpecKind fieldKind, FieldSpec fieldSpec) {
+ fieldSpecsMap.put(fieldKind, fieldSpec);
+ }
+
+ // TODO(dpb): Consider taking MethodSpec, and returning identical MethodSpec with unique name?
+ /** Adds the given method to the component. */
+ public void addMethod(MethodSpecKind methodKind, MethodSpec methodSpec) {
+ methodSpecsMap.put(methodKind, methodSpec);
+ }
+
+ /** Adds the given annotation to the component. */
+ public void addAnnotation(AnnotationSpec annotation) {
+ component.addAnnotation(annotation);
+ }
+
+ /** Adds the given type to the component. */
+ public void addType(TypeSpecKind typeKind, TypeSpec typeSpec) {
+ typeSpecsMap.put(typeKind, typeSpec);
+ }
+
+ /** Adds a {@link Supplier} for the SwitchingProvider for the component. */
+ void addTypeSupplier(Supplier<TypeSpec> typeSpecSupplier) {
+ typeSuppliers.add(typeSpecSupplier);
+ }
+
+ /** Adds the given code block to the initialize methods of the component. */
+ void addInitialization(CodeBlock codeBlock) {
+ initializations.add(codeBlock);
+ }
+
+ /** Adds the given code block that initializes a {@link ComponentRequirement}. */
+ void addComponentRequirementInitialization(CodeBlock codeBlock) {
+ componentRequirementInitializations.add(codeBlock);
+ }
+
+ /**
+ * Marks the given key of a producer as one that should have a cancellation statement in the
+ * cancellation listener method of the component.
+ */
+ void addCancellableProducerKey(Key key) {
+ cancellableProducerKeys.add(key);
+ }
+
+ /** Returns a new, unique field name for the component based on the given name. */
+ String getUniqueFieldName(String name) {
+ return componentFieldNames.getUniqueName(name);
+ }
+
+ /** Returns a new, unique method name for the component based on the given name. */
+ public String getUniqueMethodName(String name) {
+ return componentMethodNames.getUniqueName(name);
+ }
+
+ /** Returns a new, unique method name for a getter method for the given request. */
+ String getUniqueMethodName(BindingRequest request) {
+ return uniqueMethodName(request, KeyVariableNamer.name(request.key()));
+ }
+
+ private String uniqueMethodName(BindingRequest request, String bindingName) {
+ // This name is intentionally made to match the name for fields in fastInit
+ // in order to reduce the constant pool size. b/162004246
+ String baseMethodName = bindingName
+ + (request.isRequestKind(RequestKind.INSTANCE)
+ ? ""
+ : UPPER_UNDERSCORE.to(UPPER_CAMEL, request.kindName()));
+ return getUniqueMethodName(baseMethodName);
+ }
+
+ /**
+ * Gets the parameter name to use for the given requirement for this component, starting with the
+ * given base name if no parameter name has already been selected for the requirement.
+ */
+ public String getParameterName(ComponentRequirement requirement, String baseName) {
+ return componentRequirementParameterNames.computeIfAbsent(
+ requirement, r -> getUniqueFieldName(baseName));
+ }
+
+ /** Claims a new method name for the component. Does nothing if method name already exists. */
+ public void claimMethodName(CharSequence name) {
+ componentMethodNames.claim(name);
+ }
+
+ /** Returns the list of {@link CodeBlock}s that need to go in the initialize method. */
+ public ImmutableList<CodeBlock> getInitializations() {
+ return ImmutableList.copyOf(initializations);
+ }
+
+ /**
+ * Returns a list of {@link CodeBlock}s for initializing {@link ComponentRequirement}s.
+ *
+ * <p>These initializations are kept separate from {@link #getInitializations()} because they must
+ * be executed before the initializations of any framework instance initializations in a
+ * superclass implementation that may depend on the instances. We cannot use the same strategy
+ * that we use for framework instances (i.e. wrap in a {@link dagger.internal.DelegateFactory} or
+ * {@link dagger.producers.internal.DelegateProducer} since the types of these initialized fields
+ * have no interface type that we can write a proxy for.
+ */
+ // TODO(cgdecker): can these be inlined with getInitializations() now that we've turned down
+ // ahead-of-time subcomponents?
+ public ImmutableList<CodeBlock> getComponentRequirementInitializations() {
+ return ImmutableList.copyOf(componentRequirementInitializations);
+ }
+
+ /**
+ * Returns the list of producer {@link Key}s that need cancellation statements in the cancellation
+ * listener method.
+ */
+ public ImmutableList<Key> getCancellableProducerKeys() {
+ return ImmutableList.copyOf(cancellableProducerKeys);
+ }
+
+ /** Generates the component and returns the resulting {@link TypeSpec.Builder}. */
+ public TypeSpec.Builder generate() {
+ modifiers().forEach(component::addModifiers);
+ fieldSpecsMap.asMap().values().forEach(component::addFields);
+ methodSpecsMap.asMap().values().forEach(component::addMethods);
+ typeSpecsMap.asMap().values().forEach(component::addTypes);
+ typeSuppliers.stream().map(Supplier::get).forEach(component::addType);
+ return component;
+ }
+
+ private ImmutableSet<Modifier> modifiers() {
+ if (isNested()) {
+ return ImmutableSet.of(PRIVATE, FINAL);
+ }
+ return graph.componentTypeElement().getModifiers().contains(PUBLIC)
+ // TODO(ronshapiro): perhaps all generated components should be non-public?
+ ? ImmutableSet.of(PUBLIC, FINAL)
+ : ImmutableSet.of(FINAL);
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/ComponentInstanceBindingExpression.java b/java/dagger/internal/codegen/writing/ComponentInstanceBindingExpression.java
new file mode 100644
index 000000000..9fa10c6e0
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ComponentInstanceBindingExpression.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.javapoet.Expression;
+
+/** A binding expression for the instance of the component itself, i.e. {@code this}. */
+final class ComponentInstanceBindingExpression extends SimpleInvocationBindingExpression {
+ private final ClassName componentName;
+ private final ContributionBinding binding;
+
+ ComponentInstanceBindingExpression(ContributionBinding binding, ClassName componentName) {
+ super(binding);
+ this.binding = binding;
+ this.componentName = componentName;
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ return Expression.create(
+ binding.key().type(),
+ componentName.equals(requestingClass)
+ ? CodeBlock.of("this")
+ : CodeBlock.of("$T.this", componentName));
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/ComponentMethodBindingExpression.java b/java/dagger/internal/codegen/writing/ComponentMethodBindingExpression.java
new file mode 100644
index 000000000..7fa9aa3ed
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ComponentMethodBindingExpression.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.binding.BindingRequest;
+import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A binding expression that implements and uses a component method.
+ *
+ * <p>Dependents of this binding expression will just call the component method.
+ */
+final class ComponentMethodBindingExpression extends MethodBindingExpression {
+ private final ComponentImplementation componentImplementation;
+ private final ComponentMethodDescriptor componentMethod;
+
+ ComponentMethodBindingExpression(
+ BindingRequest request,
+ ContributionBinding binding,
+ MethodImplementationStrategy methodImplementationStrategy,
+ BindingExpression wrappedBindingExpression,
+ ComponentImplementation componentImplementation,
+ ComponentMethodDescriptor componentMethod,
+ DaggerTypes types) {
+ super(
+ request,
+ binding,
+ methodImplementationStrategy,
+ wrappedBindingExpression,
+ componentImplementation,
+ types);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.componentMethod = checkNotNull(componentMethod);
+ }
+
+ @Override
+ protected CodeBlock getComponentMethodImplementation(
+ ComponentMethodDescriptor componentMethod, ComponentImplementation component) {
+ // There could be several methods on the component for the same request key and kind.
+ // Only one should use the BindingMethodImplementation; the others can delegate that one. So
+ // use methodImplementation.body() only if componentMethod equals the method for this instance.
+
+ // Separately, the method might be defined on a supertype that is also a supertype of some
+ // parent component. In that case, the same ComponentMethodDescriptor will be used to add a CMBE
+ // for the parent and the child. Only the parent's should use the BindingMethodImplementation;
+ // the child's can delegate to the parent. So use methodImplementation.body() only if
+ // componentName equals the component for this instance.
+ return componentMethod.equals(this.componentMethod) && component.equals(componentImplementation)
+ ? methodBodyForComponentMethod(componentMethod)
+ : super.getComponentMethodImplementation(componentMethod, component);
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ // If a component method returns a primitive, update the expression's type which might be boxed.
+ Expression expression = super.getDependencyExpression(requestingClass);
+ TypeMirror methodReturnType = componentMethod.methodElement().getReturnType();
+ return methodReturnType.getKind().isPrimitive()
+ ? Expression.create(methodReturnType, expression.codeBlock())
+ : expression;
+ }
+
+ @Override
+ protected void addMethod() {}
+
+ @Override
+ protected String methodName() {
+ return componentMethod.methodElement().getSimpleName().toString();
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/ComponentProvisionBindingExpression.java b/java/dagger/internal/codegen/writing/ComponentProvisionBindingExpression.java
new file mode 100644
index 000000000..53914b669
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ComponentProvisionBindingExpression.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.Preconditions;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.ComponentRequirement;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.javapoet.Expression;
+
+/** A binding expression for component provision methods. */
+final class ComponentProvisionBindingExpression extends SimpleInvocationBindingExpression {
+ private final ProvisionBinding binding;
+ private final BindingGraph bindingGraph;
+ private final ComponentRequirementExpressions componentRequirementExpressions;
+ private final CompilerOptions compilerOptions;
+
+ ComponentProvisionBindingExpression(
+ ProvisionBinding binding,
+ BindingGraph bindingGraph,
+ ComponentRequirementExpressions componentRequirementExpressions,
+ CompilerOptions compilerOptions) {
+ super(binding);
+ this.binding = binding;
+ this.bindingGraph = checkNotNull(bindingGraph);
+ this.componentRequirementExpressions = checkNotNull(componentRequirementExpressions);
+ this.compilerOptions = checkNotNull(compilerOptions);
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ CodeBlock invocation =
+ CodeBlock.of(
+ "$L.$L()",
+ componentRequirementExpressions.getExpression(componentRequirement(), requestingClass),
+ binding.bindingElement().get().getSimpleName());
+ return Expression.create(
+ binding.contributedPrimitiveType().orElse(binding.key().type()),
+ maybeCheckForNull(binding, compilerOptions, invocation));
+ }
+
+ private ComponentRequirement componentRequirement() {
+ return bindingGraph
+ .componentDescriptor()
+ .getDependencyThatDefinesMethod(binding.bindingElement().get());
+ }
+
+ static CodeBlock maybeCheckForNull(
+ ProvisionBinding binding, CompilerOptions compilerOptions, CodeBlock invocation) {
+ return binding.shouldCheckForNull(compilerOptions)
+ ? CodeBlock.of("$T.checkNotNullFromComponent($L)", Preconditions.class, invocation)
+ : invocation;
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/ComponentRequirementBindingExpression.java b/java/dagger/internal/codegen/writing/ComponentRequirementBindingExpression.java
new file mode 100644
index 000000000..299f27974
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ComponentRequirementBindingExpression.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.binding.ComponentRequirement;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.javapoet.Expression;
+
+/**
+ * A binding expression for instances bound with {@link dagger.BindsInstance} and instances of
+ * {@linkplain dagger.Component#dependencies() component} and {@linkplain
+ * dagger.producers.ProductionComponent#dependencies() production component dependencies}.
+ */
+final class ComponentRequirementBindingExpression extends SimpleInvocationBindingExpression {
+ private final ComponentRequirement componentRequirement;
+ private final ComponentRequirementExpressions componentRequirementExpressions;
+
+ ComponentRequirementBindingExpression(
+ ContributionBinding binding,
+ ComponentRequirement componentRequirement,
+ ComponentRequirementExpressions componentRequirementExpressions) {
+ super(binding);
+ this.componentRequirement = componentRequirement;
+ this.componentRequirementExpressions = componentRequirementExpressions;
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ return Expression.create(
+ componentRequirement.type(),
+ componentRequirementExpressions.getExpression(componentRequirement, requestingClass));
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/ComponentRequirementExpression.java b/java/dagger/internal/codegen/writing/ComponentRequirementExpression.java
new file mode 100644
index 000000000..13008b8eb
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ComponentRequirementExpression.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.binding.ComponentRequirement;
+
+/**
+ * A factory for expressions of {@link ComponentRequirement}s in the generated component. This is
+ * <em>not</em> a {@link BindingExpression}, since {@link ComponentRequirement}s do not have a
+ * {@link dagger.model.Key}. See {@link ComponentRequirementBindingExpression} for binding
+ * expressions that are themselves a component requirement.
+ */
+interface ComponentRequirementExpression {
+ /**
+ * Returns an expression for the {@link ComponentRequirement} to be used when implementing a
+ * component method. This may add a field or method to the component in order to reference the
+ * component requirement outside of the {@code initialize()} methods.
+ */
+ CodeBlock getExpression(ClassName requestingClass);
+
+ /**
+ * Returns an expression for the {@link ComponentRequirement} to be used only within {@code
+ * initialize()} methods, where the constructor parameters are available.
+ *
+ * <p>When accessing this expression from a subcomponent, this may cause a field to be initialized
+ * or a method to be added in the component that owns this {@link ComponentRequirement}.
+ */
+ default CodeBlock getExpressionDuringInitialization(ClassName requestingClass) {
+ return getExpression(requestingClass);
+ }
+
+ /**
+ * Returns the expression for the {@link ComponentRequirement} to be used when reimplementing a
+ * modifiable module method.
+ */
+ default CodeBlock getModifiableModuleMethodExpression(ClassName requestingClass) {
+ return CodeBlock.of("return $L", getExpression(requestingClass));
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/ComponentRequirementExpressions.java b/java/dagger/internal/codegen/writing/ComponentRequirementExpressions.java
new file mode 100644
index 000000000..653a7a25a
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ComponentRequirementExpressions.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Suppliers.memoize;
+import static dagger.internal.codegen.writing.ComponentImplementation.FieldSpecKind.COMPONENT_REQUIREMENT_FIELD;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+
+import com.google.common.base.Supplier;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.ComponentRequirement;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * A central repository of expressions used to access any {@link ComponentRequirement} available to
+ * a component.
+ */
+@PerComponentImplementation
+public final class ComponentRequirementExpressions {
+
+ // TODO(dpb,ronshapiro): refactor this and ComponentBindingExpressions into a
+ // HierarchicalComponentMap<K, V>, or perhaps this use a flattened ImmutableMap, built from its
+ // parents? If so, maybe make ComponentRequirementExpression.Factory create it.
+
+ private final Optional<ComponentRequirementExpressions> parent;
+ private final Map<ComponentRequirement, ComponentRequirementExpression>
+ componentRequirementExpressions = new HashMap<>();
+ private final BindingGraph graph;
+ private final ComponentImplementation componentImplementation;
+ private final ModuleProxies moduleProxies;
+
+ // TODO(ronshapiro): give ComponentImplementation a graph() method
+ @Inject
+ ComponentRequirementExpressions(
+ @ParentComponent Optional<ComponentRequirementExpressions> parent,
+ BindingGraph graph,
+ ComponentImplementation componentImplementation,
+ DaggerElements elements,
+ ModuleProxies moduleProxies) {
+ this.parent = parent;
+ this.graph = graph;
+ this.componentImplementation = componentImplementation;
+ this.moduleProxies = moduleProxies;
+ }
+
+ /**
+ * Returns an expression for the {@code componentRequirement} to be used when implementing a
+ * component method. This may add a field or method to the component in order to reference the
+ * component requirement outside of the {@code initialize()} methods.
+ */
+ CodeBlock getExpression(ComponentRequirement componentRequirement, ClassName requestingClass) {
+ return getExpression(componentRequirement).getExpression(requestingClass);
+ }
+
+ /**
+ * Returns an expression for the {@code componentRequirement} to be used only within {@code
+ * initialize()} methods, where the component constructor parameters are available.
+ *
+ * <p>When accessing this expression from a subcomponent, this may cause a field to be initialized
+ * or a method to be added in the component that owns this {@link ComponentRequirement}.
+ */
+ CodeBlock getExpressionDuringInitialization(
+ ComponentRequirement componentRequirement, ClassName requestingClass) {
+ return getExpression(componentRequirement).getExpressionDuringInitialization(requestingClass);
+ }
+
+ ComponentRequirementExpression getExpression(ComponentRequirement componentRequirement) {
+ if (graph.componentRequirements().contains(componentRequirement)) {
+ return componentRequirementExpressions.computeIfAbsent(
+ componentRequirement, this::createField);
+ }
+ if (parent.isPresent()) {
+ return parent.get().getExpression(componentRequirement);
+ }
+
+ throw new IllegalStateException(
+ "no component requirement expression found for " + componentRequirement);
+ }
+
+ /** Returns a field for a {@link ComponentRequirement}. */
+ private ComponentRequirementExpression createField(ComponentRequirement requirement) {
+ if (componentImplementation.componentDescriptor().hasCreator()) {
+ return new ComponentParameterField(requirement, componentImplementation, Optional.empty());
+ } else if (graph.factoryMethod().isPresent()
+ && graph.factoryMethodParameters().containsKey(requirement)) {
+ String parameterName =
+ graph.factoryMethodParameters().get(requirement).getSimpleName().toString();
+ return new ComponentParameterField(
+ requirement, componentImplementation, Optional.of(parameterName));
+ } else if (requirement.kind().isModule()) {
+ return new InstantiableModuleField(requirement, componentImplementation);
+ } else {
+ throw new AssertionError(
+ String.format("Can't create %s in %s", requirement, componentImplementation.name()));
+ }
+ }
+
+ private abstract static class AbstractField implements ComponentRequirementExpression {
+ final ComponentRequirement componentRequirement;
+ final ComponentImplementation componentImplementation;
+ final String fieldName;
+ private final Supplier<MemberSelect> field = memoize(this::addField);
+
+ private AbstractField(
+ ComponentRequirement componentRequirement,
+ ComponentImplementation componentImplementation) {
+ this.componentRequirement = checkNotNull(componentRequirement);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ // Note: The field name is being claimed eagerly here even though we don't know at this point
+ // whether or not the requirement will even need a field. This is done because:
+ // A) ComponentParameterField wants to ensure that it doesn't give the parameter the same name
+ // as any field in the component, which requires that it claim a "field name" for itself
+ // when naming the parameter.
+ // B) The parameter name may be needed before the field name is.
+ // C) We want to prefer giving the best name to the field rather than the parameter given its
+ // wider scope.
+ this.fieldName =
+ componentImplementation.getUniqueFieldName(componentRequirement.variableName());
+ }
+
+ @Override
+ public CodeBlock getExpression(ClassName requestingClass) {
+ return field.get().getExpressionFor(requestingClass);
+ }
+
+ private MemberSelect addField() {
+ FieldSpec field = createField();
+ componentImplementation.addField(COMPONENT_REQUIREMENT_FIELD, field);
+ componentImplementation.addComponentRequirementInitialization(fieldInitialization(field));
+ return MemberSelect.localField(componentImplementation.name(), fieldName);
+ }
+
+ private FieldSpec createField() {
+ return FieldSpec.builder(TypeName.get(componentRequirement.type()), fieldName, PRIVATE, FINAL)
+ .build();
+ }
+
+ /** Returns the {@link CodeBlock} that initializes the component field during construction. */
+ abstract CodeBlock fieldInitialization(FieldSpec componentField);
+ }
+
+ /**
+ * A {@link ComponentRequirementExpression} for {@link ComponentRequirement}s that can be
+ * instantiated by the component (i.e. a static class with a no-arg constructor).
+ */
+ private final class InstantiableModuleField extends AbstractField {
+ private final TypeElement moduleElement;
+
+ private InstantiableModuleField(
+ ComponentRequirement module, ComponentImplementation componentImplementation) {
+ super(module, componentImplementation);
+ checkArgument(module.kind().isModule());
+ this.moduleElement = module.typeElement();
+ }
+
+ @Override
+ CodeBlock fieldInitialization(FieldSpec componentField) {
+ return CodeBlock.of(
+ "this.$N = $L;",
+ componentField,
+ moduleProxies.newModuleInstance(moduleElement, componentImplementation.name()));
+ }
+ }
+
+ /**
+ * A {@link ComponentRequirementExpression} for {@link ComponentRequirement}s that are passed in
+ * as parameters to the component's constructor.
+ */
+ private static final class ComponentParameterField extends AbstractField {
+ private final String parameterName;
+
+ private ComponentParameterField(
+ ComponentRequirement componentRequirement,
+ ComponentImplementation componentImplementation,
+ Optional<String> name) {
+ super(componentRequirement, componentImplementation);
+ // Get the name that the component implementation will use for its parameter for the
+ // requirement. If the given name is different than the name of the field created for the
+ // requirement (as may be the case when the parameter name is derived from a user-written
+ // factory method parameter), just use that as the base name for the parameter. Otherwise,
+ // append "Param" to the end of the name to differentiate.
+ // In either case, componentImplementation.getParameterName() will ensure that the final name
+ // that is used is not the same name as any field in the component even if there's something
+ // weird where the component actually has fields named, say, "foo" and "fooParam".
+ String baseName = name.filter(n -> !n.equals(fieldName)).orElse(fieldName + "Param");
+ this.parameterName = componentImplementation.getParameterName(componentRequirement, baseName);
+ }
+
+ @Override
+ public CodeBlock getExpressionDuringInitialization(ClassName requestingClass) {
+ if (componentImplementation.name().equals(requestingClass)) {
+ return CodeBlock.of("$L", parameterName);
+ } else {
+ // requesting this component requirement during initialization of a child component requires
+ // it to be accessed from a field and not the parameter (since it is no longer available)
+ return getExpression(requestingClass);
+ }
+ }
+
+ @Override
+ CodeBlock fieldInitialization(FieldSpec componentField) {
+ // Don't checkNotNull here because the parameter may be nullable; if it isn't, the caller
+ // should handle checking that before passing the parameter.
+ return CodeBlock.of("this.$N = $L;", componentField, parameterName);
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/DelegateBindingExpression.java b/java/dagger/internal/codegen/writing/DelegateBindingExpression.java
new file mode 100644
index 000000000..336113264
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/DelegateBindingExpression.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.base.RequestKinds.requestType;
+import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+import static dagger.model.BindingKind.DELEGATE;
+
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.binding.Binding;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.BindsTypeChecker;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.RequestKind;
+import javax.lang.model.type.TypeMirror;
+
+/** A {@link dagger.internal.codegen.writing.BindingExpression} for {@code @Binds} methods. */
+final class DelegateBindingExpression extends BindingExpression {
+ private final ContributionBinding binding;
+ private final RequestKind requestKind;
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final DaggerTypes types;
+ private final BindsTypeChecker bindsTypeChecker;
+
+ DelegateBindingExpression(
+ ContributionBinding binding,
+ RequestKind requestKind,
+ ComponentBindingExpressions componentBindingExpressions,
+ DaggerTypes types,
+ DaggerElements elements) {
+ this.binding = checkNotNull(binding);
+ this.requestKind = checkNotNull(requestKind);
+ this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+ this.types = checkNotNull(types);
+ this.bindsTypeChecker = new BindsTypeChecker(types, elements);
+ }
+
+ /**
+ * Returns {@code true} if the {@code @Binds} binding's scope is stronger than the scope of the
+ * binding it depends on.
+ */
+ static boolean isBindsScopeStrongerThanDependencyScope(
+ ContributionBinding bindsBinding, BindingGraph graph) {
+ checkArgument(bindsBinding.kind().equals(DELEGATE));
+ Binding dependencyBinding =
+ graph.contributionBinding(getOnlyElement(bindsBinding.dependencies()).key());
+ ScopeKind bindsScope = ScopeKind.get(bindsBinding);
+ ScopeKind dependencyScope = ScopeKind.get(dependencyBinding);
+ return bindsScope.isStrongerScopeThan(dependencyScope);
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ Expression delegateExpression =
+ componentBindingExpressions.getDependencyExpression(
+ bindingRequest(getOnlyElement(binding.dependencies()).key(), requestKind),
+ requestingClass);
+
+ TypeMirror contributedType = binding.contributedType();
+ switch (requestKind) {
+ case INSTANCE:
+ return instanceRequiresCast(delegateExpression, requestingClass)
+ ? delegateExpression.castTo(contributedType)
+ : delegateExpression;
+ default:
+ return castToRawTypeIfNecessary(
+ delegateExpression, requestType(requestKind, contributedType, types));
+ }
+ }
+
+ private boolean instanceRequiresCast(Expression delegateExpression, ClassName requestingClass) {
+ // delegateExpression.type() could be Object if expression is satisfied with a raw
+ // Provider's get() method.
+ return !bindsTypeChecker.isAssignable(
+ delegateExpression.type(), binding.contributedType(), binding.contributionType())
+ && isTypeAccessibleFrom(binding.contributedType(), requestingClass.packageName());
+ }
+
+ /**
+ * If {@code delegateExpression} can be assigned to {@code desiredType} safely, then {@code
+ * delegateExpression} is returned unchanged. If the {@code delegateExpression} is already a raw
+ * type, returns {@code delegateExpression} as well, as casting would have no effect. Otherwise,
+ * returns a {@link Expression#castTo(TypeMirror) casted} version of {@code delegateExpression}
+ * to the raw type of {@code desiredType}.
+ */
+ // TODO(ronshapiro): this probably can be generalized for usage in InjectionMethods
+ private Expression castToRawTypeIfNecessary(
+ Expression delegateExpression, TypeMirror desiredType) {
+ if (types.isAssignable(delegateExpression.type(), desiredType)) {
+ return delegateExpression;
+ }
+ return delegateExpression.castTo(types.erasure(desiredType));
+ }
+
+ private enum ScopeKind {
+ UNSCOPED,
+ SINGLE_CHECK,
+ DOUBLE_CHECK,
+ ;
+
+ static ScopeKind get(Binding binding) {
+ return binding
+ .scope()
+ .map(scope -> scope.isReusable() ? SINGLE_CHECK : DOUBLE_CHECK)
+ .orElse(UNSCOPED);
+ }
+
+ boolean isStrongerScopeThan(ScopeKind other) {
+ return this.ordinal() > other.ordinal();
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/DelegatingFrameworkInstanceCreationExpression.java b/java/dagger/internal/codegen/writing/DelegatingFrameworkInstanceCreationExpression.java
new file mode 100644
index 000000000..a7f9556d9
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/DelegatingFrameworkInstanceCreationExpression.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
+
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import dagger.model.DependencyRequest;
+
+/** A framework instance creation expression for a {@link dagger.Binds @Binds} binding. */
+final class DelegatingFrameworkInstanceCreationExpression
+ implements FrameworkInstanceCreationExpression {
+
+ private final ContributionBinding binding;
+ private final ComponentImplementation componentImplementation;
+ private final ComponentBindingExpressions componentBindingExpressions;
+
+ DelegatingFrameworkInstanceCreationExpression(
+ ContributionBinding binding,
+ ComponentImplementation componentImplementation,
+ ComponentBindingExpressions componentBindingExpressions) {
+ this.binding = checkNotNull(binding);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ DependencyRequest dependency = getOnlyElement(binding.dependencies());
+ return CodeBlocks.cast(
+ componentBindingExpressions
+ .getDependencyExpression(
+ bindingRequest(dependency.key(), binding.frameworkType()),
+ componentImplementation.name())
+ .codeBlock(),
+ binding.frameworkType().frameworkClass());
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/DependencyMethodProducerCreationExpression.java b/java/dagger/internal/codegen/writing/DependencyMethodProducerCreationExpression.java
new file mode 100644
index 000000000..5ac1e8f6b
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/DependencyMethodProducerCreationExpression.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.anonymousClassBuilder;
+import static dagger.internal.codegen.javapoet.TypeNames.dependencyMethodProducerOf;
+import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.ComponentRequirement;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+
+/**
+ * A {@link dagger.producers.Producer} creation expression for a production method on a production
+ * component's {@linkplain dagger.producers.ProductionComponent#dependencies()} dependency} that
+ * returns a {@link com.google.common.util.concurrent.ListenableFuture}.
+ */
+// TODO(dpb): Resolve with DependencyMethodProviderCreationExpression.
+final class DependencyMethodProducerCreationExpression
+ implements FrameworkInstanceCreationExpression {
+ private final ContributionBinding binding;
+ private final ComponentImplementation componentImplementation;
+ private final ComponentRequirementExpressions componentRequirementExpressions;
+ private final BindingGraph graph;
+
+ DependencyMethodProducerCreationExpression(
+ ContributionBinding binding,
+ ComponentImplementation componentImplementation,
+ ComponentRequirementExpressions componentRequirementExpressions,
+ BindingGraph graph) {
+ this.binding = checkNotNull(binding);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.componentRequirementExpressions = checkNotNull(componentRequirementExpressions);
+ this.graph = checkNotNull(graph);
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ ComponentRequirement dependency =
+ graph.componentDescriptor().getDependencyThatDefinesMethod(binding.bindingElement().get());
+ FieldSpec dependencyField =
+ FieldSpec.builder(
+ ClassName.get(dependency.typeElement()), dependency.variableName(), PRIVATE, FINAL)
+ .initializer(
+ componentRequirementExpressions.getExpressionDuringInitialization(
+ dependency,
+ // This isn't a real class name, but we want the requesting class for the
+ // expression to *not* be the same class as the component implementation,
+ // because it isn't... it's an anonymous inner class.
+ // TODO(cgdecker): If we didn't use an anonymous inner class here but instead
+ // generated a named nested class as with
+ // DependencyMethodProviderCreationExpression, we wouldn't need to deal with
+ // this and might be able to avoid potentially creating an extra field in the
+ // component?
+ componentImplementation.name().nestedClass("Anonymous")))
+ .build();
+ // TODO(b/70395982): Explore using a private static type instead of an anonymous class.
+ TypeName keyType = TypeName.get(binding.key().type());
+ return CodeBlock.of(
+ "$L",
+ anonymousClassBuilder("")
+ .superclass(dependencyMethodProducerOf(keyType))
+ .addField(dependencyField)
+ .addMethod(
+ methodBuilder("callDependencyMethod")
+ .addAnnotation(Override.class)
+ .addModifiers(PUBLIC)
+ .returns(listenableFutureOf(keyType))
+ .addStatement(
+ "return $N.$L()",
+ dependencyField,
+ binding.bindingElement().get().getSimpleName())
+ .build())
+ .build());
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/DependencyMethodProviderCreationExpression.java b/java/dagger/internal/codegen/writing/DependencyMethodProviderCreationExpression.java
new file mode 100644
index 000000000..5a40c0206
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/DependencyMethodProviderCreationExpression.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
+import static dagger.internal.codegen.writing.ComponentImplementation.TypeSpecKind.COMPONENT_PROVISION_FACTORY;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.auto.common.MoreTypes;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.ComponentRequirement;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import javax.lang.model.element.Element;
+
+/**
+ * A {@link javax.inject.Provider} creation expression for a provision method on a component's
+ * {@linkplain dagger.Component#dependencies()} dependency}.
+ */
+// TODO(dpb): Resolve with DependencyMethodProducerCreationExpression.
+final class DependencyMethodProviderCreationExpression
+ implements FrameworkInstanceCreationExpression {
+
+ private final ComponentImplementation componentImplementation;
+ private final ComponentRequirementExpressions componentRequirementExpressions;
+ private final CompilerOptions compilerOptions;
+ private final BindingGraph graph;
+ private final ContributionBinding binding;
+
+ DependencyMethodProviderCreationExpression(
+ ContributionBinding binding,
+ ComponentImplementation componentImplementation,
+ ComponentRequirementExpressions componentRequirementExpressions,
+ CompilerOptions compilerOptions,
+ BindingGraph graph) {
+ this.binding = checkNotNull(binding);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.componentRequirementExpressions = checkNotNull(componentRequirementExpressions);
+ this.compilerOptions = checkNotNull(compilerOptions);
+ this.graph = checkNotNull(graph);
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ // TODO(sameb): The Provider.get() throws a very vague NPE. The stack trace doesn't
+ // help to figure out what the method or return type is. If we include a string
+ // of the return type or method name in the error message, that can defeat obfuscation.
+ // We can easily include the raw type (no generics) + annotation type (no values),
+ // using .class & String.format -- but that wouldn't be the whole story.
+ // What should we do?
+ CodeBlock invocation =
+ ComponentProvisionBindingExpression.maybeCheckForNull(
+ (ProvisionBinding) binding,
+ compilerOptions,
+ CodeBlock.of(
+ "$N.$N()", dependency().variableName(), provisionMethod().getSimpleName()));
+ ClassName dependencyClassName = ClassName.get(dependency().typeElement());
+ TypeName keyType = TypeName.get(binding.key().type());
+ MethodSpec.Builder getMethod =
+ methodBuilder("get")
+ .addAnnotation(Override.class)
+ .addModifiers(PUBLIC)
+ .returns(keyType)
+ .addStatement("return $L", invocation);
+ if (binding.nullableType().isPresent()) {
+ getMethod.addAnnotation(ClassName.get(MoreTypes.asTypeElement(binding.nullableType().get())));
+ }
+ componentImplementation.addType(
+ COMPONENT_PROVISION_FACTORY,
+ classBuilder(factoryClassName())
+ .addSuperinterface(providerOf(keyType))
+ .addModifiers(PRIVATE, STATIC)
+ .addField(dependencyClassName, dependency().variableName(), PRIVATE, FINAL)
+ .addMethod(
+ constructorBuilder()
+ .addParameter(dependencyClassName, dependency().variableName())
+ .addStatement("this.$1L = $1L", dependency().variableName())
+ .build())
+ .addMethod(getMethod.build())
+ .build());
+ return CodeBlock.of(
+ "new $T($L)",
+ factoryClassName(),
+ componentRequirementExpressions.getExpressionDuringInitialization(
+ dependency(), componentImplementation.name()));
+ }
+
+ private ClassName factoryClassName() {
+ String factoryName =
+ ClassName.get(dependency().typeElement()).toString().replace('.', '_')
+ + "_"
+ + binding.bindingElement().get().getSimpleName();
+ return componentImplementation.name().nestedClass(factoryName);
+ }
+
+ private ComponentRequirement dependency() {
+ return graph.componentDescriptor().getDependencyThatDefinesMethod(provisionMethod());
+ }
+
+ private Element provisionMethod() {
+ return binding.bindingElement().get();
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/DerivedFromFrameworkInstanceBindingExpression.java b/java/dagger/internal/codegen/writing/DerivedFromFrameworkInstanceBindingExpression.java
new file mode 100644
index 000000000..6e5dca84e
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/DerivedFromFrameworkInstanceBindingExpression.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
+
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.binding.BindingRequest;
+import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.binding.FrameworkType;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+
+/** A binding expression that depends on a framework instance. */
+final class DerivedFromFrameworkInstanceBindingExpression extends BindingExpression {
+
+ private final BindingRequest frameworkRequest;
+ private final RequestKind requestKind;
+ private final FrameworkType frameworkType;
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final DaggerTypes types;
+
+ DerivedFromFrameworkInstanceBindingExpression(
+ Key key,
+ FrameworkType frameworkType,
+ RequestKind requestKind,
+ ComponentBindingExpressions componentBindingExpressions,
+ DaggerTypes types) {
+ this.frameworkRequest = bindingRequest(key, frameworkType);
+ this.requestKind = checkNotNull(requestKind);
+ this.frameworkType = checkNotNull(frameworkType);
+ this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+ this.types = checkNotNull(types);
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ return frameworkType.to(
+ requestKind,
+ componentBindingExpressions.getDependencyExpression(frameworkRequest, requestingClass),
+ types);
+ }
+
+ @Override
+ Expression getDependencyExpressionForComponentMethod(
+ ComponentMethodDescriptor componentMethod, ComponentImplementation component) {
+ Expression frameworkInstance =
+ componentBindingExpressions.getDependencyExpressionForComponentMethod(
+ frameworkRequest, componentMethod, component);
+ return frameworkType.to(requestKind, frameworkInstance, types);
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/FactoryGenerator.java b/java/dagger/internal/codegen/writing/FactoryGenerator.java
new file mode 100644
index 000000000..70edd1a95
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/FactoryGenerator.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Maps.transformValues;
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.assistedParameters;
+import static dagger.internal.codegen.binding.ContributionBinding.FactoryCreationStrategy.DELEGATE;
+import static dagger.internal.codegen.binding.ContributionBinding.FactoryCreationStrategy.SINGLETON_INSTANCE;
+import static dagger.internal.codegen.binding.SourceFiles.bindingTypeElementTypeVariableNames;
+import static dagger.internal.codegen.binding.SourceFiles.frameworkFieldUsages;
+import static dagger.internal.codegen.binding.SourceFiles.frameworkTypeUsageStatement;
+import static dagger.internal.codegen.binding.SourceFiles.generateBindingFieldsForDependencies;
+import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding;
+import static dagger.internal.codegen.binding.SourceFiles.parameterizedGeneratedTypeNameForBinding;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings;
+import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.factoryOf;
+import static dagger.internal.codegen.writing.GwtCompatibility.gwtIncompatibleAnnotation;
+import static dagger.model.BindingKind.PROVISION;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.Factory;
+import dagger.internal.codegen.base.SourceFileGenerator;
+import dagger.internal.codegen.base.UniqueNameSet;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.writing.InjectionMethods.InjectionSiteMethod;
+import dagger.internal.codegen.writing.InjectionMethods.ProvisionMethod;
+import dagger.model.BindingKind;
+import dagger.model.DependencyRequest;
+import java.util.List;
+import java.util.Optional;
+import javax.annotation.processing.Filer;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+
+/**
+ * Generates {@link Factory} implementations from {@link ProvisionBinding} instances for {@link
+ * Inject} constructors.
+ */
+public final class FactoryGenerator extends SourceFileGenerator<ProvisionBinding> {
+ private final DaggerTypes types;
+ private final CompilerOptions compilerOptions;
+ private final KotlinMetadataUtil metadataUtil;
+
+ @Inject
+ FactoryGenerator(
+ Filer filer,
+ SourceVersion sourceVersion,
+ DaggerTypes types,
+ DaggerElements elements,
+ CompilerOptions compilerOptions,
+ KotlinMetadataUtil metadataUtil) {
+ super(filer, elements, sourceVersion);
+ this.types = types;
+ this.compilerOptions = compilerOptions;
+ this.metadataUtil = metadataUtil;
+ }
+
+ @Override
+ public ClassName nameGeneratedType(ProvisionBinding binding) {
+ return generatedClassNameForBinding(binding);
+ }
+
+ @Override
+ public Element originatingElement(ProvisionBinding binding) {
+ // we only create factories for bindings that have a binding element
+ return binding.bindingElement().get();
+ }
+
+ @Override
+ public Optional<TypeSpec.Builder> write(ProvisionBinding binding) {
+ // We don't want to write out resolved bindings -- we want to write out the generic version.
+ checkArgument(!binding.unresolved().isPresent());
+ checkArgument(binding.bindingElement().isPresent());
+
+ if (binding.factoryCreationStrategy().equals(DELEGATE)) {
+ return Optional.empty();
+ }
+
+ return Optional.of(factoryBuilder(binding));
+ }
+
+ private TypeSpec.Builder factoryBuilder(ProvisionBinding binding) {
+ TypeSpec.Builder factoryBuilder =
+ classBuilder(nameGeneratedType(binding))
+ .addModifiers(PUBLIC, FINAL)
+ .addTypeVariables(bindingTypeElementTypeVariableNames(binding));
+
+ factoryTypeName(binding).ifPresent(factoryBuilder::addSuperinterface);
+ addConstructorAndFields(binding, factoryBuilder);
+ factoryBuilder.addMethod(getMethod(binding));
+ addCreateMethod(binding, factoryBuilder);
+
+ factoryBuilder.addMethod(ProvisionMethod.create(binding, compilerOptions, metadataUtil));
+ gwtIncompatibleAnnotation(binding).ifPresent(factoryBuilder::addAnnotation);
+
+ return factoryBuilder;
+ }
+
+ private void addConstructorAndFields(ProvisionBinding binding, TypeSpec.Builder factoryBuilder) {
+ if (binding.factoryCreationStrategy().equals(SINGLETON_INSTANCE)) {
+ return;
+ }
+ // TODO(bcorso): Make the constructor private?
+ MethodSpec.Builder constructor = constructorBuilder().addModifiers(PUBLIC);
+ constructorParams(binding).forEach(
+ param -> {
+ constructor.addParameter(param).addStatement("this.$1N = $1N", param);
+ factoryBuilder.addField(
+ FieldSpec.builder(param.type, param.name, PRIVATE, FINAL).build());
+ });
+ factoryBuilder.addMethod(constructor.build());
+ }
+
+ private ImmutableList<ParameterSpec> constructorParams(ProvisionBinding binding) {
+ ImmutableList.Builder<ParameterSpec> params = ImmutableList.builder();
+ moduleParameter(binding).ifPresent(params::add);
+ frameworkFields(binding).values().forEach(field -> params.add(toParameter(field)));
+ return params.build();
+ }
+
+ private Optional<ParameterSpec> moduleParameter(ProvisionBinding binding) {
+ if (binding.requiresModuleInstance()) {
+ // TODO(bcorso, dpb): Should this use contributingModule()?
+ TypeName type = TypeName.get(binding.bindingTypeElement().get().asType());
+ return Optional.of(ParameterSpec.builder(type, "module").build());
+ }
+ return Optional.empty();
+ }
+
+ private ImmutableMap<DependencyRequest, FieldSpec> frameworkFields(ProvisionBinding binding) {
+ UniqueNameSet uniqueFieldNames = new UniqueNameSet();
+ // TODO(bcorso, dpb): Add a test for the case when a Factory parameter is named "module".
+ moduleParameter(binding).ifPresent(module -> uniqueFieldNames.claim(module.name));
+ return ImmutableMap.copyOf(
+ transformValues(
+ generateBindingFieldsForDependencies(binding),
+ field ->
+ FieldSpec.builder(
+ field.type(), uniqueFieldNames.getUniqueName(field.name()), PRIVATE, FINAL)
+ .build()));
+ }
+
+ private void addCreateMethod(ProvisionBinding binding, TypeSpec.Builder factoryBuilder) {
+ // If constructing a factory for @Inject or @Provides bindings, we use a static create method
+ // so that generated components can avoid having to refer to the generic types
+ // of the factory. (Otherwise they may have visibility problems referring to the types.)
+ MethodSpec.Builder createMethodBuilder =
+ methodBuilder("create")
+ .addModifiers(PUBLIC, STATIC)
+ .returns(parameterizedGeneratedTypeNameForBinding(binding))
+ .addTypeVariables(bindingTypeElementTypeVariableNames(binding));
+
+ switch (binding.factoryCreationStrategy()) {
+ case SINGLETON_INSTANCE:
+ FieldSpec.Builder instanceFieldBuilder =
+ FieldSpec.builder(nameGeneratedType(binding), "INSTANCE", PRIVATE, STATIC, FINAL)
+ .initializer("new $T()", nameGeneratedType(binding));
+
+ if (!bindingTypeElementTypeVariableNames(binding).isEmpty()) {
+ // If the factory has type parameters, ignore them in the field declaration & initializer
+ instanceFieldBuilder.addAnnotation(suppressWarnings(RAWTYPES));
+ createMethodBuilder.addAnnotation(suppressWarnings(UNCHECKED));
+ }
+
+ ClassName instanceHolderName = nameGeneratedType(binding).nestedClass("InstanceHolder");
+ createMethodBuilder.addStatement("return $T.INSTANCE", instanceHolderName);
+ factoryBuilder.addType(
+ TypeSpec.classBuilder(instanceHolderName)
+ .addModifiers(PRIVATE, STATIC, FINAL)
+ .addField(instanceFieldBuilder.build())
+ .build());
+ break;
+ case CLASS_CONSTRUCTOR:
+ List<ParameterSpec> params = constructorParams(binding);
+ createMethodBuilder.addParameters(params);
+ createMethodBuilder.addStatement(
+ "return new $T($L)",
+ parameterizedGeneratedTypeNameForBinding(binding),
+ makeParametersCodeBlock(Lists.transform(params, input -> CodeBlock.of("$N", input))));
+ break;
+ default:
+ throw new AssertionError();
+ }
+ factoryBuilder.addMethod(createMethodBuilder.build());
+ }
+
+ private MethodSpec getMethod(ProvisionBinding binding) {
+ TypeName providedTypeName = providedTypeName(binding);
+ MethodSpec.Builder getMethod =
+ methodBuilder("get")
+ .addModifiers(PUBLIC)
+ .returns(providedTypeName)
+ .addParameters(
+ // The 'get' method for an assisted injection type takes in the assisted parameters.
+ assistedParameters(binding).stream()
+ .map(ParameterSpec::get)
+ .collect(toImmutableList()));
+
+ if (factoryTypeName(binding).isPresent()) {
+ getMethod.addAnnotation(Override.class);
+ }
+
+ ImmutableMap<DependencyRequest, FieldSpec> frameworkFields = frameworkFields(binding);
+ CodeBlock invokeNewInstance =
+ ProvisionMethod.invoke(
+ binding,
+ request ->
+ frameworkTypeUsageStatement(
+ CodeBlock.of("$N", frameworkFields.get(request)), request.kind()),
+ nameGeneratedType(binding),
+ moduleParameter(binding).map(module -> CodeBlock.of("$N", module)),
+ compilerOptions,
+ metadataUtil);
+
+ if (binding.kind().equals(PROVISION)) {
+ binding
+ .nullableType()
+ .ifPresent(nullableType -> CodeBlocks.addAnnotation(getMethod, nullableType));
+ getMethod.addStatement("return $L", invokeNewInstance);
+ } else if (!binding.injectionSites().isEmpty()) {
+ CodeBlock instance = CodeBlock.of("instance");
+ getMethod
+ .addStatement("$T $L = $L", providedTypeName, instance, invokeNewInstance)
+ .addCode(
+ InjectionSiteMethod.invokeAll(
+ binding.injectionSites(),
+ nameGeneratedType(binding),
+ instance,
+ binding.key().type(),
+ frameworkFieldUsages(binding.dependencies(), frameworkFields)::get,
+ types,
+ metadataUtil))
+ .addStatement("return $L", instance);
+ } else {
+ getMethod.addStatement("return $L", invokeNewInstance);
+ }
+ return getMethod.build();
+ }
+
+ private static TypeName providedTypeName(ProvisionBinding binding) {
+ return TypeName.get(binding.contributedType());
+ }
+
+ private static Optional<TypeName> factoryTypeName(ProvisionBinding binding) {
+ return binding.kind() == BindingKind.ASSISTED_INJECTION
+ ? Optional.empty()
+ : Optional.of(factoryOf(providedTypeName(binding)));
+ }
+
+ private static ParameterSpec toParameter(FieldSpec field) {
+ return ParameterSpec.builder(field.type, field.name).build();
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/FrameworkFieldInitializer.java b/java/dagger/internal/codegen/writing/FrameworkFieldInitializer.java
new file mode 100644
index 000000000..c17ff8e53
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/FrameworkFieldInitializer.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
+import static dagger.internal.codegen.writing.ComponentImplementation.FieldSpecKind.FRAMEWORK_FIELD;
+import static javax.lang.model.element.Modifier.PRIVATE;
+
+import com.google.auto.common.MoreTypes;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.DelegateFactory;
+import dagger.internal.codegen.binding.BindingType;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.binding.FrameworkField;
+import dagger.internal.codegen.javapoet.AnnotationSpecs;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.model.BindingKind;
+import dagger.producers.internal.DelegateProducer;
+import java.util.Optional;
+
+/**
+ * An object that can initialize a framework-type component field for a binding. An instance should
+ * be created for each field.
+ */
+class FrameworkFieldInitializer implements FrameworkInstanceSupplier {
+
+ /**
+ * An object that can determine the expression to use to assign to the component field for a
+ * binding.
+ */
+ interface FrameworkInstanceCreationExpression {
+ /** Returns the expression to use to assign to the component field for the binding. */
+ CodeBlock creationExpression();
+
+ /**
+ * Returns the framework class to use for the field, if different from the one implied by the
+ * binding. This implementation returns {@link Optional#empty()}.
+ */
+ default Optional<ClassName> alternativeFrameworkClass() {
+ return Optional.empty();
+ }
+
+ /**
+ * Returns {@code true} if instead of using {@link #creationExpression()} to create a framework
+ * instance, a case in {@link InnerSwitchingProviders} should be created for this binding.
+ */
+ // TODO(ronshapiro): perhaps this isn't the right approach. Instead of saying "Use
+ // SetFactory.EMPTY because you will only get 1 class for all types of bindings that use
+ // SetFactory", maybe we should still use an inner switching provider but the same switching
+ // provider index for all cases.
+ default boolean useInnerSwitchingProvider() {
+ return true;
+ }
+ }
+
+ private final ComponentImplementation componentImplementation;
+ private final ContributionBinding binding;
+ private final FrameworkInstanceCreationExpression frameworkInstanceCreationExpression;
+ private FieldSpec fieldSpec;
+ private InitializationState fieldInitializationState = InitializationState.UNINITIALIZED;
+
+ FrameworkFieldInitializer(
+ ComponentImplementation componentImplementation,
+ ContributionBinding binding,
+ FrameworkInstanceCreationExpression frameworkInstanceCreationExpression) {
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.binding = checkNotNull(binding);
+ this.frameworkInstanceCreationExpression = checkNotNull(frameworkInstanceCreationExpression);
+ }
+
+ /**
+ * Returns the {@link MemberSelect} for the framework field, and adds the field and its
+ * initialization code to the component if it's needed and not already added.
+ */
+ @Override
+ public final MemberSelect memberSelect() {
+ initializeField();
+ return MemberSelect.localField(componentImplementation.name(), checkNotNull(fieldSpec).name);
+ }
+
+ /** Adds the field and its initialization code to the component. */
+ private void initializeField() {
+ switch (fieldInitializationState) {
+ case UNINITIALIZED:
+ // Change our state in case we are recursively invoked via initializeBindingExpression
+ fieldInitializationState = InitializationState.INITIALIZING;
+ CodeBlock.Builder codeBuilder = CodeBlock.builder();
+ CodeBlock fieldInitialization = frameworkInstanceCreationExpression.creationExpression();
+ CodeBlock initCode = CodeBlock.of("this.$N = $L;", getOrCreateField(), fieldInitialization);
+
+ if (fieldInitializationState == InitializationState.DELEGATED) {
+ codeBuilder.add(
+ "$T.setDelegate($N, $L);", delegateType(), fieldSpec, fieldInitialization);
+ } else {
+ codeBuilder.add(initCode);
+ }
+ componentImplementation.addInitialization(codeBuilder.build());
+
+ fieldInitializationState = InitializationState.INITIALIZED;
+ break;
+
+ case INITIALIZING:
+ // We were recursively invoked, so create a delegate factory instead
+ fieldInitializationState = InitializationState.DELEGATED;
+ componentImplementation.addInitialization(
+ CodeBlock.of("this.$N = new $T<>();", getOrCreateField(), delegateType()));
+ break;
+
+ case DELEGATED:
+ case INITIALIZED:
+ break;
+ }
+ }
+
+ /**
+ * Adds a field representing the resolved bindings, optionally forcing it to use a particular
+ * binding type (instead of the type the resolved bindings would typically use).
+ */
+ private FieldSpec getOrCreateField() {
+ if (fieldSpec != null) {
+ return fieldSpec;
+ }
+ boolean useRawType = !componentImplementation.isTypeAccessible(binding.key().type());
+ FrameworkField contributionBindingField =
+ FrameworkField.forBinding(
+ binding, frameworkInstanceCreationExpression.alternativeFrameworkClass());
+
+ TypeName fieldType =
+ useRawType ? contributionBindingField.type().rawType : contributionBindingField.type();
+
+ if (binding.kind() == BindingKind.ASSISTED_INJECTION) {
+ // An assisted injection factory doesn't extend Provider, so we reference the generated
+ // factory type directly (i.e. Foo_Factory<T> instead of Provider<Foo<T>>).
+ TypeName[] typeParameters =
+ MoreTypes.asDeclared(binding.key().type()).getTypeArguments().stream()
+ .map(TypeName::get)
+ .toArray(TypeName[]::new);
+ fieldType =
+ typeParameters.length == 0
+ ? generatedClassNameForBinding(binding)
+ : ParameterizedTypeName.get(generatedClassNameForBinding(binding), typeParameters);
+ }
+
+ FieldSpec.Builder contributionField =
+ FieldSpec.builder(
+ fieldType, componentImplementation.getUniqueFieldName(contributionBindingField.name()));
+ contributionField.addModifiers(PRIVATE);
+ if (useRawType) {
+ contributionField.addAnnotation(AnnotationSpecs.suppressWarnings(RAWTYPES));
+ }
+
+ fieldSpec = contributionField.build();
+ componentImplementation.addField(FRAMEWORK_FIELD, fieldSpec);
+
+ return fieldSpec;
+ }
+
+ private Class<?> delegateType() {
+ return isProvider() ? DelegateFactory.class : DelegateProducer.class;
+ }
+
+ private boolean isProvider() {
+ return binding.bindingType().equals(BindingType.PROVISION)
+ && frameworkInstanceCreationExpression
+ .alternativeFrameworkClass()
+ .map(TypeNames.PROVIDER::equals)
+ .orElse(true);
+ }
+
+ /** Initialization state for a factory field. */
+ private enum InitializationState {
+ /** The field is {@code null}. */
+ UNINITIALIZED,
+
+ /**
+ * The field's dependencies are being set up. If the field is needed in this state, use a {@link
+ * DelegateFactory}.
+ */
+ INITIALIZING,
+
+ /**
+ * The field's dependencies are being set up, but the field can be used because it has already
+ * been set to a {@link DelegateFactory}.
+ */
+ DELEGATED,
+
+ /** The field is set to an undelegated factory. */
+ INITIALIZED;
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/FrameworkInstanceBindingExpression.java b/java/dagger/internal/codegen/writing/FrameworkInstanceBindingExpression.java
new file mode 100644
index 000000000..56a6ef3d6
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/FrameworkInstanceBindingExpression.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.binding.FrameworkType;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/** A binding expression that uses a {@link FrameworkType} field. */
+abstract class FrameworkInstanceBindingExpression extends BindingExpression {
+ private final ContributionBinding binding;
+ private final FrameworkInstanceSupplier frameworkInstanceSupplier;
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+
+ FrameworkInstanceBindingExpression(
+ ContributionBinding binding,
+ FrameworkInstanceSupplier frameworkInstanceSupplier,
+ DaggerTypes types,
+ DaggerElements elements) {
+ this.binding = checkNotNull(binding);
+ this.frameworkInstanceSupplier = checkNotNull(frameworkInstanceSupplier);
+ this.types = checkNotNull(types);
+ this.elements = checkNotNull(elements);
+ }
+
+ /**
+ * The expression for the framework instance for this binding. The field will be {@link
+ * ComponentImplementation#addInitialization(CodeBlock) initialized} and {@link
+ * ComponentImplementation#addField(ComponentImplementation.FieldSpecKind, FieldSpec) added} to
+ * the component the first time this method is invoked.
+ */
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ MemberSelect memberSelect = frameworkInstanceSupplier.memberSelect();
+ TypeMirror expressionType =
+ isTypeAccessibleFrom(binding.contributedType(), requestingClass.packageName())
+ || isInlinedFactoryCreation(memberSelect)
+ ? types.wrapType(binding.contributedType(), frameworkType().frameworkClass())
+ : rawFrameworkType();
+ return Expression.create(expressionType, memberSelect.getExpressionFor(requestingClass));
+ }
+
+ /** Returns the framework type for the binding. */
+ protected abstract FrameworkType frameworkType();
+
+ /**
+ * Returns {@code true} if a factory is created inline each time it is requested. For example, in
+ * the initialization {@code this.fooProvider = Foo_Factory.create(Bar_Factory.create());}, {@code
+ * Bar_Factory} is considered to be inline.
+ *
+ * <p>This is used in {@link #getDependencyExpression(ClassName)} when determining the type of a
+ * factory. Normally if the {@link ContributionBinding#contributedType()} is not accessible from
+ * the component, the type of the expression will be a raw {@link javax.inject.Provider}. However,
+ * if the factory is created inline, even if contributed type is not accessible, javac will still
+ * be able to determine the type that is returned from the {@code Foo_Factory.create()} method.
+ */
+ private static boolean isInlinedFactoryCreation(MemberSelect memberSelect) {
+ return memberSelect.staticMember();
+ }
+
+ private DeclaredType rawFrameworkType() {
+ return types.getDeclaredType(elements.getTypeElement(frameworkType().frameworkClass()));
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/FrameworkInstanceSupplier.java b/java/dagger/internal/codegen/writing/FrameworkInstanceSupplier.java
new file mode 100644
index 000000000..cc0e136f0
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/FrameworkInstanceSupplier.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+/** An object that supplies a {@link MemberSelect} for a framework instance. */
+interface FrameworkInstanceSupplier {
+ /** Returns a {@link MemberSelect}, with possible side effects on the first call. */
+ MemberSelect memberSelect();
+}
diff --git a/java/dagger/internal/codegen/writing/GwtCompatibility.java b/java/dagger/internal/codegen/writing/GwtCompatibility.java
new file mode 100644
index 000000000..2df6a998a
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/GwtCompatibility.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.squareup.javapoet.AnnotationSpec;
+import dagger.internal.codegen.binding.Binding;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.Name;
+
+final class GwtCompatibility {
+
+ /**
+ * Returns a {@code @GwtIncompatible} annotation that is applied to {@code binding}'s {@link
+ * Binding#bindingElement()} or any enclosing type.
+ */
+ static Optional<AnnotationSpec> gwtIncompatibleAnnotation(Binding binding) {
+ checkArgument(binding.bindingElement().isPresent());
+ Element element = binding.bindingElement().get();
+ while (element != null) {
+ Optional<AnnotationSpec> gwtIncompatible =
+ element
+ .getAnnotationMirrors()
+ .stream()
+ .filter(annotation -> isGwtIncompatible(annotation))
+ .map(AnnotationSpec::get)
+ .findFirst();
+ if (gwtIncompatible.isPresent()) {
+ return gwtIncompatible;
+ }
+ element = element.getEnclosingElement();
+ }
+ return Optional.empty();
+ }
+
+ private static boolean isGwtIncompatible(AnnotationMirror annotation) {
+ Name simpleName = annotation.getAnnotationType().asElement().getSimpleName();
+ return simpleName.contentEquals("GwtIncompatible");
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/HjarSourceFileGenerator.java b/java/dagger/internal/codegen/writing/HjarSourceFileGenerator.java
new file mode 100644
index 000000000..5a4b6f132
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/HjarSourceFileGenerator.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static javax.lang.model.element.Modifier.PRIVATE;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.base.SourceFileGenerator;
+import java.util.Optional;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.Modifier;
+
+/**
+ * A source file generator that only writes the relevant code necessary for Bazel to create a
+ * correct header (ABI) jar.
+ */
+public final class HjarSourceFileGenerator<T> extends SourceFileGenerator<T> {
+ private final SourceFileGenerator<T> delegate;
+
+ private HjarSourceFileGenerator(SourceFileGenerator<T> delegate) {
+ super(delegate);
+ this.delegate = delegate;
+ }
+
+ public static <T> SourceFileGenerator<T> wrap(SourceFileGenerator<T> delegate) {
+ return new HjarSourceFileGenerator<>(delegate);
+ }
+
+ @Override
+ public ClassName nameGeneratedType(T input) {
+ return delegate.nameGeneratedType(input);
+ }
+
+ @Override
+ public Element originatingElement(T input) {
+ return delegate.originatingElement(input);
+ }
+
+ @Override
+ public Optional<TypeSpec.Builder> write(T input) {
+ return delegate.write(input).map(completeType -> skeletonType(completeType.build()));
+ }
+
+ private TypeSpec.Builder skeletonType(TypeSpec completeType) {
+ TypeSpec.Builder skeleton =
+ classBuilder(completeType.name)
+ .addSuperinterfaces(completeType.superinterfaces)
+ .addTypeVariables(completeType.typeVariables)
+ .addModifiers(completeType.modifiers.toArray(new Modifier[0]))
+ .addAnnotations(completeType.annotations);
+
+ if (!completeType.superclass.equals(ClassName.OBJECT)) {
+ skeleton.superclass(completeType.superclass);
+ }
+
+ completeType.methodSpecs.stream()
+ .filter(method -> !method.modifiers.contains(PRIVATE) || method.isConstructor())
+ .map(this::skeletonMethod)
+ .forEach(skeleton::addMethod);
+
+ completeType.fieldSpecs.stream()
+ .filter(field -> !field.modifiers.contains(PRIVATE))
+ .map(this::skeletonField)
+ .forEach(skeleton::addField);
+
+ completeType.typeSpecs.stream()
+ .map(type -> skeletonType(type).build())
+ .forEach(skeleton::addType);
+
+ return skeleton;
+ }
+
+ private MethodSpec skeletonMethod(MethodSpec completeMethod) {
+ MethodSpec.Builder skeleton =
+ completeMethod.isConstructor()
+ ? constructorBuilder()
+ : methodBuilder(completeMethod.name).returns(completeMethod.returnType);
+
+ if (completeMethod.isConstructor()) {
+ // Code in Turbine must (for technical reasons in javac) have a valid super() call for
+ // constructors, otherwise javac will bark, and Turbine has no way to avoid this. So we retain
+ // constructor method bodies if they do exist
+ skeleton.addCode(completeMethod.code);
+ }
+
+ return skeleton
+ .addModifiers(completeMethod.modifiers)
+ .addTypeVariables(completeMethod.typeVariables)
+ .addParameters(completeMethod.parameters)
+ .addExceptions(completeMethod.exceptions)
+ .varargs(completeMethod.varargs)
+ .addAnnotations(completeMethod.annotations)
+ .build();
+ }
+
+ private FieldSpec skeletonField(FieldSpec completeField) {
+ return FieldSpec.builder(
+ completeField.type,
+ completeField.name,
+ completeField.modifiers.toArray(new Modifier[0]))
+ .addAnnotations(completeField.annotations)
+ .build();
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/ImmediateFutureBindingExpression.java b/java/dagger/internal/codegen/writing/ImmediateFutureBindingExpression.java
new file mode 100644
index 000000000..dcd02faef
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ImmediateFutureBindingExpression.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import javax.lang.model.SourceVersion;
+
+final class ImmediateFutureBindingExpression extends BindingExpression {
+ private final Key key;
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final DaggerTypes types;
+ private final SourceVersion sourceVersion;
+
+ ImmediateFutureBindingExpression(
+ Key key,
+ ComponentBindingExpressions componentBindingExpressions,
+ DaggerTypes types,
+ SourceVersion sourceVersion) {
+ this.key = key;
+ this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+ this.types = checkNotNull(types);
+ this.sourceVersion = checkNotNull(sourceVersion);
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ return Expression.create(
+ types.wrapType(key.type(), ListenableFuture.class),
+ CodeBlock.of("$T.immediateFuture($L)", Futures.class, instanceExpression(requestingClass)));
+ }
+
+ private CodeBlock instanceExpression(ClassName requestingClass) {
+ Expression expression =
+ componentBindingExpressions.getDependencyExpression(
+ bindingRequest(key, RequestKind.INSTANCE), requestingClass);
+ if (sourceVersion.compareTo(SourceVersion.RELEASE_7) <= 0) {
+ // Java 7 type inference is not as strong as in Java 8, and therefore some generated code must
+ // cast.
+ //
+ // For example, javac7 cannot detect that Futures.immediateFuture(ImmutableSet.of("T"))
+ // can safely be assigned to ListenableFuture<Set<T>>.
+ if (!types.isSameType(expression.type(), key.type())) {
+ return CodeBlock.of(
+ "($T) $L", types.accessibleType(key.type(), requestingClass), expression.codeBlock());
+ }
+ }
+ return expression.codeBlock();
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/InaccessibleMapKeyProxyGenerator.java b/java/dagger/internal/codegen/writing/InaccessibleMapKeyProxyGenerator.java
new file mode 100644
index 000000000..d52735963
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/InaccessibleMapKeyProxyGenerator.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.base.SourceFileGenerator;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.binding.MapKeys;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.util.Optional;
+import javax.annotation.processing.Filer;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+
+/**
+ * Generates a class that exposes a non-{@code public} {@link
+ * ContributionBinding#mapKeyAnnotation()} @MapKey} annotation.
+ */
+public final class InaccessibleMapKeyProxyGenerator
+ extends SourceFileGenerator<ContributionBinding> {
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+
+ @Inject
+ InaccessibleMapKeyProxyGenerator(
+ Filer filer, DaggerTypes types, DaggerElements elements, SourceVersion sourceVersion) {
+ super(filer, elements, sourceVersion);
+ this.types = types;
+ this.elements = elements;
+ }
+
+ @Override
+ public ClassName nameGeneratedType(ContributionBinding binding) {
+ return MapKeys.mapKeyProxyClassName(binding);
+ }
+
+ @Override
+ public Element originatingElement(ContributionBinding binding) {
+ // a map key is only ever present on bindings that have a binding element
+ return binding.bindingElement().get();
+ }
+
+ @Override
+ public Optional<TypeSpec.Builder> write(ContributionBinding binding) {
+ return MapKeys.mapKeyFactoryMethod(binding, types, elements)
+ .map(
+ method ->
+ classBuilder(nameGeneratedType(binding))
+ .addModifiers(PUBLIC, FINAL)
+ .addMethod(constructorBuilder().addModifiers(PRIVATE).build())
+ .addMethod(method));
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/InjectionMethods.java b/java/dagger/internal/codegen/writing/InjectionMethods.java
new file mode 100644
index 000000000..7cebc10db
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/InjectionMethods.java
@@ -0,0 +1,571 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.auto.common.MoreElements.asExecutable;
+import static com.google.auto.common.MoreElements.asType;
+import static com.google.auto.common.MoreElements.asVariable;
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static dagger.internal.codegen.base.RequestKinds.requestTypeName;
+import static dagger.internal.codegen.binding.ConfigurationAnnotations.getNullableType;
+import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding;
+import static dagger.internal.codegen.binding.SourceFiles.memberInjectedFieldSignatureForVariable;
+import static dagger.internal.codegen.binding.SourceFiles.membersInjectorNameForType;
+import static dagger.internal.codegen.binding.SourceFiles.protectAgainstKeywords;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
+import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toConcatenatedCodeBlock;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.rawTypeName;
+import static dagger.internal.codegen.langmodel.Accessibility.isElementAccessibleFrom;
+import static dagger.internal.codegen.langmodel.Accessibility.isRawTypeAccessible;
+import static dagger.internal.codegen.langmodel.Accessibility.isRawTypePubliclyAccessible;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+import static java.util.stream.Collectors.toList;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+import static javax.lang.model.type.TypeKind.VOID;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.AnnotationSpec;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeVariableName;
+import dagger.internal.Preconditions;
+import dagger.internal.codegen.base.UniqueNameSet;
+import dagger.internal.codegen.binding.AssistedInjectionAnnotations;
+import dagger.internal.codegen.binding.MembersInjectionBinding.InjectionSite;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.extension.DaggerCollectors;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import dagger.model.RequestKind;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Function;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Parameterizable;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+/** Convenience methods for creating and invoking {@link InjectionMethod}s. */
+final class InjectionMethods {
+
+ /**
+ * A method that returns an object from a {@code @Provides} method or an {@code @Inject}ed
+ * constructor. Its parameters match the dependency requests for constructor and members
+ * injection.
+ *
+ * <p>For {@code @Provides} methods named "foo", the method name is "proxyFoo". For example:
+ *
+ * <pre><code>
+ * abstract class FooModule {
+ * {@literal @Provides} static Foo provideFoo(Bar bar, Baz baz) { … }
+ * }
+ *
+ * public static proxyProvideFoo(Bar bar, Baz baz) { … }
+ * </code></pre>
+ *
+ * <p>For {@code @Inject}ed constructors, the method name is "newFoo". For example:
+ *
+ * <pre><code>
+ * class Foo {
+ * {@literal @Inject} Foo(Bar bar) {}
+ * }
+ *
+ * public static Foo newFoo(Bar bar) { … }
+ * </code></pre>
+ */
+ static final class ProvisionMethod {
+ // These names are already defined in factories and shouldn't be used for the proxy method name.
+ private static final ImmutableSet<String> BANNED_PROXY_NAMES = ImmutableSet.of("get", "create");
+
+ /**
+ * Returns a method that invokes the binding's {@linkplain ProvisionBinding#bindingElement()
+ * constructor} and injects the instance's members.
+ */
+ static MethodSpec create(
+ ProvisionBinding binding,
+ CompilerOptions compilerOptions,
+ KotlinMetadataUtil metadataUtil) {
+ ExecutableElement element = asExecutable(binding.bindingElement().get());
+ switch (element.getKind()) {
+ case CONSTRUCTOR:
+ return constructorProxy(element);
+ case METHOD:
+ return methodProxy(
+ element,
+ methodName(element),
+ InstanceCastPolicy.IGNORE,
+ CheckNotNullPolicy.get(binding, compilerOptions),
+ metadataUtil);
+ default:
+ throw new AssertionError(element);
+ }
+ }
+
+ /**
+ * Invokes the injection method for {@code binding}, with the dependencies transformed with the
+ * {@code dependencyUsage} function.
+ */
+ static CodeBlock invoke(
+ ProvisionBinding binding,
+ Function<DependencyRequest, CodeBlock> dependencyUsage,
+ ClassName requestingClass,
+ Optional<CodeBlock> moduleReference,
+ CompilerOptions compilerOptions,
+ KotlinMetadataUtil metadataUtil) {
+ ImmutableList.Builder<CodeBlock> arguments = ImmutableList.builder();
+ moduleReference.ifPresent(arguments::add);
+ invokeArguments(binding, dependencyUsage, requestingClass).forEach(arguments::add);
+
+ ClassName enclosingClass = generatedClassNameForBinding(binding);
+ MethodSpec methodSpec = create(binding, compilerOptions, metadataUtil);
+ return invokeMethod(methodSpec, arguments.build(), enclosingClass, requestingClass);
+ }
+
+ static ImmutableList<CodeBlock> invokeArguments(
+ ProvisionBinding binding,
+ Function<DependencyRequest, CodeBlock> dependencyUsage,
+ ClassName requestingClass) {
+ ImmutableMap<VariableElement, DependencyRequest> dependencyRequestMap =
+ binding.provisionDependencies().stream()
+ .collect(
+ toImmutableMap(
+ request -> MoreElements.asVariable(request.requestElement().get()),
+ request -> request));
+
+ ImmutableList.Builder<CodeBlock> arguments = ImmutableList.builder();
+ for (VariableElement parameter :
+ asExecutable(binding.bindingElement().get()).getParameters()) {
+ if (AssistedInjectionAnnotations.isAssistedParameter(parameter)) {
+ arguments.add(CodeBlock.of("$L", parameter.getSimpleName()));
+ } else if (dependencyRequestMap.containsKey(parameter)) {
+ DependencyRequest request = dependencyRequestMap.get(parameter);
+ arguments.add(
+ injectionMethodArgument(request, dependencyUsage.apply(request), requestingClass));
+ } else {
+ throw new AssertionError("Unexpected parameter: " + parameter);
+ }
+ }
+
+ return arguments.build();
+ }
+
+ private static MethodSpec constructorProxy(ExecutableElement constructor) {
+ TypeElement enclosingType = MoreElements.asType(constructor.getEnclosingElement());
+ MethodSpec.Builder builder =
+ methodBuilder(methodName(constructor))
+ .addModifiers(PUBLIC, STATIC)
+ .varargs(constructor.isVarArgs())
+ .returns(TypeName.get(enclosingType.asType()));
+
+ copyTypeParameters(builder, enclosingType);
+ copyThrows(builder, constructor);
+
+ CodeBlock arguments =
+ copyParameters(builder, new UniqueNameSet(), constructor.getParameters());
+ return builder.addStatement("return new $T($L)", enclosingType, arguments).build();
+ }
+
+ /**
+ * Returns {@code true} if injecting an instance of {@code binding} from {@code callingPackage}
+ * requires the use of an injection method.
+ */
+ static boolean requiresInjectionMethod(
+ ProvisionBinding binding, CompilerOptions compilerOptions, ClassName requestingClass) {
+ ExecutableElement method = MoreElements.asExecutable(binding.bindingElement().get());
+ return !binding.injectionSites().isEmpty()
+ || binding.shouldCheckForNull(compilerOptions)
+ || !isElementAccessibleFrom(method, requestingClass.packageName())
+ // This check should be removable once we drop support for -source 7
+ || method.getParameters().stream()
+ .map(VariableElement::asType)
+ .anyMatch(type -> !isRawTypeAccessible(type, requestingClass.packageName()));
+ }
+
+ /**
+ * Returns the name of the {@code static} method that wraps {@code method}. For methods that are
+ * associated with {@code @Inject} constructors, the method will also inject all {@link
+ * InjectionSite}s.
+ */
+ private static String methodName(ExecutableElement method) {
+ switch (method.getKind()) {
+ case CONSTRUCTOR:
+ return "newInstance";
+ case METHOD:
+ String methodName = method.getSimpleName().toString();
+ return BANNED_PROXY_NAMES.contains(methodName)
+ ? "proxy" + LOWER_CAMEL.to(UPPER_CAMEL, methodName)
+ : methodName;
+ default:
+ throw new AssertionError(method);
+ }
+ }
+ }
+
+ /**
+ * A static method that injects one member of an instance of a type. Its first parameter is an
+ * instance of the type to be injected. The remaining parameters match the dependency requests for
+ * the injection site.
+ *
+ * <p>Example:
+ *
+ * <pre><code>
+ * class Foo {
+ * {@literal @Inject} Bar bar;
+ * {@literal @Inject} void setThings(Baz baz, Qux qux) {}
+ * }
+ *
+ * public static injectBar(Foo instance, Bar bar) { … }
+ * public static injectSetThings(Foo instance, Baz baz, Qux qux) { … }
+ * </code></pre>
+ */
+ static final class InjectionSiteMethod {
+ /**
+ * When a type has an inaccessible member from a supertype (e.g. an @Inject field in a parent
+ * that's in a different package), a method in the supertype's package must be generated to give
+ * the subclass's members injector a way to inject it. Each potentially inaccessible member
+ * receives its own method, as the subclass may need to inject them in a different order from
+ * the parent class.
+ */
+ static MethodSpec create(InjectionSite injectionSite, KotlinMetadataUtil metadataUtil) {
+ String methodName = methodName(injectionSite);
+ switch (injectionSite.kind()) {
+ case METHOD:
+ return methodProxy(
+ asExecutable(injectionSite.element()),
+ methodName,
+ InstanceCastPolicy.CAST_IF_NOT_PUBLIC,
+ CheckNotNullPolicy.IGNORE,
+ metadataUtil);
+ case FIELD:
+ Optional<AnnotationMirror> qualifier =
+ injectionSite.dependencies().stream()
+ // methods for fields have a single dependency request
+ .collect(DaggerCollectors.onlyElement())
+ .key()
+ .qualifier();
+ return fieldProxy(asVariable(injectionSite.element()), methodName, qualifier);
+ }
+ throw new AssertionError(injectionSite);
+ }
+
+ /**
+ * Invokes each of the injection methods for {@code injectionSites}, with the dependencies
+ * transformed using the {@code dependencyUsage} function.
+ *
+ * @param instanceType the type of the {@code instance} parameter
+ */
+ static CodeBlock invokeAll(
+ ImmutableSet<InjectionSite> injectionSites,
+ ClassName generatedTypeName,
+ CodeBlock instanceCodeBlock,
+ TypeMirror instanceType,
+ Function<DependencyRequest, CodeBlock> dependencyUsage,
+ DaggerTypes types,
+ KotlinMetadataUtil metadataUtil) {
+ return injectionSites.stream()
+ .map(
+ injectionSite -> {
+ TypeMirror injectSiteType =
+ types.erasure(injectionSite.element().getEnclosingElement().asType());
+
+ // If instance has been declared as Object because it is not accessible from the
+ // component, but the injectionSite is in a supertype of instanceType that is
+ // publicly accessible, the InjectionSiteMethod will request the actual type and not
+ // Object as the first parameter. If so, cast to the supertype which is accessible
+ // from within generatedTypeName
+ CodeBlock maybeCastedInstance =
+ !types.isSubtype(instanceType, injectSiteType)
+ && isTypeAccessibleFrom(injectSiteType, generatedTypeName.packageName())
+ ? CodeBlock.of("($T) $L", injectSiteType, instanceCodeBlock)
+ : instanceCodeBlock;
+ return CodeBlock.of(
+ "$L;",
+ invoke(
+ injectionSite,
+ generatedTypeName,
+ maybeCastedInstance,
+ dependencyUsage,
+ metadataUtil));
+ })
+ .collect(toConcatenatedCodeBlock());
+ }
+
+ /**
+ * Invokes the injection method for {@code injectionSite}, with the dependencies transformed
+ * using the {@code dependencyUsage} function.
+ */
+ private static CodeBlock invoke(
+ InjectionSite injectionSite,
+ ClassName generatedTypeName,
+ CodeBlock instanceCodeBlock,
+ Function<DependencyRequest, CodeBlock> dependencyUsage,
+ KotlinMetadataUtil metadataUtil) {
+ ImmutableList.Builder<CodeBlock> arguments = ImmutableList.builder();
+ arguments.add(instanceCodeBlock);
+ if (!injectionSite.dependencies().isEmpty()) {
+ arguments.addAll(
+ injectionSite.dependencies().stream().map(dependencyUsage).collect(toList()));
+ }
+
+ ClassName enclosingClass =
+ membersInjectorNameForType(asType(injectionSite.element().getEnclosingElement()));
+ MethodSpec methodSpec = create(injectionSite, metadataUtil);
+ return invokeMethod(methodSpec, arguments.build(), enclosingClass, generatedTypeName);
+ }
+
+ /*
+ * TODO(ronshapiro): this isn't perfect, as collisions could still exist. Some examples:
+ *
+ * - @Inject void members() {} will generate a method that conflicts with the instance
+ * method `injectMembers(T)`
+ * - Adding the index could conflict with another member:
+ * @Inject void a(Object o) {}
+ * @Inject void a(String s) {}
+ * @Inject void a1(String s) {}
+ *
+ * Here, Method a(String) will add the suffix "1", which will conflict with the method
+ * generated for a1(String)
+ * - Members named "members" or "methods" could also conflict with the {@code static} injection
+ * method.
+ */
+ private static String methodName(InjectionSite injectionSite) {
+ int index = injectionSite.indexAmongAtInjectMembersWithSameSimpleName();
+ String indexString = index == 0 ? "" : String.valueOf(index + 1);
+ return "inject"
+ + LOWER_CAMEL.to(UPPER_CAMEL, injectionSite.element().getSimpleName().toString())
+ + indexString;
+ }
+ }
+
+ private static CodeBlock injectionMethodArgument(
+ DependencyRequest dependency, CodeBlock argument, ClassName generatedTypeName) {
+ TypeMirror keyType = dependency.key().type();
+ CodeBlock.Builder codeBlock = CodeBlock.builder();
+ if (!isRawTypeAccessible(keyType, generatedTypeName.packageName())
+ && isTypeAccessibleFrom(keyType, generatedTypeName.packageName())) {
+ if (!dependency.kind().equals(RequestKind.INSTANCE)) {
+ TypeName usageTypeName = accessibleType(dependency);
+ codeBlock.add("($T) ($T)", usageTypeName, rawTypeName(usageTypeName));
+ } else if (dependency.requestElement().get().asType().getKind().equals(TypeKind.TYPEVAR)) {
+ codeBlock.add("($T)", keyType);
+ }
+ }
+ return codeBlock.add(argument).build();
+ }
+
+ /**
+ * Returns the parameter type for {@code dependency}. If the raw type is not accessible, returns
+ * {@link Object}.
+ */
+ private static TypeName accessibleType(DependencyRequest dependency) {
+ TypeName typeName = requestTypeName(dependency.kind(), accessibleType(dependency.key().type()));
+ return dependency
+ .requestElement()
+ .map(element -> element.asType().getKind().isPrimitive())
+ .orElse(false)
+ ? typeName.unbox()
+ : typeName;
+ }
+
+ /**
+ * Returns the accessible type for {@code type}. If the raw type is not accessible, returns {@link
+ * Object}.
+ */
+ private static TypeName accessibleType(TypeMirror type) {
+ return isRawTypePubliclyAccessible(type) ? TypeName.get(type) : TypeName.OBJECT;
+ }
+
+ private enum InstanceCastPolicy {
+ CAST_IF_NOT_PUBLIC, IGNORE;
+
+ boolean useObjectType(TypeMirror instanceType) {
+ return this == CAST_IF_NOT_PUBLIC && !isRawTypePubliclyAccessible(instanceType);
+ }
+ }
+
+ private enum CheckNotNullPolicy {
+ IGNORE, CHECK_FOR_NULL;
+
+ CodeBlock checkForNull(CodeBlock maybeNull) {
+ return this.equals(IGNORE)
+ ? maybeNull
+ : CodeBlock.of("$T.checkNotNullFromProvides($L)", Preconditions.class, maybeNull);
+ }
+
+ static CheckNotNullPolicy get(ProvisionBinding binding, CompilerOptions compilerOptions) {
+ return binding.shouldCheckForNull(compilerOptions) ? CHECK_FOR_NULL : IGNORE;
+ }
+ }
+
+ private static MethodSpec methodProxy(
+ ExecutableElement method,
+ String methodName,
+ InstanceCastPolicy instanceCastPolicy,
+ CheckNotNullPolicy checkNotNullPolicy,
+ KotlinMetadataUtil metadataUtil) {
+ MethodSpec.Builder builder =
+ methodBuilder(methodName).addModifiers(PUBLIC, STATIC).varargs(method.isVarArgs());
+
+ TypeElement enclosingType = asType(method.getEnclosingElement());
+ boolean isMethodInKotlinObject = metadataUtil.isObjectClass(enclosingType);
+ boolean isMethodInKotlinCompanionObject = metadataUtil.isCompanionObjectClass(enclosingType);
+ UniqueNameSet parameterNameSet = new UniqueNameSet();
+ CodeBlock instance;
+ if (isMethodInKotlinCompanionObject || method.getModifiers().contains(STATIC)) {
+ instance = CodeBlock.of("$T", rawTypeName(TypeName.get(enclosingType.asType())));
+ } else if (isMethodInKotlinObject) {
+ // Call through the singleton instance.
+ // See: https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#static-methods
+ instance = CodeBlock.of("$T.INSTANCE", rawTypeName(TypeName.get(enclosingType.asType())));
+ } else {
+ copyTypeParameters(builder, enclosingType);
+ boolean useObject = instanceCastPolicy.useObjectType(enclosingType.asType());
+ instance = copyInstance(builder, parameterNameSet, enclosingType.asType(), useObject);
+ }
+ CodeBlock arguments = copyParameters(builder, parameterNameSet, method.getParameters());
+ CodeBlock invocation =
+ checkNotNullPolicy.checkForNull(
+ CodeBlock.of("$L.$L($L)", instance, method.getSimpleName(), arguments));
+
+ copyTypeParameters(builder, method);
+ copyThrows(builder, method);
+
+ if (method.getReturnType().getKind().equals(VOID)) {
+ return builder.addStatement("$L", invocation).build();
+ } else {
+ getNullableType(method)
+ .ifPresent(annotation -> CodeBlocks.addAnnotation(builder, annotation));
+ return builder
+ .returns(TypeName.get(method.getReturnType()))
+ .addStatement("return $L", invocation).build();
+ }
+ }
+
+ private static MethodSpec fieldProxy(
+ VariableElement field, String methodName, Optional<AnnotationMirror> qualifierAnnotation) {
+ MethodSpec.Builder builder =
+ methodBuilder(methodName)
+ .addModifiers(PUBLIC, STATIC)
+ .addAnnotation(
+ AnnotationSpec.builder(TypeNames.INJECTED_FIELD_SIGNATURE)
+ .addMember("value", "$S", memberInjectedFieldSignatureForVariable(field))
+ .build());
+
+ qualifierAnnotation.map(AnnotationSpec::get).ifPresent(builder::addAnnotation);
+
+ TypeElement enclosingType = asType(field.getEnclosingElement());
+ copyTypeParameters(builder, enclosingType);
+
+ boolean useObject = !isRawTypePubliclyAccessible(enclosingType.asType());
+ UniqueNameSet parameterNameSet = new UniqueNameSet();
+ CodeBlock instance = copyInstance(builder, parameterNameSet, enclosingType.asType(), useObject);
+ CodeBlock argument = copyParameters(builder, parameterNameSet, ImmutableList.of(field));
+ return builder.addStatement("$L.$L = $L", instance, field.getSimpleName(), argument).build();
+ }
+
+ private static CodeBlock invokeMethod(
+ MethodSpec methodSpec,
+ ImmutableList<CodeBlock> parameters,
+ ClassName enclosingClass,
+ ClassName requestingClass) {
+ checkArgument(methodSpec.parameters.size() == parameters.size());
+ CodeBlock parameterBlock = makeParametersCodeBlock(parameters);
+ return enclosingClass.equals(requestingClass)
+ ? CodeBlock.of("$L($L)", methodSpec.name, parameterBlock)
+ : CodeBlock.of("$T.$L($L)", enclosingClass, methodSpec.name, parameterBlock);
+ }
+
+ private static void copyTypeParameters(
+ MethodSpec.Builder methodBuilder, Parameterizable element) {
+ element.getTypeParameters().stream()
+ .map(TypeVariableName::get)
+ .forEach(methodBuilder::addTypeVariable);
+ }
+
+ private static void copyThrows(MethodSpec.Builder methodBuilder, ExecutableElement method) {
+ method.getThrownTypes().stream().map(TypeName::get).forEach(methodBuilder::addException);
+ }
+
+ private static CodeBlock copyParameters(
+ MethodSpec.Builder methodBuilder,
+ UniqueNameSet parameterNameSet,
+ List<? extends VariableElement> parameters) {
+ return parameters.stream()
+ .map(
+ parameter -> {
+ String name =
+ parameterNameSet.getUniqueName(validJavaName(parameter.getSimpleName()));
+ TypeMirror type = parameter.asType();
+ boolean useObject = !isRawTypePubliclyAccessible(type);
+ return copyParameter(methodBuilder, type, name, useObject);
+ })
+ .collect(toParametersCodeBlock());
+ }
+
+ private static CodeBlock copyParameter(
+ MethodSpec.Builder methodBuilder, TypeMirror type, String name, boolean useObject) {
+ TypeName typeName = useObject ? TypeName.OBJECT : TypeName.get(type);
+ methodBuilder.addParameter(ParameterSpec.builder(typeName, name).build());
+ return useObject ? CodeBlock.of("($T) $L", type, name) : CodeBlock.of("$L", name);
+ }
+
+ private static CodeBlock copyInstance(
+ MethodSpec.Builder methodBuilder,
+ UniqueNameSet parameterNameSet,
+ TypeMirror type,
+ boolean useObject) {
+ CodeBlock instance =
+ copyParameter(methodBuilder, type, parameterNameSet.getUniqueName("instance"), useObject);
+ // If we had to cast the instance add an extra parenthesis incase we're calling a method on it.
+ return useObject ? CodeBlock.of("($L)", instance) : instance;
+ }
+
+ private static String validJavaName(CharSequence name) {
+ if (SourceVersion.isIdentifier(name)) {
+ return protectAgainstKeywords(name.toString());
+ }
+
+ StringBuilder newName = new StringBuilder(name.length());
+ char firstChar = name.charAt(0);
+ if (!Character.isJavaIdentifierStart(firstChar)) {
+ newName.append('_');
+ }
+
+ name.chars().forEach(c -> newName.append(Character.isJavaIdentifierPart(c) ? c : '_'));
+ return newName.toString();
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/InjectionOrProvisionProviderCreationExpression.java b/java/dagger/internal/codegen/writing/InjectionOrProvisionProviderCreationExpression.java
new file mode 100644
index 000000000..b5135b057
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/InjectionOrProvisionProviderCreationExpression.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding;
+import static dagger.model.BindingKind.INJECTION;
+
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import javax.inject.Provider;
+
+/**
+ * A {@link Provider} creation expression for an {@link javax.inject.Inject @Inject}-constructed
+ * class or a {@link dagger.Provides @Provides}-annotated module method.
+ */
+// TODO(dpb): Resolve with ProducerCreationExpression.
+final class InjectionOrProvisionProviderCreationExpression
+ implements FrameworkInstanceCreationExpression {
+
+ private final ContributionBinding binding;
+ private final ComponentBindingExpressions componentBindingExpressions;
+
+ InjectionOrProvisionProviderCreationExpression(
+ ContributionBinding binding, ComponentBindingExpressions componentBindingExpressions) {
+ this.binding = checkNotNull(binding);
+ this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ CodeBlock createFactory =
+ CodeBlock.of(
+ "$T.create($L)",
+ generatedClassNameForBinding(binding),
+ componentBindingExpressions.getCreateMethodArgumentsCodeBlock(binding));
+
+ // When scoping a parameterized factory for an @Inject class, Java 7 cannot always infer the
+ // type properly, so cast to a raw framework type before scoping.
+ if (binding.kind().equals(INJECTION)
+ && binding.unresolved().isPresent()
+ && binding.scope().isPresent()) {
+ return CodeBlocks.cast(createFactory, Provider.class);
+ } else {
+ return createFactory;
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/InnerSwitchingProviders.java b/java/dagger/internal/codegen/writing/InnerSwitchingProviders.java
new file mode 100644
index 000000000..c2f9893b7
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/InnerSwitchingProviders.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
+import static dagger.model.RequestKind.INSTANCE;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Key;
+import javax.inject.Provider;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Generates {@linkplain BindingExpression binding expressions} for a binding that is represented by
+ * an inner {@code SwitchingProvider} class.
+ */
+final class InnerSwitchingProviders extends SwitchingProviders {
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final DaggerTypes types;
+
+ InnerSwitchingProviders(
+ ComponentImplementation componentImplementation,
+ ComponentBindingExpressions componentBindingExpressions,
+ DaggerTypes types) {
+ super(componentImplementation, types);
+ this.componentBindingExpressions = componentBindingExpressions;
+ this.types = types;
+ }
+
+ /**
+ * Returns the binding expression for a binding that satisfies a {@link Provider} requests with a
+ * inner {@code SwitchingProvider} class.
+ */
+ BindingExpression newBindingExpression(ContributionBinding binding) {
+ return new BindingExpression() {
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ return getProviderExpression(new SwitchCase(binding, requestingClass));
+ }
+ };
+ }
+
+ @Override
+ protected TypeSpec createSwitchingProviderType(TypeSpec.Builder builder) {
+ return builder
+ .addModifiers(PRIVATE, FINAL)
+ .addField(TypeName.INT, "id", PRIVATE, FINAL)
+ .addMethod(
+ constructorBuilder()
+ .addParameter(TypeName.INT, "id")
+ .addStatement("this.id = id")
+ .build())
+ .build();
+ }
+
+ private final class SwitchCase implements SwitchingProviders.SwitchCase {
+ private final ContributionBinding binding;
+ private final ClassName requestingClass;
+
+ SwitchCase(ContributionBinding binding, ClassName requestingClass) {
+ this.binding = binding;
+ this.requestingClass = requestingClass;
+ }
+
+ @Override
+ public Key key() {
+ return binding.key();
+ }
+
+ @Override
+ public Expression getProviderExpression(ClassName switchingProviderClass, int switchId) {
+ TypeMirror instanceType = types.accessibleType(binding.contributedType(), requestingClass);
+ return Expression.create(
+ types.wrapType(instanceType, Provider.class),
+ CodeBlock.of("new $T<>($L)", switchingProviderClass, switchId));
+ }
+
+ @Override
+ public Expression getReturnExpression(ClassName switchingProviderClass) {
+ return componentBindingExpressions.getDependencyExpression(
+ bindingRequest(binding.key(), INSTANCE), switchingProviderClass);
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/InstanceFactoryCreationExpression.java b/java/dagger/internal/codegen/writing/InstanceFactoryCreationExpression.java
new file mode 100644
index 000000000..a7d6685be
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/InstanceFactoryCreationExpression.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.InstanceFactory;
+import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import java.util.function.Supplier;
+
+/**
+ * A {@link FrameworkInstanceCreationExpression} that creates an {@link InstanceFactory} for an
+ * instance.
+ */
+final class InstanceFactoryCreationExpression implements FrameworkInstanceCreationExpression {
+
+ private final boolean nullable;
+ private final Supplier<CodeBlock> instanceExpression;
+
+ InstanceFactoryCreationExpression(Supplier<CodeBlock> instanceExpression) {
+ this(false, instanceExpression);
+ }
+
+ InstanceFactoryCreationExpression(boolean nullable, Supplier<CodeBlock> instanceExpression) {
+ this.nullable = nullable;
+ this.instanceExpression = checkNotNull(instanceExpression);
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ return CodeBlock.of(
+ "$T.$L($L)",
+ InstanceFactory.class,
+ nullable ? "createNullable" : "create",
+ instanceExpression.get());
+ }
+
+ @Override
+ public boolean useInnerSwitchingProvider() {
+ return false;
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/MapBindingExpression.java b/java/dagger/internal/codegen/writing/MapBindingExpression.java
new file mode 100644
index 000000000..255e85d9c
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/MapBindingExpression.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
+import static dagger.internal.codegen.binding.MapKeys.getMapKeyExpression;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+import static dagger.model.BindingKind.MULTIBOUND_MAP;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.MapBuilder;
+import dagger.internal.codegen.base.MapType;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.BindingKind;
+import dagger.model.DependencyRequest;
+import java.util.Collections;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/** A {@link BindingExpression} for multibound maps. */
+final class MapBindingExpression extends SimpleInvocationBindingExpression {
+ /** Maximum number of key-value pairs that can be passed to ImmutableMap.of(K, V, K, V, ...). */
+ private static final int MAX_IMMUTABLE_MAP_OF_KEY_VALUE_PAIRS = 5;
+
+ private final ProvisionBinding binding;
+ private final ImmutableMap<DependencyRequest, ContributionBinding> dependencies;
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+
+ MapBindingExpression(
+ ProvisionBinding binding,
+ BindingGraph graph,
+ ComponentBindingExpressions componentBindingExpressions,
+ DaggerTypes types,
+ DaggerElements elements) {
+ super(binding);
+ this.binding = binding;
+ BindingKind bindingKind = this.binding.kind();
+ checkArgument(bindingKind.equals(MULTIBOUND_MAP), bindingKind);
+ this.componentBindingExpressions = componentBindingExpressions;
+ this.types = types;
+ this.elements = elements;
+ this.dependencies =
+ Maps.toMap(binding.dependencies(), dep -> graph.contributionBinding(dep.key()));
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ // TODO(ronshapiro): We should also make an ImmutableMap version of MapFactory
+ boolean isImmutableMapAvailable = isImmutableMapAvailable();
+ // TODO(ronshapiro, gak): Use Maps.immutableEnumMap() if it's available?
+ if (isImmutableMapAvailable && dependencies.size() <= MAX_IMMUTABLE_MAP_OF_KEY_VALUE_PAIRS) {
+ return Expression.create(
+ immutableMapType(),
+ CodeBlock.builder()
+ .add("$T.", ImmutableMap.class)
+ .add(maybeTypeParameters(requestingClass))
+ .add(
+ "of($L)",
+ dependencies
+ .keySet()
+ .stream()
+ .map(dependency -> keyAndValueExpression(dependency, requestingClass))
+ .collect(toParametersCodeBlock()))
+ .build());
+ }
+ switch (dependencies.size()) {
+ case 0:
+ return collectionsStaticFactoryInvocation(requestingClass, CodeBlock.of("emptyMap()"));
+ case 1:
+ return collectionsStaticFactoryInvocation(
+ requestingClass,
+ CodeBlock.of(
+ "singletonMap($L)",
+ keyAndValueExpression(getOnlyElement(dependencies.keySet()), requestingClass)));
+ default:
+ CodeBlock.Builder instantiation = CodeBlock.builder();
+ instantiation
+ .add("$T.", isImmutableMapAvailable ? ImmutableMap.class : MapBuilder.class)
+ .add(maybeTypeParameters(requestingClass));
+ if (isImmutableMapBuilderWithExpectedSizeAvailable()) {
+ instantiation.add("builderWithExpectedSize($L)", dependencies.size());
+ } else if (isImmutableMapAvailable) {
+ instantiation.add("builder()");
+ } else {
+ instantiation.add("newMapBuilder($L)", dependencies.size());
+ }
+ for (DependencyRequest dependency : dependencies.keySet()) {
+ instantiation.add(".put($L)", keyAndValueExpression(dependency, requestingClass));
+ }
+ return Expression.create(
+ isImmutableMapAvailable ? immutableMapType() : binding.key().type(),
+ instantiation.add(".build()").build());
+ }
+ }
+
+ private DeclaredType immutableMapType() {
+ MapType mapType = MapType.from(binding.key());
+ return types.getDeclaredType(
+ elements.getTypeElement(ImmutableMap.class), mapType.keyType(), mapType.valueType());
+ }
+
+ private CodeBlock keyAndValueExpression(DependencyRequest dependency, ClassName requestingClass) {
+ return CodeBlock.of(
+ "$L, $L",
+ getMapKeyExpression(dependencies.get(dependency), requestingClass, elements),
+ componentBindingExpressions
+ .getDependencyExpression(bindingRequest(dependency), requestingClass)
+ .codeBlock());
+ }
+
+ private Expression collectionsStaticFactoryInvocation(
+ ClassName requestingClass, CodeBlock methodInvocation) {
+ return Expression.create(
+ binding.key().type(),
+ CodeBlock.builder()
+ .add("$T.", Collections.class)
+ .add(maybeTypeParameters(requestingClass))
+ .add(methodInvocation)
+ .build());
+ }
+
+ private CodeBlock maybeTypeParameters(ClassName requestingClass) {
+ TypeMirror bindingKeyType = binding.key().type();
+ MapType mapType = MapType.from(binding.key());
+ return isTypeAccessibleFrom(bindingKeyType, requestingClass.packageName())
+ ? CodeBlock.of("<$T, $T>", mapType.keyType(), mapType.valueType())
+ : CodeBlock.of("");
+ }
+
+ private boolean isImmutableMapBuilderWithExpectedSizeAvailable() {
+ if (isImmutableMapAvailable()) {
+ return methodsIn(elements.getTypeElement(ImmutableMap.class).getEnclosedElements())
+ .stream()
+ .anyMatch(method -> method.getSimpleName().contentEquals("builderWithExpectedSize"));
+ }
+ return false;
+ }
+
+ private boolean isImmutableMapAvailable() {
+ return elements.getTypeElement(ImmutableMap.class) != null;
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/MapFactoryCreationExpression.java b/java/dagger/internal/codegen/writing/MapFactoryCreationExpression.java
new file mode 100644
index 000000000..104d48a1d
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/MapFactoryCreationExpression.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.binding.MapKeys.getMapKeyExpression;
+import static dagger.internal.codegen.binding.SourceFiles.mapFactoryClassName;
+
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.base.MapType;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.model.DependencyRequest;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import javax.inject.Provider;
+import javax.lang.model.type.TypeMirror;
+
+/** A factory creation expression for a multibound map. */
+final class MapFactoryCreationExpression extends MultibindingFactoryCreationExpression {
+
+ private final ComponentImplementation componentImplementation;
+ private final BindingGraph graph;
+ private final ContributionBinding binding;
+ private final DaggerElements elements;
+
+ MapFactoryCreationExpression(
+ ContributionBinding binding,
+ ComponentImplementation componentImplementation,
+ ComponentBindingExpressions componentBindingExpressions,
+ BindingGraph graph,
+ DaggerElements elements) {
+ super(binding, componentImplementation, componentBindingExpressions);
+ this.binding = checkNotNull(binding);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.graph = checkNotNull(graph);
+ this.elements = checkNotNull(elements);
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ CodeBlock.Builder builder = CodeBlock.builder().add("$T.", mapFactoryClassName(binding));
+ if (!useRawType()) {
+ MapType mapType = MapType.from(binding.key().type());
+ // TODO(ronshapiro): either inline this into mapFactoryClassName, or add a
+ // mapType.unwrappedValueType() method that doesn't require a framework type
+ TypeMirror valueType = mapType.valueType();
+ for (Class<?> frameworkClass :
+ ImmutableSet.of(Provider.class, Producer.class, Produced.class)) {
+ if (mapType.valuesAreTypeOf(frameworkClass)) {
+ valueType = mapType.unwrappedValueType(frameworkClass);
+ break;
+ }
+ }
+ builder.add("<$T, $T>", mapType.keyType(), valueType);
+ }
+
+ builder.add("builder($L)", binding.dependencies().size());
+
+ for (DependencyRequest dependency : binding.dependencies()) {
+ ContributionBinding contributionBinding = graph.contributionBinding(dependency.key());
+ builder.add(
+ ".put($L, $L)",
+ getMapKeyExpression(contributionBinding, componentImplementation.name(), elements),
+ multibindingDependencyExpression(dependency));
+ }
+ builder.add(".build()");
+
+ return builder.build();
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/MemberSelect.java b/java/dagger/internal/codegen/writing/MemberSelect.java
new file mode 100644
index 000000000..b04e21be0
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/MemberSelect.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.binding.ContributionBinding.FactoryCreationStrategy.SINGLETON_INSTANCE;
+import static dagger.internal.codegen.binding.SourceFiles.bindingTypeElementTypeVariableNames;
+import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding;
+import static dagger.internal.codegen.binding.SourceFiles.setFactoryClassName;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.FACTORY;
+import static dagger.internal.codegen.javapoet.TypeNames.MAP_FACTORY;
+import static dagger.internal.codegen.javapoet.TypeNames.PRODUCER;
+import static dagger.internal.codegen.javapoet.TypeNames.PRODUCERS;
+import static dagger.internal.codegen.javapoet.TypeNames.PROVIDER;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+import static javax.lang.model.type.TypeKind.DECLARED;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.TypeVariableName;
+import dagger.internal.codegen.base.SetType;
+import dagger.internal.codegen.binding.BindingType;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import java.util.List;
+import java.util.Optional;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Represents a {@link com.sun.source.tree.MemberSelectTree} as a {@link CodeBlock}.
+ */
+abstract class MemberSelect {
+
+ /**
+ * Returns a {@link MemberSelect} that accesses the field given by {@code fieldName} owned by
+ * {@code owningClass}. In this context "local" refers to the fact that the field is owned by the
+ * type (or an enclosing type) from which the code block will be used. The returned
+ * {@link MemberSelect} will not be valid for accessing the field from a different class
+ * (regardless of accessibility).
+ */
+ static MemberSelect localField(ClassName owningClass, String fieldName) {
+ return new LocalField(owningClass, fieldName);
+ }
+
+ private static final class LocalField extends MemberSelect {
+ final String fieldName;
+
+ LocalField(ClassName owningClass, String fieldName) {
+ super(owningClass, false);
+ this.fieldName = checkNotNull(fieldName);
+ }
+
+ @Override
+ CodeBlock getExpressionFor(ClassName usingClass) {
+ return owningClass().equals(usingClass)
+ ? CodeBlock.of("$N", fieldName)
+ : CodeBlock.of("$T.this.$N", owningClass(), fieldName);
+ }
+ }
+
+ /**
+ * Returns a {@link MemberSelect} that accesses the method given by {@code methodName} owned by
+ * {@code owningClass}. In this context "local" refers to the fact that the method is owned by the
+ * type (or an enclosing type) from which the code block will be used. The returned {@link
+ * MemberSelect} will not be valid for accessing the method from a different class (regardless of
+ * accessibility).
+ */
+ static MemberSelect localMethod(ClassName owningClass, String methodName) {
+ return new LocalMethod(owningClass, methodName);
+ }
+
+ private static final class LocalMethod extends MemberSelect {
+ final String methodName;
+
+ LocalMethod(ClassName owningClass, String methodName) {
+ super(owningClass, false);
+ this.methodName = checkNotNull(methodName);
+ }
+
+ @Override
+ CodeBlock getExpressionFor(ClassName usingClass) {
+ return owningClass().equals(usingClass)
+ ? CodeBlock.of("$N()", methodName)
+ : CodeBlock.of("$T.this.$N()", owningClass(), methodName);
+ }
+ }
+
+ /**
+ * If {@code resolvedBindings} is an unscoped provision binding with no factory arguments or a
+ * no-op members injection binding, then we don't need a field to hold its factory. In that case,
+ * this method returns the static member select that returns the factory or no-op members
+ * injector.
+ */
+ static Optional<MemberSelect> staticFactoryCreation(ContributionBinding contributionBinding) {
+ if (contributionBinding.factoryCreationStrategy().equals(SINGLETON_INSTANCE)
+ && !contributionBinding.scope().isPresent()) {
+ switch (contributionBinding.kind()) {
+ case MULTIBOUND_MAP:
+ return Optional.of(emptyMapFactory(contributionBinding));
+
+ case MULTIBOUND_SET:
+ return Optional.of(emptySetFactory(contributionBinding));
+
+ case INJECTION:
+ case PROVISION:
+ TypeMirror keyType = contributionBinding.key().type();
+ if (keyType.getKind().equals(DECLARED)) {
+ ImmutableList<TypeVariableName> typeVariables =
+ bindingTypeElementTypeVariableNames(contributionBinding);
+ if (!typeVariables.isEmpty()) {
+ List<? extends TypeMirror> typeArguments =
+ ((DeclaredType) keyType).getTypeArguments();
+ return Optional.of(
+ MemberSelect.parameterizedFactoryCreateMethod(
+ generatedClassNameForBinding(contributionBinding), typeArguments));
+ }
+ }
+ // fall through
+
+ default:
+ return Optional.of(
+ new StaticMethod(
+ generatedClassNameForBinding(contributionBinding), CodeBlock.of("create()")));
+ }
+ }
+
+ return Optional.empty();
+ }
+
+ /**
+ * Returns a {@link MemberSelect} for the instance of a {@code create()} method on a factory. This
+ * only applies for factories that do not have any dependencies.
+ */
+ private static MemberSelect parameterizedFactoryCreateMethod(
+ ClassName owningClass, List<? extends TypeMirror> parameters) {
+ return new ParameterizedStaticMethod(
+ owningClass, ImmutableList.copyOf(parameters), CodeBlock.of("create()"), FACTORY);
+ }
+
+ private static final class StaticMethod extends MemberSelect {
+ final CodeBlock methodCodeBlock;
+
+ StaticMethod(ClassName owningClass, CodeBlock methodCodeBlock) {
+ super(owningClass, true);
+ this.methodCodeBlock = checkNotNull(methodCodeBlock);
+ }
+
+ @Override
+ CodeBlock getExpressionFor(ClassName usingClass) {
+ return owningClass().equals(usingClass)
+ ? methodCodeBlock
+ : CodeBlock.of("$T.$L", owningClass(), methodCodeBlock);
+ }
+ }
+
+ /** A {@link MemberSelect} for a factory of an empty map. */
+ private static MemberSelect emptyMapFactory(ContributionBinding contributionBinding) {
+ BindingType bindingType = contributionBinding.bindingType();
+ ImmutableList<TypeMirror> typeParameters =
+ ImmutableList.copyOf(
+ MoreTypes.asDeclared(contributionBinding.key().type()).getTypeArguments());
+ if (bindingType.equals(BindingType.PRODUCTION)) {
+ return new ParameterizedStaticMethod(
+ PRODUCERS, typeParameters, CodeBlock.of("emptyMapProducer()"), PRODUCER);
+ } else {
+ return new ParameterizedStaticMethod(
+ MAP_FACTORY, typeParameters, CodeBlock.of("emptyMapProvider()"), PROVIDER);
+ }
+ }
+
+ /**
+ * A static member select for an empty set factory. Calls {@link
+ * dagger.internal.SetFactory#empty()}, {@link dagger.producers.internal.SetProducer#empty()}, or
+ * {@link dagger.producers.internal.SetOfProducedProducer#empty()}, depending on the set bindings.
+ */
+ private static MemberSelect emptySetFactory(ContributionBinding binding) {
+ return new ParameterizedStaticMethod(
+ setFactoryClassName(binding),
+ ImmutableList.of(SetType.from(binding.key()).elementType()),
+ CodeBlock.of("empty()"),
+ FACTORY);
+ }
+
+ private static final class ParameterizedStaticMethod extends MemberSelect {
+ final ImmutableList<TypeMirror> typeParameters;
+ final CodeBlock methodCodeBlock;
+ final ClassName rawReturnType;
+
+ ParameterizedStaticMethod(
+ ClassName owningClass,
+ ImmutableList<TypeMirror> typeParameters,
+ CodeBlock methodCodeBlock,
+ ClassName rawReturnType) {
+ super(owningClass, true);
+ this.typeParameters = typeParameters;
+ this.methodCodeBlock = methodCodeBlock;
+ this.rawReturnType = rawReturnType;
+ }
+
+ @Override
+ CodeBlock getExpressionFor(ClassName usingClass) {
+ boolean accessible = true;
+ for (TypeMirror typeParameter : typeParameters) {
+ accessible &= isTypeAccessibleFrom(typeParameter, usingClass.packageName());
+ }
+
+ if (accessible) {
+ return CodeBlock.of(
+ "$T.<$L>$L",
+ owningClass(),
+ typeParameters.stream().map(CodeBlocks::type).collect(toParametersCodeBlock()),
+ methodCodeBlock);
+ } else {
+ return CodeBlock.of("(($T) $T.$L)", rawReturnType, owningClass(), methodCodeBlock);
+ }
+ }
+ }
+
+ private final ClassName owningClass;
+ private final boolean staticMember;
+
+ MemberSelect(ClassName owningClass, boolean staticMemeber) {
+ this.owningClass = owningClass;
+ this.staticMember = staticMemeber;
+ }
+
+ /** Returns the class that owns the member being selected. */
+ ClassName owningClass() {
+ return owningClass;
+ }
+
+ /**
+ * Returns true if the member being selected is static and does not require an instance of
+ * {@link #owningClass()}.
+ */
+ boolean staticMember() {
+ return staticMember;
+ }
+
+ /**
+ * Returns a {@link CodeBlock} suitable for accessing the member from the given {@code
+ * usingClass}.
+ */
+ abstract CodeBlock getExpressionFor(ClassName usingClass);
+}
diff --git a/java/dagger/internal/codegen/writing/MembersInjectionBindingExpression.java b/java/dagger/internal/codegen/writing/MembersInjectionBindingExpression.java
new file mode 100644
index 000000000..abf9d03d3
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/MembersInjectionBindingExpression.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static javax.lang.model.type.TypeKind.VOID;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.ParameterSpec;
+import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.binding.MembersInjectionBinding;
+import dagger.internal.codegen.javapoet.Expression;
+import javax.lang.model.element.ExecutableElement;
+
+/**
+ * A binding expression for members injection component methods. See {@link
+ * MembersInjectionMethods}.
+ */
+final class MembersInjectionBindingExpression extends BindingExpression {
+ private final MembersInjectionBinding binding;
+ private final MembersInjectionMethods membersInjectionMethods;
+
+ MembersInjectionBindingExpression(
+ MembersInjectionBinding binding, MembersInjectionMethods membersInjectionMethods) {
+ this.binding = binding;
+ this.membersInjectionMethods = membersInjectionMethods;
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ throw new UnsupportedOperationException(binding.toString());
+ }
+
+ // TODO(ronshapiro): This class doesn't need to be a BindingExpression, as
+ // getDependencyExpression() should never be called for members injection methods. It's probably
+ // better suited as a method on MembersInjectionMethods
+ @Override
+ protected CodeBlock getComponentMethodImplementation(
+ ComponentMethodDescriptor componentMethod, ComponentImplementation component) {
+ ExecutableElement methodElement = componentMethod.methodElement();
+ ParameterSpec parameter = ParameterSpec.get(getOnlyElement(methodElement.getParameters()));
+
+ if (binding.injectionSites().isEmpty()) {
+ return methodElement.getReturnType().getKind().equals(VOID)
+ ? CodeBlock.of("")
+ : CodeBlock.of("return $N;", parameter);
+ } else {
+ return methodElement.getReturnType().getKind().equals(VOID)
+ ? CodeBlock.of("$L;", membersInjectionInvocation(parameter))
+ : CodeBlock.of("return $L;", membersInjectionInvocation(parameter));
+ }
+ }
+
+ CodeBlock membersInjectionInvocation(ParameterSpec target) {
+ return CodeBlock.of("$N($N)", membersInjectionMethods.getOrCreate(binding.key()), target);
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/MembersInjectionMethods.java b/java/dagger/internal/codegen/writing/MembersInjectionMethods.java
new file mode 100644
index 000000000..3d602b3d6
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/MembersInjectionMethods.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+import static dagger.internal.codegen.writing.ComponentImplementation.MethodSpecKind.MEMBERS_INJECTION_METHOD;
+import static javax.lang.model.element.Modifier.PRIVATE;
+
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.binding.Binding;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.MembersInjectionBinding;
+import dagger.internal.codegen.binding.MembersInjectionBinding.InjectionSite;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.writing.InjectionMethods.InjectionSiteMethod;
+import dagger.model.Key;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+/** Manages the member injection methods for a component. */
+final class MembersInjectionMethods {
+ private final Map<Key, MethodSpec> membersInjectionMethods = new LinkedHashMap<>();
+ private final ComponentImplementation componentImplementation;
+ private final ComponentBindingExpressions bindingExpressions;
+ private final BindingGraph graph;
+ private final DaggerElements elements;
+ private final DaggerTypes types;
+ private final KotlinMetadataUtil metadataUtil;
+
+ MembersInjectionMethods(
+ ComponentImplementation componentImplementation,
+ ComponentBindingExpressions bindingExpressions,
+ BindingGraph graph,
+ DaggerElements elements,
+ DaggerTypes types,
+ KotlinMetadataUtil metadataUtil) {
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.bindingExpressions = checkNotNull(bindingExpressions);
+ this.graph = checkNotNull(graph);
+ this.elements = checkNotNull(elements);
+ this.types = checkNotNull(types);
+ this.metadataUtil = metadataUtil;
+ }
+
+ /**
+ * Returns the members injection {@link MethodSpec} for the given {@link Key}, creating it if
+ * necessary.
+ */
+ MethodSpec getOrCreate(Key key) {
+ return reentrantComputeIfAbsent(membersInjectionMethods, key, this::membersInjectionMethod);
+ }
+
+ private MethodSpec membersInjectionMethod(Key key) {
+ Binding binding =
+ graph.membersInjectionBinding(key).isPresent()
+ ? graph.membersInjectionBinding(key).get()
+ : graph.contributionBinding(key);
+ TypeMirror keyType = binding.key().type();
+ TypeMirror membersInjectedType =
+ isTypeAccessibleFrom(keyType, componentImplementation.name().packageName())
+ ? keyType
+ : elements.getTypeElement(Object.class).asType();
+ TypeName membersInjectedTypeName = TypeName.get(membersInjectedType);
+ Name bindingTypeName = binding.bindingTypeElement().get().getSimpleName();
+ // TODO(ronshapiro): include type parameters in this name e.g. injectFooOfT, and outer class
+ // simple names Foo.Builder -> injectFooBuilder
+ String methodName = componentImplementation.getUniqueMethodName("inject" + bindingTypeName);
+ ParameterSpec parameter = ParameterSpec.builder(membersInjectedTypeName, "instance").build();
+ MethodSpec.Builder methodBuilder =
+ methodBuilder(methodName)
+ .addModifiers(PRIVATE)
+ .returns(membersInjectedTypeName)
+ .addParameter(parameter);
+ TypeElement canIgnoreReturnValue =
+ elements.getTypeElement("com.google.errorprone.annotations.CanIgnoreReturnValue");
+ if (canIgnoreReturnValue != null) {
+ methodBuilder.addAnnotation(ClassName.get(canIgnoreReturnValue));
+ }
+ CodeBlock instance = CodeBlock.of("$N", parameter);
+ methodBuilder.addCode(
+ InjectionSiteMethod.invokeAll(
+ injectionSites(binding),
+ componentImplementation.name(),
+ instance,
+ membersInjectedType,
+ request ->
+ bindingExpressions
+ .getDependencyArgumentExpression(request, componentImplementation.name())
+ .codeBlock(),
+ types,
+ metadataUtil));
+ methodBuilder.addStatement("return $L", instance);
+
+ MethodSpec method = methodBuilder.build();
+ componentImplementation.addMethod(MEMBERS_INJECTION_METHOD, method);
+ return method;
+ }
+
+ private static ImmutableSet<InjectionSite> injectionSites(Binding binding) {
+ if (binding instanceof ProvisionBinding) {
+ return ((ProvisionBinding) binding).injectionSites();
+ } else if (binding instanceof MembersInjectionBinding) {
+ return ((MembersInjectionBinding) binding).injectionSites();
+ }
+ throw new IllegalArgumentException(binding.key().toString());
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/MembersInjectorGenerator.java b/java/dagger/internal/codegen/writing/MembersInjectorGenerator.java
new file mode 100644
index 000000000..df618b070
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/MembersInjectorGenerator.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkState;
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.assistedInjectedConstructors;
+import static dagger.internal.codegen.binding.InjectionAnnotations.injectedConstructors;
+import static dagger.internal.codegen.binding.SourceFiles.bindingTypeElementTypeVariableNames;
+import static dagger.internal.codegen.binding.SourceFiles.frameworkFieldUsages;
+import static dagger.internal.codegen.binding.SourceFiles.generateBindingFieldsForDependencies;
+import static dagger.internal.codegen.binding.SourceFiles.membersInjectorNameForType;
+import static dagger.internal.codegen.binding.SourceFiles.parameterizedGeneratedTypeNameForBinding;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.membersInjectorOf;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+import static dagger.internal.codegen.writing.GwtCompatibility.gwtIncompatibleAnnotation;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.TypeVariableName;
+import dagger.MembersInjector;
+import dagger.internal.codegen.base.SourceFileGenerator;
+import dagger.internal.codegen.base.UniqueNameSet;
+import dagger.internal.codegen.binding.FrameworkField;
+import dagger.internal.codegen.binding.MembersInjectionBinding;
+import dagger.internal.codegen.binding.MembersInjectionBinding.InjectionSite;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.writing.InjectionMethods.InjectionSiteMethod;
+import dagger.model.DependencyRequest;
+import java.util.Map.Entry;
+import java.util.Optional;
+import javax.annotation.processing.Filer;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+
+/**
+ * Generates {@link MembersInjector} implementations from {@link MembersInjectionBinding} instances.
+ */
+public final class MembersInjectorGenerator extends SourceFileGenerator<MembersInjectionBinding> {
+ private final DaggerTypes types;
+ private final KotlinMetadataUtil metadataUtil;
+
+ @Inject
+ MembersInjectorGenerator(
+ Filer filer,
+ DaggerElements elements,
+ DaggerTypes types,
+ SourceVersion sourceVersion,
+ KotlinMetadataUtil metadataUtil) {
+ super(filer, elements, sourceVersion);
+ this.types = types;
+ this.metadataUtil = metadataUtil;
+ }
+
+ @Override
+ public ClassName nameGeneratedType(MembersInjectionBinding binding) {
+ return membersInjectorNameForType(binding.membersInjectedType());
+ }
+
+ @Override
+ public Element originatingElement(MembersInjectionBinding binding) {
+ return binding.membersInjectedType();
+ }
+
+ @Override
+ public Optional<TypeSpec.Builder> write(MembersInjectionBinding binding) {
+ // Empty members injection bindings are special and don't need source files.
+ if (binding.injectionSites().isEmpty()) {
+ return Optional.empty();
+ }
+
+ // Members injectors for classes with no local injection sites and no @Inject
+ // constructor are unused.
+ if (!binding.hasLocalInjectionSites()
+ && injectedConstructors(binding.membersInjectedType()).isEmpty()
+ && assistedInjectedConstructors(binding.membersInjectedType()).isEmpty()) {
+ return Optional.empty();
+ }
+
+
+ // We don't want to write out resolved bindings -- we want to write out the generic version.
+ checkState(
+ !binding.unresolved().isPresent(),
+ "tried to generate a MembersInjector for a binding of a resolved generic type: %s",
+ binding);
+
+ ClassName generatedTypeName = nameGeneratedType(binding);
+ ImmutableList<TypeVariableName> typeParameters = bindingTypeElementTypeVariableNames(binding);
+ TypeSpec.Builder injectorTypeBuilder =
+ classBuilder(generatedTypeName)
+ .addModifiers(PUBLIC, FINAL)
+ .addTypeVariables(typeParameters);
+
+ TypeName injectedTypeName = TypeName.get(binding.key().type());
+ TypeName implementedType = membersInjectorOf(injectedTypeName);
+ injectorTypeBuilder.addSuperinterface(implementedType);
+
+ MethodSpec.Builder injectMembersBuilder =
+ methodBuilder("injectMembers")
+ .addModifiers(PUBLIC)
+ .addAnnotation(Override.class)
+ .addParameter(injectedTypeName, "instance");
+
+ ImmutableMap<DependencyRequest, FrameworkField> fields =
+ generateBindingFieldsForDependencies(binding);
+
+ ImmutableMap.Builder<DependencyRequest, FieldSpec> dependencyFieldsBuilder =
+ ImmutableMap.builder();
+
+ MethodSpec.Builder constructorBuilder = constructorBuilder().addModifiers(PUBLIC);
+
+ // We use a static create method so that generated components can avoid having
+ // to refer to the generic types of the factory.
+ // (Otherwise they may have visibility problems referring to the types.)
+ MethodSpec.Builder createMethodBuilder =
+ methodBuilder("create")
+ .returns(implementedType)
+ .addModifiers(PUBLIC, STATIC)
+ .addTypeVariables(typeParameters);
+
+ createMethodBuilder.addCode(
+ "return new $T(", parameterizedGeneratedTypeNameForBinding(binding));
+ ImmutableList.Builder<CodeBlock> constructorInvocationParameters = ImmutableList.builder();
+
+ boolean usesRawFrameworkTypes = false;
+ UniqueNameSet fieldNames = new UniqueNameSet();
+ for (Entry<DependencyRequest, FrameworkField> fieldEntry : fields.entrySet()) {
+ DependencyRequest dependency = fieldEntry.getKey();
+ FrameworkField bindingField = fieldEntry.getValue();
+
+ // If the dependency type is not visible to this members injector, then use the raw framework
+ // type for the field.
+ boolean useRawFrameworkType =
+ !isTypeAccessibleFrom(dependency.key().type(), generatedTypeName.packageName());
+
+ String fieldName = fieldNames.getUniqueName(bindingField.name());
+ TypeName fieldType = useRawFrameworkType ? bindingField.type().rawType : bindingField.type();
+ FieldSpec.Builder fieldBuilder = FieldSpec.builder(fieldType, fieldName, PRIVATE, FINAL);
+ ParameterSpec.Builder parameterBuilder = ParameterSpec.builder(fieldType, fieldName);
+
+ // If we're using the raw type for the field, then suppress the injectMembers method's
+ // unchecked-type warning and the field's and the constructor and create-method's
+ // parameters' raw-type warnings.
+ if (useRawFrameworkType) {
+ usesRawFrameworkTypes = true;
+ fieldBuilder.addAnnotation(suppressWarnings(RAWTYPES));
+ parameterBuilder.addAnnotation(suppressWarnings(RAWTYPES));
+ }
+ constructorBuilder.addParameter(parameterBuilder.build());
+ createMethodBuilder.addParameter(parameterBuilder.build());
+
+ FieldSpec field = fieldBuilder.build();
+ injectorTypeBuilder.addField(field);
+ constructorBuilder.addStatement("this.$1N = $1N", field);
+ dependencyFieldsBuilder.put(dependency, field);
+ constructorInvocationParameters.add(CodeBlock.of("$N", field));
+ }
+
+ createMethodBuilder.addCode(
+ constructorInvocationParameters.build().stream().collect(toParametersCodeBlock()));
+ createMethodBuilder.addCode(");");
+
+ injectorTypeBuilder.addMethod(constructorBuilder.build());
+ injectorTypeBuilder.addMethod(createMethodBuilder.build());
+
+ ImmutableMap<DependencyRequest, FieldSpec> dependencyFields = dependencyFieldsBuilder.build();
+
+ injectMembersBuilder.addCode(
+ InjectionSiteMethod.invokeAll(
+ binding.injectionSites(),
+ generatedTypeName,
+ CodeBlock.of("instance"),
+ binding.key().type(),
+ frameworkFieldUsages(binding.dependencies(), dependencyFields)::get,
+ types,
+ metadataUtil));
+
+ if (usesRawFrameworkTypes) {
+ injectMembersBuilder.addAnnotation(suppressWarnings(UNCHECKED));
+ }
+ injectorTypeBuilder.addMethod(injectMembersBuilder.build());
+
+ for (InjectionSite injectionSite : binding.injectionSites()) {
+ if (injectionSite.element().getEnclosingElement().equals(binding.membersInjectedType())) {
+ injectorTypeBuilder.addMethod(InjectionSiteMethod.create(injectionSite, metadataUtil));
+ }
+ }
+
+ gwtIncompatibleAnnotation(binding).ifPresent(injectorTypeBuilder::addAnnotation);
+
+ return Optional.of(injectorTypeBuilder);
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/MembersInjectorProviderCreationExpression.java b/java/dagger/internal/codegen/writing/MembersInjectorProviderCreationExpression.java
new file mode 100644
index 000000000..534c0573b
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/MembersInjectorProviderCreationExpression.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.binding.SourceFiles.membersInjectorNameForType;
+import static dagger.internal.codegen.javapoet.TypeNames.INSTANCE_FACTORY;
+import static dagger.internal.codegen.javapoet.TypeNames.MEMBERS_INJECTORS;
+
+import com.google.auto.common.MoreTypes;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+/** A {@code Provider<MembersInjector<Foo>>} creation expression. */
+final class MembersInjectorProviderCreationExpression
+ implements FrameworkInstanceCreationExpression {
+
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final ProvisionBinding binding;
+
+ MembersInjectorProviderCreationExpression(
+ ProvisionBinding binding, ComponentBindingExpressions componentBindingExpressions) {
+ this.binding = checkNotNull(binding);
+ this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ TypeMirror membersInjectedType =
+ getOnlyElement(MoreTypes.asDeclared(binding.key().type()).getTypeArguments());
+
+ boolean castThroughRawType = false;
+ CodeBlock membersInjector;
+ if (binding.injectionSites().isEmpty()) {
+ membersInjector = CodeBlock.of("$T.<$T>noOp()", MEMBERS_INJECTORS, membersInjectedType);
+ } else {
+ TypeElement injectedTypeElement = MoreTypes.asTypeElement(membersInjectedType);
+ while (!hasLocalInjectionSites(injectedTypeElement)) {
+ // Cast through a raw type since we're going to be using the MembersInjector for the
+ // parent type.
+ castThroughRawType = true;
+ injectedTypeElement = MoreTypes.asTypeElement(injectedTypeElement.getSuperclass());
+ }
+
+ membersInjector = CodeBlock.of(
+ "$T.create($L)",
+ membersInjectorNameForType(injectedTypeElement),
+ componentBindingExpressions.getCreateMethodArgumentsCodeBlock(binding));
+ }
+
+ // TODO(ronshapiro): consider adding a MembersInjectorBindingExpression to return this directly
+ // (as it's rarely requested as a Provider).
+ CodeBlock providerExpression = CodeBlock.of("$T.create($L)", INSTANCE_FACTORY, membersInjector);
+ // If needed we cast through raw type around the InstanceFactory type as opposed to the
+ // MembersInjector since we end up with an InstanceFactory<MembersInjector> as opposed to a
+ // InstanceFactory<MembersInjector<Foo>> and that becomes unassignable. To fix it would require
+ // a second cast. If we just cast to the raw type InstanceFactory though, that becomes
+ // assignable.
+ return castThroughRawType
+ ? CodeBlock.of("($T) $L", INSTANCE_FACTORY, providerExpression) : providerExpression;
+ }
+
+ private boolean hasLocalInjectionSites(TypeElement injectedTypeElement) {
+ return binding.injectionSites()
+ .stream()
+ .anyMatch(
+ injectionSite ->
+ injectionSite.element().getEnclosingElement().equals(injectedTypeElement));
+ }
+
+ @Override
+ public boolean useInnerSwitchingProvider() {
+ return !binding.injectionSites().isEmpty();
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/MethodBindingExpression.java b/java/dagger/internal/codegen/writing/MethodBindingExpression.java
new file mode 100644
index 000000000..9c0c74aca
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/MethodBindingExpression.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.assistedParameterSpecs;
+import static dagger.internal.codegen.javapoet.CodeBlocks.parameterNames;
+import static dagger.internal.codegen.writing.ComponentImplementation.FieldSpecKind.PRIVATE_METHOD_SCOPED_FIELD;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.VOLATILE;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.DoubleCheck;
+import dagger.internal.MemoizedSentinel;
+import dagger.internal.codegen.binding.BindingRequest;
+import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.binding.FrameworkField;
+import dagger.internal.codegen.binding.KeyVariableNamer;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.BindingKind;
+import dagger.model.RequestKind;
+import java.util.Optional;
+import javax.lang.model.type.TypeMirror;
+
+/** A binding expression that wraps another in a nullary method on the component. */
+abstract class MethodBindingExpression extends BindingExpression {
+ private final BindingRequest request;
+ private final ContributionBinding binding;
+ private final BindingMethodImplementation bindingMethodImplementation;
+ private final ComponentImplementation componentImplementation;
+ private final ProducerEntryPointView producerEntryPointView;
+ private final BindingExpression wrappedBindingExpression;
+ private final DaggerTypes types;
+
+ protected MethodBindingExpression(
+ BindingRequest request,
+ ContributionBinding binding,
+ MethodImplementationStrategy methodImplementationStrategy,
+ BindingExpression wrappedBindingExpression,
+ ComponentImplementation componentImplementation,
+ DaggerTypes types) {
+ this.request = checkNotNull(request);
+ this.binding = checkNotNull(binding);
+ this.bindingMethodImplementation = bindingMethodImplementation(methodImplementationStrategy);
+ this.wrappedBindingExpression = checkNotNull(wrappedBindingExpression);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.producerEntryPointView = new ProducerEntryPointView(types);
+ this.types = checkNotNull(types);
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ if (request.frameworkType().isPresent()) {
+ // Initializing a framework instance that participates in a cycle requires that the underlying
+ // FrameworkInstanceBindingExpression is invoked in order for a cycle to be detected properly.
+ // When a MethodBindingExpression wraps a FrameworkInstanceBindingExpression, the wrapped
+ // expression will only be invoked once to implement the method body. This is a hack to work
+ // around that weirdness - methodImplementation.body() will invoke the framework instance
+ // initialization again in case the field is not fully initialized.
+ // TODO(b/121196706): use a less hacky approach to fix this bug
+ Object unused = methodBody();
+ }
+
+ addMethod();
+
+ CodeBlock methodCall =
+ binding.kind() == BindingKind.ASSISTED_INJECTION
+ // Private methods for assisted injection take assisted parameters as input.
+ ? CodeBlock.of(
+ "$N($L)", methodName(), parameterNames(assistedParameterSpecs(binding, types)))
+ : CodeBlock.of("$N()", methodName());
+
+ return Expression.create(
+ returnType(),
+ requestingClass.equals(componentImplementation.name())
+ ? methodCall
+ : CodeBlock.of("$L.$L", componentImplementation.externalReferenceBlock(), methodCall));
+ }
+
+ @Override
+ Expression getDependencyExpressionForComponentMethod(ComponentMethodDescriptor componentMethod,
+ ComponentImplementation component) {
+ return producerEntryPointView
+ .getProducerEntryPointField(this, componentMethod, component)
+ .orElseGet(
+ () -> super.getDependencyExpressionForComponentMethod(componentMethod, component));
+ }
+
+ /** Adds the method to the component (if necessary) the first time it's called. */
+ protected abstract void addMethod();
+
+ /** Returns the name of the method to call. */
+ protected abstract String methodName();
+
+ /** The method's body. */
+ protected final CodeBlock methodBody() {
+ return implementation(
+ wrappedBindingExpression.getDependencyExpression(componentImplementation.name())
+ ::codeBlock);
+ }
+
+ /** The method's body if this method is a component method. */
+ protected final CodeBlock methodBodyForComponentMethod(
+ ComponentMethodDescriptor componentMethod) {
+ return implementation(
+ wrappedBindingExpression.getDependencyExpressionForComponentMethod(
+ componentMethod, componentImplementation)
+ ::codeBlock);
+ }
+
+ private CodeBlock implementation(Supplier<CodeBlock> simpleBindingExpression) {
+ return bindingMethodImplementation.implementation(simpleBindingExpression);
+ }
+
+ private BindingMethodImplementation bindingMethodImplementation(
+ MethodImplementationStrategy methodImplementationStrategy) {
+ switch (methodImplementationStrategy) {
+ case SIMPLE:
+ return new SimpleMethodImplementation();
+ case SINGLE_CHECK:
+ return new SingleCheckedMethodImplementation();
+ case DOUBLE_CHECK:
+ return new DoubleCheckedMethodImplementation();
+ }
+ throw new AssertionError(methodImplementationStrategy);
+ }
+
+ /** Returns the return type for the dependency request. */
+ protected TypeMirror returnType() {
+ if (request.isRequestKind(RequestKind.INSTANCE)
+ && binding.contributedPrimitiveType().isPresent()) {
+ return binding.contributedPrimitiveType().get();
+ }
+
+ if (matchingComponentMethod().isPresent()) {
+ // Component methods are part of the user-defined API, and thus we must use the user-defined
+ // type.
+ return matchingComponentMethod().get().resolvedReturnType(types);
+ }
+
+ TypeMirror requestedType = request.requestedType(binding.contributedType(), types);
+ return types.accessibleType(requestedType, componentImplementation.name());
+ }
+
+ private Optional<ComponentMethodDescriptor> matchingComponentMethod() {
+ return componentImplementation.componentDescriptor().firstMatchingComponentMethod(request);
+ }
+
+ /** Strateg for implementing the body of this method. */
+ enum MethodImplementationStrategy {
+ SIMPLE,
+ SINGLE_CHECK,
+ DOUBLE_CHECK,
+ ;
+ }
+
+ private abstract static class BindingMethodImplementation {
+ /**
+ * Returns the method body, which contains zero or more statements (including semicolons).
+ *
+ * <p>If the implementation has a non-void return type, the body will also include the {@code
+ * return} statement.
+ *
+ * @param simpleBindingExpression the expression to retrieve an instance of this binding without
+ * the wrapping method.
+ */
+ abstract CodeBlock implementation(Supplier<CodeBlock> simpleBindingExpression);
+ }
+
+ /** Returns the {@code wrappedBindingExpression} directly. */
+ private static final class SimpleMethodImplementation extends BindingMethodImplementation {
+ @Override
+ CodeBlock implementation(Supplier<CodeBlock> simpleBindingExpression) {
+ return CodeBlock.of("return $L;", simpleBindingExpression.get());
+ }
+ }
+
+ /**
+ * Defines a method body for single checked caching of the given {@code wrappedBindingExpression}.
+ */
+ private final class SingleCheckedMethodImplementation extends BindingMethodImplementation {
+ private final Supplier<FieldSpec> field = Suppliers.memoize(this::createField);
+
+ @Override
+ CodeBlock implementation(Supplier<CodeBlock> simpleBindingExpression) {
+ String fieldExpression = field.get().name.equals("local") ? "this.local" : field.get().name;
+
+ CodeBlock.Builder builder = CodeBlock.builder()
+ .addStatement("Object local = $N", fieldExpression);
+
+ if (isNullable()) {
+ builder.beginControlFlow("if (local instanceof $T)", MemoizedSentinel.class);
+ } else {
+ builder.beginControlFlow("if (local == null)");
+ }
+
+ return builder
+ .addStatement("local = $L", simpleBindingExpression.get())
+ .addStatement("$N = ($T) local", fieldExpression, returnType())
+ .endControlFlow()
+ .addStatement("return ($T) local", returnType())
+ .build();
+ }
+
+ FieldSpec createField() {
+ String name =
+ componentImplementation.getUniqueFieldName(
+ request.isRequestKind(RequestKind.INSTANCE)
+ ? KeyVariableNamer.name(binding.key())
+ : FrameworkField.forBinding(binding, Optional.empty()).name());
+
+ FieldSpec.Builder builder = FieldSpec.builder(fieldType(), name, PRIVATE, VOLATILE);
+ if (isNullable()) {
+ builder.initializer("new $T()", MemoizedSentinel.class);
+ }
+
+ FieldSpec field = builder.build();
+ componentImplementation.addField(PRIVATE_METHOD_SCOPED_FIELD, field);
+ return field;
+ }
+
+ TypeName fieldType() {
+ if (isNullable()) {
+ // Nullable instances use `MemoizedSentinel` instead of `null` as the initialization value,
+ // so the field type must accept that and the return type
+ return TypeName.OBJECT;
+ }
+ TypeName returnType = TypeName.get(returnType());
+ return returnType.isPrimitive() ? returnType.box() : returnType;
+ }
+
+ private boolean isNullable() {
+ return request.isRequestKind(RequestKind.INSTANCE) && binding.isNullable();
+ }
+ }
+
+ /**
+ * Defines a method body for double checked caching of the given {@code wrappedBindingExpression}.
+ */
+ private final class DoubleCheckedMethodImplementation extends BindingMethodImplementation {
+ private final Supplier<String> fieldName = Suppliers.memoize(this::createField);
+
+ @Override
+ CodeBlock implementation(Supplier<CodeBlock> simpleBindingExpression) {
+ String fieldExpression = fieldName.get().equals("local") ? "this.local" : fieldName.get();
+ return CodeBlock.builder()
+ .addStatement("$T local = $L", TypeName.OBJECT, fieldExpression)
+ .beginControlFlow("if (local instanceof $T)", MemoizedSentinel.class)
+ .beginControlFlow("synchronized (local)")
+ .addStatement("local = $L", fieldExpression)
+ .beginControlFlow("if (local instanceof $T)", MemoizedSentinel.class)
+ .addStatement("local = $L", simpleBindingExpression.get())
+ .addStatement("$1L = $2T.reentrantCheck($1L, local)", fieldExpression, DoubleCheck.class)
+ .endControlFlow()
+ .endControlFlow()
+ .endControlFlow()
+ .addStatement("return ($T) local", returnType())
+ .build();
+ }
+
+ private String createField() {
+ String name =
+ componentImplementation.getUniqueFieldName(KeyVariableNamer.name(binding.key()));
+ componentImplementation.addField(
+ PRIVATE_METHOD_SCOPED_FIELD,
+ FieldSpec.builder(TypeName.OBJECT, name, PRIVATE, VOLATILE)
+ .initializer("new $T()", MemoizedSentinel.class)
+ .build());
+ return name;
+ }
+ }
+
+}
diff --git a/java/dagger/internal/codegen/writing/ModuleGenerator.java b/java/dagger/internal/codegen/writing/ModuleGenerator.java
new file mode 100644
index 000000000..afd0e997a
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ModuleGenerator.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import dagger.internal.codegen.base.SourceFileGenerator;
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/** Qualifier for a {@link SourceFileGenerator} for modules. */
+@Qualifier
+@Retention(RUNTIME)
+public @interface ModuleGenerator {}
diff --git a/java/dagger/internal/codegen/writing/ModuleProxies.java b/java/dagger/internal/codegen/writing/ModuleProxies.java
new file mode 100644
index 000000000..fcd9b56e5
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ModuleProxies.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.langmodel.Accessibility.isElementAccessibleFrom;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+import static javax.lang.model.util.ElementFilter.constructorsIn;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.base.SourceFileGenerator;
+import dagger.internal.codegen.binding.ModuleKind;
+import dagger.internal.codegen.binding.SourceFiles;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.Accessibility;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import java.util.Optional;
+import javax.annotation.processing.Filer;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+
+/** Convenience methods for generating and using module constructor proxy methods. */
+public final class ModuleProxies {
+
+ private final DaggerElements elements;
+ private final KotlinMetadataUtil metadataUtil;
+
+ @Inject
+ public ModuleProxies(DaggerElements elements, KotlinMetadataUtil metadataUtil) {
+ this.elements = elements;
+ this.metadataUtil = metadataUtil;
+ }
+
+ /** Generates a {@code public static} proxy method for constructing module instances. */
+ // TODO(dpb): See if this can become a SourceFileGenerator<ModuleDescriptor> instead. Doing so may
+ // cause ModuleProcessingStep to defer elements multiple times.
+ public static final class ModuleConstructorProxyGenerator
+ extends SourceFileGenerator<TypeElement> {
+
+ private final ModuleProxies moduleProxies;
+ private final KotlinMetadataUtil metadataUtil;
+
+ @Inject
+ ModuleConstructorProxyGenerator(
+ Filer filer,
+ DaggerElements elements,
+ SourceVersion sourceVersion,
+ ModuleProxies moduleProxies,
+ KotlinMetadataUtil metadataUtil) {
+ super(filer, elements, sourceVersion);
+ this.moduleProxies = moduleProxies;
+ this.metadataUtil = metadataUtil;
+ }
+
+ @Override
+ public ClassName nameGeneratedType(TypeElement moduleElement) {
+ return moduleProxies.constructorProxyTypeName(moduleElement);
+ }
+
+ @Override
+ public Element originatingElement(TypeElement moduleElement) {
+ return moduleElement;
+ }
+
+ @Override
+ public Optional<TypeSpec.Builder> write(TypeElement moduleElement) {
+ ModuleKind.checkIsModule(moduleElement, metadataUtil);
+ return moduleProxies.nonPublicNullaryConstructor(moduleElement).isPresent()
+ ? Optional.of(buildProxy(moduleElement))
+ : Optional.empty();
+ }
+
+ private TypeSpec.Builder buildProxy(TypeElement moduleElement) {
+ return classBuilder(nameGeneratedType(moduleElement))
+ .addModifiers(PUBLIC, FINAL)
+ .addMethod(constructorBuilder().addModifiers(PRIVATE).build())
+ .addMethod(
+ methodBuilder("newInstance")
+ .addModifiers(PUBLIC, STATIC)
+ .returns(ClassName.get(moduleElement))
+ .addStatement("return new $T()", moduleElement)
+ .build());
+ }
+ }
+
+ /** The name of the class that hosts the module constructor proxy method. */
+ private ClassName constructorProxyTypeName(TypeElement moduleElement) {
+ ModuleKind.checkIsModule(moduleElement, metadataUtil);
+ ClassName moduleClassName = ClassName.get(moduleElement);
+ return moduleClassName
+ .topLevelClassName()
+ .peerClass(SourceFiles.classFileName(moduleClassName) + "_Proxy");
+ }
+
+ /**
+ * The module constructor being proxied. A proxy is generated if it is not publicly accessible and
+ * has no arguments. If an implicit reference to the enclosing class exists, or the module is
+ * abstract, no proxy method can be generated.
+ */
+ private Optional<ExecutableElement> nonPublicNullaryConstructor(TypeElement moduleElement) {
+ ModuleKind.checkIsModule(moduleElement, metadataUtil);
+ if (moduleElement.getModifiers().contains(ABSTRACT)
+ || (moduleElement.getNestingKind().isNested()
+ && !moduleElement.getModifiers().contains(STATIC))) {
+ return Optional.empty();
+ }
+ return constructorsIn(elements.getAllMembers(moduleElement)).stream()
+ .filter(constructor -> !Accessibility.isElementPubliclyAccessible(constructor))
+ .filter(constructor -> !constructor.getModifiers().contains(PRIVATE))
+ .filter(constructor -> constructor.getParameters().isEmpty())
+ .findAny();
+ }
+
+ /**
+ * Returns a code block that creates a new module instance, either by invoking the nullary
+ * constructor if it's accessible from {@code requestingClass} or else by invoking the
+ * constructor's generated proxy method.
+ */
+ public CodeBlock newModuleInstance(TypeElement moduleElement, ClassName requestingClass) {
+ ModuleKind.checkIsModule(moduleElement, metadataUtil);
+ String packageName = requestingClass.packageName();
+ return nonPublicNullaryConstructor(moduleElement)
+ .filter(constructor -> !isElementAccessibleFrom(constructor, packageName))
+ .map(
+ constructor ->
+ CodeBlock.of("$T.newInstance()", constructorProxyTypeName(moduleElement)))
+ .orElse(CodeBlock.of("new $T()", moduleElement));
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/MultibindingFactoryCreationExpression.java b/java/dagger/internal/codegen/writing/MultibindingFactoryCreationExpression.java
new file mode 100644
index 000000000..64e008ee1
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/MultibindingFactoryCreationExpression.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.binding.BindingRequest;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import dagger.model.DependencyRequest;
+
+/** An abstract factory creation expression for multibindings. */
+abstract class MultibindingFactoryCreationExpression
+ implements FrameworkInstanceCreationExpression {
+ private final ComponentImplementation componentImplementation;
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final ContributionBinding binding;
+
+ MultibindingFactoryCreationExpression(
+ ContributionBinding binding,
+ ComponentImplementation componentImplementation,
+ ComponentBindingExpressions componentBindingExpressions) {
+ this.binding = checkNotNull(binding);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+ }
+
+ /** Returns the expression for a dependency of this multibinding. */
+ protected final CodeBlock multibindingDependencyExpression(DependencyRequest dependency) {
+ CodeBlock expression =
+ componentBindingExpressions
+ .getDependencyExpression(
+ BindingRequest.bindingRequest(dependency.key(), binding.frameworkType()),
+ componentImplementation.name())
+ .codeBlock();
+
+ return useRawType()
+ ? CodeBlocks.cast(expression, binding.frameworkType().frameworkClass())
+ : expression;
+ }
+
+ /** The binding request for this framework instance. */
+ protected final BindingRequest bindingRequest() {
+ return BindingRequest.bindingRequest(binding.key(), binding.frameworkType());
+ }
+
+ /**
+ * Returns true if the {@linkplain ContributionBinding#key() key type} is inaccessible from the
+ * component, and therefore a raw type must be used.
+ */
+ protected final boolean useRawType() {
+ return !componentImplementation.isTypeAccessible(binding.key().type());
+ }
+
+ @Override
+ public final boolean useInnerSwitchingProvider() {
+ return !binding.dependencies().isEmpty();
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/OptionalBindingExpression.java b/java/dagger/internal/codegen/writing/OptionalBindingExpression.java
new file mode 100644
index 000000000..4faf3fa6f
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/OptionalBindingExpression.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.base.OptionalType;
+import dagger.internal.codegen.base.OptionalType.OptionalKind;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+
+/** A binding expression for optional bindings. */
+final class OptionalBindingExpression extends SimpleInvocationBindingExpression {
+ private final ProvisionBinding binding;
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final DaggerTypes types;
+ private final SourceVersion sourceVersion;
+
+ @Inject
+ OptionalBindingExpression(
+ ProvisionBinding binding,
+ ComponentBindingExpressions componentBindingExpressions,
+ DaggerTypes types,
+ SourceVersion sourceVersion) {
+ super(binding);
+ this.binding = binding;
+ this.componentBindingExpressions = componentBindingExpressions;
+ this.types = types;
+ this.sourceVersion = sourceVersion;
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ OptionalType optionalType = OptionalType.from(binding.key());
+ OptionalKind optionalKind = optionalType.kind();
+ if (binding.dependencies().isEmpty()) {
+ if (sourceVersion.compareTo(SourceVersion.RELEASE_7) <= 0) {
+ // When compiling with -source 7, javac's type inference isn't strong enough to detect
+ // Futures.immediateFuture(Optional.absent()) for keys that aren't Object. It also has
+ // issues
+ // when used as an argument to some members injection proxy methods (see
+ // https://github.com/google/dagger/issues/916)
+ if (isTypeAccessibleFrom(binding.key().type(), requestingClass.packageName())) {
+ return Expression.create(
+ binding.key().type(), optionalKind.parameterizedAbsentValueExpression(optionalType));
+ }
+ }
+ return Expression.create(binding.key().type(), optionalKind.absentValueExpression());
+ }
+ DependencyRequest dependency = getOnlyElement(binding.dependencies());
+
+ CodeBlock dependencyExpression =
+ componentBindingExpressions
+ .getDependencyExpression(bindingRequest(dependency), requestingClass)
+ .codeBlock();
+
+ // If the dependency type is inaccessible, then we have to use Optional.<Object>of(...), or else
+ // we will get "incompatible types: inference variable has incompatible bounds.
+ return isTypeAccessibleFrom(dependency.key().type(), requestingClass.packageName())
+ ? Expression.create(
+ binding.key().type(), optionalKind.presentExpression(dependencyExpression))
+ : Expression.create(
+ types.erasure(binding.key().type()),
+ optionalKind.presentObjectExpression(dependencyExpression));
+ }
+
+ @Override
+ boolean requiresMethodEncapsulation() {
+ // TODO(dpb): Maybe require it for present bindings.
+ return false;
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/OptionalFactories.java b/java/dagger/internal/codegen/writing/OptionalFactories.java
new file mode 100644
index 000000000..2d809633f
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/OptionalFactories.java
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
+import static com.google.common.base.Verify.verify;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.anonymousClassBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.base.RequestKinds.requestTypeName;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
+import static dagger.internal.codegen.javapoet.TypeNames.PROVIDER;
+import static dagger.internal.codegen.javapoet.TypeNames.abstractProducerOf;
+import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf;
+import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
+import static dagger.internal.codegen.writing.ComponentImplementation.FieldSpecKind.ABSENT_OPTIONAL_FIELD;
+import static dagger.internal.codegen.writing.ComponentImplementation.MethodSpecKind.ABSENT_OPTIONAL_METHOD;
+import static dagger.internal.codegen.writing.ComponentImplementation.TypeSpecKind.PRESENT_FACTORY;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Function;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.TypeVariableName;
+import dagger.internal.InstanceFactory;
+import dagger.internal.Preconditions;
+import dagger.internal.codegen.base.OptionalType;
+import dagger.internal.codegen.base.OptionalType.OptionalKind;
+import dagger.internal.codegen.binding.BindingType;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.binding.FrameworkType;
+import dagger.internal.codegen.javapoet.AnnotationSpecs;
+import dagger.model.RequestKind;
+import dagger.producers.Producer;
+import dagger.producers.internal.Producers;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Optional;
+import java.util.TreeMap;
+import java.util.concurrent.Executor;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+/** The nested class and static methods required by the component to implement optional bindings. */
+// TODO(dpb): Name members simply if a component uses only one of Guava or JDK Optional.
+@PerGeneratedFile
+final class OptionalFactories {
+ private final ComponentImplementation componentImplementation;
+
+ @Inject OptionalFactories(@TopLevel ComponentImplementation componentImplementation) {
+ this.componentImplementation = componentImplementation;
+ }
+
+ /**
+ * The factory classes that implement {@code Provider<Optional<T>>} or {@code
+ * Producer<Optional<T>>} for present optional bindings for a given kind of dependency request
+ * within the component.
+ *
+ * <p>The key is the {@code Provider<Optional<T>>} type.
+ */
+ private final Map<PresentFactorySpec, TypeSpec> presentFactoryClasses =
+ new TreeMap<>(
+ Comparator.comparing(PresentFactorySpec::valueKind)
+ .thenComparing(PresentFactorySpec::frameworkType)
+ .thenComparing(PresentFactorySpec::optionalKind));
+
+ /**
+ * The static methods that return a {@code Provider<Optional<T>>} that always returns an absent
+ * value.
+ */
+ private final Map<OptionalKind, MethodSpec> absentOptionalProviderMethods = new TreeMap<>();
+
+ /**
+ * The static fields for {@code Provider<Optional<T>>} objects that always return an absent value.
+ */
+ private final Map<OptionalKind, FieldSpec> absentOptionalProviderFields = new TreeMap<>();
+
+ /**
+ * Returns an expression that calls a static method that returns a {@code Provider<Optional<T>>}
+ * for absent optional bindings.
+ */
+ CodeBlock absentOptionalProvider(ContributionBinding binding) {
+ verify(
+ binding.bindingType().equals(BindingType.PROVISION),
+ "Absent optional bindings should be provisions: %s",
+ binding);
+ OptionalKind optionalKind = OptionalType.from(binding.key()).kind();
+ return CodeBlock.of(
+ "$N()",
+ absentOptionalProviderMethods.computeIfAbsent(
+ optionalKind,
+ kind -> {
+ MethodSpec method = absentOptionalProviderMethod(kind);
+ componentImplementation.addMethod(ABSENT_OPTIONAL_METHOD, method);
+ return method;
+ }));
+ }
+
+ /**
+ * Creates a method specification for a {@code Provider<Optional<T>>} that always returns an
+ * absent value.
+ */
+ private MethodSpec absentOptionalProviderMethod(OptionalKind optionalKind) {
+ TypeVariableName typeVariable = TypeVariableName.get("T");
+ return methodBuilder(
+ String.format(
+ "absent%sProvider", UPPER_UNDERSCORE.to(UPPER_CAMEL, optionalKind.name())))
+ .addModifiers(PRIVATE, STATIC)
+ .addTypeVariable(typeVariable)
+ .returns(providerOf(optionalKind.of(typeVariable)))
+ .addJavadoc(
+ "Returns a {@link $T} that returns {@code $L}.",
+ Provider.class,
+ optionalKind.absentValueExpression())
+ .addCode("$L // safe covariant cast\n", AnnotationSpecs.suppressWarnings(UNCHECKED))
+ .addCode(
+ "$1T provider = ($1T) $2N;",
+ providerOf(optionalKind.of(typeVariable)),
+ absentOptionalProviderFields.computeIfAbsent(
+ optionalKind,
+ kind -> {
+ FieldSpec field = absentOptionalProviderField(kind);
+ componentImplementation.addField(ABSENT_OPTIONAL_FIELD, field);
+ return field;
+ }))
+ .addCode("return provider;")
+ .build();
+ }
+
+ /**
+ * Creates a field specification for a {@code Provider<Optional<T>>} that always returns an absent
+ * value.
+ */
+ private FieldSpec absentOptionalProviderField(OptionalKind optionalKind) {
+ return FieldSpec.builder(
+ PROVIDER,
+ String.format("ABSENT_%s_PROVIDER", optionalKind.name()),
+ PRIVATE,
+ STATIC,
+ FINAL)
+ .addAnnotation(AnnotationSpecs.suppressWarnings(RAWTYPES))
+ .initializer("$T.create($L)", InstanceFactory.class, optionalKind.absentValueExpression())
+ .addJavadoc(
+ "A {@link $T} that returns {@code $L}.",
+ Provider.class,
+ optionalKind.absentValueExpression())
+ .build();
+ }
+
+ /** Information about the type of a factory for present bindings. */
+ @AutoValue
+ abstract static class PresentFactorySpec {
+ /** Whether the factory is a {@link Provider} or a {@link Producer}. */
+ abstract FrameworkType frameworkType();
+
+ /** What kind of {@code Optional} is returned. */
+ abstract OptionalKind optionalKind();
+
+ /** The kind of request satisfied by the value of the {@code Optional}. */
+ abstract RequestKind valueKind();
+
+ /** The type variable for the factory class. */
+ TypeVariableName typeVariable() {
+ return TypeVariableName.get("T");
+ }
+
+ /** The type contained by the {@code Optional}. */
+ TypeName valueType() {
+ return requestTypeName(valueKind(), typeVariable());
+ }
+
+ /** The type provided or produced by the factory. */
+ ParameterizedTypeName optionalType() {
+ return optionalKind().of(valueType());
+ }
+
+ /** The type of the factory. */
+ ParameterizedTypeName factoryType() {
+ return frameworkType().frameworkClassOf(optionalType());
+ }
+
+ /** The type of the delegate provider or producer. */
+ ParameterizedTypeName delegateType() {
+ return frameworkType().frameworkClassOf(typeVariable());
+ }
+
+ /** Returns the superclass the generated factory should have, if any. */
+ Optional<ParameterizedTypeName> superclass() {
+ switch (frameworkType()) {
+ case PRODUCER_NODE:
+ // TODO(cgdecker): This probably isn't a big issue for now, but it's possible this
+ // shouldn't be an AbstractProducer:
+ // - As AbstractProducer, it'll only call the delegate's get() method once and then cache
+ // that result (essentially) rather than calling the delegate's get() method each time
+ // its get() method is called (which was what it did before the cancellation change).
+ // - It's not 100% clear to me whether the view-creation methods should return a view of
+ // the same view created by the delegate or if they should just return their own views.
+ return Optional.of(abstractProducerOf(optionalType()));
+ default:
+ return Optional.empty();
+ }
+ }
+
+ /** Returns the superinterface the generated factory should have, if any. */
+ Optional<ParameterizedTypeName> superinterface() {
+ switch (frameworkType()) {
+ case PROVIDER:
+ return Optional.of(factoryType());
+ default:
+ return Optional.empty();
+ }
+ }
+
+ /** Returns the name of the factory method to generate. */
+ String factoryMethodName() {
+ switch (frameworkType()) {
+ case PROVIDER:
+ return "get";
+ case PRODUCER_NODE:
+ return "compute";
+ }
+ throw new AssertionError(frameworkType());
+ }
+
+ /** The name of the factory class. */
+ String factoryClassName() {
+ return new StringBuilder("Present")
+ .append(UPPER_UNDERSCORE.to(UPPER_CAMEL, optionalKind().name()))
+ .append(UPPER_UNDERSCORE.to(UPPER_CAMEL, valueKind().toString()))
+ .append(frameworkType().frameworkClass().getSimpleName())
+ .toString();
+ }
+
+ private static PresentFactorySpec of(ContributionBinding binding) {
+ return new AutoValue_OptionalFactories_PresentFactorySpec(
+ FrameworkType.forBindingType(binding.bindingType()),
+ OptionalType.from(binding.key()).kind(),
+ getOnlyElement(binding.dependencies()).kind());
+ }
+ }
+
+ /**
+ * Returns an expression for an instance of a nested class that implements {@code
+ * Provider<Optional<T>>} or {@code Producer<Optional<T>>} for a present optional binding, where
+ * {@code T} represents dependency requests of that kind.
+ *
+ * <ul>
+ * <li>If {@code optionalRequestKind} is {@link RequestKind#INSTANCE}, the class implements
+ * {@code ProviderOrProducer<Optional<T>>}.
+ * <li>If {@code optionalRequestKind} is {@link RequestKind#PROVIDER}, the class implements
+ * {@code Provider<Optional<Provider<T>>>}.
+ * <li>If {@code optionalRequestKind} is {@link RequestKind#LAZY}, the class implements {@code
+ * Provider<Optional<Lazy<T>>>}.
+ * <li>If {@code optionalRequestKind} is {@link RequestKind#PROVIDER_OF_LAZY}, the class
+ * implements {@code Provider<Optional<Provider<Lazy<T>>>>}.
+ * <li>If {@code optionalRequestKind} is {@link RequestKind#PRODUCER}, the class implements
+ * {@code Producer<Optional<Producer<T>>>}.
+ * <li>If {@code optionalRequestKind} is {@link RequestKind#PRODUCED}, the class implements
+ * {@code Producer<Optional<Produced<T>>>}.
+ * </ul>
+ *
+ * @param delegateFactory an expression for a {@link Provider} or {@link Producer} of the
+ * underlying type
+ */
+ CodeBlock presentOptionalFactory(ContributionBinding binding, CodeBlock delegateFactory) {
+ return CodeBlock.of(
+ "$N.of($L)",
+ presentFactoryClasses.computeIfAbsent(
+ PresentFactorySpec.of(binding),
+ spec -> {
+ TypeSpec type = presentOptionalFactoryClass(spec);
+ componentImplementation.addType(PRESENT_FACTORY, type);
+ return type;
+ }),
+ delegateFactory);
+ }
+
+ private TypeSpec presentOptionalFactoryClass(PresentFactorySpec spec) {
+ FieldSpec delegateField =
+ FieldSpec.builder(spec.delegateType(), "delegate", PRIVATE, FINAL).build();
+ ParameterSpec delegateParameter = ParameterSpec.builder(delegateField.type, "delegate").build();
+ TypeSpec.Builder factoryClassBuilder =
+ classBuilder(spec.factoryClassName())
+ .addTypeVariable(spec.typeVariable())
+ .addModifiers(PRIVATE, STATIC, FINAL)
+ .addJavadoc(
+ "A {@code $T} that uses a delegate {@code $T}.",
+ spec.factoryType(),
+ delegateField.type);
+
+ spec.superclass().ifPresent(factoryClassBuilder::superclass);
+ spec.superinterface().ifPresent(factoryClassBuilder::addSuperinterface);
+
+ return factoryClassBuilder
+ .addField(delegateField)
+ .addMethod(
+ constructorBuilder()
+ .addModifiers(PRIVATE)
+ .addParameter(delegateParameter)
+ .addCode(
+ "this.$N = $T.checkNotNull($N);",
+ delegateField,
+ Preconditions.class,
+ delegateParameter)
+ .build())
+ .addMethod(presentOptionalFactoryGetMethod(spec, delegateField))
+ .addMethod(
+ methodBuilder("of")
+ .addModifiers(PRIVATE, STATIC)
+ .addTypeVariable(spec.typeVariable())
+ .returns(spec.factoryType())
+ .addParameter(delegateParameter)
+ .addCode(
+ "return new $L<$T>($N);",
+ spec.factoryClassName(),
+ spec.typeVariable(),
+ delegateParameter)
+ .build())
+ .build();
+ }
+
+ private MethodSpec presentOptionalFactoryGetMethod(
+ PresentFactorySpec spec, FieldSpec delegateField) {
+ MethodSpec.Builder getMethodBuilder =
+ methodBuilder(spec.factoryMethodName()).addAnnotation(Override.class).addModifiers(PUBLIC);
+
+ switch (spec.frameworkType()) {
+ case PROVIDER:
+ return getMethodBuilder
+ .returns(spec.optionalType())
+ .addCode(
+ "return $L;",
+ spec.optionalKind()
+ .presentExpression(
+ FrameworkType.PROVIDER.to(
+ spec.valueKind(), CodeBlock.of("$N", delegateField))))
+ .build();
+
+ case PRODUCER_NODE:
+ getMethodBuilder.returns(listenableFutureOf(spec.optionalType()));
+
+ switch (spec.valueKind()) {
+ case FUTURE: // return a ListenableFuture<Optional<ListenableFuture<T>>>
+ case PRODUCER: // return a ListenableFuture<Optional<Producer<T>>>
+ return getMethodBuilder
+ .addCode(
+ "return $T.immediateFuture($L);",
+ Futures.class,
+ spec.optionalKind()
+ .presentExpression(
+ FrameworkType.PRODUCER_NODE.to(
+ spec.valueKind(), CodeBlock.of("$N", delegateField))))
+ .build();
+
+ case INSTANCE: // return a ListenableFuture<Optional<T>>
+ return getMethodBuilder
+ .addCode(
+ "return $L;",
+ transformFutureToOptional(
+ spec.optionalKind(),
+ spec.typeVariable(),
+ CodeBlock.of("$N.get()", delegateField)))
+ .build();
+
+ case PRODUCED: // return a ListenableFuture<Optional<Produced<T>>>
+ return getMethodBuilder
+ .addCode(
+ "return $L;",
+ transformFutureToOptional(
+ spec.optionalKind(),
+ spec.valueType(),
+ CodeBlock.of(
+ "$T.createFutureProduced($N.get())", Producers.class, delegateField)))
+ .build();
+
+ default:
+ throw new UnsupportedOperationException(
+ spec.factoryType() + " objects are not supported");
+ }
+ }
+ throw new AssertionError(spec.frameworkType());
+ }
+
+ /**
+ * An expression that uses {@link Futures#transform(ListenableFuture, Function, Executor)} to
+ * transform a {@code ListenableFuture<inputType>} into a {@code
+ * ListenableFuture<Optional<inputType>>}.
+ *
+ * @param inputFuture an expression of type {@code ListenableFuture<inputType>}
+ */
+ private static CodeBlock transformFutureToOptional(
+ OptionalKind optionalKind, TypeName inputType, CodeBlock inputFuture) {
+ return CodeBlock.of(
+ "$T.transform($L, $L, $T.directExecutor())",
+ Futures.class,
+ inputFuture,
+ anonymousClassBuilder("")
+ .addSuperinterface(
+ ParameterizedTypeName.get(
+ ClassName.get(Function.class), inputType, optionalKind.of(inputType)))
+ .addMethod(
+ methodBuilder("apply")
+ .addAnnotation(Override.class)
+ .addModifiers(PUBLIC)
+ .returns(optionalKind.of(inputType))
+ .addParameter(inputType, "input")
+ .addCode("return $L;", optionalKind.presentExpression(CodeBlock.of("input")))
+ .build())
+ .build(),
+ MoreExecutors.class);
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/OptionalFactoryInstanceCreationExpression.java b/java/dagger/internal/codegen/writing/OptionalFactoryInstanceCreationExpression.java
new file mode 100644
index 000000000..593921ef9
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/OptionalFactoryInstanceCreationExpression.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
+
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+
+/**
+ * A {@link FrameworkInstanceCreationExpression} for {@link dagger.model.BindingKind#OPTIONAL
+ * optional bindings}.
+ */
+final class OptionalFactoryInstanceCreationExpression
+ implements FrameworkInstanceCreationExpression {
+ private final OptionalFactories optionalFactories;
+ private final ContributionBinding binding;
+ private final ComponentImplementation componentImplementation;
+ private final ComponentBindingExpressions componentBindingExpressions;
+
+ OptionalFactoryInstanceCreationExpression(
+ OptionalFactories optionalFactories,
+ ContributionBinding binding,
+ ComponentImplementation componentImplementation,
+ ComponentBindingExpressions componentBindingExpressions) {
+ this.optionalFactories = optionalFactories;
+ this.binding = binding;
+ this.componentImplementation = componentImplementation;
+ this.componentBindingExpressions = componentBindingExpressions;
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ return binding.dependencies().isEmpty()
+ ? optionalFactories.absentOptionalProvider(binding)
+ : optionalFactories.presentOptionalFactory(
+ binding,
+ componentBindingExpressions
+ .getDependencyExpression(
+ bindingRequest(
+ getOnlyElement(binding.dependencies()).key(), binding.frameworkType()),
+ componentImplementation.name())
+ .codeBlock());
+ }
+
+ @Override
+ public boolean useInnerSwitchingProvider() {
+ // Share providers for empty optionals from OptionalFactories so we don't have numerous
+ // switch cases that all return Optional.empty().
+ return !binding.dependencies().isEmpty();
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/ParentComponent.java b/java/dagger/internal/codegen/writing/ParentComponent.java
new file mode 100644
index 000000000..20b946e35
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ParentComponent.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/**
+ * A {@link Qualifier} for bindings that are associated with a component implementation's parent
+ * component.
+ */
+@Retention(RUNTIME)
+@Qualifier
+public @interface ParentComponent {}
diff --git a/java/dagger/internal/codegen/writing/PerComponentImplementation.java b/java/dagger/internal/codegen/writing/PerComponentImplementation.java
new file mode 100644
index 000000000..80888df42
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/PerComponentImplementation.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import javax.inject.Scope;
+
+/** A {@link Scope} that encompasses a single component implementation. */
+@Retention(RUNTIME)
+@Scope
+public @interface PerComponentImplementation {}
diff --git a/java/dagger/internal/codegen/writing/PerGeneratedFile.java b/java/dagger/internal/codegen/writing/PerGeneratedFile.java
new file mode 100644
index 000000000..c1fcaf769
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/PerGeneratedFile.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import javax.inject.Scope;
+
+/**
+ * A {@link Scope} that encompasses a top-level component implementation and any of its inner
+ * descendant component implementations in the same generated file.
+ */
+@Retention(RUNTIME)
+@Scope
+public @interface PerGeneratedFile {}
diff --git a/java/dagger/internal/codegen/writing/PrivateMethodBindingExpression.java b/java/dagger/internal/codegen/writing/PrivateMethodBindingExpression.java
new file mode 100644
index 000000000..b6502ea3f
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/PrivateMethodBindingExpression.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.assistedParameterSpecs;
+import static dagger.internal.codegen.writing.ComponentImplementation.MethodSpecKind.PRIVATE_METHOD;
+import static javax.lang.model.element.Modifier.PRIVATE;
+
+import com.google.common.collect.ImmutableList;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.binding.BindingRequest;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.BindingKind;
+
+/**
+ * A binding expression that wraps the dependency expressions in a private, no-arg method.
+ *
+ * <p>Dependents of this binding expression will just call the no-arg private method.
+ */
+final class PrivateMethodBindingExpression extends MethodBindingExpression {
+ private final ContributionBinding binding;
+ private final BindingRequest request;
+ private final ComponentImplementation componentImplementation;
+ private final CompilerOptions compilerOptions;
+ private final DaggerTypes types;
+ private String methodName;
+
+ PrivateMethodBindingExpression(
+ BindingRequest request,
+ ContributionBinding binding,
+ MethodImplementationStrategy methodImplementationStrategy,
+ BindingExpression wrappedBindingExpression,
+ ComponentImplementation componentImplementation,
+ DaggerTypes types,
+ CompilerOptions compilerOptions) {
+ super(
+ request,
+ binding,
+ methodImplementationStrategy,
+ wrappedBindingExpression,
+ componentImplementation,
+ types);
+ this.binding = binding;
+ this.request = checkNotNull(request);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.compilerOptions = checkNotNull(compilerOptions);
+ this.types = types;
+ }
+
+ @Override
+ protected void addMethod() {
+ if (methodName == null) {
+ // Have to set methodName field before implementing the method in order to handle recursion.
+ methodName = componentImplementation.getUniqueMethodName(request);
+
+ // TODO(bcorso): Fix the order that these generated methods are written to the component.
+ componentImplementation.addMethod(
+ PRIVATE_METHOD,
+ methodBuilder(methodName)
+ .addModifiers(PRIVATE)
+ .addParameters(
+ // Private methods for assisted injection take assisted parameters as input.
+ binding.kind() == BindingKind.ASSISTED_INJECTION
+ ? assistedParameterSpecs(binding, types)
+ : ImmutableList.of())
+ .returns(TypeName.get(returnType()))
+ .addCode(methodBody())
+ .build());
+ }
+ }
+
+ @Override
+ protected String methodName() {
+ checkState(methodName != null, "addMethod() must be called before methodName()");
+ return methodName;
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/ProducerCreationExpression.java b/java/dagger/internal/codegen/writing/ProducerCreationExpression.java
new file mode 100644
index 000000000..0d1ccf3b8
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ProducerCreationExpression.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding;
+
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+
+/**
+ * A {@link dagger.producers.Producer} creation expression for a {@link
+ * dagger.producers.Produces @Produces}-annotated module method.
+ */
+// TODO(dpb): Resolve with InjectionOrProvisionProviderCreationExpression.
+final class ProducerCreationExpression implements FrameworkInstanceCreationExpression {
+
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final ContributionBinding binding;
+
+ ProducerCreationExpression(
+ ContributionBinding binding, ComponentBindingExpressions componentBindingExpressions) {
+ this.binding = checkNotNull(binding);
+ this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ return CodeBlock.of(
+ "$T.create($L)",
+ generatedClassNameForBinding(binding),
+ componentBindingExpressions.getCreateMethodArgumentsCodeBlock(binding));
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/ProducerEntryPointView.java b/java/dagger/internal/codegen/writing/ProducerEntryPointView.java
new file mode 100644
index 000000000..c58f4e0bf
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ProducerEntryPointView.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static dagger.internal.codegen.writing.ComponentImplementation.FieldSpecKind.FRAMEWORK_FIELD;
+import static javax.lang.model.element.Modifier.PRIVATE;
+
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.RequestKind;
+import dagger.producers.Producer;
+import dagger.producers.internal.CancellationListener;
+import dagger.producers.internal.Producers;
+import java.util.Optional;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A factory of {@linkplain Producers#entryPointViewOf(Producer, CancellationListener) entry point
+ * views} of {@link Producer}s.
+ */
+final class ProducerEntryPointView {
+ private final DaggerTypes types;
+
+ ProducerEntryPointView(DaggerTypes types) {
+ this.types = types;
+ }
+
+ /**
+ * Returns an expression for an {@linkplain Producers#entryPointViewOf(Producer,
+ * CancellationListener) entry point view} of a producer if the component method returns a {@link
+ * Producer} or {@link com.google.common.util.concurrent.ListenableFuture}.
+ *
+ * <p>This is intended to be a replacement implementation for {@link
+ * dagger.internal.codegen.writing.BindingExpression#getDependencyExpressionForComponentMethod(ComponentMethodDescriptor,
+ * ComponentImplementation)}, and in cases where {@link Optional#empty()} is returned, callers
+ * should call {@code super.getDependencyExpressionForComponentMethod()}.
+ */
+ Optional<Expression> getProducerEntryPointField(
+ BindingExpression producerExpression,
+ ComponentMethodDescriptor componentMethod,
+ ComponentImplementation component) {
+ if (component.componentDescriptor().isProduction()
+ && (componentMethod.dependencyRequest().get().kind().equals(RequestKind.FUTURE)
+ || componentMethod.dependencyRequest().get().kind().equals(RequestKind.PRODUCER))) {
+ return Optional.of(
+ Expression.create(
+ fieldType(componentMethod),
+ "$N",
+ createField(producerExpression, componentMethod, component)));
+ } else {
+ // If the component isn't a production component, it won't implement CancellationListener and
+ // as such we can't create an entry point. But this binding must also just be a Producer from
+ // Provider anyway in that case, so there shouldn't be an issue.
+ // TODO(b/116855531): Is it really intended that a non-production component can have Producer
+ // entry points?
+ return Optional.empty();
+ }
+ }
+
+ private FieldSpec createField(
+ BindingExpression producerExpression,
+ ComponentMethodDescriptor componentMethod,
+ ComponentImplementation component) {
+ // TODO(cgdecker): Use a FrameworkFieldInitializer for this?
+ // Though I don't think we need the once-only behavior of that, since I think
+ // getComponentMethodImplementation will only be called once anyway
+ String methodName = componentMethod.methodElement().getSimpleName().toString();
+ FieldSpec field =
+ FieldSpec.builder(
+ TypeName.get(fieldType(componentMethod)),
+ component.getUniqueFieldName(methodName + "EntryPoint"),
+ PRIVATE)
+ .build();
+ component.addField(FRAMEWORK_FIELD, field);
+
+ CodeBlock fieldInitialization =
+ CodeBlock.of(
+ "this.$N = $T.entryPointViewOf($L, this);",
+ field,
+ Producers.class,
+ producerExpression.getDependencyExpression(component.name()).codeBlock());
+ component.addInitialization(fieldInitialization);
+
+ return field;
+ }
+
+ // TODO(cgdecker): Can we use producerExpression.getDependencyExpression().type() instead of
+ // needing to (re)compute this?
+ private TypeMirror fieldType(ComponentMethodDescriptor componentMethod) {
+ return types.wrapType(componentMethod.dependencyRequest().get().key().type(), Producer.class);
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/ProducerFactoryGenerator.java b/java/dagger/internal/codegen/writing/ProducerFactoryGenerator.java
new file mode 100644
index 000000000..5e5292359
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ProducerFactoryGenerator.java
@@ -0,0 +1,567 @@
+/*
+ * Copyright (C) 2014 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Verify.verifyNotNull;
+import static com.squareup.javapoet.ClassName.OBJECT;
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.binding.SourceFiles.bindingTypeElementTypeVariableNames;
+import static dagger.internal.codegen.binding.SourceFiles.generateBindingFieldsForDependencies;
+import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding;
+import static dagger.internal.codegen.binding.SourceFiles.parameterizedGeneratedTypeNameForBinding;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.FUTURE_RETURN_VALUE_IGNORED;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
+import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.FUTURES;
+import static dagger.internal.codegen.javapoet.TypeNames.PRODUCERS;
+import static dagger.internal.codegen.javapoet.TypeNames.PRODUCER_TOKEN;
+import static dagger.internal.codegen.javapoet.TypeNames.VOID_CLASS;
+import static dagger.internal.codegen.javapoet.TypeNames.listOf;
+import static dagger.internal.codegen.javapoet.TypeNames.listenableFutureOf;
+import static dagger.internal.codegen.javapoet.TypeNames.producedOf;
+import static dagger.internal.codegen.writing.GwtCompatibility.gwtIncompatibleAnnotation;
+import static java.util.stream.Collectors.joining;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PROTECTED;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.base.SourceFileGenerator;
+import dagger.internal.codegen.base.UniqueNameSet;
+import dagger.internal.codegen.binding.Binding;
+import dagger.internal.codegen.binding.FrameworkField;
+import dagger.internal.codegen.binding.KeyFactory;
+import dagger.internal.codegen.binding.ProductionBinding;
+import dagger.internal.codegen.binding.SourceFiles;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.javapoet.AnnotationSpecs;
+import dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.model.RequestKind;
+import dagger.producers.Producer;
+import dagger.producers.internal.AbstractProducesMethodProducer;
+import dagger.producers.internal.Producers;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Optional;
+import javax.annotation.processing.Filer;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.type.TypeMirror;
+
+/** Generates {@link Producer} implementations from {@link ProductionBinding} instances. */
+public final class ProducerFactoryGenerator extends SourceFileGenerator<ProductionBinding> {
+ private final CompilerOptions compilerOptions;
+ private final KeyFactory keyFactory;
+
+ @Inject
+ ProducerFactoryGenerator(
+ Filer filer,
+ DaggerElements elements,
+ SourceVersion sourceVersion,
+ CompilerOptions compilerOptions,
+ KeyFactory keyFactory) {
+ super(filer, elements, sourceVersion);
+ this.compilerOptions = compilerOptions;
+ this.keyFactory = keyFactory;
+ }
+
+ @Override
+ public ClassName nameGeneratedType(ProductionBinding binding) {
+ return generatedClassNameForBinding(binding);
+ }
+
+ @Override
+ public Element originatingElement(ProductionBinding binding) {
+ // we only create factories for bindings that have a binding element
+ return binding.bindingElement().get();
+ }
+
+ @Override
+ public Optional<TypeSpec.Builder> write(ProductionBinding binding) {
+ // We don't want to write out resolved bindings -- we want to write out the generic version.
+ checkArgument(!binding.unresolved().isPresent());
+ checkArgument(binding.bindingElement().isPresent());
+
+ TypeName providedTypeName = TypeName.get(binding.contributedType());
+ TypeName futureTypeName = listenableFutureOf(providedTypeName);
+
+ ClassName generatedTypeName = nameGeneratedType(binding);
+ TypeSpec.Builder factoryBuilder =
+ classBuilder(generatedTypeName)
+ .addModifiers(PUBLIC, FINAL)
+ .addTypeVariables(bindingTypeElementTypeVariableNames(binding));
+
+ UniqueNameSet uniqueFieldNames = new UniqueNameSet();
+ ImmutableMap.Builder<DependencyRequest, FieldSpec> fieldsBuilder = ImmutableMap.builder();
+
+ MethodSpec.Builder constructorBuilder = constructorBuilder().addModifiers(PRIVATE);
+
+ Optional<FieldSpec> moduleField =
+ binding.requiresModuleInstance()
+ ? Optional.of(
+ addFieldAndConstructorParameter(
+ factoryBuilder,
+ constructorBuilder,
+ uniqueFieldNames.getUniqueName("module"),
+ TypeName.get(binding.bindingTypeElement().get().asType())))
+ : Optional.empty();
+
+ List<CodeBlock> frameworkFieldAssignments = new ArrayList<>();
+
+ String executorParameterName = null;
+ String monitorParameterName = null;
+ ImmutableMap<DependencyRequest, FrameworkField> bindingFieldsForDependencies =
+ generateBindingFieldsForDependencies(binding);
+ for (Entry<DependencyRequest, FrameworkField> entry : bindingFieldsForDependencies.entrySet()) {
+ DependencyRequest dependency = entry.getKey();
+ Key key = dependency.key();
+ FrameworkField bindingField = entry.getValue();
+ String fieldName = uniqueFieldNames.getUniqueName(bindingField.name());
+ if (key.equals(keyFactory.forProductionImplementationExecutor())) {
+ executorParameterName = fieldName;
+ constructorBuilder.addParameter(bindingField.type(), executorParameterName);
+ } else if (key.equals(keyFactory.forProductionComponentMonitor())) {
+ monitorParameterName = fieldName;
+ constructorBuilder.addParameter(bindingField.type(), monitorParameterName);
+ } else {
+ FieldSpec field =
+ addFieldAndConstructorParameter(
+ factoryBuilder, constructorBuilder, fieldName, bindingField.type());
+ fieldsBuilder.put(dependency, field);
+ frameworkFieldAssignments.add(fieldAssignment(field, bindingField.type()));
+ }
+ }
+ ImmutableMap<DependencyRequest, FieldSpec> fields = fieldsBuilder.build();
+
+ constructorBuilder.addStatement(
+ "super($N, $L, $N)",
+ verifyNotNull(monitorParameterName),
+ producerTokenConstruction(generatedTypeName, binding),
+ verifyNotNull(executorParameterName));
+
+ if (binding.requiresModuleInstance()) {
+ assignField(constructorBuilder, moduleField.get(), null);
+ }
+
+ constructorBuilder.addCode(CodeBlock.join(frameworkFieldAssignments, "\n"));
+
+ MethodSpec.Builder collectDependenciesBuilder =
+ methodBuilder("collectDependencies").addAnnotation(Override.class).addModifiers(PROTECTED);
+
+ ImmutableList<DependencyRequest> asyncDependencies = asyncDependencies(binding);
+ for (DependencyRequest dependency : asyncDependencies) {
+ TypeName futureType = listenableFutureOf(asyncDependencyType(dependency));
+ CodeBlock futureAccess = CodeBlock.of("$N.get()", fields.get(dependency));
+ collectDependenciesBuilder.addStatement(
+ "$T $L = $L",
+ futureType,
+ dependencyFutureName(dependency),
+ dependency.kind().equals(RequestKind.PRODUCED)
+ ? CodeBlock.of("$T.createFutureProduced($L)", PRODUCERS, futureAccess)
+ : futureAccess);
+ }
+ FutureTransform futureTransform = FutureTransform.create(fields, binding, asyncDependencies);
+
+ collectDependenciesBuilder
+ .returns(listenableFutureOf(futureTransform.applyArgType()))
+ .addStatement("return $L", futureTransform.futureCodeBlock());
+
+ MethodSpec.Builder callProducesMethod =
+ methodBuilder("callProducesMethod")
+ .returns(futureTypeName)
+ .addAnnotation(Override.class)
+ .addModifiers(PUBLIC)
+ .addParameter(futureTransform.applyArgType(), futureTransform.applyArgName())
+ .addExceptions(getThrownTypeNames(binding.thrownTypes()))
+ .addCode(
+ getInvocationCodeBlock(
+ binding, providedTypeName, futureTransform.parameterCodeBlocks()));
+ if (futureTransform.hasUncheckedCast()) {
+ callProducesMethod.addAnnotation(AnnotationSpecs.suppressWarnings(UNCHECKED));
+ }
+
+ MethodSpec constructor = constructorBuilder.build();
+ factoryBuilder
+ .superclass(
+ ParameterizedTypeName.get(
+ ClassName.get(AbstractProducesMethodProducer.class),
+ futureTransform.applyArgType(),
+ providedTypeName))
+ .addMethod(constructor)
+ .addMethod(staticFactoryMethod(binding, constructor))
+ .addMethod(collectDependenciesBuilder.build())
+ .addMethod(callProducesMethod.build());
+
+ gwtIncompatibleAnnotation(binding).ifPresent(factoryBuilder::addAnnotation);
+
+ // TODO(gak): write a sensible toString
+ return Optional.of(factoryBuilder);
+ }
+
+ private MethodSpec staticFactoryMethod(ProductionBinding binding, MethodSpec constructor) {
+ return MethodSpec.methodBuilder("create")
+ .addModifiers(PUBLIC, STATIC)
+ .returns(parameterizedGeneratedTypeNameForBinding(binding))
+ .addTypeVariables(bindingTypeElementTypeVariableNames(binding))
+ .addParameters(constructor.parameters)
+ .addStatement(
+ "return new $T($L)",
+ parameterizedGeneratedTypeNameForBinding(binding),
+ constructor.parameters.stream()
+ .map(p -> CodeBlock.of("$N", p.name))
+ .collect(toParametersCodeBlock()))
+ .build();
+ }
+
+ // TODO(ronshapiro): consolidate versions of these
+ private static FieldSpec addFieldAndConstructorParameter(
+ TypeSpec.Builder typeBuilder,
+ MethodSpec.Builder constructorBuilder,
+ String variableName,
+ TypeName variableType) {
+ FieldSpec field = FieldSpec.builder(variableType, variableName, PRIVATE, FINAL).build();
+ typeBuilder.addField(field);
+ constructorBuilder.addParameter(field.type, field.name);
+ return field;
+ }
+
+ private static CodeBlock fieldAssignment(FieldSpec field, ParameterizedTypeName type) {
+ CodeBlock.Builder statement = CodeBlock.builder();
+ if (type != null && type.rawType.equals(TypeNames.PRODUCER)) {
+ statement.addStatement(
+ "this.$1N = $2T.nonCancellationPropagatingViewOf($1N)", field, Producers.class);
+ } else {
+ statement.addStatement("this.$1N = $1N", field);
+ }
+ return statement.build();
+ }
+
+ private static void assignField(
+ MethodSpec.Builder constructorBuilder, FieldSpec field, ParameterizedTypeName type) {
+ if (type != null && type.rawType.equals(TypeNames.PRODUCER)) {
+ constructorBuilder.addStatement(
+ "this.$1N = $2T.nonCancellationPropagatingViewOf($1N)", field, Producers.class);
+ } else {
+ constructorBuilder.addStatement("this.$1N = $1N", field);
+ }
+ }
+
+ /** Returns a list of dependencies that are generated asynchronously. */
+ private static ImmutableList<DependencyRequest> asyncDependencies(Binding binding) {
+ return binding.dependencies().stream()
+ .filter(ProducerFactoryGenerator::isAsyncDependency)
+ .collect(toImmutableList());
+ }
+
+ private CodeBlock producerTokenConstruction(
+ ClassName generatedTypeName, ProductionBinding binding) {
+ CodeBlock producerTokenArgs =
+ compilerOptions.writeProducerNameInToken()
+ ? CodeBlock.of(
+ "$S",
+ String.format(
+ "%s#%s",
+ ClassName.get(binding.bindingTypeElement().get()),
+ binding.bindingElement().get().getSimpleName()))
+ : CodeBlock.of("$T.class", generatedTypeName);
+ return CodeBlock.of("$T.create($L)", PRODUCER_TOKEN, producerTokenArgs);
+ }
+
+ /** Returns a name of the variable representing this dependency's future. */
+ private static String dependencyFutureName(DependencyRequest dependency) {
+ return dependency.requestElement().get().getSimpleName() + "Future";
+ }
+
+ /** Represents the transformation of an input future by a producer method. */
+ abstract static class FutureTransform {
+ protected final ImmutableMap<DependencyRequest, FieldSpec> fields;
+ protected final ProductionBinding binding;
+
+ FutureTransform(ImmutableMap<DependencyRequest, FieldSpec> fields, ProductionBinding binding) {
+ this.fields = fields;
+ this.binding = binding;
+ }
+
+ /** The code block representing the future that should be transformed. */
+ abstract CodeBlock futureCodeBlock();
+
+ /** The type of the argument to the apply method. */
+ abstract TypeName applyArgType();
+
+ /** The name of the argument to the apply method */
+ abstract String applyArgName();
+
+ /** The code blocks to be passed to the produces method itself. */
+ abstract ImmutableList<CodeBlock> parameterCodeBlocks();
+
+ /** Whether the transform method has an unchecked cast. */
+ boolean hasUncheckedCast() {
+ return false;
+ }
+
+ CodeBlock frameworkTypeUsageStatement(DependencyRequest dependency) {
+ return SourceFiles.frameworkTypeUsageStatement(
+ CodeBlock.of("$N", fields.get(dependency)), dependency.kind());
+ }
+
+ static FutureTransform create(
+ ImmutableMap<DependencyRequest, FieldSpec> fields,
+ ProductionBinding binding,
+ ImmutableList<DependencyRequest> asyncDependencies) {
+ if (asyncDependencies.isEmpty()) {
+ return new NoArgFutureTransform(fields, binding);
+ } else if (asyncDependencies.size() == 1) {
+ return new SingleArgFutureTransform(
+ fields, binding, Iterables.getOnlyElement(asyncDependencies));
+ } else {
+ return new MultiArgFutureTransform(fields, binding, asyncDependencies);
+ }
+ }
+ }
+
+ static final class NoArgFutureTransform extends FutureTransform {
+ NoArgFutureTransform(
+ ImmutableMap<DependencyRequest, FieldSpec> fields, ProductionBinding binding) {
+ super(fields, binding);
+ }
+
+ @Override
+ CodeBlock futureCodeBlock() {
+ return CodeBlock.of("$T.<$T>immediateFuture(null)", FUTURES, VOID_CLASS);
+ }
+
+ @Override
+ TypeName applyArgType() {
+ return VOID_CLASS;
+ }
+
+ @Override
+ String applyArgName() {
+ return "ignoredVoidArg";
+ }
+
+ @Override
+ ImmutableList<CodeBlock> parameterCodeBlocks() {
+ return binding.explicitDependencies().stream()
+ .map(this::frameworkTypeUsageStatement)
+ .collect(toImmutableList());
+ }
+ }
+
+ static final class SingleArgFutureTransform extends FutureTransform {
+ private final DependencyRequest asyncDependency;
+
+ SingleArgFutureTransform(
+ ImmutableMap<DependencyRequest, FieldSpec> fields,
+ ProductionBinding binding,
+ DependencyRequest asyncDependency) {
+ super(fields, binding);
+ this.asyncDependency = asyncDependency;
+ }
+
+ @Override
+ CodeBlock futureCodeBlock() {
+ return CodeBlock.of("$L", dependencyFutureName(asyncDependency));
+ }
+
+ @Override
+ TypeName applyArgType() {
+ return asyncDependencyType(asyncDependency);
+ }
+
+ @Override
+ String applyArgName() {
+ String argName = asyncDependency.requestElement().get().getSimpleName().toString();
+ if (argName.equals("module")) {
+ return "moduleArg";
+ }
+ return argName;
+ }
+
+ @Override
+ ImmutableList<CodeBlock> parameterCodeBlocks() {
+ ImmutableList.Builder<CodeBlock> parameterCodeBlocks = ImmutableList.builder();
+ for (DependencyRequest dependency : binding.explicitDependencies()) {
+ // We really want to compare instances here, because asyncDependency is an element in the
+ // set binding.dependencies().
+ if (dependency == asyncDependency) {
+ parameterCodeBlocks.add(CodeBlock.of("$L", applyArgName()));
+ } else {
+ parameterCodeBlocks.add(frameworkTypeUsageStatement(dependency));
+ }
+ }
+ return parameterCodeBlocks.build();
+ }
+ }
+
+ static final class MultiArgFutureTransform extends FutureTransform {
+ private final ImmutableList<DependencyRequest> asyncDependencies;
+
+ MultiArgFutureTransform(
+ ImmutableMap<DependencyRequest, FieldSpec> fields,
+ ProductionBinding binding,
+ ImmutableList<DependencyRequest> asyncDependencies) {
+ super(fields, binding);
+ this.asyncDependencies = asyncDependencies;
+ }
+
+ @Override
+ CodeBlock futureCodeBlock() {
+ return CodeBlock.of(
+ "$T.<$T>allAsList($L)",
+ FUTURES,
+ OBJECT,
+ asyncDependencies
+ .stream()
+ .map(ProducerFactoryGenerator::dependencyFutureName)
+ .collect(joining(", ")));
+ }
+
+ @Override
+ TypeName applyArgType() {
+ return listOf(OBJECT);
+ }
+
+ @Override
+ String applyArgName() {
+ return "args";
+ }
+
+ @Override
+ ImmutableList<CodeBlock> parameterCodeBlocks() {
+ int argIndex = 0;
+ ImmutableList.Builder<CodeBlock> codeBlocks = ImmutableList.builder();
+ for (DependencyRequest dependency : binding.explicitDependencies()) {
+ if (isAsyncDependency(dependency)) {
+ codeBlocks.add(
+ CodeBlock.of(
+ "($T) $L.get($L)", asyncDependencyType(dependency), applyArgName(), argIndex));
+ argIndex++;
+ } else {
+ codeBlocks.add(frameworkTypeUsageStatement(dependency));
+ }
+ }
+ return codeBlocks.build();
+ }
+
+ @Override
+ boolean hasUncheckedCast() {
+ return true;
+ }
+ }
+
+ private static boolean isAsyncDependency(DependencyRequest dependency) {
+ switch (dependency.kind()) {
+ case INSTANCE:
+ case PRODUCED:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private static TypeName asyncDependencyType(DependencyRequest dependency) {
+ TypeName keyName = TypeName.get(dependency.key().type());
+ switch (dependency.kind()) {
+ case INSTANCE:
+ return keyName;
+ case PRODUCED:
+ return producedOf(keyName);
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * Creates a code block for the invocation of the producer method from the module, which should be
+ * used entirely within a method body.
+ *
+ * @param binding The binding to generate the invocation code block for.
+ * @param providedTypeName The type name that should be provided by this producer.
+ * @param parameterCodeBlocks The code blocks for all the parameters to the producer method.
+ */
+ private CodeBlock getInvocationCodeBlock(
+ ProductionBinding binding,
+ TypeName providedTypeName,
+ ImmutableList<CodeBlock> parameterCodeBlocks) {
+ CodeBlock moduleCodeBlock =
+ CodeBlock.of(
+ "$L.$L($L)",
+ binding.requiresModuleInstance()
+ ? "module"
+ : CodeBlock.of("$T", ClassName.get(binding.bindingTypeElement().get())),
+ binding.bindingElement().get().getSimpleName(),
+ makeParametersCodeBlock(parameterCodeBlocks));
+
+ final CodeBlock returnCodeBlock;
+ switch (binding.productionKind().get()) {
+ case IMMEDIATE:
+ returnCodeBlock =
+ CodeBlock.of("$T.<$T>immediateFuture($L)", FUTURES, providedTypeName, moduleCodeBlock);
+ break;
+ case FUTURE:
+ returnCodeBlock = moduleCodeBlock;
+ break;
+ case SET_OF_FUTURE:
+ returnCodeBlock = CodeBlock.of("$T.allAsSet($L)", PRODUCERS, moduleCodeBlock);
+ break;
+ default:
+ throw new AssertionError();
+ }
+ return CodeBlock.of("return $L;", returnCodeBlock);
+ }
+
+ /**
+ * Converts the list of thrown types into type names.
+ *
+ * @param thrownTypes the list of thrown types.
+ */
+ private FluentIterable<? extends TypeName> getThrownTypeNames(
+ Iterable<? extends TypeMirror> thrownTypes) {
+ return FluentIterable.from(thrownTypes).transform(TypeName::get);
+ }
+
+ @Override
+ protected ImmutableSet<Suppression> warningSuppressions() {
+ // TODO(beder): examine if we can remove this or prevent subtypes of Future from being produced
+ return ImmutableSet.of(FUTURE_RETURN_VALUE_IGNORED);
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/ProducerFromProviderCreationExpression.java b/java/dagger/internal/codegen/writing/ProducerFromProviderCreationExpression.java
new file mode 100644
index 000000000..9b0d4e8ba
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ProducerFromProviderCreationExpression.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.binding.FrameworkType;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import dagger.model.RequestKind;
+import dagger.producers.Producer;
+import java.util.Optional;
+
+/** An {@link Producer} creation expression for provision bindings. */
+final class ProducerFromProviderCreationExpression implements FrameworkInstanceCreationExpression {
+ private final ContributionBinding binding;
+ private final ComponentImplementation componentImplementation;
+ private final ComponentBindingExpressions componentBindingExpressions;
+
+ ProducerFromProviderCreationExpression(
+ ContributionBinding binding,
+ ComponentImplementation componentImplementation,
+ ComponentBindingExpressions componentBindingExpressions) {
+ this.binding = checkNotNull(binding);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ return FrameworkType.PROVIDER.to(
+ RequestKind.PRODUCER,
+ componentBindingExpressions
+ .getDependencyExpression(
+ bindingRequest(binding.key(), FrameworkType.PROVIDER),
+ componentImplementation.name())
+ .codeBlock());
+ }
+
+ @Override
+ public Optional<ClassName> alternativeFrameworkClass() {
+ return Optional.of(TypeNames.PRODUCER);
+ }
+
+ // TODO(ronshapiro): should this have a simple factory if the delegate expression is simple?
+}
diff --git a/java/dagger/internal/codegen/writing/ProducerNodeInstanceBindingExpression.java b/java/dagger/internal/codegen/writing/ProducerNodeInstanceBindingExpression.java
new file mode 100644
index 000000000..0d3f7e876
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ProducerNodeInstanceBindingExpression.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.binding.FrameworkType;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Key;
+
+/** Binding expression for producer node instances. */
+final class ProducerNodeInstanceBindingExpression extends FrameworkInstanceBindingExpression {
+ /** The component defining this binding. */
+ private final ComponentImplementation componentImplementation;
+ private final Key key;
+ private final ProducerEntryPointView producerEntryPointView;
+
+ ProducerNodeInstanceBindingExpression(
+ ContributionBinding binding,
+ FrameworkInstanceSupplier frameworkInstanceSupplier,
+ DaggerTypes types,
+ DaggerElements elements,
+ ComponentImplementation componentImplementation) {
+ super(binding, frameworkInstanceSupplier, types, elements);
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.key = binding.key();
+ this.producerEntryPointView = new ProducerEntryPointView(types);
+ }
+
+ @Override
+ protected FrameworkType frameworkType() {
+ return FrameworkType.PRODUCER_NODE;
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ Expression result = super.getDependencyExpression(requestingClass);
+ componentImplementation.addCancellableProducerKey(key);
+ return result;
+ }
+
+ @Override
+ Expression getDependencyExpressionForComponentMethod(
+ ComponentMethodDescriptor componentMethod, ComponentImplementation component) {
+ return producerEntryPointView
+ .getProducerEntryPointField(this, componentMethod, component)
+ .orElseGet(
+ () -> super.getDependencyExpressionForComponentMethod(componentMethod, component));
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/ProviderInstanceBindingExpression.java b/java/dagger/internal/codegen/writing/ProviderInstanceBindingExpression.java
new file mode 100644
index 000000000..400c6a259
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ProviderInstanceBindingExpression.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.binding.FrameworkType;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+
+/** Binding expression for provider instances. */
+final class ProviderInstanceBindingExpression extends FrameworkInstanceBindingExpression {
+
+ ProviderInstanceBindingExpression(
+ ContributionBinding binding,
+ FrameworkInstanceSupplier frameworkInstanceSupplier,
+ DaggerTypes types,
+ DaggerElements elements) {
+ super(
+ binding,
+ frameworkInstanceSupplier,
+ types,
+ elements);
+ }
+
+ @Override
+ protected FrameworkType frameworkType() {
+ return FrameworkType.PROVIDER;
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/SetBindingExpression.java b/java/dagger/internal/codegen/writing/SetBindingExpression.java
new file mode 100644
index 000000000..0b2e11a9a
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/SetBindingExpression.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.SetBuilder;
+import dagger.internal.codegen.base.ContributionType;
+import dagger.internal.codegen.base.SetType;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.DependencyRequest;
+import java.util.Collections;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/** A binding expression for multibound sets. */
+final class SetBindingExpression extends SimpleInvocationBindingExpression {
+ private final ProvisionBinding binding;
+ private final BindingGraph graph;
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final DaggerTypes types;
+ private final DaggerElements elements;
+
+ SetBindingExpression(
+ ProvisionBinding binding,
+ BindingGraph graph,
+ ComponentBindingExpressions componentBindingExpressions,
+ DaggerTypes types,
+ DaggerElements elements) {
+ super(binding);
+ this.binding = binding;
+ this.graph = graph;
+ this.componentBindingExpressions = componentBindingExpressions;
+ this.types = types;
+ this.elements = elements;
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ // TODO(ronshapiro): We should also make an ImmutableSet version of SetFactory
+ boolean isImmutableSetAvailable = isImmutableSetAvailable();
+ // TODO(ronshapiro, gak): Use Sets.immutableEnumSet() if it's available?
+ if (isImmutableSetAvailable && binding.dependencies().stream().allMatch(this::isSingleValue)) {
+ return Expression.create(
+ immutableSetType(),
+ CodeBlock.builder()
+ .add("$T.", ImmutableSet.class)
+ .add(maybeTypeParameter(requestingClass))
+ .add(
+ "of($L)",
+ binding
+ .dependencies()
+ .stream()
+ .map(dependency -> getContributionExpression(dependency, requestingClass))
+ .collect(toParametersCodeBlock()))
+ .build());
+ }
+ switch (binding.dependencies().size()) {
+ case 0:
+ return collectionsStaticFactoryInvocation(requestingClass, CodeBlock.of("emptySet()"));
+ case 1:
+ {
+ DependencyRequest dependency = getOnlyElement(binding.dependencies());
+ CodeBlock contributionExpression = getContributionExpression(dependency, requestingClass);
+ if (isSingleValue(dependency)) {
+ return collectionsStaticFactoryInvocation(
+ requestingClass, CodeBlock.of("singleton($L)", contributionExpression));
+ } else if (isImmutableSetAvailable) {
+ return Expression.create(
+ immutableSetType(),
+ CodeBlock.builder()
+ .add("$T.", ImmutableSet.class)
+ .add(maybeTypeParameter(requestingClass))
+ .add("copyOf($L)", contributionExpression)
+ .build());
+ }
+ }
+ // fall through
+ default:
+ CodeBlock.Builder instantiation = CodeBlock.builder();
+ instantiation
+ .add("$T.", isImmutableSetAvailable ? ImmutableSet.class : SetBuilder.class)
+ .add(maybeTypeParameter(requestingClass));
+ if (isImmutableSetBuilderWithExpectedSizeAvailable()) {
+ instantiation.add("builderWithExpectedSize($L)", binding.dependencies().size());
+ } else if (isImmutableSetAvailable) {
+ instantiation.add("builder()");
+ } else {
+ instantiation.add("newSetBuilder($L)", binding.dependencies().size());
+ }
+ for (DependencyRequest dependency : binding.dependencies()) {
+ String builderMethod = isSingleValue(dependency) ? "add" : "addAll";
+ instantiation.add(
+ ".$L($L)", builderMethod, getContributionExpression(dependency, requestingClass));
+ }
+ instantiation.add(".build()");
+ return Expression.create(
+ isImmutableSetAvailable ? immutableSetType() : binding.key().type(),
+ instantiation.build());
+ }
+ }
+
+ private DeclaredType immutableSetType() {
+ return types.getDeclaredType(
+ elements.getTypeElement(ImmutableSet.class), SetType.from(binding.key()).elementType());
+ }
+
+ private CodeBlock getContributionExpression(
+ DependencyRequest dependency, ClassName requestingClass) {
+ return componentBindingExpressions
+ .getDependencyExpression(bindingRequest(dependency), requestingClass)
+ .codeBlock();
+ }
+
+ private Expression collectionsStaticFactoryInvocation(
+ ClassName requestingClass, CodeBlock methodInvocation) {
+ return Expression.create(
+ binding.key().type(),
+ CodeBlock.builder()
+ .add("$T.", Collections.class)
+ .add(maybeTypeParameter(requestingClass))
+ .add(methodInvocation)
+ .build());
+ }
+
+ private CodeBlock maybeTypeParameter(ClassName requestingClass) {
+ TypeMirror elementType = SetType.from(binding.key()).elementType();
+ return isTypeAccessibleFrom(elementType, requestingClass.packageName())
+ ? CodeBlock.of("<$T>", elementType)
+ : CodeBlock.of("");
+ }
+
+ private boolean isSingleValue(DependencyRequest dependency) {
+ return graph.contributionBinding(dependency.key())
+ .contributionType()
+ .equals(ContributionType.SET);
+ }
+
+ private boolean isImmutableSetBuilderWithExpectedSizeAvailable() {
+ if (isImmutableSetAvailable()) {
+ return methodsIn(elements.getTypeElement(ImmutableSet.class).getEnclosedElements())
+ .stream()
+ .anyMatch(method -> method.getSimpleName().contentEquals("builderWithExpectedSize"));
+ }
+ return false;
+ }
+
+ private boolean isImmutableSetAvailable() {
+ return elements.getTypeElement(ImmutableSet.class) != null;
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/SetFactoryCreationExpression.java b/java/dagger/internal/codegen/writing/SetFactoryCreationExpression.java
new file mode 100644
index 000000000..754f909ad
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/SetFactoryCreationExpression.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.binding.SourceFiles.setFactoryClassName;
+
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.base.ContributionType;
+import dagger.internal.codegen.base.SetType;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.BindingType;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.model.DependencyRequest;
+import dagger.producers.Produced;
+
+/** A factory creation expression for a multibound set. */
+final class SetFactoryCreationExpression extends MultibindingFactoryCreationExpression {
+ private final BindingGraph graph;
+ private final ContributionBinding binding;
+
+ SetFactoryCreationExpression(
+ ContributionBinding binding,
+ ComponentImplementation componentImplementation,
+ ComponentBindingExpressions componentBindingExpressions,
+ BindingGraph graph) {
+ super(binding, componentImplementation, componentBindingExpressions);
+ this.binding = checkNotNull(binding);
+ this.graph = checkNotNull(graph);
+ }
+
+ @Override
+ public CodeBlock creationExpression() {
+ CodeBlock.Builder builder = CodeBlock.builder().add("$T.", setFactoryClassName(binding));
+ if (!useRawType()) {
+ SetType setType = SetType.from(binding.key());
+ builder.add(
+ "<$T>",
+ setType.elementsAreTypeOf(Produced.class)
+ ? setType.unwrappedElementType(Produced.class)
+ : setType.elementType());
+ }
+
+ int individualProviders = 0;
+ int setProviders = 0;
+ CodeBlock.Builder builderMethodCalls = CodeBlock.builder();
+ String methodNameSuffix =
+ binding.bindingType().equals(BindingType.PROVISION) ? "Provider" : "Producer";
+
+ for (DependencyRequest dependency : binding.dependencies()) {
+ ContributionType contributionType =
+ graph.contributionBinding(dependency.key()).contributionType();
+ String methodNamePrefix;
+ switch (contributionType) {
+ case SET:
+ individualProviders++;
+ methodNamePrefix = "add";
+ break;
+ case SET_VALUES:
+ setProviders++;
+ methodNamePrefix = "addCollection";
+ break;
+ default:
+ throw new AssertionError(dependency + " is not a set multibinding");
+ }
+
+ builderMethodCalls.add(
+ ".$N$N($L)",
+ methodNamePrefix,
+ methodNameSuffix,
+ multibindingDependencyExpression(dependency));
+ }
+ builder.add("builder($L, $L)", individualProviders, setProviders);
+ builder.add(builderMethodCalls.build());
+
+ return builder.add(".build()").build();
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/SimpleInvocationBindingExpression.java b/java/dagger/internal/codegen/writing/SimpleInvocationBindingExpression.java
new file mode 100644
index 000000000..f2062f4e8
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/SimpleInvocationBindingExpression.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import dagger.internal.codegen.binding.ContributionBinding;
+
+/** A simple binding expression for instance requests. Does not scope. */
+abstract class SimpleInvocationBindingExpression extends BindingExpression {
+ private final ContributionBinding binding;
+
+ SimpleInvocationBindingExpression(ContributionBinding binding) {
+ this.binding = checkNotNull(binding);
+ }
+
+ @Override
+ boolean requiresMethodEncapsulation() {
+ return !binding.dependencies().isEmpty();
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/SimpleMethodBindingExpression.java b/java/dagger/internal/codegen/writing/SimpleMethodBindingExpression.java
new file mode 100644
index 000000000..82e662857
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/SimpleMethodBindingExpression.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.auto.common.MoreElements.asExecutable;
+import static com.google.auto.common.MoreElements.asType;
+import static com.google.common.base.Preconditions.checkArgument;
+import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.rawTypeName;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+import static dagger.internal.codegen.writing.InjectionMethods.ProvisionMethod.requiresInjectionMethod;
+
+import com.google.auto.common.MoreTypes;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.binding.ComponentRequirement;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.writing.InjectionMethods.ProvisionMethod;
+import dagger.model.DependencyRequest;
+import java.util.Optional;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A binding expression that invokes methods or constructors directly (without attempting to scope)
+ * {@link dagger.model.RequestKind#INSTANCE} requests.
+ */
+final class SimpleMethodBindingExpression extends SimpleInvocationBindingExpression {
+ private final CompilerOptions compilerOptions;
+ private final ProvisionBinding provisionBinding;
+ private final ComponentBindingExpressions componentBindingExpressions;
+ private final MembersInjectionMethods membersInjectionMethods;
+ private final ComponentRequirementExpressions componentRequirementExpressions;
+ private final DaggerElements elements;
+ private final SourceVersion sourceVersion;
+ private final KotlinMetadataUtil metadataUtil;
+
+ SimpleMethodBindingExpression(
+ ProvisionBinding binding,
+ CompilerOptions compilerOptions,
+ ComponentBindingExpressions componentBindingExpressions,
+ MembersInjectionMethods membersInjectionMethods,
+ ComponentRequirementExpressions componentRequirementExpressions,
+ DaggerElements elements,
+ SourceVersion sourceVersion,
+ KotlinMetadataUtil metadataUtil) {
+ super(binding);
+ this.compilerOptions = compilerOptions;
+ this.provisionBinding = binding;
+ this.metadataUtil = metadataUtil;
+ checkArgument(
+ provisionBinding.implicitDependencies().isEmpty(),
+ "framework deps are not currently supported");
+ checkArgument(provisionBinding.bindingElement().isPresent());
+ this.componentBindingExpressions = componentBindingExpressions;
+ this.membersInjectionMethods = membersInjectionMethods;
+ this.componentRequirementExpressions = componentRequirementExpressions;
+ this.elements = elements;
+ this.sourceVersion = sourceVersion;
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ return requiresInjectionMethod(provisionBinding, compilerOptions, requestingClass)
+ ? invokeInjectionMethod(requestingClass)
+ : invokeMethod(requestingClass);
+ }
+
+ private Expression invokeMethod(ClassName requestingClass) {
+ // TODO(dpb): align this with the contents of InlineMethods.create
+ CodeBlock arguments =
+ makeParametersCodeBlock(
+ ProvisionMethod.invokeArguments(
+ provisionBinding,
+ request -> dependencyArgument(request, requestingClass).codeBlock(),
+ requestingClass));
+ ExecutableElement method = asExecutable(provisionBinding.bindingElement().get());
+ CodeBlock invocation;
+ switch (method.getKind()) {
+ case CONSTRUCTOR:
+ invocation = CodeBlock.of("new $T($L)", constructorTypeName(requestingClass), arguments);
+ break;
+ case METHOD:
+ CodeBlock module;
+ Optional<CodeBlock> requiredModuleInstance = moduleReference(requestingClass);
+ if (requiredModuleInstance.isPresent()) {
+ module = requiredModuleInstance.get();
+ } else if (metadataUtil.isObjectClass(asType(method.getEnclosingElement()))) {
+ // Call through the singleton instance.
+ // See: https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#static-methods
+ module = CodeBlock.of("$T.INSTANCE", provisionBinding.bindingTypeElement().get());
+ } else {
+ module = CodeBlock.of("$T", provisionBinding.bindingTypeElement().get());
+ }
+ invocation = CodeBlock.of("$L.$L($L)", module, method.getSimpleName(), arguments);
+ break;
+ default:
+ throw new IllegalStateException();
+ }
+
+ return Expression.create(simpleMethodReturnType(), invocation);
+ }
+
+ private TypeName constructorTypeName(ClassName requestingClass) {
+ DeclaredType type = MoreTypes.asDeclared(provisionBinding.key().type());
+ TypeName typeName = TypeName.get(type);
+ if (type.getTypeArguments().stream()
+ .allMatch(t -> isTypeAccessibleFrom(t, requestingClass.packageName()))) {
+ return typeName;
+ }
+ return rawTypeName(typeName);
+ }
+
+ private Expression invokeInjectionMethod(ClassName requestingClass) {
+ return injectMembers(
+ ProvisionMethod.invoke(
+ provisionBinding,
+ request -> dependencyArgument(request, requestingClass).codeBlock(),
+ requestingClass,
+ moduleReference(requestingClass),
+ compilerOptions,
+ metadataUtil));
+ }
+
+ private Expression dependencyArgument(DependencyRequest dependency, ClassName requestingClass) {
+ return componentBindingExpressions.getDependencyArgumentExpression(dependency, requestingClass);
+ }
+
+ private Expression injectMembers(CodeBlock instance) {
+ if (provisionBinding.injectionSites().isEmpty()) {
+ return Expression.create(simpleMethodReturnType(), instance);
+ }
+ if (sourceVersion.compareTo(SourceVersion.RELEASE_7) <= 0) {
+ // Java 7 type inference can't figure out that instance in
+ // injectParameterized(Parameterized_Factory.newParameterized()) is Parameterized<T> and not
+ // Parameterized<Object>
+ if (!MoreTypes.asDeclared(provisionBinding.key().type()).getTypeArguments().isEmpty()) {
+ TypeName keyType = TypeName.get(provisionBinding.key().type());
+ instance = CodeBlock.of("($T) ($T) $L", keyType, rawTypeName(keyType), instance);
+ }
+ }
+ MethodSpec membersInjectionMethod = membersInjectionMethods.getOrCreate(provisionBinding.key());
+ TypeMirror returnType =
+ membersInjectionMethod.returnType.equals(TypeName.OBJECT)
+ ? elements.getTypeElement(Object.class).asType()
+ : provisionBinding.key().type();
+ return Expression.create(returnType, CodeBlock.of("$N($L)", membersInjectionMethod, instance));
+ }
+
+ private Optional<CodeBlock> moduleReference(ClassName requestingClass) {
+ return provisionBinding.requiresModuleInstance()
+ ? provisionBinding
+ .contributingModule()
+ .map(Element::asType)
+ .map(ComponentRequirement::forModule)
+ .map(module -> componentRequirementExpressions.getExpression(module, requestingClass))
+ : Optional.empty();
+ }
+
+ private TypeMirror simpleMethodReturnType() {
+ return provisionBinding.contributedPrimitiveType().orElse(provisionBinding.key().type());
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/SubcomponentCreatorBindingExpression.java b/java/dagger/internal/codegen/writing/SubcomponentCreatorBindingExpression.java
new file mode 100644
index 000000000..3099048e6
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/SubcomponentCreatorBindingExpression.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.javapoet.Expression;
+import javax.lang.model.type.TypeMirror;
+
+/** A binding expression for a subcomponent creator that just invokes the constructor. */
+final class SubcomponentCreatorBindingExpression extends SimpleInvocationBindingExpression {
+ private final TypeMirror creatorType;
+ private final String creatorImplementationName;
+
+ SubcomponentCreatorBindingExpression(
+ ContributionBinding binding, String creatorImplementationName) {
+ super(binding);
+ this.creatorType = binding.key().type();
+ this.creatorImplementationName = creatorImplementationName;
+ }
+
+ @Override
+ Expression getDependencyExpression(ClassName requestingClass) {
+ return Expression.create(creatorType, "new $L()", creatorImplementationName);
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/SubcomponentNames.java b/java/dagger/internal/codegen/writing/SubcomponentNames.java
new file mode 100644
index 000000000..fa0037b67
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/SubcomponentNames.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
+import static java.lang.Character.isUpperCase;
+import static java.lang.String.format;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimaps;
+import dagger.internal.codegen.base.UniqueNameSet;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.ComponentCreatorDescriptor;
+import dagger.internal.codegen.binding.ComponentDescriptor;
+import dagger.internal.codegen.binding.KeyFactory;
+import dagger.model.Key;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * Holds the unique simple names for all subcomponents, keyed by their {@link ComponentDescriptor}
+ * and {@link Key} of the subcomponent builder.
+ */
+public final class SubcomponentNames {
+ private static final Splitter QUALIFIED_NAME_SPLITTER = Splitter.on('.');
+
+ private final ImmutableMap<ComponentDescriptor, String> namesByDescriptor;
+ private final ImmutableMap<Key, ComponentDescriptor> descriptorsByCreatorKey;
+
+ public SubcomponentNames(BindingGraph graph, KeyFactory keyFactory) {
+ this.namesByDescriptor = namesByDescriptor(graph);
+ this.descriptorsByCreatorKey = descriptorsByCreatorKey(keyFactory, namesByDescriptor.keySet());
+ }
+
+ /** Returns the simple component name for the given {@link ComponentDescriptor}. */
+ String get(ComponentDescriptor componentDescriptor) {
+ return namesByDescriptor.get(componentDescriptor);
+ }
+
+ /**
+ * Returns the simple name for the subcomponent creator implementation with the given {@link Key}.
+ */
+ String getCreatorName(Key key) {
+ return getCreatorName(descriptorsByCreatorKey.get(key));
+ }
+
+ /**
+ * Returns the simple name for the subcomponent creator implementation for the given {@link
+ * ComponentDescriptor}.
+ */
+ String getCreatorName(ComponentDescriptor componentDescriptor) {
+ checkArgument(componentDescriptor.creatorDescriptor().isPresent());
+ ComponentCreatorDescriptor creatorDescriptor = componentDescriptor.creatorDescriptor().get();
+ return get(componentDescriptor) + creatorDescriptor.kind().typeName();
+ }
+
+ private static ImmutableMap<ComponentDescriptor, String> namesByDescriptor(BindingGraph graph) {
+ ImmutableListMultimap<String, ComponentDescriptor> componentDescriptorsBySimpleName =
+ Multimaps.index(graph.componentDescriptors(), SubcomponentNames::simpleName);
+ Map<ComponentDescriptor, String> subcomponentImplSimpleNames = new LinkedHashMap<>();
+ componentDescriptorsBySimpleName
+ .asMap()
+ .values()
+ .stream()
+ .map(SubcomponentNames::disambiguateConflictingSimpleNames)
+ .forEach(subcomponentImplSimpleNames::putAll);
+ subcomponentImplSimpleNames.remove(graph.componentDescriptor());
+ return ImmutableMap.copyOf(subcomponentImplSimpleNames);
+ }
+
+ private static ImmutableMap<Key, ComponentDescriptor> descriptorsByCreatorKey(
+ KeyFactory keyFactory, ImmutableSet<ComponentDescriptor> subcomponents) {
+ return subcomponents.stream()
+ .filter(subcomponent -> subcomponent.creatorDescriptor().isPresent())
+ .collect(
+ toImmutableMap(
+ subcomponent ->
+ keyFactory.forSubcomponentCreator(
+ subcomponent.creatorDescriptor().get().typeElement().asType()),
+ subcomponent -> subcomponent));
+ }
+
+ private static ImmutableMap<ComponentDescriptor, String> disambiguateConflictingSimpleNames(
+ Collection<ComponentDescriptor> componentsWithConflictingNames) {
+ // If there's only 1 component there's nothing to disambiguate so return the simple name.
+ if (componentsWithConflictingNames.size() == 1) {
+ ComponentDescriptor component = Iterables.getOnlyElement(componentsWithConflictingNames);
+ return ImmutableMap.of(component, simpleName(component));
+ }
+
+ // There are conflicting simple names, so disambiguate them with a unique prefix.
+ // We keep them small to fix https://github.com/google/dagger/issues/421.
+ UniqueNameSet nameSet = new UniqueNameSet();
+ ImmutableMap.Builder<ComponentDescriptor, String> uniqueNames = ImmutableMap.builder();
+ for (ComponentDescriptor component : componentsWithConflictingNames) {
+ String simpleName = simpleName(component);
+ String basePrefix = uniquingPrefix(component);
+ uniqueNames.put(component, format("%s_%s", nameSet.getUniqueName(basePrefix), simpleName));
+ }
+ return uniqueNames.build();
+ }
+
+ private static String simpleName(ComponentDescriptor component) {
+ return component.typeElement().getSimpleName().toString();
+ }
+
+ /** Returns a prefix that could make the component's simple name more unique. */
+ private static String uniquingPrefix(ComponentDescriptor component) {
+ TypeElement typeElement = component.typeElement();
+ String containerName = typeElement.getEnclosingElement().getSimpleName().toString();
+
+ // If parent element looks like a class, use its initials as a prefix.
+ if (!containerName.isEmpty() && isUpperCase(containerName.charAt(0))) {
+ return CharMatcher.javaLowerCase().removeFrom(containerName);
+ }
+
+ // Not in a normally named class. Prefix with the initials of the elements leading here.
+ Name qualifiedName = typeElement.getQualifiedName();
+ Iterator<String> pieces = QUALIFIED_NAME_SPLITTER.split(qualifiedName).iterator();
+ StringBuilder b = new StringBuilder();
+
+ while (pieces.hasNext()) {
+ String next = pieces.next();
+ if (pieces.hasNext()) {
+ b.append(next.charAt(0));
+ }
+ }
+
+ // Note that a top level class in the root package will be prefixed "$_".
+ return b.length() > 0 ? b.toString() : "$";
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/SwitchingProviders.java b/java/dagger/internal/codegen/writing/SwitchingProviders.java
new file mode 100644
index 000000000..d38b9d957
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/SwitchingProviders.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.getLast;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings;
+import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.TypeVariableName;
+import dagger.internal.codegen.base.UniqueNameSet;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.model.Key;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Keeps track of all provider expression requests for a component.
+ *
+ * <p>The provider expression request will be satisfied by a single generated {@code Provider} inner
+ * class that can provide instances for all types by switching on an id.
+ */
+// TODO(ronshapiro): either merge this with InnerSwitchingProviders, or repurpose this for
+// SwitchingProducers
+abstract class SwitchingProviders {
+ /**
+ * Defines the {@linkplain Expression expressions} for a switch case in a {@code SwitchProvider}
+ * for a particular binding.
+ */
+ // TODO(bcorso): Consider handling SwitchingProviders with dependency arguments in this class,
+ // then we wouldn't need the getProviderExpression method.
+ // TODO(bcorso): Consider making this an abstract class with equals/hashCode defined by the key
+ // and then using this class directly in Map types instead of Key.
+ interface SwitchCase {
+ /** Returns the {@link Key} for this switch case. */
+ Key key();
+
+ /** Returns the {@link Expression} that returns the provided instance for this case. */
+ Expression getReturnExpression(ClassName switchingProviderClass);
+
+ /**
+ * Returns the {@link Expression} that returns the {@code SwitchProvider} instance for this
+ * case.
+ */
+ Expression getProviderExpression(ClassName switchingProviderClass, int switchId);
+ }
+
+ /**
+ * Each switch size is fixed at 100 cases each and put in its own method. This is to limit the
+ * size of the methods so that we don't reach the "huge" method size limit for Android that will
+ * prevent it from being AOT compiled in some versions of Android (b/77652521). This generally
+ * starts to happen around 1500 cases, but we are choosing 100 to be safe.
+ */
+ // TODO(bcorso): Include a proguard_spec in the Dagger library to prevent inlining these methods?
+ // TODO(ronshapiro): Consider making this configurable via a flag.
+ private static final int MAX_CASES_PER_SWITCH = 100;
+
+ private static final long MAX_CASES_PER_CLASS = MAX_CASES_PER_SWITCH * MAX_CASES_PER_SWITCH;
+ private static final TypeVariableName T = TypeVariableName.get("T");
+
+ /**
+ * Maps a {@link Key} to an instance of a {@link SwitchingProviderBuilder}. Each group of {@code
+ * MAX_CASES_PER_CLASS} keys will share the same instance.
+ */
+ private final Map<Key, SwitchingProviderBuilder> switchingProviderBuilders =
+ new LinkedHashMap<>();
+
+ private final ComponentImplementation componentImplementation;
+ private final ClassName owningComponent;
+ private final DaggerTypes types;
+ private final UniqueNameSet switchingProviderNames = new UniqueNameSet();
+
+ SwitchingProviders(ComponentImplementation componentImplementation, DaggerTypes types) {
+ this.componentImplementation = checkNotNull(componentImplementation);
+ this.types = checkNotNull(types);
+ this.owningComponent = checkNotNull(componentImplementation).name();
+ }
+
+ /** Returns the {@link TypeSpec} for a {@code SwitchingProvider} based on the given builder. */
+ protected abstract TypeSpec createSwitchingProviderType(TypeSpec.Builder builder);
+
+ /**
+ * Returns the {@link Expression} that returns the {@code SwitchProvider} instance for the case.
+ */
+ protected final Expression getProviderExpression(SwitchCase switchCase) {
+ return switchingProviderBuilders
+ .computeIfAbsent(switchCase.key(), key -> getSwitchingProviderBuilder())
+ .getProviderExpression(switchCase);
+ }
+
+ private SwitchingProviderBuilder getSwitchingProviderBuilder() {
+ if (switchingProviderBuilders.size() % MAX_CASES_PER_CLASS == 0) {
+ String name = switchingProviderNames.getUniqueName("SwitchingProvider");
+ SwitchingProviderBuilder switchingProviderBuilder =
+ new SwitchingProviderBuilder(owningComponent.nestedClass(name));
+ componentImplementation.addTypeSupplier(switchingProviderBuilder::build);
+ return switchingProviderBuilder;
+ }
+ return getLast(switchingProviderBuilders.values());
+ }
+
+ // TODO(bcorso): Consider just merging this class with SwitchingProviders.
+ private final class SwitchingProviderBuilder {
+ // Keep the switch cases ordered by switch id. The switch Ids are assigned in pre-order
+ // traversal, but the switch cases are assigned in post-order traversal of the binding graph.
+ private final Map<Integer, CodeBlock> switchCases = new TreeMap<>();
+ private final Map<Key, Integer> switchIds = new HashMap<>();
+ private final ClassName switchingProviderType;
+
+ SwitchingProviderBuilder(ClassName switchingProviderType) {
+ this.switchingProviderType = checkNotNull(switchingProviderType);
+ }
+
+ Expression getProviderExpression(SwitchCase switchCase) {
+ Key key = switchCase.key();
+ if (!switchIds.containsKey(key)) {
+ int switchId = switchIds.size();
+ switchIds.put(key, switchId);
+ switchCases.put(switchId, createSwitchCaseCodeBlock(switchCase));
+ }
+ return switchCase.getProviderExpression(switchingProviderType, switchIds.get(key));
+ }
+
+ private CodeBlock createSwitchCaseCodeBlock(SwitchCase switchCase) {
+ CodeBlock instanceCodeBlock =
+ switchCase.getReturnExpression(switchingProviderType).box(types).codeBlock();
+
+ return CodeBlock.builder()
+ // TODO(bcorso): Is there something else more useful than the key?
+ .add("case $L: // $L \n", switchIds.get(switchCase.key()), switchCase.key())
+ .addStatement("return ($T) $L", T, instanceCodeBlock)
+ .build();
+ }
+
+ private TypeSpec build() {
+ return createSwitchingProviderType(
+ classBuilder(switchingProviderType)
+ .addTypeVariable(T)
+ .addSuperinterface(providerOf(T))
+ .addMethods(getMethods()));
+ }
+
+ private ImmutableList<MethodSpec> getMethods() {
+ ImmutableList<CodeBlock> switchCodeBlockPartitions = switchCodeBlockPartitions();
+ if (switchCodeBlockPartitions.size() == 1) {
+ // There are less than MAX_CASES_PER_SWITCH cases, so no need for extra get methods.
+ return ImmutableList.of(
+ methodBuilder("get")
+ .addModifiers(PUBLIC)
+ .addAnnotation(suppressWarnings(UNCHECKED))
+ .addAnnotation(Override.class)
+ .returns(T)
+ .addCode(getOnlyElement(switchCodeBlockPartitions))
+ .build());
+ }
+
+ // This is the main public "get" method that will route to private getter methods.
+ MethodSpec.Builder routerMethod =
+ methodBuilder("get")
+ .addModifiers(PUBLIC)
+ .addAnnotation(Override.class)
+ .returns(T)
+ .beginControlFlow("switch (id / $L)", MAX_CASES_PER_SWITCH);
+
+ ImmutableList.Builder<MethodSpec> getMethods = ImmutableList.builder();
+ for (int i = 0; i < switchCodeBlockPartitions.size(); i++) {
+ MethodSpec method =
+ methodBuilder("get" + i)
+ .addModifiers(PRIVATE)
+ .addAnnotation(suppressWarnings(UNCHECKED))
+ .returns(T)
+ .addCode(switchCodeBlockPartitions.get(i))
+ .build();
+ getMethods.add(method);
+ routerMethod.addStatement("case $L: return $N()", i, method);
+ }
+
+ routerMethod.addStatement("default: throw new $T(id)", AssertionError.class).endControlFlow();
+
+ return getMethods.add(routerMethod.build()).build();
+ }
+
+ private ImmutableList<CodeBlock> switchCodeBlockPartitions() {
+ return Lists.partition(ImmutableList.copyOf(switchCases.values()), MAX_CASES_PER_SWITCH)
+ .stream()
+ .map(
+ partitionCases ->
+ CodeBlock.builder()
+ .beginControlFlow("switch (id)")
+ .add(CodeBlocks.concat(partitionCases))
+ .addStatement("default: throw new $T(id)", AssertionError.class)
+ .endControlFlow()
+ .build())
+ .collect(toImmutableList());
+ }
+ }
+}
diff --git a/java/dagger/internal/codegen/writing/TopLevel.java b/java/dagger/internal/codegen/writing/TopLevel.java
new file mode 100644
index 000000000..ce7190745
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/TopLevel.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/**
+ * A {@link Qualifier} for bindings that are associated with the top level component implementation.
+ */
+@Retention(RUNTIME)
+@Qualifier
+public @interface TopLevel {}
diff --git a/java/dagger/internal/codegen/writing/UnwrappedMapKeyGenerator.java b/java/dagger/internal/codegen/writing/UnwrappedMapKeyGenerator.java
new file mode 100644
index 000000000..f07b882b2
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/UnwrappedMapKeyGenerator.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen.writing;
+
+import dagger.MapKey;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import java.util.Set;
+import javax.annotation.processing.Filer;
+import javax.inject.Inject;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * Generates classes that create annotation instances for an unwrapped {@link MapKey} annotation
+ * type whose nested value is an annotation. The generated class will have a private empty
+ * constructor and a static method that creates each annotation type that is nested in the top-level
+ * annotation type.
+ *
+ * <p>So for an example {@link MapKey} annotation:
+ *
+ * <pre>
+ * {@literal @MapKey}(unwrapValue = true)
+ * {@literal @interface} Foo {
+ * Bar bar();
+ * }
+ *
+ * {@literal @interface} Bar {
+ * {@literal Class<?> baz();}
+ * }
+ * </pre>
+ *
+ * the generated class will look like:
+ *
+ * <pre>
+ * public final class FooCreator {
+ * private FooCreator() {}
+ *
+ * public static Bar createBar({@literal Class<?> baz}) { … }
+ * }
+ * </pre>
+ */
+public final class UnwrappedMapKeyGenerator extends AnnotationCreatorGenerator {
+
+ @Inject
+ UnwrappedMapKeyGenerator(Filer filer, DaggerElements elements, SourceVersion sourceVersion) {
+ super(filer, elements, sourceVersion);
+ }
+
+ @Override
+ protected Set<TypeElement> annotationsToCreate(TypeElement annotationElement) {
+ Set<TypeElement> nestedAnnotationElements = super.annotationsToCreate(annotationElement);
+ nestedAnnotationElements.remove(annotationElement);
+ return nestedAnnotationElements;
+ }
+}
diff --git a/java/dagger/internal/guava/BUILD b/java/dagger/internal/guava/BUILD
new file mode 100644
index 000000000..0bd4dcc60
--- /dev/null
+++ b/java/dagger/internal/guava/BUILD
@@ -0,0 +1,68 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Aliases from guava libraries to //third_party/java/guava.
+
+package(default_visibility = ["//:src"])
+
+alias(
+ name = "annotations",
+ actual = "@google_bazel_common//third_party/java/guava",
+)
+
+alias(
+ name = "base",
+ actual = "@google_bazel_common//third_party/java/guava",
+)
+
+alias(
+ name = "cache",
+ actual = "@google_bazel_common//third_party/java/guava",
+)
+
+alias(
+ name = "collect",
+ actual = "@google_bazel_common//third_party/java/guava",
+)
+
+alias(
+ name = "graph",
+ actual = "@google_bazel_common//third_party/java/guava",
+)
+
+alias(
+ name = "io",
+ actual = "@google_bazel_common//third_party/java/guava",
+)
+
+alias(
+ name = "concurrent",
+ actual = "@google_bazel_common//third_party/java/guava",
+)
+
+alias(
+ name = "base-android",
+ actual = "@maven//:com_google_guava_guava",
+)
+
+alias(
+ name = "collect-android",
+ actual = "@maven//:com_google_guava_guava",
+)
+
+alias(
+ name = "concurrent-android",
+ actual = "@maven//:com_google_guava_guava",
+)
diff --git a/java/dagger/lint/AndroidManifest.xml b/java/dagger/lint/AndroidManifest.xml
new file mode 100644
index 000000000..f75cee158
--- /dev/null
+++ b/java/dagger/lint/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2020 The Dagger Authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.lint">
+ <uses-sdk android:minSdkVersion="14" />
+</manifest>
diff --git a/java/dagger/lint/BUILD b/java/dagger/lint/BUILD
new file mode 100644
index 000000000..3e0ffec2e
--- /dev/null
+++ b/java/dagger/lint/BUILD
@@ -0,0 +1,85 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Dagger Lint Rules
+
+load("//:build_defs.bzl", "POM_VERSION")
+load("//tools:maven.bzl", "gen_maven_artifact")
+load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_library")
+
+package(default_visibility = ["//:src"])
+
+kt_jvm_library(
+ name = "lint-artifact-lib",
+ srcs = glob(["*.kt"]),
+ tags = ["maven_coordinates=com.google.dagger:dagger-lint:" + POM_VERSION],
+ deps = [
+ "@google_bazel_common//third_party/java/auto:service",
+ "@maven//:com_android_tools_external_com_intellij_intellij_core",
+ "@maven//:com_android_tools_external_com_intellij_kotlin_compiler",
+ "@maven//:com_android_tools_external_org_jetbrains_uast",
+ "@maven//:com_android_tools_lint_lint",
+ "@maven//:com_android_tools_lint_lint_api",
+ ],
+)
+
+# Current `kt_jvm_library` does not output source jars and gen_maven_artifact expects one.
+# See: https://github.com/bazelbuild/rules_kotlin/issues/324
+genrule(
+ name = "dagger-lint-sources",
+ srcs = glob(["*.kt"]),
+ outs = ["liblint-artifact-lib-src.jar"],
+ cmd = """
+ TEMP="$$(mktemp -d)"
+ for file in $(SRCS); do
+ filename="$$TEMP/$${file#java/}"
+ mkdir -p `dirname $$filename` && cp $$file $$filename
+ done
+ jar cf $@ -C $$TEMP .
+ """,
+)
+
+gen_maven_artifact(
+ name = "lint-artifact",
+ artifact_coordinates = "com.google.dagger:dagger-lint:" + POM_VERSION,
+ artifact_name = "Dagger Lint Rules",
+ artifact_target = ":lint-artifact-lib",
+ artifact_target_maven_deps = [
+ "com.android.tools.external.com-intellij:intellij-core",
+ "com.android.tools.external.com-intellij:kotlin-compiler",
+ "com.android.tools.external.org-jetbrains:uast",
+ "com.android.tools.lint:lint",
+ "com.android.tools.lint:lint-api",
+ ],
+ pom_name = "lint-pom",
+)
+
+# An empty android artifact to distribute and share the Dagger lint rules for
+# the Android sub-projects.
+android_library(
+ name = "lint-android-artifact-lib",
+ tags = ["maven_coordinates=com.google.dagger:dagger-lint-aar:" + POM_VERSION],
+)
+
+gen_maven_artifact(
+ name = "lint-android-artifact",
+ artifact_coordinates = "com.google.dagger:dagger-lint-aar:" + POM_VERSION,
+ artifact_name = "Dagger Lint Rules AAR Distribution",
+ artifact_target = ":lint-android-artifact-lib",
+ lint_deps = [":lint-artifact-lib"],
+ manifest = "AndroidManifest.xml",
+ packaging = "aar",
+ pom_name = "lint-android-pom",
+)
diff --git a/java/dagger/lint/DaggerIssueRegistry.kt b/java/dagger/lint/DaggerIssueRegistry.kt
new file mode 100644
index 000000000..113e85c01
--- /dev/null
+++ b/java/dagger/lint/DaggerIssueRegistry.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.lint
+
+import com.android.tools.lint.client.api.IssueRegistry
+import com.android.tools.lint.detector.api.CURRENT_API
+import com.android.tools.lint.detector.api.Issue
+import com.google.auto.service.AutoService
+
+/**
+ * Dagger Lint Issues Registry.
+ *
+ * A META-INF/services entry is added for this class that Lint will discover and call into for
+ * detecting issues.
+ */
+@AutoService(IssueRegistry::class)
+@Suppress("unused", "UnstableApiUsage")
+class DaggerIssueRegistry : IssueRegistry() {
+ // The minApi is set to the Api this registry was compiled with, if a user has an older Api, Lint
+ // will show a warning asking users to upgrade.
+ override val minApi: Int = CURRENT_API
+ // The api is meant to be the current api for which this registry was compiled, but we set a
+ // higher number without depending on a newer Lint to avoid Lint warning users of custom checks
+ // that might not work. This value eventually has to be updated as newer Api become available.
+ override val api: Int = 8
+ override val issues: List<Issue> = DaggerKotlinIssueDetector.issues
+}
diff --git a/java/dagger/lint/DaggerKotlinIssueDetector.kt b/java/dagger/lint/DaggerKotlinIssueDetector.kt
new file mode 100644
index 000000000..f3fdbd318
--- /dev/null
+++ b/java/dagger/lint/DaggerKotlinIssueDetector.kt
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.lint
+
+import com.android.tools.lint.client.api.JavaEvaluator
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.LintFix
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.android.tools.lint.detector.api.TextFormat
+import com.android.tools.lint.detector.api.isKotlin
+import dagger.lint.DaggerKotlinIssueDetector.Companion.ISSUE_FIELD_SITE_TARGET_ON_QUALIFIER_ANNOTATION
+import dagger.lint.DaggerKotlinIssueDetector.Companion.ISSUE_JVM_STATIC_PROVIDES_IN_OBJECT
+import dagger.lint.DaggerKotlinIssueDetector.Companion.ISSUE_MODULE_COMPANION_OBJECTS
+import dagger.lint.DaggerKotlinIssueDetector.Companion.ISSUE_MODULE_COMPANION_OBJECTS_NOT_IN_MODULE_PARENT
+import java.util.EnumSet
+import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.KtAnnotationEntry
+import org.jetbrains.kotlin.psi.KtObjectDeclaration
+import org.jetbrains.uast.UClass
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.UField
+import org.jetbrains.uast.UMethod
+import org.jetbrains.uast.getUastParentOfType
+import org.jetbrains.uast.kotlin.KotlinUClass
+import org.jetbrains.uast.toUElement
+
+/**
+ * This is a simple lint check to catch common Dagger+Kotlin usage issues.
+ *
+ * - [ISSUE_FIELD_SITE_TARGET_ON_QUALIFIER_ANNOTATION] covers using `field:` site targets for member
+ * injections, which are redundant as of Dagger 2.25.
+ * - [ISSUE_JVM_STATIC_PROVIDES_IN_OBJECT] covers using `@JvmStatic` for object
+ * `@Provides`-annotated functions, which are redundant as of Dagger 2.25. @JvmStatic on companion
+ * object functions are redundant as of Dagger 2.26.
+ * - [ISSUE_MODULE_COMPANION_OBJECTS] covers annotating companion objects with `@Module`, as they
+ * are now part of the enclosing module class's API in Dagger 2.26. This will also error if the
+ * enclosing class is _not_ in a `@Module`-annotated class, as this object just should be moved to a
+ * top-level object to avoid confusion.
+ * - [ISSUE_MODULE_COMPANION_OBJECTS_NOT_IN_MODULE_PARENT] covers annotating companion objects with
+ * `@Module` when the parent class is _not_ also annotated with `@Module`. While technically legal,
+ * these should be moved up to top-level objects to avoid confusion.
+ */
+@Suppress("UnstableApiUsage") // Lots of Lint APIs are marked with @Beta.
+class DaggerKotlinIssueDetector : Detector(), SourceCodeScanner {
+
+ companion object {
+ // We use the overloaded constructor that takes a varargs of `Scope` as the last param.
+ // This is to enable on-the-fly IDE checks. We are telling lint to run on both
+ // JAVA and TEST_SOURCES in the `scope` parameter but by providing the `analysisScopes`
+ // params, we're indicating that this check can run on either JAVA or TEST_SOURCES and
+ // doesn't require both of them together.
+ // From discussion on lint-dev https://groups.google.com/d/msg/lint-dev/ULQMzW1ZlP0/1dG4Vj3-AQAJ
+ // This was supposed to be fixed in AS 3.4 but still required as recently as 3.6.
+ private val SCOPES = Implementation(
+ DaggerKotlinIssueDetector::class.java,
+ EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES),
+ EnumSet.of(Scope.JAVA_FILE),
+ EnumSet.of(Scope.TEST_SOURCES)
+ )
+
+ private val ISSUE_JVM_STATIC_PROVIDES_IN_OBJECT: Issue = Issue.create(
+ id = "JvmStaticProvidesInObjectDetector",
+ briefDescription = "@JvmStatic used for @Provides function in an object class",
+ explanation =
+ """
+ It's redundant to annotate @Provides functions in object classes with @JvmStatic.
+ """,
+ category = Category.CORRECTNESS,
+ priority = 5,
+ severity = Severity.WARNING,
+ implementation = SCOPES
+ )
+
+ private val ISSUE_FIELD_SITE_TARGET_ON_QUALIFIER_ANNOTATION: Issue = Issue.create(
+ id = "FieldSiteTargetOnQualifierAnnotation",
+ briefDescription = "Redundant 'field:' used for Dagger qualifier annotation.",
+ explanation =
+ """
+ It's redundant to use 'field:' site-targets for qualifier annotations.
+ """,
+ category = Category.CORRECTNESS,
+ priority = 5,
+ severity = Severity.WARNING,
+ implementation = SCOPES
+ )
+
+ private val ISSUE_MODULE_COMPANION_OBJECTS: Issue = Issue.create(
+ id = "ModuleCompanionObjects",
+ briefDescription = "Module companion objects should not be annotated with @Module.",
+ explanation =
+ """
+ Companion objects in @Module-annotated classes are considered part of the API.
+ """,
+ category = Category.CORRECTNESS,
+ priority = 5,
+ severity = Severity.WARNING,
+ implementation = SCOPES
+ )
+
+ private val ISSUE_MODULE_COMPANION_OBJECTS_NOT_IN_MODULE_PARENT: Issue = Issue.create(
+ id = "ModuleCompanionObjectsNotInModuleParent",
+ briefDescription = "Companion objects should not be annotated with @Module.",
+ explanation =
+ """
+ Companion objects in @Module-annotated classes are considered part of the API. This
+ companion object is not a companion to an @Module-annotated class though, and should be
+ moved to a top-level object declaration instead otherwise Dagger will ignore companion
+ object.
+ """,
+ category = Category.CORRECTNESS,
+ priority = 5,
+ severity = Severity.WARNING,
+ implementation = SCOPES
+ )
+
+ private const val PROVIDES_ANNOTATION = "dagger.Provides"
+ private const val JVM_STATIC_ANNOTATION = "kotlin.jvm.JvmStatic"
+ private const val INJECT_ANNOTATION = "javax.inject.Inject"
+ private const val QUALIFIER_ANNOTATION = "javax.inject.Qualifier"
+ private const val MODULE_ANNOTATION = "dagger.Module"
+
+ val issues: List<Issue> = listOf(
+ ISSUE_JVM_STATIC_PROVIDES_IN_OBJECT,
+ ISSUE_FIELD_SITE_TARGET_ON_QUALIFIER_ANNOTATION,
+ ISSUE_MODULE_COMPANION_OBJECTS,
+ ISSUE_MODULE_COMPANION_OBJECTS_NOT_IN_MODULE_PARENT
+ )
+ }
+
+ override fun getApplicableUastTypes(): List<Class<out UElement>>? {
+ return listOf(UMethod::class.java, UField::class.java, UClass::class.java)
+ }
+
+ override fun createUastHandler(context: JavaContext): UElementHandler? {
+ if (!isKotlin(context.psiFile)) {
+ // This is only relevant for Kotlin files.
+ return null
+ }
+ return object : UElementHandler() {
+ override fun visitField(node: UField) {
+ if (!context.evaluator.isLateInit(node)) {
+ return
+ }
+ // Can't use hasAnnotation because it doesn't capture all annotations!
+ val injectAnnotation =
+ node.annotations.find { it.qualifiedName == INJECT_ANNOTATION } ?: return
+ // Look for qualifier annotations
+ node.annotations.forEach { annotation ->
+ if (annotation === injectAnnotation) {
+ // Skip the inject annotation
+ return@forEach
+ }
+ // Check if it's a FIELD site target
+ val sourcePsi = annotation.sourcePsi
+ if (sourcePsi is KtAnnotationEntry &&
+ sourcePsi.useSiteTarget?.getAnnotationUseSiteTarget() == AnnotationUseSiteTarget.FIELD
+ ) {
+ // Check if this annotation is a qualifier annotation
+ if (annotation.resolve()?.hasAnnotation(QUALIFIER_ANNOTATION) == true) {
+ context.report(
+ ISSUE_FIELD_SITE_TARGET_ON_QUALIFIER_ANNOTATION,
+ context.getLocation(annotation),
+ ISSUE_FIELD_SITE_TARGET_ON_QUALIFIER_ANNOTATION
+ .getBriefDescription(TextFormat.TEXT),
+ LintFix.create()
+ .name("Remove 'field:'")
+ .replace()
+ .text("field:")
+ .with("")
+ .autoFix()
+ .build()
+ )
+ }
+ }
+ }
+ }
+
+ override fun visitMethod(node: UMethod) {
+ if (!node.isConstructor &&
+ node.hasAnnotation(PROVIDES_ANNOTATION) &&
+ node.hasAnnotation(JVM_STATIC_ANNOTATION)
+ ) {
+ val containingClass = node.containingClass?.toUElement(UClass::class.java) ?: return
+ if (containingClass.isObject()) {
+ val annotation = node.findAnnotation(JVM_STATIC_ANNOTATION)!!
+ context.report(
+ ISSUE_JVM_STATIC_PROVIDES_IN_OBJECT,
+ context.getLocation(annotation),
+ ISSUE_JVM_STATIC_PROVIDES_IN_OBJECT.getBriefDescription(TextFormat.TEXT),
+ LintFix.create()
+ .name("Remove @JvmStatic")
+ .replace()
+ .pattern("(@(kotlin\\.jvm\\.)?JvmStatic)")
+ .with("")
+ .autoFix()
+ .build()
+ )
+ }
+ }
+ }
+
+ override fun visitClass(node: UClass) {
+ if (node.hasAnnotation(MODULE_ANNOTATION) && node.isCompanionObject(context.evaluator)) {
+ val parent = node.getUastParentOfType(UClass::class.java, false)!!
+ if (parent.hasAnnotation(MODULE_ANNOTATION)) {
+ context.report(
+ ISSUE_MODULE_COMPANION_OBJECTS,
+ context.getLocation(node as UElement),
+ ISSUE_MODULE_COMPANION_OBJECTS.getBriefDescription(TextFormat.TEXT),
+ LintFix.create()
+ .name("Remove @Module")
+ .replace()
+ .pattern("(@(dagger\\.)?Module)")
+ .with("")
+ .autoFix()
+ .build()
+
+ )
+ } else {
+ context.report(
+ ISSUE_MODULE_COMPANION_OBJECTS_NOT_IN_MODULE_PARENT,
+ context.getLocation(node as UElement),
+ ISSUE_MODULE_COMPANION_OBJECTS_NOT_IN_MODULE_PARENT
+ .getBriefDescription(TextFormat.TEXT)
+ )
+ }
+ }
+ }
+ }
+ }
+
+ /** @return whether or not the [this] is a Kotlin `companion object` type. */
+ private fun UClass.isCompanionObject(evaluator: JavaEvaluator): Boolean {
+ return isObject() && evaluator.hasModifier(this, KtTokens.COMPANION_KEYWORD)
+ }
+
+ /** @return whether or not the [this] is a Kotlin `object` type. */
+ private fun UClass.isObject(): Boolean {
+ return this is KotlinUClass && ktClass is KtObjectDeclaration
+ }
+}
diff --git a/java/dagger/model/BUILD b/java/dagger/model/BUILD
index 5fe0db375..0be8fc5fc 100644
--- a/java/dagger/model/BUILD
+++ b/java/dagger/model/BUILD
@@ -15,14 +15,23 @@
# Description:
# Dagger's core APIs exposed for plugins
-package(default_visibility = ["//:src"])
-
+load("@rules_java//java:defs.bzl", "java_library")
load(
"//:build_defs.bzl",
"DOCLINT_HTML_AND_SYNTAX",
"DOCLINT_REFERENCES",
)
+package(
+ default_visibility = [
+ # The dagger/spi should be the only direct dependent on this target.
+ # If you need to depend on :model, depend on dagger/spi instead so
+ # that pom files correctly pick up the spi maven dependency.
+ # TODO(bcorso): Consider if :model should have its own maven coordinates.
+ "//java/dagger/spi:__pkg__",
+ ],
+)
+
INTERNAL_PROXIES = ["BindingGraphProxies.java"]
filegroup(
@@ -39,14 +48,16 @@ java_library(
javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
deps = [
"//java/dagger:core",
- "//java/dagger/internal/codegen:jdk-and-guava-extras",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:graph",
"//java/dagger/producers",
- "@google_bazel_common//third_party/java/auto:common",
"@google_bazel_common//third_party/java/auto:value",
"@google_bazel_common//third_party/java/error_prone:annotations",
- "@google_bazel_common//third_party/java/guava",
"@google_bazel_common//third_party/java/javapoet",
"@google_bazel_common//third_party/java/jsr330_inject",
+ "@maven//:com_google_auto_auto_common",
],
)
@@ -54,8 +65,11 @@ java_library(
name = "internal-proxies",
srcs = INTERNAL_PROXIES,
tags = ["maven:merged"],
+ visibility = ["//:src"],
deps = [
":model",
- "@google_bazel_common//third_party/java/guava",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:graph",
+ "@google_bazel_common//third_party/java/auto:value",
],
)
diff --git a/java/dagger/model/BindingGraph.java b/java/dagger/model/BindingGraph.java
index 748bf3825..1ccba4326 100644
--- a/java/dagger/model/BindingGraph.java
+++ b/java/dagger/model/BindingGraph.java
@@ -20,12 +20,10 @@ import static com.google.common.collect.Sets.intersection;
import static com.google.common.graph.Graphs.inducedSubgraph;
import static com.google.common.graph.Graphs.reachableNodes;
import static com.google.common.graph.Graphs.transpose;
-import static dagger.internal.codegen.DaggerStreams.instancesOf;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSetMultimap;
+import static dagger.internal.codegen.extension.DaggerStreams.instancesOf;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSetMultimap;
-import com.google.auto.value.AutoValue;
-import com.google.auto.value.extension.memoized.Memoized;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.graph.EndpointPair;
@@ -48,8 +46,8 @@ import javax.lang.model.element.TypeElement;
* <li>an entire component hierarchy rooted at a {@link dagger.Component} or {@link
* dagger.producers.ProductionComponent}
* <li>a partial component hierarchy rooted at a {@link dagger.Subcomponent} or {@link
- * dagger.producers.ProductionSubcomponent} (only when {@code
- * -Adagger.experimentalAheadOfTimeSubcomponents=enabled} is passed to the compiler)
+ * dagger.producers.ProductionSubcomponent} (only when the value of {@code
+ * -Adagger.fullBindingGraphValidation} is not {@code NONE})
* <li>the bindings installed by a {@link Module} or {@link dagger.producers.ProducerModule},
* including all subcomponents generated by {@link Module#subcomponents()} ()} and {@link
* dagger.producers.ProducerModule#subcomponents()} ()}
@@ -88,20 +86,12 @@ import javax.lang.model.element.TypeElement;
*
* <p><b>Note that this API is experimental and will change.</b>
*/
-@AutoValue
public abstract class BindingGraph {
-
- static BindingGraph create(Network<Node, Edge> network, boolean isFullBindingGraph) {
- return new AutoValue_BindingGraph(ImmutableNetwork.copyOf(network), isFullBindingGraph);
- }
-
- BindingGraph() {}
-
/** Returns the graph in its {@link Network} representation. */
public abstract ImmutableNetwork<Node, Edge> network();
@Override
- public final String toString() {
+ public String toString() {
return network().toString();
}
@@ -115,7 +105,7 @@ public abstract class BindingGraph {
* full binding graphs for components and subcomponents as well as modules.
*/
@Deprecated
- public final boolean isModuleBindingGraph() {
+ public boolean isModuleBindingGraph() {
return !rootComponentNode().isRealComponent();
}
@@ -131,54 +121,54 @@ public abstract class BindingGraph {
/**
* Returns {@code true} if the {@link #rootComponentNode()} is a subcomponent. This occurs in
- * ahead-of-time-subcomponents mode.
+ * when {@code -Adagger.fullBindingGraphValidation} is used in a compilation with a subcomponent.
*
* @deprecated use {@link ComponentNode#isSubcomponent() rootComponentNode().isSubcomponent()}
* instead
*/
@Deprecated
- public final boolean isPartialBindingGraph() {
+ public boolean isPartialBindingGraph() {
return rootComponentNode().isSubcomponent();
}
/** Returns the bindings. */
- public final ImmutableSet<Binding> bindings() {
+ public ImmutableSet<Binding> bindings() {
return nodes(Binding.class);
}
/** Returns the bindings for a key. */
- public final ImmutableSet<Binding> bindings(Key key) {
+ public ImmutableSet<Binding> bindings(Key key) {
return nodes(Binding.class).stream()
.filter(binding -> binding.key().equals(key))
.collect(toImmutableSet());
}
/** Returns the nodes that represent missing bindings. */
- public final ImmutableSet<MissingBinding> missingBindings() {
+ public ImmutableSet<MissingBinding> missingBindings() {
return nodes(MissingBinding.class);
}
/** Returns the component nodes. */
- public final ImmutableSet<ComponentNode> componentNodes() {
+ public ImmutableSet<ComponentNode> componentNodes() {
return nodes(ComponentNode.class);
}
/** Returns the component node for a component. */
- public final Optional<ComponentNode> componentNode(ComponentPath component) {
+ public Optional<ComponentNode> componentNode(ComponentPath component) {
return componentNodes().stream()
.filter(node -> node.componentPath().equals(component))
.findFirst();
}
/** Returns the component nodes for a component. */
- public final ImmutableSet<ComponentNode> componentNodes(TypeElement component) {
+ public ImmutableSet<ComponentNode> componentNodes(TypeElement component) {
return componentNodes().stream()
.filter(node -> node.componentPath().currentComponent().equals(component))
.collect(toImmutableSet());
}
/** Returns the component node for the root component. */
- public final ComponentNode rootComponentNode() {
+ public ComponentNode rootComponentNode() {
return componentNodes().stream()
.filter(node -> node.componentPath().atRoot())
.findFirst()
@@ -186,7 +176,7 @@ public abstract class BindingGraph {
}
/** Returns the dependency edges. */
- public final ImmutableSet<DependencyEdge> dependencyEdges() {
+ public ImmutableSet<DependencyEdge> dependencyEdges() {
return dependencyEdgeStream().collect(toImmutableSet());
}
@@ -197,14 +187,14 @@ public abstract class BindingGraph {
* have no binding for a key will have an edge whose {@linkplain EndpointPair#target() target
* node} is a {@link MissingBinding}.
*/
- public final ImmutableSetMultimap<DependencyRequest, DependencyEdge> dependencyEdges(
+ public ImmutableSetMultimap<DependencyRequest, DependencyEdge> dependencyEdges(
Binding binding) {
return dependencyEdgeStream(binding)
.collect(toImmutableSetMultimap(DependencyEdge::dependencyRequest, edge -> edge));
}
/** Returns the dependency edges for a dependency request. */
- public final ImmutableSet<DependencyEdge> dependencyEdges(DependencyRequest dependencyRequest) {
+ public ImmutableSet<DependencyEdge> dependencyEdges(DependencyRequest dependencyRequest) {
return dependencyEdgeStream()
.filter(edge -> edge.dependencyRequest().equals(dependencyRequest))
.collect(toImmutableSet());
@@ -214,7 +204,7 @@ public abstract class BindingGraph {
* Returns the dependency edges for the entry points of a given {@code component}. Each edge's
* source node is that component's component node.
*/
- public final ImmutableSet<DependencyEdge> entryPointEdges(ComponentPath component) {
+ public ImmutableSet<DependencyEdge> entryPointEdges(ComponentPath component) {
return dependencyEdgeStream(componentNode(component).get()).collect(toImmutableSet());
}
@@ -226,12 +216,12 @@ public abstract class BindingGraph {
* Returns the dependency edges for all entry points for all components and subcomponents. Each
* edge's source node is a component node.
*/
- public final ImmutableSet<DependencyEdge> entryPointEdges() {
+ public ImmutableSet<DependencyEdge> entryPointEdges() {
return entryPointEdgeStream().collect(toImmutableSet());
}
/** Returns the binding or missing binding nodes that directly satisfy entry points. */
- public final ImmutableSet<MaybeBinding> entryPointBindings() {
+ public ImmutableSet<MaybeBinding> entryPointBindings() {
return entryPointEdgeStream()
.map(edge -> (MaybeBinding) network().incidentNodes(edge).target())
.collect(toImmutableSet());
@@ -241,7 +231,7 @@ public abstract class BindingGraph {
* Returns the edges for entry points that transitively depend on a binding or missing binding for
* a key.
*/
- public final ImmutableSet<DependencyEdge> entryPointEdgesDependingOnBinding(
+ public ImmutableSet<DependencyEdge> entryPointEdgesDependingOnBinding(
MaybeBinding binding) {
ImmutableNetwork<Node, DependencyEdge> dependencyGraph = dependencyGraph();
Network<Node, DependencyEdge> subgraphDependingOnBinding =
@@ -251,19 +241,19 @@ public abstract class BindingGraph {
}
/** Returns the bindings that directly request a given binding as a dependency. */
- public final ImmutableSet<Binding> requestingBindings(MaybeBinding binding) {
+ public ImmutableSet<Binding> requestingBindings(MaybeBinding binding) {
return network().predecessors(binding).stream()
.flatMap(instancesOf(Binding.class))
.collect(toImmutableSet());
}
/**
- * Returns the bindings that a given binding directly request as a dependency. Does not include
+ * Returns the bindings that a given binding directly requests as a dependency. Does not include
* any {@link MissingBinding}s.
*
* @see #requestedMaybeMissingBindings(Binding)
*/
- public final ImmutableSet<Binding> requestedBindings(Binding binding) {
+ public ImmutableSet<Binding> requestedBindings(Binding binding) {
return network().successors(binding).stream()
.flatMap(instancesOf(Binding.class))
.collect(toImmutableSet());
@@ -275,7 +265,7 @@ public abstract class BindingGraph {
*
* @see #requestedBindings(Binding)
*/
- public final ImmutableSet<MaybeBinding> requestedMaybeMissingBindings(Binding binding) {
+ public ImmutableSet<MaybeBinding> requestedMaybeMissingBindings(Binding binding) {
return network().successors(binding).stream()
.flatMap(instancesOf(MaybeBinding.class))
.collect(toImmutableSet());
@@ -307,8 +297,7 @@ public abstract class BindingGraph {
private static final ImmutableSet<Class<? extends Node>> NODE_TYPES =
ImmutableSet.of(Binding.class, MissingBinding.class, ComponentNode.class);
- @Memoized
- ImmutableSetMultimap<Class<? extends Node>, ? extends Node> nodesByClass() {
+ protected ImmutableSetMultimap<Class<? extends Node>, ? extends Node> nodesByClass() {
return network().nodes().stream()
.collect(
toImmutableSetMultimap(
@@ -402,37 +391,26 @@ public abstract class BindingGraph {
}
/** A node in the binding graph that represents a missing binding for a key in a component. */
- @AutoValue
public abstract static class MissingBinding implements MaybeBinding {
- static MissingBinding create(ComponentPath component, Key key) {
- return new AutoValue_BindingGraph_MissingBinding(component, key);
- }
-
/** The component in which the binding is missing. */
@Override
public abstract ComponentPath componentPath();
/** The key for which there is no binding. */
+ @Override
public abstract Key key();
/** @deprecated This always returns {@code Optional.empty()}. */
@Override
@Deprecated
- public final Optional<Binding> binding() {
+ public Optional<Binding> binding() {
return Optional.empty();
}
@Override
- public final String toString() {
+ public String toString() {
return String.format("missing binding for %s in %s", key(), componentPath());
}
-
- @Memoized
- @Override
- public abstract int hashCode();
-
- @Override
- public abstract boolean equals(Object o);
}
/**
diff --git a/java/dagger/model/BindingGraphProxies.java b/java/dagger/model/BindingGraphProxies.java
index c4504759b..85d4df8ca 100644
--- a/java/dagger/model/BindingGraphProxies.java
+++ b/java/dagger/model/BindingGraphProxies.java
@@ -16,6 +16,10 @@
package dagger.model;
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.graph.ImmutableNetwork;
import com.google.common.graph.Network;
import dagger.model.BindingGraph.Edge;
import dagger.model.BindingGraph.MissingBinding;
@@ -27,14 +31,35 @@ import dagger.model.BindingGraph.Node;
* API.</em>
*/
public final class BindingGraphProxies {
+
+ @AutoValue
+ abstract static class BindingGraphImpl extends BindingGraph {
+ @Override
+ @Memoized
+ public ImmutableSetMultimap<Class<? extends Node>, ? extends Node> nodesByClass() {
+ return super.nodesByClass();
+ }
+ }
+
+ @AutoValue
+ abstract static class MissingBindingImpl extends MissingBinding {
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object o);
+ }
+
/** Creates a new {@link BindingGraph}. */
public static BindingGraph bindingGraph(Network<Node, Edge> network, boolean isFullBindingGraph) {
- return BindingGraph.create(network, isFullBindingGraph);
+ return new AutoValue_BindingGraphProxies_BindingGraphImpl(
+ ImmutableNetwork.copyOf(network), isFullBindingGraph);
}
/** Creates a new {@link MissingBinding}. */
public static MissingBinding missingBindingNode(ComponentPath component, Key key) {
- return MissingBinding.create(component, key);
+ return new AutoValue_BindingGraphProxies_MissingBindingImpl(component, key);
}
private BindingGraphProxies() {}
diff --git a/java/dagger/model/BindingKind.java b/java/dagger/model/BindingKind.java
index 20d7b425c..9bef8fce4 100644
--- a/java/dagger/model/BindingKind.java
+++ b/java/dagger/model/BindingKind.java
@@ -25,6 +25,15 @@ public enum BindingKind {
PROVISION,
/**
+ * A binding for an {@link javax.inject.Inject}-annotated constructor that contains at least one
+ * {@link dagger.assisted.Assisted}-annotated parameter.
+ */
+ ASSISTED_INJECTION,
+
+ /** A binding for an {@link dagger.assisted.AssistedFactory}-annotated type. */
+ ASSISTED_FACTORY,
+
+ /**
* An implicit binding for a {@link dagger.Component}- or {@link
* dagger.producers.ProductionComponent}-annotated type.
*/
diff --git a/java/dagger/model/testing/BUILD b/java/dagger/model/testing/BUILD
index a9d5f1989..9c9f44aea 100644
--- a/java/dagger/model/testing/BUILD
+++ b/java/dagger/model/testing/BUILD
@@ -15,25 +15,27 @@
# Description:
# Test utilities for the Dagger model
-package(default_visibility = ["//:src"])
-
+load("@rules_java//java:defs.bzl", "java_library")
load(
"//:build_defs.bzl",
"DOCLINT_HTML_AND_SYNTAX",
"DOCLINT_REFERENCES",
)
+package(default_visibility = ["//:src"])
+
java_library(
name = "testing",
testonly = 1,
srcs = glob(["*.java"]),
javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
deps = [
- "//java/dagger/internal/codegen:jdk-and-guava-extras",
- "//java/dagger/model",
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/spi",
"@google_bazel_common//third_party/java/auto:value",
"@google_bazel_common//third_party/java/checker_framework_annotations",
- "@google_bazel_common//third_party/java/guava",
"@google_bazel_common//third_party/java/truth",
],
)
diff --git a/java/dagger/model/testing/BindingGraphSubject.java b/java/dagger/model/testing/BindingGraphSubject.java
index dc17c1dd5..2f032cddb 100644
--- a/java/dagger/model/testing/BindingGraphSubject.java
+++ b/java/dagger/model/testing/BindingGraphSubject.java
@@ -18,7 +18,7 @@ package dagger.model.testing;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertAbout;
-import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
import com.google.common.collect.ImmutableSet;
import com.google.common.truth.FailureMetadata;
@@ -29,7 +29,7 @@ import javax.lang.model.type.TypeMirror;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;
/** A Truth subject for making assertions on a {@link BindingGraph}. */
-public final class BindingGraphSubject extends Subject<BindingGraphSubject, BindingGraph> {
+public final class BindingGraphSubject extends Subject {
/** Starts a fluent assertion about a {@link BindingGraph}. */
public static BindingGraphSubject assertThat(BindingGraph bindingGraph) {
@@ -107,7 +107,7 @@ public final class BindingGraphSubject extends Subject<BindingGraphSubject, Bind
}
/** A Truth subject for a {@link Binding}. */
- public final class BindingSubject extends Subject<BindingSubject, Binding> {
+ public final class BindingSubject extends Subject {
private final Binding actual;
diff --git a/java/dagger/producers/BUILD b/java/dagger/producers/BUILD
index ad065a1b9..41762e6e1 100644
--- a/java/dagger/producers/BUILD
+++ b/java/dagger/producers/BUILD
@@ -15,15 +15,17 @@
# Description:
# An asynchronous dependency injection system that extends JSR-330.
-package(default_visibility = ["//:src"])
-
+load("@rules_java//java:defs.bzl", "java_library")
load(
"//:build_defs.bzl",
"DOCLINT_HTML_AND_SYNTAX",
"DOCLINT_REFERENCES",
+ "POM_VERSION",
"SOURCE_7_TARGET_7",
)
-load("//tools:maven.bzl", "pom_file", "POM_VERSION")
+load("//tools:maven.bzl", "gen_maven_artifact")
+
+package(default_visibility = ["//:src"])
# Work around b/70476182 which prevents Kythe from connecting :producers to the .java files it
# contains.
@@ -40,35 +42,38 @@ java_library(
javacopts = SOURCE_7_TARGET_7 + DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
tags = ["maven_coordinates=com.google.dagger:dagger-producers:" + POM_VERSION],
exports = [
- # TODO(dpb): Don't export any of Guava.
- "@google_bazel_common//third_party/java/guava",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:concurrent",
"@google_bazel_common//third_party/java/jsr330_inject",
],
deps = [
"//java/dagger:core",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:concurrent",
"@google_bazel_common//third_party/java/checker_framework_annotations",
"@google_bazel_common//third_party/java/error_prone:annotations",
- "@google_bazel_common//third_party/java/guava",
"@google_bazel_common//third_party/java/jsr330_inject",
],
)
-pom_file(
- name = "pom",
- artifact_id = "dagger-producers",
+gen_maven_artifact(
+ name = "artifact",
+ artifact_coordinates = "com.google.dagger:dagger-producers:" + POM_VERSION,
artifact_name = "Dagger Producers",
- targets = [":producers"],
-)
-
-load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
-
-javadoc_library(
- name = "producers-javadoc",
- srcs = SRCS,
- exclude_packages = [
+ artifact_target = ":producers",
+ artifact_target_maven_deps = [
+ "com.google.dagger:dagger",
+ "com.google.guava:failureaccess",
+ "com.google.guava:guava", # TODO(bcorso): Remove guava dependency and ban it?
+ "javax.inject:javax.inject",
+ "org.checkerframework:checker-compat-qual",
+ ],
+ javadoc_exclude_packages = [
"dagger.producers.internal",
"dagger.producers.monitoring.internal",
],
- root_packages = ["dagger.producers"],
- deps = [":producers"],
+ javadoc_root_packages = ["dagger.producers"],
+ javadoc_srcs = SRCS,
+ # TODO(bcorso): Look more into why auto/common shading isn't needed here.
)
diff --git a/java/dagger/producers/internal/MissingBindingProducer.java b/java/dagger/producers/internal/MissingBindingProducer.java
deleted file mode 100644
index 5721569a0..000000000
--- a/java/dagger/producers/internal/MissingBindingProducer.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.producers.internal;
-
-import com.google.common.util.concurrent.ListenableFuture;
-import dagger.producers.Producer;
-
-/**
- * A {@link Producer} that always throws on calls to {@link Producer#get()}. This is necessary in
- * ahead-of-time subcomponents mode, where modifiable binding methods need to return a {@code
- * Producer<T>} to a framework instance initialization that is pruned and no longer in the binding
- * graph, but was present in a superclass implementation. This class fulfills that requirement but
- * is still practically unusable.
- */
-public final class MissingBindingProducer<T> extends AbstractProducer<T> {
- private static final MissingBindingProducer<Object> INSTANCE = new MissingBindingProducer<>();
-
- private MissingBindingProducer() {}
-
- @SuppressWarnings({"unchecked", "rawtypes"}) // safe covariant cast
- public static <T> Producer<T> create() {
- return (Producer) INSTANCE;
- }
-
- @Override
- protected ListenableFuture<T> compute() {
- throw new AssertionError(
- "This binding is not part of the final binding graph. The key was requested by a binding "
- + "that was believed to possibly be part of the graph, but is no longer requested. "
- + "If this exception is thrown, it is the result of a Dagger bug.");
- }
-}
diff --git a/java/dagger/producers/monitoring/TimingProducerMonitor.java b/java/dagger/producers/monitoring/TimingProducerMonitor.java
index c63e10822..ebb90cf7f 100644
--- a/java/dagger/producers/monitoring/TimingProducerMonitor.java
+++ b/java/dagger/producers/monitoring/TimingProducerMonitor.java
@@ -25,6 +25,7 @@ import com.google.common.base.Ticker;
* A monitor that measures the timing of the execution of a producer method, and logs those timings
* with the given recorder.
*/
+@SuppressWarnings("GoodTime") // should use java.time.Duration
final class TimingProducerMonitor extends ProducerMonitor {
private final ProducerTimingRecorder recorder;
private final Stopwatch stopwatch;
diff --git a/java/dagger/producers/monitoring/TimingRecorders.java b/java/dagger/producers/monitoring/TimingRecorders.java
index 5fbe3e349..be89319f8 100644
--- a/java/dagger/producers/monitoring/TimingRecorders.java
+++ b/java/dagger/producers/monitoring/TimingRecorders.java
@@ -30,6 +30,7 @@ import java.util.logging.Logger;
*/
// TODO(beder): Reduce the visibility of this class to package-private.
@Beta
+@SuppressWarnings("GoodTime") // Should be using java.time.Instant/Duration as opposed to nanos
public final class TimingRecorders {
private static final Logger logger = Logger.getLogger(TimingRecorders.class.getName());
diff --git a/java/dagger/spi/BUILD b/java/dagger/spi/BUILD
index 9c045824c..cbec4658d 100644
--- a/java/dagger/spi/BUILD
+++ b/java/dagger/spi/BUILD
@@ -15,42 +15,67 @@
# Description:
# The Service Provider Interface for Dagger's binding graph model
-package(default_visibility = ["//:src"])
-
+load("@rules_java//java:defs.bzl", "java_library")
load(
"//:build_defs.bzl",
"DOCLINT_HTML_AND_SYNTAX",
"DOCLINT_REFERENCES",
+ "POM_VERSION",
)
+load("//tools:maven.bzl", "gen_maven_artifact")
+
+package(default_visibility = ["//:src"])
filegroup(
name = "spi-srcs",
- srcs = glob(["*.java"]),
+ srcs = glob(["*.java"]) + [
+ "//java/dagger/model:model-srcs",
+ ],
)
-load("//tools:maven.bzl", "POM_VERSION", "pom_file")
-
java_library(
name = "spi",
- srcs = [":spi-srcs"],
+ srcs = glob(["*.java"]),
javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
tags = ["maven_coordinates=com.google.dagger:dagger-spi:" + POM_VERSION],
+ exports = [
+ "//java/dagger/model",
+ ],
deps = [
"//java/dagger:core",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
"//java/dagger/model",
"@google_bazel_common//third_party/java/auto:value",
"@google_bazel_common//third_party/java/error_prone:annotations",
- "@google_bazel_common//third_party/java/guava",
"@google_bazel_common//third_party/java/jsr330_inject",
],
)
-pom_file(
- name = "pom",
- artifact_id = "dagger-spi",
+gen_maven_artifact(
+ name = "artifact",
+ artifact_coordinates = "com.google.dagger:dagger-spi:" + POM_VERSION,
artifact_name = "Dagger SPI",
- targets = [
+ artifact_target = ":spi",
+ artifact_target_libs = [
+ "//java/dagger/internal/codegen/extension",
"//java/dagger/model",
- ":spi",
],
+ artifact_target_maven_deps = [
+ "com.google.auto:auto-common",
+ "com.google.code.findbugs:jsr305",
+ "com.google.dagger:dagger-producers",
+ "com.google.dagger:dagger",
+ "com.google.guava:failureaccess",
+ "com.google.guava:guava",
+ "com.squareup:javapoet",
+ "javax.inject:javax.inject",
+ ],
+ javadoc_root_packages = [
+ "dagger.model",
+ "dagger.spi",
+ ],
+ javadoc_srcs = [":spi-srcs"],
+ shaded_deps = ["@maven//:com_google_auto_auto_common"],
+ shaded_rules = ["rule com.google.auto.common.** dagger.spi.shaded.auto.common.@1"],
)
diff --git a/java/dagger/testing/compile/BUILD b/java/dagger/testing/compile/BUILD
new file mode 100644
index 000000000..20e6a3d20
--- /dev/null
+++ b/java/dagger/testing/compile/BUILD
@@ -0,0 +1,32 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Helpers class for java compiler tests.
+
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "compile",
+ testonly = 1,
+ srcs = ["CompilerTests.java"],
+ deps = [
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:io",
+ "@google_bazel_common//third_party/java/compile_testing",
+ ],
+)
diff --git a/java/dagger/testing/compile/CompilerTests.java b/java/dagger/testing/compile/CompilerTests.java
new file mode 100644
index 000000000..6750d0c65
--- /dev/null
+++ b/java/dagger/testing/compile/CompilerTests.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.testing.compile;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static com.google.common.collect.MoreCollectors.onlyElement;
+import static com.google.common.collect.Streams.stream;
+import static com.google.testing.compile.Compiler.javac;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.Files;
+import com.google.testing.compile.Compiler;
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+/**
+ * A helper class for working with java compiler tests.
+ */
+public final class CompilerTests {
+ private CompilerTests() {}
+
+ /** Returns the {@plainlink File jar file} containing the compiler deps. */
+ public static File compilerDepsJar() {
+ try {
+ return stream(Files.fileTraverser().breadthFirst(getRunfilesDir()))
+ .filter(file -> file.getName().endsWith("_compiler_deps_deploy.jar"))
+ .collect(onlyElement());
+ } catch (NoSuchElementException e) {
+ throw new IllegalStateException(
+ "No compiler deps jar found. Are you using the Dagger compiler_test macro?", e);
+ }
+ }
+
+ /** Returns a {@link Compiler} with the compiler deps jar added to the class path. */
+ public static Compiler compiler() {
+ return javac().withClasspath(ImmutableList.of(compilerDepsJar()));
+ }
+
+ private static File getRunfilesDir() {
+ return getRunfilesPath().toFile();
+ }
+
+ private static Path getRunfilesPath() {
+ Path propPath = getRunfilesPath(System.getProperties());
+ if (propPath != null) {
+ return propPath;
+ }
+
+ Path envPath = getRunfilesPath(System.getenv());
+ if (envPath != null) {
+ return envPath;
+ }
+
+ Path cwd = Paths.get("").toAbsolutePath();
+ return cwd.getParent();
+ }
+
+ private static Path getRunfilesPath(Map<?, ?> map) {
+ String runfilesPath = (String) map.get("TEST_SRCDIR");
+ return isNullOrEmpty(runfilesPath) ? null : Paths.get(runfilesPath);
+ }
+}
diff --git a/java/dagger/testing/compile/macros.bzl b/java/dagger/testing/compile/macros.bzl
new file mode 100644
index 000000000..9543e0e5c
--- /dev/null
+++ b/java/dagger/testing/compile/macros.bzl
@@ -0,0 +1,89 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Macros for building compiler tests."""
+
+load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_library")
+
+def compiler_test(name, size = "large", compiler_deps = None, **kwargs):
+ """Generates a java_test that tests java compilation with the given compiler deps.
+
+ This macro separates the compiler dependencies from the test dependencies to avoid
+ 1-version violations. For example, this often happens when the java_test uses java
+ dependencies but the compiler test expects the android version of the dependencies.
+
+ Args:
+ name: The name of the java_test.
+ size: The size of the test (default "large" since this test does disk I/O).
+ compiler_deps: The deps needed during compilation.
+ **kwargs: The parameters to pass to the generated java_test.
+
+ Returns:
+ None
+ """
+
+ # This JAR is loaded at runtime and contains the dependencies used by the compiler during tests.
+ # We separate these dependencies from the java_test dependencies to avoid 1 version violations.
+ native.java_binary(
+ name = name + "_compiler_deps",
+ testonly = 1,
+ tags = ["notap"],
+ visibility = ["//visibility:private"],
+ main_class = "Object.class",
+ runtime_deps = compiler_deps,
+ )
+
+ # Add the compiler deps jar, generated above, to the test's data.
+ kwargs["data"] = kwargs.get("data", []) + [name + "_compiler_deps_deploy.jar"]
+
+ # Need to check for srcs since for Kotlin tests we use a runtime dep on the kt_jvm_library
+ # target. We don't need to worry about adding a compile testing dep since kt_compiler_test
+ # adds that in the kt_jvm_library. Adding this dep automatically is merely a convenience
+ # for cases with srcs anyway.
+ if kwargs.get("srcs", None):
+ # Add a dep to allow usage of CompilerTests.
+ kwargs["deps"] = kwargs.get("deps", []) + ["//java/dagger/testing/compile"]
+
+ native.java_test(name = name, size = size, **kwargs)
+
+def kt_compiler_test(name, srcs = [], deps = [], **kwargs):
+ """Generates a java_test that tests java compilation with the given compiler deps.
+
+ This macro works the same as the above compiler_test, but for Kotlin sources.
+
+ Args:
+ name: The name of the java_test.
+ srcs: Source files for the test (typically should include Kotlin sources). If no
+ sources are needed, just use compiler_test with runtime_deps.
+ deps: Deps for compiling the files in srcs.
+ **kwargs: The parameters to pass to compiler_test
+
+ Returns:
+ None
+ """
+ kt_jvm_library(
+ name = name + "_ktlib",
+ testonly = 1,
+ srcs = srcs,
+ deps = deps + ["//java/dagger/testing/compile"],
+ visibility = ["//visibility:private"],
+ )
+
+ compiler_test(
+ name = name,
+ runtime_deps = [
+ ":" + name + "_ktlib",
+ ],
+ **kwargs
+ )
diff --git a/javatests/artifacts/BUILD b/javatests/artifacts/BUILD
new file mode 100644
index 000000000..9b282deef
--- /dev/null
+++ b/javatests/artifacts/BUILD
@@ -0,0 +1,18 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# This package contains gradle tests of the LOCAL-SNAPSHOT artifacts.
+
+package(default_visibility = ["//:src"])
diff --git a/javatests/artifacts/dagger-android/simple/app/build.gradle b/javatests/artifacts/dagger-android/simple/app/build.gradle
new file mode 100644
index 000000000..20a37340e
--- /dev/null
+++ b/javatests/artifacts/dagger-android/simple/app/build.gradle
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.2"
+
+ defaultConfig {
+ applicationId "dagger.android.simple"
+ minSdkVersion 15
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+ }
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+ testOptions {
+ unitTests.includeAndroidResources = true
+ }
+ sourceSets {
+ String sharedTestDir = 'src/sharedTest/java'
+ test {
+ java.srcDirs += sharedTestDir
+ }
+ androidTest {
+ java.srcDirs += sharedTestDir
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'com.google.dagger:dagger:LOCAL-SNAPSHOT'
+ implementation 'com.google.dagger:dagger-android-support:LOCAL-SNAPSHOT'
+ annotationProcessor 'com.google.dagger:dagger-compiler:LOCAL-SNAPSHOT'
+ annotationProcessor 'com.google.dagger:dagger-android-processor:LOCAL-SNAPSHOT'
+
+ testImplementation 'com.google.truth:truth:1.0.1'
+ testImplementation 'org.robolectric:robolectric:4.5-alpha-3'
+ testImplementation 'androidx.core:core:1.3.2'
+ testImplementation 'androidx.test.ext:junit:1.1.2'
+ testImplementation 'androidx.test:runner:1.3.0'
+ testImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+ testImplementation 'com.google.dagger:dagger-compiler:LOCAL-SNAPSHOT'
+ testAnnotationProcessor 'com.google.dagger:dagger-android-processor:LOCAL-SNAPSHOT'
+
+ androidTestImplementation 'com.google.truth:truth:1.0.1'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+ androidTestImplementation 'androidx.test:runner:1.3.0'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+ androidTestImplementation 'com.google.dagger:dagger-compiler:LOCAL-SNAPSHOT'
+ androidTestAnnotationProcessor 'com.google.dagger:dagger-android-processor:LOCAL-SNAPSHOT'
+
+ // To help us catch usages of Guava APIs for Java 8 in the '-jre' variant.
+ annotationProcessor'com.google.guava:guava:28.1-android'
+ testAnnotationProcessor'com.google.guava:guava:28.1-android'
+ androidTestAnnotationProcessor'com.google.guava:guava:28.1-android'
+}
diff --git a/javatests/artifacts/dagger-android/simple/app/src/main/AndroidManifest.xml b/javatests/artifacts/dagger-android/simple/app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..0f765b521
--- /dev/null
+++ b/javatests/artifacts/dagger-android/simple/app/src/main/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.android.simple">
+
+ <application
+ android:name=".SimpleApplication"
+ android:label="@string/appName"
+ android:theme="@style/Theme.AppCompat.Light">
+ <activity android:name=".SimpleActivity" android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/Model.java b/javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/Model.java
new file mode 100644
index 000000000..d74a5016a
--- /dev/null
+++ b/javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/Model.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.simple;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/** Qualifies bindings relating to {@link android.os.Build#MODEL}. */
+@Qualifier
+@Retention(RUNTIME)
+@Documented
+@interface Model {}
diff --git a/javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/ModelModule.java b/javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/ModelModule.java
new file mode 100644
index 000000000..5e04071f3
--- /dev/null
+++ b/javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/ModelModule.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.simple;
+
+import static android.os.Build.MODEL;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+final class ModelModule {
+ @Provides
+ @Model
+ static String provideModel() {
+ return MODEL;
+ }
+}
diff --git a/javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/SimpleActivity.java b/javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/SimpleActivity.java
new file mode 100644
index 000000000..39a88e344
--- /dev/null
+++ b/javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/SimpleActivity.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.simple;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.TextView;
+import dagger.Binds;
+import dagger.Module;
+import dagger.Subcomponent;
+import dagger.android.AndroidInjector;
+import dagger.android.support.DaggerAppCompatActivity;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+import javax.inject.Inject;
+
+/**
+ * The main activity of the application.
+ *
+ * <p>It can be injected with any binding from both {@link SimpleActivityComponent} and {@link
+ * SimpleApplication.SimpleComponent}.
+ */
+public class SimpleActivity extends DaggerAppCompatActivity {
+ @Subcomponent
+ interface SimpleActivityComponent extends AndroidInjector<SimpleActivity> {
+
+ @Subcomponent.Factory
+ interface Factory extends AndroidInjector.Factory<SimpleActivity> {}
+ }
+
+ @Module(
+ subcomponents = SimpleActivityComponent.class,
+ includes = UserNameModule.class
+ )
+ abstract static class InjectorModule {
+
+ @Binds
+ @IntoMap
+ @ClassKey(SimpleActivity.class)
+ abstract AndroidInjector.Factory<?> bind(SimpleActivityComponent.Factory factory);
+ }
+
+ private static final String TAG = SimpleActivity.class.getSimpleName();
+
+ @Inject @UserName String userName;
+ @Inject @Model String model;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.i(TAG, "Injected with userName and model: " + userName + ", " + model);
+
+ setContentView(R.layout.activity_main);
+
+ TextView greeting = (TextView) findViewById(R.id.greeting);
+ String text = getResources().getString(R.string.welcome, userName, model);
+ greeting.setText(text);
+ }
+}
diff --git a/javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/SimpleApplication.java b/javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/SimpleApplication.java
new file mode 100644
index 000000000..dcc7bcb10
--- /dev/null
+++ b/javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/SimpleApplication.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.simple;
+
+import android.util.Log;
+import dagger.Component;
+import dagger.android.AndroidInjectionModule;
+import dagger.android.AndroidInjector;
+import dagger.android.DaggerApplication;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * A simple, skeletal application that demonstrates a dependency-injected application using the
+ * utilities in {@code dagger.android}.
+ */
+public class SimpleApplication extends DaggerApplication {
+ private static final String TAG = SimpleApplication.class.getSimpleName();
+
+ @Singleton
+ @Component(
+ modules = {
+ AndroidInjectionModule.class,
+ SimpleActivity.InjectorModule.class,
+ ModelModule.class
+ }
+ )
+ interface SimpleComponent extends AndroidInjector<SimpleApplication> {
+ @Component.Factory
+ interface Factory extends AndroidInjector.Factory<SimpleApplication> {}
+ }
+
+ @Inject @Model String model;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.i(TAG, "Injected with model: " + model);
+ }
+
+ @Override
+ protected AndroidInjector<SimpleApplication> applicationInjector() {
+ return DaggerSimpleApplication_SimpleComponent.factory().create(this);
+ }
+}
diff --git a/javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/UserName.java b/javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/UserName.java
new file mode 100644
index 000000000..2f861b145
--- /dev/null
+++ b/javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/UserName.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.simple;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/** Qualifies bindings relating to the user name. */
+@Qualifier
+@Retention(RUNTIME)
+@Documented
+@interface UserName {}
diff --git a/javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/UserNameModule.java b/javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/UserNameModule.java
new file mode 100644
index 000000000..d6bc0accb
--- /dev/null
+++ b/javatests/artifacts/dagger-android/simple/app/src/main/java/dagger/android/simple/UserNameModule.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.simple;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+final class UserNameModule {
+ @UserName
+ @Provides
+ static String provideUserName() {
+ return "ProdUser";
+ }
+
+ private UserNameModule() {}
+}
diff --git a/javatests/artifacts/dagger-android/simple/app/src/main/res/layout/activity_main.xml b/javatests/artifacts/dagger-android/simple/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 000000000..3cecc4cc4
--- /dev/null
+++ b/javatests/artifacts/dagger-android/simple/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/background_light">
+
+ <TextView
+ android:id="@+id/greeting"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentTop="true"
+ android:textColor="@android:color/primary_text_light"
+ />
+</RelativeLayout>
diff --git a/javatests/artifacts/dagger-android/simple/app/src/main/res/values/strings.xml b/javatests/artifacts/dagger-android/simple/app/src/main/res/values/strings.xml
new file mode 100644
index 000000000..579f4786d
--- /dev/null
+++ b/javatests/artifacts/dagger-android/simple/app/src/main/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <!--The app name [CHAR_LIMIT=25]-->
+ <string name="appName">Simple Dagger Android</string>
+
+ <!--The greeting message [CHAR_LIMIT=100]-->
+ <string name="welcome">Hello, %1$s! You are on build %2$s.</string>
+</resources>
diff --git a/javatests/artifacts/dagger-android/simple/app/src/sharedTest/java/dagger/android/simple/SimpleActivityTest.java b/javatests/artifacts/dagger-android/simple/app/src/sharedTest/java/dagger/android/simple/SimpleActivityTest.java
new file mode 100644
index 000000000..024da5c5f
--- /dev/null
+++ b/javatests/artifacts/dagger-android/simple/app/src/sharedTest/java/dagger/android/simple/SimpleActivityTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android.simple;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.Build;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+/** A simple test using dagger-android that can be run with instrumentation or Robolectric tests. */
+@RunWith(AndroidJUnit4.class)
+// Robolectric requires Java9 to run API 29 and above, so use API 28 instead
+@Config(sdk = Build.VERSION_CODES.P)
+public final class SimpleActivityTest {
+
+ @Test
+ public void testActivityInject() throws Exception {
+ try (ActivityScenario<SimpleActivity> scenario =
+ ActivityScenario.launch(SimpleActivity.class)) {
+ scenario.onActivity(
+ activity -> {
+ onView(withId(R.id.greeting))
+ .check(matches(withText("Hello, ProdUser! You are on build robolectric.")));
+ });
+ }
+ }
+
+ @Test
+ public void verifyApplicationInstance() {
+ assertThat((Context) ApplicationProvider.getApplicationContext())
+ .isInstanceOf(SimpleApplication.class);
+ }
+}
diff --git a/javatests/artifacts/dagger-android/simple/build.gradle b/javatests/artifacts/dagger-android/simple/build.gradle
new file mode 100644
index 000000000..8c179e503
--- /dev/null
+++ b/javatests/artifacts/dagger-android/simple/build.gradle
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+buildscript {
+ ext {
+ agp_version = System.getenv('AGP_VERSION') ?: "4.2.0-beta04"
+ }
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:$agp_version"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ mavenCentral()
+ mavenLocal()
+ }
+}
+
diff --git a/javatests/artifacts/dagger-android/simple/gradle.properties b/javatests/artifacts/dagger-android/simple/gradle.properties
new file mode 100644
index 000000000..2d8d1e4dd
--- /dev/null
+++ b/javatests/artifacts/dagger-android/simple/gradle.properties
@@ -0,0 +1 @@
+android.useAndroidX=true \ No newline at end of file
diff --git a/javatests/artifacts/dagger-android/simple/gradle/wrapper/gradle-wrapper.jar b/javatests/artifacts/dagger-android/simple/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..5c2d1cf01
--- /dev/null
+++ b/javatests/artifacts/dagger-android/simple/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/javatests/artifacts/dagger-android/simple/gradle/wrapper/gradle-wrapper.properties b/javatests/artifacts/dagger-android/simple/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..4d9ca1649
--- /dev/null
+++ b/javatests/artifacts/dagger-android/simple/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/javatests/artifacts/dagger-android/simple/gradlew b/javatests/artifacts/dagger-android/simple/gradlew
new file mode 100755
index 000000000..b0d6d0ab5
--- /dev/null
+++ b/javatests/artifacts/dagger-android/simple/gradlew
@@ -0,0 +1,188 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/javatests/artifacts/dagger-android/simple/settings.gradle b/javatests/artifacts/dagger-android/simple/settings.gradle
new file mode 100644
index 000000000..c5a07bc20
--- /dev/null
+++ b/javatests/artifacts/dagger-android/simple/settings.gradle
@@ -0,0 +1,2 @@
+include ':app'
+rootProject.name='Simple Dagger Android' \ No newline at end of file
diff --git a/javatests/artifacts/dagger/simple/build.gradle b/javatests/artifacts/dagger/simple/build.gradle
new file mode 100644
index 000000000..97c966e50
--- /dev/null
+++ b/javatests/artifacts/dagger/simple/build.gradle
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ id 'java'
+ id 'application'
+}
+
+repositories {
+ mavenCentral()
+ mavenLocal()
+}
+
+java {
+ // Make sure the generated source is compatible with Java 7.
+ sourceCompatibility = JavaVersion.VERSION_1_7
+}
+
+dependencies {
+ implementation 'com.google.dagger:dagger:LOCAL-SNAPSHOT'
+ annotationProcessor 'com.google.dagger:dagger-compiler:LOCAL-SNAPSHOT'
+}
+
+mainClassName = 'dagger.simple.SimpleApplication' \ No newline at end of file
diff --git a/javatests/artifacts/dagger/simple/gradle/wrapper/gradle-wrapper.jar b/javatests/artifacts/dagger/simple/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..5c2d1cf01
--- /dev/null
+++ b/javatests/artifacts/dagger/simple/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/javatests/artifacts/dagger/simple/gradle/wrapper/gradle-wrapper.properties b/javatests/artifacts/dagger/simple/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..4d9ca1649
--- /dev/null
+++ b/javatests/artifacts/dagger/simple/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/javatests/artifacts/dagger/simple/gradlew b/javatests/artifacts/dagger/simple/gradlew
new file mode 100755
index 000000000..b0d6d0ab5
--- /dev/null
+++ b/javatests/artifacts/dagger/simple/gradlew
@@ -0,0 +1,188 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/javatests/artifacts/dagger/simple/src/main/java/dagger/simple/AssistedInjects.java b/javatests/artifacts/dagger/simple/src/main/java/dagger/simple/AssistedInjects.java
new file mode 100644
index 000000000..eb4946fe3
--- /dev/null
+++ b/javatests/artifacts/dagger/simple/src/main/java/dagger/simple/AssistedInjects.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2021 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.simple;
+
+import dagger.Component;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import javax.inject.Inject;
+
+// This is a regression test for https://github.com/google/dagger/issues/2309
+/** A simple, skeletal application that defines an assisted inject binding. */
+public class AssistedInjects {
+ @Component
+ interface MyComponent {
+ FooFactory fooFactory();
+
+ ParameterizedFooFactory<Bar, String> parameterizedFooFactory();
+ }
+
+ static final class Bar {
+ @Inject
+ Bar() {}
+ }
+
+ static class Foo {
+ @AssistedInject
+ Foo(Bar bar, @Assisted String str) {}
+ }
+
+ @AssistedFactory
+ interface FooFactory {
+ Foo create(String str);
+ }
+
+ static class ParameterizedFoo<T1, T2> {
+ @AssistedInject
+ ParameterizedFoo(T1 t1, @Assisted T2 t2) {}
+ }
+
+ @AssistedFactory
+ interface ParameterizedFooFactory<T1, T2> {
+ ParameterizedFoo<T1, T2> create(T2 t2);
+ }
+
+ public static void main(String[] args) {
+ Foo foo = DaggerAssistedInjects_MyComponent.create().fooFactory().create("");
+
+ ParameterizedFoo<Bar, String> parameterizedFoo =
+ DaggerAssistedInjects_MyComponent.create().parameterizedFooFactory().create("");
+ }
+}
diff --git a/javatests/artifacts/dagger/simple/src/main/java/dagger/simple/SimpleApplication.java b/javatests/artifacts/dagger/simple/src/main/java/dagger/simple/SimpleApplication.java
new file mode 100644
index 000000000..f13610101
--- /dev/null
+++ b/javatests/artifacts/dagger/simple/src/main/java/dagger/simple/SimpleApplication.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.simple;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** A simple, skeletal application that defines a simple component. */
+public class SimpleApplication {
+ static final class Foo {
+ @Inject Foo() {}
+ }
+
+ @Module
+ static final class SimpleModule {
+ @Provides
+ static Foo provideFoo() {
+ return new Foo();
+ }
+ }
+
+ @Singleton
+ @Component(modules = { SimpleModule.class })
+ interface SimpleComponent {
+ Foo foo();
+ }
+
+ public static void main(String[] args) {
+ Foo foo = DaggerSimpleApplication_SimpleComponent.create().foo();
+ }
+}
diff --git a/javatests/artifacts/hilt-android/gradleConfigCache/app/build.gradle b/javatests/artifacts/hilt-android/gradleConfigCache/app/build.gradle
new file mode 100644
index 000000000..43ccd1d55
--- /dev/null
+++ b/javatests/artifacts/hilt-android/gradleConfigCache/app/build.gradle
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-kapt'
+apply plugin: 'dagger.hilt.android.plugin'
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.2"
+
+ defaultConfig {
+ applicationId "dagger.hilt.android.gradleConfigCache"
+ minSdkVersion 15
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "dagger.hilt.android.gradleConfigCache.TestRunner"
+ }
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+ testOptions {
+ unitTests.includeAndroidResources = true
+ }
+}
+
+hilt {
+ enableTransformForLocalTests = true
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'androidx.activity:activity-ktx:1.1.0'
+ implementation 'androidx.multidex:multidex:2.0.0'
+ implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'
+ kapt 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'
+
+ testImplementation 'junit:junit:4.13'
+ testImplementation 'androidx.test.ext:junit:1.1.2'
+ testImplementation 'androidx.test:runner:1.3.0'
+ testImplementation 'org.robolectric:robolectric:4.5-alpha-3'
+ testImplementation 'com.google.dagger:hilt-android-testing:LOCAL-SNAPSHOT'
+ kaptTest 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'
+
+ androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+ androidTestImplementation 'androidx.test:runner:1.3.0'
+ androidTestImplementation 'com.google.dagger:hilt-android-testing:LOCAL-SNAPSHOT'
+ kaptAndroidTest 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'
+}
diff --git a/javatests/artifacts/hilt-android/gradleConfigCache/app/src/androidTest/java/dagger/hilt/android/gradleConfigCache/EmulatorTest.kt b/javatests/artifacts/hilt-android/gradleConfigCache/app/src/androidTest/java/dagger/hilt/android/gradleConfigCache/EmulatorTest.kt
new file mode 100644
index 000000000..9099db42e
--- /dev/null
+++ b/javatests/artifacts/hilt-android/gradleConfigCache/app/src/androidTest/java/dagger/hilt/android/gradleConfigCache/EmulatorTest.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.gradleConfigCache
+
+import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import dagger.hilt.android.testing.HiltAndroidRule
+import dagger.hilt.android.testing.HiltAndroidTest
+import org.junit.Assert.assertNotNull
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@HiltAndroidTest
+@RunWith(AndroidJUnit4::class)
+class EmulatorTest {
+
+ @get:Rule
+ val rule = HiltAndroidRule(this)
+
+ @Test
+ fun testFooInjected() {
+ ActivityScenario.launch(MainActivity::class.java).use {
+ it.onActivity { activity ->
+ assertNotNull(activity.foo)
+ }
+ }
+ }
+}
diff --git a/javatests/artifacts/hilt-android/gradleConfigCache/app/src/androidTest/java/dagger/hilt/android/gradleConfigCache/TestRunner.kt b/javatests/artifacts/hilt-android/gradleConfigCache/app/src/androidTest/java/dagger/hilt/android/gradleConfigCache/TestRunner.kt
new file mode 100644
index 000000000..9fce7cdb0
--- /dev/null
+++ b/javatests/artifacts/hilt-android/gradleConfigCache/app/src/androidTest/java/dagger/hilt/android/gradleConfigCache/TestRunner.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.gradleConfigCache
+
+import android.app.Application
+import android.content.Context
+import androidx.test.runner.AndroidJUnitRunner
+import dagger.hilt.android.testing.HiltTestApplication
+
+class TestRunner : AndroidJUnitRunner() {
+ override fun newApplication(cl: ClassLoader, appName: String, context: Context): Application {
+ return super.newApplication(cl, HiltTestApplication::class.java.name, context)
+ }
+}
diff --git a/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/AndroidManifest.xml b/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..ce1fb7129
--- /dev/null
+++ b/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.hilt.android.gradleConfigCache">
+
+ <application
+ android:name=".App"
+ android:allowBackup="true"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.AppCompat.Light">
+ <activity android:name=".MainActivity" android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/java/dagger/hilt/android/gradleConfigCache/App.kt b/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/java/dagger/hilt/android/gradleConfigCache/App.kt
new file mode 100644
index 000000000..1cbd4b062
--- /dev/null
+++ b/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/java/dagger/hilt/android/gradleConfigCache/App.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.gradleConfigCache
+
+import androidx.multidex.MultiDexApplication
+import dagger.hilt.android.HiltAndroidApp
+
+@HiltAndroidApp
+class App : MultiDexApplication()
diff --git a/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/java/dagger/hilt/android/gradleConfigCache/Foo.kt b/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/java/dagger/hilt/android/gradleConfigCache/Foo.kt
new file mode 100644
index 000000000..969e183eb
--- /dev/null
+++ b/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/java/dagger/hilt/android/gradleConfigCache/Foo.kt
@@ -0,0 +1,5 @@
+package dagger.hilt.android.gradleConfigCache
+
+import javax.inject.Inject
+
+class Foo @Inject constructor()
diff --git a/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/java/dagger/hilt/android/gradleConfigCache/MainActivity.kt b/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/java/dagger/hilt/android/gradleConfigCache/MainActivity.kt
new file mode 100644
index 000000000..0011bdefc
--- /dev/null
+++ b/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/java/dagger/hilt/android/gradleConfigCache/MainActivity.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.gradleConfigCache
+
+import android.os.Bundle
+import android.util.Log
+import androidx.appcompat.app.AppCompatActivity
+import dagger.hilt.android.AndroidEntryPoint
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class MainActivity : AppCompatActivity() {
+
+ @Inject
+ lateinit var foo: Foo
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+ Log.d("Hilt", "Foo is $foo")
+ }
+}
diff --git a/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/res/layout/activity_main.xml b/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 000000000..cb5a8df9c
--- /dev/null
+++ b/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/background_light">
+</RelativeLayout>
diff --git a/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/res/values/strings.xml b/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/res/values/strings.xml
new file mode 100644
index 000000000..05afdbf55
--- /dev/null
+++ b/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/res/values/strings.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <!--The app name [CHAR_LIMIT=40]-->
+ <string name="app_name">Gradle Configuration Cache App</string>
+</resources>
diff --git a/javatests/artifacts/hilt-android/gradleConfigCache/app/src/test/java/dagger/hilt/android/gradleConfigCache/LocalTest.kt b/javatests/artifacts/hilt-android/gradleConfigCache/app/src/test/java/dagger/hilt/android/gradleConfigCache/LocalTest.kt
new file mode 100644
index 000000000..527d88739
--- /dev/null
+++ b/javatests/artifacts/hilt-android/gradleConfigCache/app/src/test/java/dagger/hilt/android/gradleConfigCache/LocalTest.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.gradleConfigCache
+
+import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import dagger.hilt.android.testing.HiltAndroidRule
+import dagger.hilt.android.testing.HiltAndroidTest
+import dagger.hilt.android.testing.HiltTestApplication
+import org.junit.Assert.assertNotNull
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.annotation.Config
+
+@HiltAndroidTest
+@RunWith(AndroidJUnit4::class)
+@Config(application = HiltTestApplication::class)
+class LocalTest {
+
+ @get:Rule
+ val rule = HiltAndroidRule(this)
+
+ @Test
+ fun testFooInjected() {
+ ActivityScenario.launch(MainActivity::class.java).use {
+ it.onActivity { activity ->
+ assertNotNull(activity.foo)
+ }
+ }
+ }
+}
diff --git a/javatests/artifacts/hilt-android/gradleConfigCache/build.gradle b/javatests/artifacts/hilt-android/gradleConfigCache/build.gradle
new file mode 100644
index 000000000..3b0cddeb4
--- /dev/null
+++ b/javatests/artifacts/hilt-android/gradleConfigCache/build.gradle
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+buildscript {
+ ext {
+ kotlin_version = '1.4.20'
+ agp_version = "4.2.0-beta04"
+ }
+ repositories {
+ google()
+ jcenter()
+ mavenLocal()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:$agp_version"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath 'com.google.dagger:hilt-android-gradle-plugin:LOCAL-SNAPSHOT'
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ mavenCentral()
+ mavenLocal()
+ }
+}
diff --git a/javatests/artifacts/hilt-android/gradleConfigCache/gradle.properties b/javatests/artifacts/hilt-android/gradleConfigCache/gradle.properties
new file mode 100644
index 000000000..1813493a2
--- /dev/null
+++ b/javatests/artifacts/hilt-android/gradleConfigCache/gradle.properties
@@ -0,0 +1,8 @@
+android.useAndroidX=true
+android.enableJetifier=true
+
+# Error out if an issue is found that disallows the configuration cache.
+# These options along with this app being built in presubmit helps us cache
+# changes that would cause config cache to be disabled via the HiltGradlePlugin.
+org.gradle.unsafe.configuration-cache=ERROR
+org.gradle.unsafe.configuration-cache.max-problems=0
diff --git a/javatests/artifacts/hilt-android/gradleConfigCache/gradle/wrapper/gradle-wrapper.jar b/javatests/artifacts/hilt-android/gradleConfigCache/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..62d4c0535
--- /dev/null
+++ b/javatests/artifacts/hilt-android/gradleConfigCache/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/javatests/artifacts/hilt-android/gradleConfigCache/gradle/wrapper/gradle-wrapper.properties b/javatests/artifacts/hilt-android/gradleConfigCache/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..4d9ca1649
--- /dev/null
+++ b/javatests/artifacts/hilt-android/gradleConfigCache/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/javatests/artifacts/hilt-android/gradleConfigCache/gradlew b/javatests/artifacts/hilt-android/gradleConfigCache/gradlew
new file mode 100755
index 000000000..fbd7c5158
--- /dev/null
+++ b/javatests/artifacts/hilt-android/gradleConfigCache/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/javatests/artifacts/hilt-android/gradleConfigCache/gradlew.bat b/javatests/artifacts/hilt-android/gradleConfigCache/gradlew.bat
new file mode 100644
index 000000000..a9f778a7a
--- /dev/null
+++ b/javatests/artifacts/hilt-android/gradleConfigCache/gradlew.bat
@@ -0,0 +1,104 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/javatests/artifacts/hilt-android/gradleConfigCache/settings.gradle b/javatests/artifacts/hilt-android/gradleConfigCache/settings.gradle
new file mode 100644
index 000000000..d0d5fee1d
--- /dev/null
+++ b/javatests/artifacts/hilt-android/gradleConfigCache/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name='Gradle Configuration Cache App'
+include ':app'
diff --git a/javatests/artifacts/hilt-android/simple/app/build.gradle b/javatests/artifacts/hilt-android/simple/app/build.gradle
new file mode 100644
index 000000000..98b79e188
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/build.gradle
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+apply plugin: 'com.android.application'
+apply plugin: 'dagger.hilt.android.plugin'
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.2"
+
+ defaultConfig {
+ applicationId "dagger.hilt.android.simple"
+ minSdkVersion 15
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "dagger.hilt.android.simple.SimpleEmulatorTestRunner"
+ }
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+ testOptions {
+ unitTests.includeAndroidResources = true
+ }
+ lintOptions {
+ checkReleaseBuilds = false
+ }
+ sourceSets {
+ String sharedTestDir = 'src/sharedTest/java'
+ test {
+ java.srcDirs += sharedTestDir
+ }
+ androidTest {
+ java.srcDirs += sharedTestDir
+ }
+ }
+}
+
+hilt {
+ enableExperimentalClasspathAggregation = true
+ enableTransformForLocalTests = true
+}
+
+configurations.all {
+ resolutionStrategy.eachDependency { DependencyResolveDetails details ->
+ if ("$dagger_version" == 'LOCAL-SNAPSHOT'
+ && details.requested.group == 'com.google.dagger') {
+ details.useVersion 'LOCAL-SNAPSHOT'
+ details.because 'LOCAL-SNAPSHOT should act as latest version.'
+ }
+ }
+}
+
+dependencies {
+ implementation project(':feature')
+ implementation project(':lib')
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation "com.google.dagger:hilt-android:$dagger_version"
+ annotationProcessor "com.google.dagger:hilt-compiler:$dagger_version"
+
+ testImplementation 'com.google.truth:truth:1.0.1'
+ testImplementation 'junit:junit:4.13'
+ testImplementation 'org.robolectric:robolectric:4.5-alpha-3'
+ testImplementation 'androidx.core:core:1.3.2'
+ testImplementation 'androidx.test.ext:junit:1.1.2'
+ testImplementation 'androidx.test:runner:1.3.0'
+ testImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+ testImplementation "com.google.dagger:hilt-android-testing:$dagger_version"
+ testAnnotationProcessor "com.google.dagger:hilt-compiler:$dagger_version"
+
+ androidTestImplementation 'com.google.truth:truth:1.0.1'
+ androidTestImplementation 'junit:junit:4.13'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+ androidTestImplementation 'androidx.test:runner:1.3.0'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+ androidTestImplementation "com.google.dagger:hilt-android-testing:$dagger_version"
+ androidTestAnnotationProcessor "com.google.dagger:hilt-compiler:$dagger_version"
+
+ // To help us catch usages of Guava APIs for Java 8 in the '-jre' variant.
+ annotationProcessor'com.google.guava:guava:28.1-android'
+ testAnnotationProcessor'com.google.guava:guava:28.1-android'
+ androidTestAnnotationProcessor'com.google.guava:guava:28.1-android'
+
+ // To help us catch version skew related issues in hilt extensions.
+ // TODO(bcorso): Add examples testing the actual API.
+ implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha01'
+ implementation 'androidx.hilt:hilt-work:1.0.0-alpha01'
+ annotationProcessor 'androidx.hilt:hilt-compiler:1.0.0-alpha01'
+ testAnnotationProcessor 'androidx.hilt:hilt-compiler:1.0.0-alpha01'
+ androidTestAnnotationProcessor 'androidx.hilt:hilt-compiler:1.0.0-alpha01'
+}
diff --git a/javatests/artifacts/hilt-android/simple/app/src/androidTest/java/dagger/hilt/android/simple/SimpleEmulatorTestRunner.java b/javatests/artifacts/hilt-android/simple/app/src/androidTest/java/dagger/hilt/android/simple/SimpleEmulatorTestRunner.java
new file mode 100644
index 000000000..6ee721c84
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/src/androidTest/java/dagger/hilt/android/simple/SimpleEmulatorTestRunner.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simple;
+
+import android.app.Application;
+import android.content.Context;
+import androidx.test.runner.AndroidJUnitRunner;
+import dagger.hilt.android.testing.HiltTestApplication;
+
+/** A custom runner to setup the emulator application class for tests. */
+public final class SimpleEmulatorTestRunner extends AndroidJUnitRunner {
+
+ @Override
+ public Application newApplication(ClassLoader cl, String className, Context context)
+ throws ClassNotFoundException, IllegalAccessException, InstantiationException {
+ return super.newApplication(cl, HiltTestApplication.class.getName(), context);
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simple/app/src/debug/AndroidManifest.xml b/javatests/artifacts/hilt-android/simple/app/src/debug/AndroidManifest.xml
new file mode 100644
index 000000000..14d33b0c7
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.hilt.android.simple">
+
+ <application>
+ <activity
+ android:name=".Injection1Test$TestActivity"
+ android:theme="@style/Theme.AppCompat.Light"
+ android:exported="false" />
+ <activity
+ android:name=".Injection2Test$TestActivity"
+ android:theme="@style/Theme.AppCompat.Light"
+ android:exported="false"/>
+ <activity
+ android:name=".ActivityScenarioRuleTest$TestActivity"
+ android:theme="@style/Theme.AppCompat.Light"
+ android:exported="false"/>
+ </application>
+</manifest>
diff --git a/javatests/artifacts/hilt-android/simple/app/src/main/AndroidManifest.xml b/javatests/artifacts/hilt-android/simple/app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..0b12b13d8
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/src/main/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.hilt.android.simple">
+
+ <application android:name=".SimpleApplication" android:label="@string/appName">
+ <activity
+ android:name=".SimpleActivity"
+ android:theme="@style/Theme.AppCompat.Light"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name=".SettingsActivity"
+ android:theme="@style/Theme.AppCompat.Light"
+ android:exported="false">
+ </activity>
+ </application>
+</manifest>
diff --git a/javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/Model.java b/javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/Model.java
new file mode 100644
index 000000000..9965b2e30
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/Model.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simple;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/** Qualifies bindings relating to {@link android.os.Build#MODEL}. */
+@Qualifier
+@Retention(RUNTIME)
+@Documented
+@interface Model {}
diff --git a/javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/ModelModule.java b/javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/ModelModule.java
new file mode 100644
index 000000000..9a63f1733
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/ModelModule.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simple;
+
+import static android.os.Build.MODEL;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.hilt.InstallIn;
+import dagger.hilt.components.SingletonComponent;
+
+@Module
+@InstallIn(SingletonComponent.class)
+final class ModelModule {
+ @Provides
+ @Model
+ static String provideModel() {
+ return MODEL;
+ }
+
+ private ModelModule() {}
+}
diff --git a/javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/SimpleActivity.java b/javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/SimpleActivity.java
new file mode 100644
index 000000000..5d16cd40b
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/SimpleActivity.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simple;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Button;
+import android.widget.TextView;
+import androidx.appcompat.app.AppCompatActivity;
+import dagger.hilt.android.AndroidEntryPoint;
+import dagger.hilt.android.simple.feature.FeatureActivity;
+import dagger.hilt.android.simple.lib.Thing;
+import javax.inject.Inject;
+
+/** The main activity of the application. */
+@AndroidEntryPoint
+public class SimpleActivity extends AppCompatActivity {
+ private static final String TAG = SimpleActivity.class.getSimpleName();
+
+ @Inject @UserName String userName;
+ @Inject @Model String model;
+ @Inject Thing thing;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.i(TAG, "Injected with userName and model: " + userName + ", " + model);
+
+ setContentView(R.layout.activity_main);
+
+ ((TextView) findViewById(R.id.greeting))
+ .setText(getResources().getString(R.string.welcome, userName, model));
+
+ Button featureButton = (Button) findViewById(R.id.goto_feature);
+ featureButton.setOnClickListener(
+ view -> startActivity(new Intent(this, FeatureActivity.class)));
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/SimpleApplication.java b/javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/SimpleApplication.java
new file mode 100644
index 000000000..3c7d1f30f
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/SimpleApplication.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simple;
+
+import android.app.Application;
+import android.util.Log;
+import dagger.hilt.android.HiltAndroidApp;
+import javax.inject.Inject;
+
+/**
+ * A simple, skeletal application that demonstrates a dependency-injected application using the
+ * utilities in {@code Hilt} in Android.
+ */
+@HiltAndroidApp
+public class SimpleApplication extends Application {
+ private static final String TAG = SimpleApplication.class.getSimpleName();
+
+ @Inject @Model String model;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.i(TAG, "Injected with model: " + model);
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/UserName.java b/javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/UserName.java
new file mode 100644
index 000000000..19f0679c0
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/UserName.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simple;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+/** Qualifies bindings relating to the user name. */
+@Qualifier
+@Retention(RUNTIME)
+@Documented
+@interface UserName {}
diff --git a/javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/UserNameModule.java b/javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/UserNameModule.java
new file mode 100644
index 000000000..26d52df1d
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/src/main/java/dagger/hilt/android/simple/UserNameModule.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simple;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.components.ActivityComponent;
+
+@Module
+@InstallIn(ActivityComponent.class)
+final class UserNameModule {
+ @UserName
+ @Provides
+ static String provideUserName() {
+ return "ProdUser";
+ }
+
+ private UserNameModule() {}
+}
diff --git a/javatests/artifacts/hilt-android/simple/app/src/main/res/layout/activity_main.xml b/javatests/artifacts/hilt-android/simple/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 000000000..b78610698
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/background_light">
+
+ <TextView
+ android:id="@+id/greeting"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:textColor="@android:color/primary_text_light"
+ />
+
+ <Button
+ android:id="@+id/goto_feature"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/greeting"
+ android:text="@string/navigateToFeature"
+ />
+</RelativeLayout>
diff --git a/javatests/artifacts/hilt-android/simple/app/src/main/res/values/strings.xml b/javatests/artifacts/hilt-android/simple/app/src/main/res/values/strings.xml
new file mode 100644
index 000000000..c8f9b8a24
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/src/main/res/values/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <!--The app name [CHAR_LIMIT=20]-->
+ <string name="appName">Simple Hilt Android</string>
+
+ <!--The greeting message [CHAR_LIMIT=100]-->
+ <string name="welcome">Hello, %1$s! You are on build %2$s.</string>
+
+ <!--The feature button message [CHAR_LIMIT=100]-->
+ <string name="navigateToFeature">Navigate to a feature!</string>
+</resources>
diff --git a/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/ActivityScenarioRuleTest.java b/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/ActivityScenarioRuleTest.java
new file mode 100644
index 000000000..b49e666fe
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/ActivityScenarioRuleTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simple;
+
+import static androidx.lifecycle.Lifecycle.State.RESUMED;
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.hilt.android.AndroidEntryPoint;
+import dagger.hilt.android.testing.BindValue;
+import dagger.hilt.android.testing.HiltAndroidRule;
+import dagger.hilt.android.testing.HiltAndroidTest;
+import javax.inject.Inject;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests that {@link ActivityScenarioRule} works with Hilt tests. */
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+public final class ActivityScenarioRuleTest {
+ private static final String STR_VALUE = "STR_VALUE";
+
+ /** An activity to test injection. */
+ @AndroidEntryPoint
+ public static final class TestActivity extends AppCompatActivity {
+ @Inject String str;
+ }
+
+ @Rule(order = 0) public HiltAndroidRule hiltRule = new HiltAndroidRule(this);
+
+ @Rule(order = 1)
+ public ActivityScenarioRule<TestActivity> scenarioRule =
+ new ActivityScenarioRule<>(TestActivity.class);
+
+ @BindValue String str = STR_VALUE;
+
+ @Test
+ public void testState() {
+ assertThat(scenarioRule.getScenario().getState()).isEqualTo(RESUMED);
+ }
+
+ @Test
+ public void testInjection() {
+ scenarioRule
+ .getScenario()
+ .onActivity(activity -> assertThat(activity.str).isEqualTo(STR_VALUE));
+ }
+
+
+ @Test
+ public void verifyMainActivity() {
+ try (ActivityScenario<TestActivity> scenario =
+ ActivityScenario.launch(TestActivity.class)) {
+ scenario.onActivity(
+ activity -> {
+ assertThat(activity.getClass().getSuperclass().getSimpleName())
+ .isEqualTo("Hilt_ActivityScenarioRuleTest_TestActivity");
+ }
+ );
+ }
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/BindValueTest.java b/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/BindValueTest.java
new file mode 100644
index 000000000..e77f60583
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/BindValueTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simple;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.google.common.collect.ImmutableSet;
+import dagger.MapKey;
+import dagger.hilt.EntryPoint;
+import dagger.hilt.EntryPoints;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.testing.BindElementsIntoSet;
+import dagger.hilt.android.testing.BindValue;
+import dagger.hilt.android.testing.BindValueIntoMap;
+import dagger.hilt.android.testing.BindValueIntoSet;
+import dagger.hilt.android.testing.HiltAndroidRule;
+import dagger.hilt.android.testing.HiltAndroidTest;
+import dagger.hilt.components.SingletonComponent;
+import java.util.Map;
+import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** A simple test using Hilt. */
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+public final class BindValueTest {
+ private static final String BIND_VALUE_STRING = "BIND_VALUE_STRING";
+ private static final String TEST_QUALIFIER = "TEST_QUALIFIER";
+
+ private static final String SET_STRING_1 = "SetString1";
+ private static final String SET_STRING_2 = "SetString2";
+ private static final String SET_STRING_3 = "SetString3";
+
+ private static final String KEY_1 = "Key1";
+ private static final String KEY_2 = "Key2";
+ private static final String VALUE_1 = "Value1";
+ private static final String VALUE_2 = "Value2";
+ private static final String VALUE_3 = "Value3";
+
+ private static final Integer SET_INT_1 = 1;
+ private static final Integer SET_INT_2 = 2;
+ private static final Integer SET_INT_3 = 3;
+
+ @EntryPoint
+ @InstallIn(SingletonComponent.class)
+ interface BindValueEntryPoint {
+ @Named(TEST_QUALIFIER)
+ String bindValueString();
+
+ Set<String> getStringSet();
+ }
+
+ @Rule public HiltAndroidRule rule = new HiltAndroidRule(this);
+
+ @BindValue
+ @Named(TEST_QUALIFIER)
+ String bindValueString = BIND_VALUE_STRING;
+
+ @BindElementsIntoSet Set<String> bindElementsSet1 = ImmutableSet.of(SET_STRING_1);
+ @BindElementsIntoSet Set<String> bindElementsSet2 = ImmutableSet.of(SET_STRING_2);
+
+ @BindValueIntoMap
+ @MyMapKey(KEY_1)
+ String boundValue1 = VALUE_1;
+
+ @BindValueIntoMap
+ @MyMapKey(KEY_2)
+ String boundValue2 = VALUE_2;
+
+ @BindValueIntoSet Integer bindValueSetInt1 = SET_INT_1;
+ @BindValueIntoSet Integer bindValueSetInt2 = SET_INT_2;
+
+ @Inject Set<String> stringSet;
+ @Inject Provider<Set<String>> providedStringSet;
+ @Inject Provider<Map<String, String>> mapProvider;
+ @Inject Set<Integer> intSet;
+ @Inject Provider<Set<Integer>> providedIntSet;
+
+ @Test
+ public void testBindValueFieldIsProvided() throws Exception {
+ assertThat(bindValueString).isEqualTo(BIND_VALUE_STRING);
+ assertThat(getBinding()).isEqualTo(BIND_VALUE_STRING);
+ }
+
+ @Test
+ public void testBindValueIsMutable() throws Exception {
+ bindValueString = "newValue";
+ assertThat(getBinding()).isEqualTo("newValue");
+ }
+
+ @Test
+ public void testElementsIntoSet() throws Exception {
+ rule.inject();
+ // basic check that initial/default values are properly injected
+ assertThat(providedStringSet.get()).containsExactly(SET_STRING_1, SET_STRING_2);
+ // Test empty sets (something that cannot be done with @BindValueIntoSet)
+ bindElementsSet1 = ImmutableSet.of();
+ bindElementsSet2 = ImmutableSet.of();
+ assertThat(providedStringSet.get()).isEmpty();
+ // Test multiple elements in set.
+ bindElementsSet1 = ImmutableSet.of(SET_STRING_1, SET_STRING_2, SET_STRING_3);
+ assertThat(providedStringSet.get()).containsExactly(SET_STRING_1, SET_STRING_2, SET_STRING_3);
+ }
+
+ @Test
+ public void testBindValueIntoMap() throws Exception {
+ rule.inject();
+ Map<String, String> oldMap = mapProvider.get();
+ assertThat(oldMap).containsExactly(KEY_1, VALUE_1, KEY_2, VALUE_2);
+ boundValue1 = VALUE_3;
+ Map<String, String> newMap = mapProvider.get();
+ assertThat(oldMap).containsExactly(KEY_1, VALUE_1, KEY_2, VALUE_2);
+ assertThat(newMap).containsExactly(KEY_1, VALUE_3, KEY_2, VALUE_2);
+ }
+
+ @Test
+ public void testBindValueIntoSet() throws Exception {
+ rule.inject();
+ // basic check that initial/default values are properly injected
+ assertThat(providedIntSet.get()).containsExactly(SET_INT_1, SET_INT_2);
+ bindValueSetInt1 = SET_INT_3;
+ // change the value for bindValueSetString from 1 to 3
+ assertThat(providedIntSet.get()).containsExactly(SET_INT_2, SET_INT_3);
+ }
+
+ @MapKey
+ @interface MyMapKey {
+ String value();
+ }
+
+ private static String getBinding() {
+ return EntryPoints.get(getApplicationContext(), BindValueEntryPoint.class).bindValueString();
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/DelayComponentReadyTest.java b/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/DelayComponentReadyTest.java
new file mode 100644
index 000000000..81afb6e70
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/DelayComponentReadyTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simple;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.hilt.EntryPoint;
+import dagger.hilt.EntryPoints;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.testing.BindValue;
+import dagger.hilt.android.testing.HiltAndroidRule;
+import dagger.hilt.android.testing.HiltAndroidTest;
+import dagger.hilt.android.testing.OnComponentReadyRunner;
+import dagger.hilt.components.SingletonComponent;
+import java.util.concurrent.atomic.AtomicReference;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** A simple test using Hilt. */
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+public final class DelayComponentReadyTest {
+
+ private static final String EXPECTED_VALUE = "expected";
+
+ @EntryPoint
+ @InstallIn(SingletonComponent.class)
+ public interface FooEntryPoint {
+ String foo();
+ }
+
+ @Rule public HiltAndroidRule rule = new HiltAndroidRule(this).delayComponentReady();
+
+ @BindValue String foo;
+
+ @Test
+ public void testLateBindValue() throws Exception {
+ AtomicReference<String> fooRef = new AtomicReference<>();
+ OnComponentReadyRunner.addListener(
+ ApplicationProvider.getApplicationContext(),
+ FooEntryPoint.class,
+ entryPoint -> fooRef.set(entryPoint.foo()));
+
+ // Test that setting the listener before the component is ready doesn't run the listener.
+ assertThat(fooRef.get()).isNull();
+
+ foo = EXPECTED_VALUE;
+ rule.componentReady().inject();
+ assertThat(EntryPoints.get(getApplicationContext(), FooEntryPoint.class).foo())
+ .isEqualTo(EXPECTED_VALUE);
+ }
+
+ @Test
+ public void testUnitializedBindValue_fails() throws Exception {
+ OnComponentReadyRunner.addListener(
+ ApplicationProvider.getApplicationContext(), FooEntryPoint.class, FooEntryPoint::foo);
+
+ // foo not set
+ NullPointerException expected = assertThrows(NullPointerException.class, rule::componentReady);
+ // This is not the best error message, but it is equivalent to the message from a regular
+ // (non-delayed) @BindValue returning null;
+ assertThat(expected)
+ .hasMessageThat()
+ .isEqualTo("Cannot return null from a non-@Nullable @Provides method");
+ }
+
+ @Test
+ public void testDoubleComponentReady_fails() throws Exception {
+ foo = EXPECTED_VALUE;
+ rule.componentReady();
+ IllegalStateException expected =
+ assertThrows(IllegalStateException.class, rule::componentReady);
+ assertThat(expected).hasMessageThat().isEqualTo("Called componentReady() multiple times");
+ }
+
+ @Test
+ public void testMissingComponentReady_fails() throws Exception {
+ // componentReady not called
+ foo = EXPECTED_VALUE;
+ IllegalStateException expected = assertThrows(IllegalStateException.class, rule::inject);
+ assertThat(expected)
+ .hasMessageThat()
+ .isEqualTo("Called inject() before calling componentReady()");
+ }
+
+ @Test
+ public void testDelayComponentReadyAfterStart_fails() throws Exception {
+ IllegalStateException expected =
+ assertThrows(IllegalStateException.class, rule::delayComponentReady);
+ assertThat(expected)
+ .hasMessageThat()
+ .isEqualTo("Called delayComponentReady after test execution started");
+ // Prevents failure due to never calling componentReady()
+ foo = EXPECTED_VALUE;
+ rule.componentReady();
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/Injection1Test.java b/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/Injection1Test.java
new file mode 100644
index 000000000..6e5ad6a90
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/Injection1Test.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simple;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.Module;
+import dagger.Provides;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.AndroidEntryPoint;
+import dagger.hilt.android.components.ActivityComponent;
+import dagger.hilt.android.testing.HiltAndroidRule;
+import dagger.hilt.android.testing.HiltAndroidTest;
+import dagger.hilt.components.SingletonComponent;
+import javax.inject.Inject;
+import javax.inject.Named;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests basic injection APIs, and that bindings don't conflict with {@link Injection2Test}. */
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+public final class Injection1Test {
+ private static final String APPLICATION_QUALIFIER = "APPLICATION_QUALIFIER";
+ private static final String ACTIVITY_QUALIFIER = "ACTIVITY_QUALIFIER";
+ private static final String APPLICATION_VALUE = "Injection1Test_ApplicationValue";
+ private static final String ACTIVITY_VALUE = "Injection1Test_ActivityValue";
+
+ @Module
+ @InstallIn(SingletonComponent.class)
+ interface TestApplicationModule {
+ @Provides
+ @Named(APPLICATION_QUALIFIER)
+ static String provideString() {
+ return APPLICATION_VALUE;
+ }
+ }
+
+ @Module
+ @InstallIn(ActivityComponent.class)
+ interface TestActivityModule {
+ @Provides
+ @Named(ACTIVITY_QUALIFIER)
+ static String provideString() {
+ return ACTIVITY_VALUE;
+ }
+ }
+
+ /** Test activity used to test activity injection */
+ @AndroidEntryPoint
+ public static final class TestActivity extends AppCompatActivity {
+ @Inject @Named(ACTIVITY_QUALIFIER) String activityValue;
+ }
+
+ @Rule public HiltAndroidRule rule = new HiltAndroidRule(this);
+
+ @Inject @Named(APPLICATION_QUALIFIER) String applicationValue;
+
+ @Test
+ public void testApplicationInjection() throws Exception {
+ assertThat(applicationValue).isNull();
+ rule.inject();
+ assertThat(applicationValue).isEqualTo(APPLICATION_VALUE);
+ }
+
+ @Test
+ public void testActivityInjection() throws Exception {
+ try (ActivityScenario<TestActivity> scenario = ActivityScenario.launch(TestActivity.class)) {
+ scenario.onActivity(activity -> assertThat(activity.activityValue).isEqualTo(ACTIVITY_VALUE));
+ }
+ }
+
+ @Test
+ public void testSuperClassTransformation() {
+ try (ActivityScenario<TestActivity> scenario = ActivityScenario.launch(TestActivity.class)) {
+ scenario.onActivity(
+ activity ->
+ assertThat(activity.getClass().getSuperclass().getSimpleName())
+ .isEqualTo("Hilt_Injection1Test_TestActivity"));
+ }
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/Injection2Test.java b/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/Injection2Test.java
new file mode 100644
index 000000000..1ba3c2fed
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/Injection2Test.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simple;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.Module;
+import dagger.Provides;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.AndroidEntryPoint;
+import dagger.hilt.android.components.ActivityComponent;
+import dagger.hilt.android.testing.HiltAndroidRule;
+import dagger.hilt.android.testing.HiltAndroidTest;
+import dagger.hilt.components.SingletonComponent;
+import javax.inject.Inject;
+import javax.inject.Named;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests basic injection APIs, and that bindings don't conflict with {@link Injection1Test}. */
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+public final class Injection2Test {
+ private static final String APPLICATION_QUALIFIER = "APPLICATION_QUALIFIER";
+ private static final String ACTIVITY_QUALIFIER = "ACTIVITY_QUALIFIER";
+ private static final String APPLICATION_VALUE = "Injection2Test_ApplicationValue";
+ private static final String ACTIVITY_VALUE = "Injection2Test_ActivityValue";
+
+ @Module
+ @InstallIn(SingletonComponent.class)
+ interface TestApplicationModule {
+ @Provides
+ @Named(APPLICATION_QUALIFIER)
+ static String provideString() {
+ return APPLICATION_VALUE;
+ }
+ }
+
+ @Module
+ @InstallIn(ActivityComponent.class)
+ interface TestActivityModule {
+ @Provides
+ @Named(ACTIVITY_QUALIFIER)
+ static String provideString() {
+ return ACTIVITY_VALUE;
+ }
+ }
+
+ /** Test activity used to test activity injection */
+ @AndroidEntryPoint
+ public static final class TestActivity extends AppCompatActivity {
+ @Inject @Named(ACTIVITY_QUALIFIER) String activityValue;
+ }
+
+ @Rule public HiltAndroidRule rule = new HiltAndroidRule(this);
+
+ @Inject @Named(APPLICATION_QUALIFIER) String applicationValue;
+
+ @Test
+ public void testApplicationInjection() throws Exception {
+ assertThat(applicationValue).isNull();
+ rule.inject();
+ assertThat(applicationValue).isEqualTo(APPLICATION_VALUE);
+ }
+
+ @Test
+ public void testActivityInjection() throws Exception {
+ try (ActivityScenario<TestActivity> scenario = ActivityScenario.launch(TestActivity.class)) {
+ scenario.onActivity(activity -> assertThat(activity.activityValue).isEqualTo(ACTIVITY_VALUE));
+ }
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/ModuleTest.java b/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/ModuleTest.java
new file mode 100644
index 000000000..e1f442198
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/ModuleTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simple;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.testing.HiltAndroidRule;
+import dagger.hilt.android.testing.HiltAndroidTest;
+import dagger.hilt.components.SingletonComponent;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+import javax.inject.Inject;
+import javax.inject.Qualifier;
+import javax.inject.Singleton;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests basic functionality of using modules in test. */
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+public final class ModuleTest {
+ @Rule public final HiltAndroidRule rules = new HiltAndroidRule(this);
+
+ /** Qualifier for distinguishing test Strings, */
+ @Qualifier
+ @Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
+ public @interface TestQualifier {
+ int value();
+ }
+
+ @Inject
+ @TestQualifier(1)
+ String testString1;
+
+ @Inject
+ @TestQualifier(2)
+ String testString2;
+
+ @Inject
+ @TestQualifier(3)
+ String testString3;
+
+ @Inject
+ @TestQualifier(4)
+ String testString4;
+
+ @Inject FooImpl fooImpl;
+ @Inject Foo foo;
+
+ /**
+ * Module which is used to test if non-static test modules get registered in the component
+ * correctly.
+ */
+ @Module
+ @InstallIn(SingletonComponent.class)
+ public final class NonStaticModuleNonStaticProvides {
+ @Provides
+ @TestQualifier(1)
+ String provideString() {
+ return "1";
+ }
+ }
+
+ /**
+ * Module which is used to test if static test modules get registered in the component correctly.
+ */
+ @Module
+ @InstallIn(SingletonComponent.class)
+ public static final class StaticModuleStaticProvides {
+ @Provides
+ @TestQualifier(2)
+ static String provideString() {
+ return "2";
+ }
+
+ private StaticModuleStaticProvides() {}
+ }
+
+ /**
+ * Module which is used to test if static test modules with a non-static methods get registered in
+ * the component correctly.
+ */
+ @Module
+ @InstallIn(SingletonComponent.class)
+ public static final class StaticModuleNonStaticProvidesDefaultConstructor {
+ @Provides
+ @TestQualifier(3)
+ String provideString() {
+ return "3";
+ }
+ }
+
+ /**
+ * Module which is used to test if abstract test modules get registered in the component
+ * correctly.
+ */
+ @Module
+ @InstallIn(SingletonComponent.class)
+ public abstract static class AbstractModuleStaticProvides {
+ @Provides
+ @TestQualifier(4)
+ static String provideString() {
+ return "4";
+ }
+
+ private AbstractModuleStaticProvides() {}
+ }
+
+ /**
+ * Module which is used to test if abstract test modules with a binds method get registered in the
+ * component correctly.
+ */
+ @Module
+ @InstallIn(SingletonComponent.class)
+ public abstract static class AbstractModuleBindsMethod {
+ @Binds
+ abstract Foo foo(FooImpl fooImpl);
+ }
+
+ interface Foo {}
+
+ @Singleton
+ static final class FooImpl implements Foo {
+ @Inject
+ FooImpl() {}
+ }
+
+ @Test
+ public void testInjection() throws Exception {
+ rules.inject();
+ assertEquals("1", testString1);
+ assertEquals("2", testString2);
+ assertEquals("3", testString3);
+ assertEquals("4", testString4);
+ assertEquals(fooImpl, foo);
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/SimpleActivityTest.java b/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/SimpleActivityTest.java
new file mode 100644
index 000000000..f2e2e77f0
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/SimpleActivityTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simple;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.hilt.android.simple.lib.ThingImpl;
+import dagger.hilt.android.testing.BindValue;
+import dagger.hilt.android.testing.HiltAndroidRule;
+import dagger.hilt.android.testing.HiltAndroidTest;
+import dagger.hilt.android.testing.UninstallModules;
+import javax.inject.Inject;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** A simple test using Hilt that can be run with instrumentation or Robolectric tests. */
+@UninstallModules({
+ UserNameModule.class,
+ ModelModule.class
+})
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+public final class SimpleActivityTest {
+ @Rule public HiltAndroidRule rule = new HiltAndroidRule(this);
+
+ @BindValue @UserName String fakeUserName = "FakeUser";
+ @BindValue @Model String fakeModel = "FakeModel";
+
+ @Inject @UserName String injectedUserName;
+ @Inject @Model String injectedModel;
+
+ @Test
+ public void testInjectedUserName() throws Exception {
+ assertThat(injectedUserName).isNull();
+ rule.inject();
+ assertThat(injectedUserName).isEqualTo("FakeUser");
+ }
+
+ @Test
+ public void testInjectedModel() throws Exception {
+ assertThat(injectedModel).isNull();
+ rule.inject();
+ assertThat(injectedModel).isEqualTo("FakeModel");
+ }
+
+ @Test
+ public void testActivityInject() throws Exception {
+ try (ActivityScenario<SimpleActivity> scenario =
+ ActivityScenario.launch(SimpleActivity.class)) {
+ onView(withId(R.id.greeting))
+ .check(matches(withText("Hello, FakeUser! You are on build FakeModel.")));
+ } catch (RuntimeException e) {
+ // Just skip this test if the root view never becomes active.
+ // This issue occurs sporadically in emulator tests and causes the test to be flaky.
+ // It is likely caused by a race between our activity and a dialog or lock screen but
+ // it's difficult to debug this since it only fails in CI builds.
+ // TODO(b/176111885): Remove this once this bug is fixed.
+ if (!e.getMessage().startsWith("Waited for the root of the view hierarchy")) {
+ throw e;
+ }
+ }
+ }
+
+ @Test
+ public void verifyMainActivity() {
+ try (ActivityScenario<SimpleActivity> scenario =
+ ActivityScenario.launch(SimpleActivity.class)) {
+ scenario.onActivity(
+ activity -> {
+ assertThat(activity.getClass().getSuperclass().getSimpleName())
+ .isEqualTo("Hilt_SimpleActivity");
+ }
+ );
+ }
+ }
+
+ @Test
+ public void verifyThing() {
+ try (ActivityScenario<SimpleActivity> scenario =
+ ActivityScenario.launch(SimpleActivity.class)) {
+ scenario.onActivity(activity -> assertThat(activity.thing).isInstanceOf(ThingImpl.class));
+ }
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/TransitiveDependenciesTest.java b/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/TransitiveDependenciesTest.java
new file mode 100644
index 000000000..a4f900d73
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/TransitiveDependenciesTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simple;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+import dagger.hilt.android.simple.feature.FeatureEntryPoints;
+import dagger.hilt.android.testing.HiltAndroidRule;
+import dagger.hilt.android.testing.HiltAndroidTest;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests that entry points in deep dependencies with `implementation` boundaries are available at
+ * runtime.
+ */
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+public class TransitiveDependenciesTest {
+
+ @Rule public HiltAndroidRule rule = new HiltAndroidRule(this);
+
+ @Test
+ public void testEntryPoint() {
+ FeatureEntryPoints.invokeEntryPoints(
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getApplicationContext());
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simple/app/src/test/resources/dagger/hilt/android/simple/robolectric.properties b/javatests/artifacts/hilt-android/simple/app/src/test/resources/dagger/hilt/android/simple/robolectric.properties
new file mode 100644
index 000000000..0234ffe6f
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/src/test/resources/dagger/hilt/android/simple/robolectric.properties
@@ -0,0 +1,2 @@
+sdk=28
+application=dagger.hilt.android.testing.HiltTestApplication \ No newline at end of file
diff --git a/javatests/artifacts/hilt-android/simple/build.gradle b/javatests/artifacts/hilt-android/simple/build.gradle
new file mode 100644
index 000000000..770fabd31
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/build.gradle
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+buildscript {
+ ext {
+ dagger_version = 'LOCAL-SNAPSHOT'
+ kotlin_version = '1.3.61'
+ agp_version = System.getenv('AGP_VERSION') ?: "4.2.0-beta04"
+ }
+ repositories {
+ google()
+ jcenter()
+ mavenLocal()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:$agp_version"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath "com.google.dagger:hilt-android-gradle-plugin:$dagger_version"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ mavenCentral()
+ mavenLocal()
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simple/deep-android-lib/build.gradle b/javatests/artifacts/hilt-android/simple/deep-android-lib/build.gradle
new file mode 100644
index 000000000..e5ed04638
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/deep-android-lib/build.gradle
@@ -0,0 +1,26 @@
+plugins {
+ id 'com.android.library'
+ id 'dagger.hilt.android.plugin'
+}
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.2"
+
+ defaultConfig {
+ minSdkVersion 15
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+ }
+
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+}
+
+dependencies {
+ implementation "com.google.dagger:hilt-android:$dagger_version"
+ annotationProcessor "com.google.dagger:hilt-compiler:$dagger_version"
+} \ No newline at end of file
diff --git a/javatests/artifacts/hilt-android/simple/deep-android-lib/src/main/AndroidManifest.xml b/javatests/artifacts/hilt-android/simple/deep-android-lib/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..49a304edb
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/deep-android-lib/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.hilt.android.simple.deep">
+
+</manifest> \ No newline at end of file
diff --git a/javatests/artifacts/hilt-android/simple/deep-android-lib/src/main/java/dagger/hilt/android/simple/deep/DeepAndroidLib.java b/javatests/artifacts/hilt-android/simple/deep-android-lib/src/main/java/dagger/hilt/android/simple/deep/DeepAndroidLib.java
new file mode 100644
index 000000000..57fd803ea
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/deep-android-lib/src/main/java/dagger/hilt/android/simple/deep/DeepAndroidLib.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simple.deep;
+
+import android.content.Context;
+import dagger.hilt.EntryPoint;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.EntryPointAccessors;
+import dagger.hilt.components.SingletonComponent;
+import javax.inject.Inject;
+
+public class DeepAndroidLib {
+
+ @EntryPoint
+ @InstallIn(SingletonComponent.class)
+ interface LibEntryPoint {
+ DeepAndroidLib getDeepAndroidInstance();
+ }
+
+ @Inject
+ public DeepAndroidLib() {}
+
+ public static DeepAndroidLib getInstance(Context context) {
+ return EntryPointAccessors.fromApplication(context, LibEntryPoint.class)
+ .getDeepAndroidInstance();
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simple/deep-lib/build.gradle b/javatests/artifacts/hilt-android/simple/deep-lib/build.gradle
new file mode 100644
index 000000000..22f815c17
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/deep-lib/build.gradle
@@ -0,0 +1,13 @@
+plugins {
+ id 'java-library'
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_7
+ targetCompatibility = JavaVersion.VERSION_1_7
+}
+
+dependencies {
+ implementation "com.google.dagger:hilt-core:$dagger_version"
+ annotationProcessor "com.google.dagger:hilt-compiler:$dagger_version"
+} \ No newline at end of file
diff --git a/javatests/artifacts/hilt-android/simple/deep-lib/src/main/java/dagger/hilt/android/deep/DeepLib.java b/javatests/artifacts/hilt-android/simple/deep-lib/src/main/java/dagger/hilt/android/deep/DeepLib.java
new file mode 100644
index 000000000..419e53903
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/deep-lib/src/main/java/dagger/hilt/android/deep/DeepLib.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.deep;
+
+import dagger.hilt.EntryPoint;
+import dagger.hilt.EntryPoints;
+import dagger.hilt.InstallIn;
+import dagger.hilt.components.SingletonComponent;
+import javax.inject.Inject;
+
+public class DeepLib {
+
+ @EntryPoint
+ @InstallIn(SingletonComponent.class)
+ interface LibEntryPoint {
+ DeepLib getDeepInstance();
+ }
+
+ @Inject
+ public DeepLib() {}
+
+ public static DeepLib getInstance(Object componentManager) {
+ return EntryPoints.get(componentManager, LibEntryPoint.class).getDeepInstance();
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simple/feature/build.gradle b/javatests/artifacts/hilt-android/simple/feature/build.gradle
new file mode 100644
index 000000000..0e772a8bf
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/feature/build.gradle
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-kapt'
+apply plugin: 'dagger.hilt.android.plugin'
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.2"
+
+ defaultConfig {
+ minSdkVersion 15
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+ }
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+}
+
+kapt {
+ correctErrorTypes true
+}
+
+hilt {
+ enableTransformForLocalTests = true
+}
+
+dependencies {
+ // This is api instead of implementation since Kotlin modules here consumed
+ // by the app need to expose @kotlin.Metadata
+ api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+
+ implementation project(":deep-android-lib")
+ implementation project(":deep-lib")
+
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation "com.google.dagger:hilt-android:$dagger_version"
+ kapt "com.google.dagger:hilt-compiler:$dagger_version"
+}
diff --git a/javatests/artifacts/hilt-android/simple/feature/src/main/AndroidManifest.xml b/javatests/artifacts/hilt-android/simple/feature/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..fe583e551
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/feature/src/main/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.hilt.android.simple.feature">
+ <application>
+ <activity
+ android:name=".FeatureActivity"
+ android:theme="@style/Theme.AppCompat.Light"
+ android:exported="true"/>
+ </application>
+</manifest>
diff --git a/javatests/artifacts/hilt-android/simple/feature/src/main/java/dagger/hilt/android/simple/feature/FeatureActivity.kt b/javatests/artifacts/hilt-android/simple/feature/src/main/java/dagger/hilt/android/simple/feature/FeatureActivity.kt
new file mode 100644
index 000000000..c1931ac9f
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/feature/src/main/java/dagger/hilt/android/simple/feature/FeatureActivity.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simple.feature
+
+import android.os.Bundle
+import android.widget.Button
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import dagger.hilt.android.AndroidEntryPoint
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class FeatureActivity : AppCompatActivity() {
+ @Inject lateinit var counter: FeatureCounter
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.activity_feature)
+
+ findViewById<Button>(R.id.feature_button).setOnClickListener {
+ counter.count++
+ updateCountText()
+ }
+
+ updateCountText()
+ }
+
+ private fun updateCountText() {
+ findViewById<TextView>(R.id.feature_count).text = "The count: ${counter.count}"
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simple/feature/src/main/java/dagger/hilt/android/simple/feature/FeatureCounter.kt b/javatests/artifacts/hilt-android/simple/feature/src/main/java/dagger/hilt/android/simple/feature/FeatureCounter.kt
new file mode 100644
index 000000000..d55bb5a9f
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/feature/src/main/java/dagger/hilt/android/simple/feature/FeatureCounter.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simple.feature
+
+class FeatureCounter(var count: Int)
diff --git a/javatests/artifacts/hilt-android/simple/feature/src/main/java/dagger/hilt/android/simple/feature/FeatureEntryPoints.java b/javatests/artifacts/hilt-android/simple/feature/src/main/java/dagger/hilt/android/simple/feature/FeatureEntryPoints.java
new file mode 100644
index 000000000..c1ca22f54
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/feature/src/main/java/dagger/hilt/android/simple/feature/FeatureEntryPoints.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simple.feature;
+
+import android.content.Context;
+import dagger.hilt.android.deep.DeepLib;
+import dagger.hilt.android.simple.deep.DeepAndroidLib;
+
+public final class FeatureEntryPoints {
+
+ public static void invokeEntryPoints(Context context) {
+ DeepAndroidLib.getInstance(context);
+ DeepLib.getInstance(context);
+ }
+
+ private FeatureEntryPoints() {}
+}
diff --git a/javatests/artifacts/hilt-android/simple/feature/src/main/java/dagger/hilt/android/simple/feature/FeatureModule.kt b/javatests/artifacts/hilt-android/simple/feature/src/main/java/dagger/hilt/android/simple/feature/FeatureModule.kt
new file mode 100644
index 000000000..d513f370c
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/feature/src/main/java/dagger/hilt/android/simple/feature/FeatureModule.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simple.feature
+
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.ActivityRetainedComponent
+import dagger.hilt.android.scopes.ActivityRetainedScoped
+
+@Module
+@InstallIn(ActivityRetainedComponent::class)
+object FeatureModule {
+ @Provides
+ @ActivityRetainedScoped
+ fun provideData() = FeatureCounter(0)
+}
diff --git a/javatests/artifacts/hilt-android/simple/feature/src/main/res/layout/activity_feature.xml b/javatests/artifacts/hilt-android/simple/feature/src/main/res/layout/activity_feature.xml
new file mode 100644
index 000000000..99adcfd48
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/feature/src/main/res/layout/activity_feature.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/background_light">
+
+ <TextView
+ android:id="@+id/feature_count"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:textColor="@android:color/primary_text_light"
+ />
+
+ <Button
+ android:id="@+id/feature_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/feature_count"
+ android:text="Count++"
+ />
+</RelativeLayout>
diff --git a/javatests/artifacts/hilt-android/simple/gradle.properties b/javatests/artifacts/hilt-android/simple/gradle.properties
new file mode 100644
index 000000000..646c51b97
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/gradle.properties
@@ -0,0 +1,2 @@
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/javatests/artifacts/hilt-android/simple/gradle/wrapper/gradle-wrapper.jar b/javatests/artifacts/hilt-android/simple/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..5c2d1cf01
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/javatests/artifacts/hilt-android/simple/gradle/wrapper/gradle-wrapper.properties b/javatests/artifacts/hilt-android/simple/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..4d9ca1649
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/javatests/artifacts/hilt-android/simple/gradlew b/javatests/artifacts/hilt-android/simple/gradlew
new file mode 100755
index 000000000..b0d6d0ab5
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/gradlew
@@ -0,0 +1,188 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/javatests/artifacts/hilt-android/simple/lib/build.gradle b/javatests/artifacts/hilt-android/simple/lib/build.gradle
new file mode 100644
index 000000000..d67be3bde
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/lib/build.gradle
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ id 'java-library'
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+dependencies {
+ implementation "com.google.dagger:hilt-core:$dagger_version"
+ annotationProcessor "com.google.dagger:hilt-compiler:$dagger_version"
+}
diff --git a/javatests/artifacts/hilt-android/simple/lib/src/main/java/dagger/hilt/android/simple/lib/Thing.java b/javatests/artifacts/hilt-android/simple/lib/src/main/java/dagger/hilt/android/simple/lib/Thing.java
new file mode 100644
index 000000000..baded09ee
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/lib/src/main/java/dagger/hilt/android/simple/lib/Thing.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simple.lib;
+
+/** A thing. */
+public interface Thing {}
diff --git a/javatests/artifacts/hilt-android/simple/lib/src/main/java/dagger/hilt/android/simple/lib/ThingImpl.java b/javatests/artifacts/hilt-android/simple/lib/src/main/java/dagger/hilt/android/simple/lib/ThingImpl.java
new file mode 100644
index 000000000..f6a628ecf
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/lib/src/main/java/dagger/hilt/android/simple/lib/ThingImpl.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simple.lib;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.hilt.InstallIn;
+import dagger.hilt.components.SingletonComponent;
+import javax.inject.Inject;
+
+/** An implementation of a thing. */
+public final class ThingImpl implements Thing {
+ @Inject
+ ThingImpl() {}
+
+ @Module
+ @InstallIn(SingletonComponent.class)
+ interface FooStringModule {
+ @Binds Thing bind(ThingImpl impl);
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simple/settings.gradle b/javatests/artifacts/hilt-android/simple/settings.gradle
new file mode 100644
index 000000000..bad895d28
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/settings.gradle
@@ -0,0 +1,6 @@
+rootProject.name='Simple Hilt Android'
+include ':app'
+include ':feature'
+include ':lib'
+include ':deep-android-lib'
+include ':deep-lib' \ No newline at end of file
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/android-library/build.gradle b/javatests/artifacts/hilt-android/simpleKotlin/android-library/build.gradle
new file mode 100644
index 000000000..383c4be2f
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/android-library/build.gradle
@@ -0,0 +1,31 @@
+plugins {
+ id 'com.android.library'
+ id 'kotlin-android'
+ id 'kotlin-kapt'
+}
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.2"
+
+ defaultConfig {
+ minSdkVersion 15
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+}
+
+dependencies {
+ implementation project(':deep-android-lib')
+ implementation project(':deep-kotlin-lib')
+ implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'
+ kapt 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'
+} \ No newline at end of file
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/android-library/src/main/AndroidManifest.xml b/javatests/artifacts/hilt-android/simpleKotlin/android-library/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..c60ba7319
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/android-library/src/main/AndroidManifest.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.hilt.android.simpleKotlin.lib">
+
+</manifest> \ No newline at end of file
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/android-library/src/main/java/dagger/hilt/android/simpleKotlin/lib/AndroidLibraryEntryPoints.kt b/javatests/artifacts/hilt-android/simpleKotlin/android-library/src/main/java/dagger/hilt/android/simpleKotlin/lib/AndroidLibraryEntryPoints.kt
new file mode 100644
index 000000000..cb10c8be7
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/android-library/src/main/java/dagger/hilt/android/simpleKotlin/lib/AndroidLibraryEntryPoints.kt
@@ -0,0 +1,12 @@
+package dagger.hilt.android.simpleKotlin.lib
+
+import android.content.Context
+import dagger.hilt.android.simpleKotlin.deep.DeepAndroidLib
+import dagger.hilt.android.simpleKotlin.deep.DeepLib
+
+object AndroidLibraryEntryPoints {
+ fun invokeEntryPoints(context: Context) {
+ DeepAndroidLib.getInstance(context)
+ DeepLib.getInstance(context)
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/build.gradle b/javatests/artifacts/hilt-android/simpleKotlin/app/build.gradle
new file mode 100644
index 000000000..ecfaf6e92
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/build.gradle
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'dagger.hilt.android.plugin'
+apply plugin: 'kotlin-kapt'
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.2"
+
+ defaultConfig {
+ applicationId "dagger.hilt.android.simpleKotlin"
+ minSdkVersion 15
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "dagger.hilt.android.example.gradle.simpleKotlin.TestRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled true
+ shrinkResources true
+ }
+ }
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+ testOptions {
+ unitTests.includeAndroidResources = true
+ }
+ lintOptions {
+ checkReleaseBuilds = false
+ }
+ sourceSets {
+ String sharedTestDir = 'src/sharedTest/java'
+ test {
+ java.srcDirs += sharedTestDir
+ }
+ androidTest {
+ java.srcDirs += sharedTestDir
+ }
+ }
+}
+
+hilt {
+ enableExperimentalClasspathAggregation = true
+ enableTransformForLocalTests = true
+}
+
+dependencies {
+ implementation project(':android-library')
+ implementation project(':kotlin-library')
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'androidx.activity:activity-ktx:1.1.0'
+
+ implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'
+ kapt 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'
+
+ testImplementation 'androidx.test.ext:junit:1.1.2'
+ testImplementation 'androidx.test:runner:1.3.0'
+ testImplementation 'com.google.truth:truth:1.0.1'
+ testImplementation 'junit:junit:4.13'
+ testImplementation 'org.robolectric:robolectric:4.5-alpha-3'
+ testImplementation 'androidx.core:core:1.3.2'
+ // TODO(bcorso): This multidex dep shouldn't be required -- it's a dep for the generated code.
+ testImplementation 'androidx.multidex:multidex:2.0.0'
+ testImplementation 'com.google.dagger:hilt-android-testing:LOCAL-SNAPSHOT'
+ kaptTest 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'
+
+ androidTestImplementation 'androidx.fragment:fragment-ktx:1.2.5'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+ androidTestImplementation 'androidx.test:runner:1.3.0'
+ androidTestImplementation 'com.google.truth:truth:1.0.1'
+ androidTestImplementation 'com.google.dagger:hilt-android-testing:LOCAL-SNAPSHOT'
+ kaptAndroidTest 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/SimpleEmulatorTestRunner.kt b/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/SimpleEmulatorTestRunner.kt
new file mode 100644
index 000000000..d79026594
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/SimpleEmulatorTestRunner.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.hilt.android.simpleKotlin
+
+import android.app.Application
+import android.content.Context
+import androidx.test.runner.AndroidJUnitRunner
+import dagger.hilt.android.testing.HiltTestApplication
+
+/** A custom runner to setup the emulator application class for tests. */
+class SimpleEmulatorTestRunner : AndroidJUnitRunner() {
+ override fun newApplication(cl: ClassLoader, className: String, context: Context): Application {
+ return super.newApplication(cl, HiltTestApplication::class.java.name, context)
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/TestRunner.kt b/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/TestRunner.kt
new file mode 100644
index 000000000..29ffc9e69
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/TestRunner.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.example.gradle.simpleKotlin
+
+import android.app.Application
+import android.content.Context
+import androidx.test.runner.AndroidJUnitRunner
+import dagger.hilt.android.testing.HiltTestApplication
+
+class TestRunner : AndroidJUnitRunner() {
+ override fun newApplication(cl: ClassLoader, appName: String, context: Context): Application {
+ return super.newApplication(cl, HiltTestApplication::class.java.name, context)
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/ActivityInjectionTest.kt b/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/ActivityInjectionTest.kt
new file mode 100644
index 000000000..6786e9d82
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/ActivityInjectionTest.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simpleKotlin.viewmodel
+
+import androidx.activity.viewModels
+import androidx.fragment.app.FragmentActivity
+import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import dagger.hilt.android.AndroidEntryPoint
+import dagger.hilt.android.testing.HiltAndroidRule
+import dagger.hilt.android.testing.HiltAndroidTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@HiltAndroidTest
+@RunWith(AndroidJUnit4::class)
+class ActivityInjectionTest {
+
+ @get:Rule
+ val rule = HiltAndroidRule(this)
+
+ @Test
+ fun verifyInjection() {
+ ActivityScenario.launch(TestActivity::class.java).use {
+ it.onActivity { activity ->
+ assertThat(activity.myAndroidViewModel).isNotNull()
+ assertThat(activity.myViewModel).isNotNull()
+ assertThat(activity.myInjectedViewModel).isNotNull()
+ assertThat(activity.myNestedInjectedViewModel).isNotNull()
+ assertThat(activity.myInjectedViewModelWithSavedState).isNotNull()
+ }
+ }
+ }
+
+ @AndroidEntryPoint
+ class TestActivity : FragmentActivity() {
+ val myAndroidViewModel by viewModels<MyAndroidViewModel>()
+ val myViewModel by viewModels<MyViewModel>()
+ val myInjectedViewModel by viewModels<MyInjectedViewModel>()
+ val myNestedInjectedViewModel by viewModels<TopClass.MyNestedInjectedViewModel>()
+ val myInjectedViewModelWithSavedState by viewModels<MyInjectedViewModelWithSavedState>()
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/BaseActivityInjectionTest.kt b/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/BaseActivityInjectionTest.kt
new file mode 100644
index 000000000..7ac4ebf16
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/BaseActivityInjectionTest.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simpleKotlin.viewmodel
+
+import androidx.activity.viewModels
+import androidx.fragment.app.FragmentActivity
+import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import dagger.hilt.android.AndroidEntryPoint
+import dagger.hilt.android.testing.HiltAndroidRule
+import dagger.hilt.android.testing.HiltAndroidTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@HiltAndroidTest
+@RunWith(AndroidJUnit4::class)
+class BaseActivityInjectionTest {
+
+ @get:Rule
+ val rule = HiltAndroidRule(this)
+
+ @Test
+ fun verifyInjection() {
+ ActivityScenario.launch(TestActivity::class.java).use {
+ it.onActivity { activity ->
+ assertThat(activity.myAndroidViewModel).isNotNull()
+ assertThat(activity.myViewModel).isNotNull()
+ assertThat(activity.myInjectedViewModel).isNotNull()
+ }
+ }
+ }
+
+ @AndroidEntryPoint
+ class TestActivity : BaseActivity()
+
+ abstract class BaseActivity : FragmentActivity() {
+ val myAndroidViewModel by viewModels<MyAndroidViewModel>()
+ val myViewModel by viewModels<MyViewModel>()
+ val myInjectedViewModel by viewModels<MyInjectedViewModel>()
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/BaseFragmentInjectionTest.kt b/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/BaseFragmentInjectionTest.kt
new file mode 100644
index 000000000..679a0d92f
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/BaseFragmentInjectionTest.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simpleKotlin.viewmodel
+
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentActivity
+import androidx.fragment.app.viewModels
+import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import dagger.hilt.android.AndroidEntryPoint
+import dagger.hilt.android.testing.HiltAndroidRule
+import dagger.hilt.android.testing.HiltAndroidTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@HiltAndroidTest
+@RunWith(AndroidJUnit4::class)
+class BaseFragmentInjectionTest {
+
+ @get:Rule
+ val rule = HiltAndroidRule(this)
+
+ @Test
+ fun verifyInjection() {
+ ActivityScenario.launch(TestActivity::class.java).use {
+ it.onActivity { activity ->
+ val fragment = activity.supportFragmentManager.fragmentFactory.instantiate(
+ TestFragment::class.java.classLoader!!,
+ TestFragment::class.java.name
+ ) as TestFragment
+ activity.supportFragmentManager.beginTransaction()
+ .add(0, fragment, FragmentInjectionTest.FRAGMENT_TAG)
+ .commitNow()
+ assertThat(fragment.myAndroidViewModel).isNotNull()
+ assertThat(fragment.myViewModel).isNotNull()
+ assertThat(fragment.myInjectedViewModel).isNotNull()
+ }
+ }
+ }
+
+ @AndroidEntryPoint
+ class TestActivity : FragmentActivity()
+
+ @AndroidEntryPoint
+ class TestFragment : BaseFragment()
+
+ abstract class BaseFragment : Fragment() {
+ val myAndroidViewModel by viewModels<MyAndroidViewModel>()
+ val myViewModel by viewModels<MyViewModel>()
+ val myInjectedViewModel by viewModels<MyInjectedViewModel>()
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/Bindings.kt b/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/Bindings.kt
new file mode 100644
index 000000000..480aea1d8
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/Bindings.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simpleKotlin.viewmodel
+
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Inject
+import javax.inject.Named
+
+class Foo @Inject constructor()
+
+class Bar
+
+@Module
+@InstallIn(SingletonComponent::class)
+object StringModule {
+ @Provides
+ @Named("SayMyName")
+ fun provide() = "Heisenberg"
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/FragmentInjectionTest.kt b/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/FragmentInjectionTest.kt
new file mode 100644
index 000000000..8d98e9217
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/FragmentInjectionTest.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simpleKotlin.viewmodel
+
+import android.os.Bundle
+import androidx.activity.viewModels
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentActivity
+import androidx.fragment.app.FragmentManager
+import androidx.fragment.app.activityViewModels
+import androidx.fragment.app.viewModels
+import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth.assertThat
+import dagger.hilt.android.AndroidEntryPoint
+import dagger.hilt.android.testing.HiltAndroidRule
+import dagger.hilt.android.testing.HiltAndroidTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@HiltAndroidTest
+@RunWith(AndroidJUnit4::class)
+class FragmentInjectionTest {
+
+ @get:Rule
+ val rule = HiltAndroidRule(this)
+
+ @Test
+ fun verifyInjection() {
+ ActivityScenario.launch(TestActivity::class.java).use {
+ it.onActivity { activity ->
+ activity.addTestFragment()
+ activity.supportFragmentManager.findTestFragment().let { fragment ->
+ assertThat(fragment.myAndroidViewModel).isNotNull()
+ assertThat(fragment.myViewModel).isNotNull()
+ assertThat(fragment.myInjectedViewModel).isNotNull()
+ assertThat(fragment.myInjectedViewModelWithSavedState).isNotNull()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun verifyActivityViewModelInjection() {
+ ActivityScenario.launch(TestActivity::class.java).use {
+ it.onActivity { activity ->
+ activity.addTestFragment()
+ activity.supportFragmentManager.findTestFragment().let { fragment ->
+ assertThat(fragment.myInjectedViewModel).isNotNull()
+ assertThat(fragment.myActivityLevelInjectedViewModel).isNotNull()
+ assertThat(fragment.myInjectedViewModel)
+ .isNotEqualTo(fragment.myActivityLevelInjectedViewModel)
+ assertThat(fragment.myActivityLevelInjectedViewModel)
+ .isEqualTo(activity.myInjectedViewModel)
+ }
+ activity.removeTestFragment()
+ activity.addTestFragment()
+ activity.supportFragmentManager.findTestFragment().let { fragment ->
+ assertThat(fragment.myInjectedViewModel).isNotNull()
+ assertThat(fragment.myActivityLevelInjectedViewModel).isNotNull()
+ assertThat(fragment.myInjectedViewModel)
+ .isNotEqualTo(fragment.myActivityLevelInjectedViewModel)
+ assertThat(fragment.myActivityLevelInjectedViewModel)
+ .isEqualTo(activity.myInjectedViewModel)
+ }
+ }
+ }
+ }
+
+ @AndroidEntryPoint
+ class TestActivity : FragmentActivity() {
+
+ val myInjectedViewModel by viewModels<MyInjectedViewModel>()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ addTestFragment()
+ }
+
+ fun addTestFragment() {
+ val fragment = supportFragmentManager.fragmentFactory.instantiate(
+ TestFragment::class.java.classLoader!!,
+ TestFragment::class.java.name
+ )
+ supportFragmentManager.beginTransaction()
+ .add(0, fragment, FRAGMENT_TAG)
+ .commitNow()
+ }
+
+ fun removeTestFragment() {
+ supportFragmentManager.beginTransaction()
+ .remove(supportFragmentManager.findFragmentByTag(FRAGMENT_TAG)!!)
+ .commitNow()
+ }
+ }
+
+ @AndroidEntryPoint
+ class TestFragment : Fragment() {
+ val myAndroidViewModel by viewModels<MyAndroidViewModel>()
+ val myViewModel by viewModels<MyViewModel>()
+ val myInjectedViewModel by viewModels<MyInjectedViewModel>()
+ val myInjectedViewModelWithSavedState by viewModels<MyInjectedViewModelWithSavedState>()
+ val myActivityLevelInjectedViewModel by activityViewModels<MyInjectedViewModel>()
+ }
+
+ private fun FragmentManager.findTestFragment() =
+ findFragmentByTag(FRAGMENT_TAG) as TestFragment
+
+ companion object {
+ const val FRAGMENT_TAG = "tag"
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/MyViewModels.kt b/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/MyViewModels.kt
new file mode 100644
index 000000000..c24c6b7fb
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/MyViewModels.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simpleKotlin.viewmodel
+
+import android.app.Application
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import dagger.hilt.android.lifecycle.HiltViewModel
+import javax.inject.Inject
+import javax.inject.Named
+
+class MyAndroidViewModel(app: Application) : AndroidViewModel(app)
+
+class MyViewModel() : ViewModel()
+
+@Suppress("UNUSED_PARAMETER")
+@HiltViewModel
+class MyInjectedViewModel @Inject constructor(
+ foo: Foo,
+ @Named("SayMyName") theName: String
+) : ViewModel()
+
+object TopClass {
+ @Suppress("UNUSED_PARAMETER")
+ @HiltViewModel
+ class MyNestedInjectedViewModel @Inject constructor(foo: Foo) : ViewModel()
+}
+
+@Suppress("UNUSED_PARAMETER")
+@HiltViewModel
+class MyInjectedViewModelWithSavedState @Inject constructor(
+ foo: Foo,
+ handle: SavedStateHandle
+) : ViewModel()
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/ViewModelScopedTest.kt b/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/ViewModelScopedTest.kt
new file mode 100644
index 000000000..cdf6a653b
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/androidTest/java/dagger/hilt/android/simpleKotlin/viewmodel/ViewModelScopedTest.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simpleKotlin.viewmodel
+
+import android.os.Bundle
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentActivity
+import androidx.fragment.app.FragmentManager
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.ViewModel
+import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.AndroidEntryPoint
+import dagger.hilt.android.components.ViewModelComponent
+import dagger.hilt.android.lifecycle.HiltViewModel
+import dagger.hilt.android.scopes.ViewModelScoped
+import dagger.hilt.android.testing.HiltAndroidRule
+import dagger.hilt.android.testing.HiltAndroidTest
+import javax.inject.Inject
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@HiltAndroidTest
+@RunWith(AndroidJUnit4::class)
+class ViewModelScopedTest {
+
+ @get:Rule
+ val rule = HiltAndroidRule(this)
+
+ @Test
+ fun testViewModelScopeInFragment() {
+ ActivityScenario.launch(TestActivity::class.java).use {
+ it.onActivity { activity ->
+ activity.supportFragmentManager.findTestFragment().let { fragment ->
+ val vm = fragment.vm
+ assertThat(vm.one.bar).isEqualTo(vm.two.bar)
+ }
+ }
+ }
+ }
+
+ @Module
+ @InstallIn(ViewModelComponent::class)
+ object ScopedModule {
+ @Provides
+ @ViewModelScoped
+ fun provideBar() = Bar()
+ }
+
+ @AndroidEntryPoint
+ class TestActivity : FragmentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ if (savedInstanceState == null) {
+ val fragment = supportFragmentManager.fragmentFactory.instantiate(
+ TestFragment::class.java.classLoader!!,
+ TestFragment::class.java.name
+ )
+ supportFragmentManager.beginTransaction()
+ .add(0, fragment, FRAGMENT_TAG)
+ .commitNow()
+ }
+ }
+ }
+
+ @AndroidEntryPoint
+ class TestFragment : Fragment() {
+ val vm by viewModels<TestViewModel>()
+ }
+
+ @HiltViewModel
+ class TestViewModel @Inject constructor(
+ val one: DependsOnBarOne,
+ val two: DependsOnBarTwo
+ ) : ViewModel()
+
+ class DependsOnBarOne @Inject constructor(val bar: Bar)
+ class DependsOnBarTwo @Inject constructor(val bar: Bar)
+
+ private fun FragmentManager.findTestFragment() =
+ findFragmentByTag(FRAGMENT_TAG) as TestFragment
+
+ companion object {
+ const val FRAGMENT_TAG = "tag"
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/debug/AndroidManifest.xml b/javatests/artifacts/hilt-android/simpleKotlin/app/src/debug/AndroidManifest.xml
new file mode 100644
index 000000000..0a0270e8e
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.hilt.android.simpleKotlin">
+
+ <application>
+ <activity android:name=".SimpleTest$TestActivity" android:exported="false"/>
+ <activity android:name=".viewmodel.ActivityInjectionTest$TestActivity" android:exported="false"/>
+ <activity android:name=".viewmodel.BaseActivityInjectionTest$TestActivity" android:exported="false"/>
+ <activity android:name=".viewmodel.FragmentInjectionTest$TestActivity" android:exported="false"/>
+ <activity android:name=".viewmodel.BaseFragmentInjectionTest$TestActivity" android:exported="false"/>
+ <activity android:name=".viewmodel.ViewModelScopedTest$TestActivity" android:exported="false"/>
+ </application>
+</manifest>
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/AndroidManifest.xml b/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..ca700b242
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.hilt.android.simpleKotlin">
+
+ <application
+ android:name=".KotlinApplication"
+ android:allowBackup="true"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.AppCompat.Light">
+ <activity android:name=".MainActivity" android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/ActivityRetainedModule.kt b/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/ActivityRetainedModule.kt
new file mode 100644
index 000000000..b830f3136
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/ActivityRetainedModule.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.hilt.android.simpleKotlin
+
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.ActivityRetainedComponent
+
+@Module
+@InstallIn(ActivityRetainedComponent::class)
+object ActivityRetainedModule {
+ @UserName
+ @Provides
+ fun provideUserName(): String {
+ return "Android User"
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/ApplicationModule.kt b/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/ApplicationModule.kt
new file mode 100644
index 000000000..59b79bbb9
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/ApplicationModule.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simpleKotlin
+
+import android.os.Build
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+
+@Module
+@InstallIn(SingletonComponent::class)
+object ApplicationModule {
+ @Provides
+ @Model
+ fun provideModel(): String {
+ return Build.MODEL
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/DefinedStrings.kt b/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/DefinedStrings.kt
new file mode 100644
index 000000000..b22e588a7
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/DefinedStrings.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2021 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.hilt.android.simpleKotlin
+
+object DefinedStrings {
+ internal val morningSweet = "Mallorca"
+ internal fun getInternalSweet() = "Sugar Donuts"
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/KotlinApplication.kt b/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/KotlinApplication.kt
new file mode 100644
index 000000000..c638706a0
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/KotlinApplication.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simpleKotlin
+
+import android.app.Application
+import dagger.hilt.android.HiltAndroidApp
+import javax.inject.Inject
+
+@HiltAndroidApp
+class KotlinApplication : Application() {
+ // Shows that we can inject ApplicationComponent bindings into an application.
+ @Inject
+ @Model
+ @JvmField
+ var model: String? = null
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/MainActivity.kt b/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/MainActivity.kt
new file mode 100644
index 000000000..bd7f8a78b
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/MainActivity.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simpleKotlin
+
+import android.os.Bundle
+import android.view.View
+import android.widget.TextView
+import androidx.activity.viewModels
+import androidx.appcompat.app.AppCompatActivity
+import androidx.lifecycle.ViewModel
+import dagger.hilt.android.AndroidEntryPoint
+import dagger.hilt.android.lifecycle.HiltViewModel
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class MainActivity : AppCompatActivity() {
+
+ val viewModel by viewModels<MainViewModel>()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+ val greeting = findViewById<View>(R.id.greeting) as TextView
+ val text = resources.getString(R.string.welcome, viewModel.name, viewModel.model)
+ greeting.text = text
+ }
+
+ // Shows that we can inject bindings into a ViewModel
+ @HiltViewModel
+ class MainViewModel @Inject constructor(
+ @Model val model: String,
+ @UserName val name: String
+ ) : ViewModel()
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/Model.kt b/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/Model.kt
new file mode 100644
index 000000000..7e7e9aec4
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/Model.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simpleKotlin
+
+import javax.inject.Qualifier
+
+/** Qualifies bindings relating to [android.os.Build.MODEL]. */
+@Qualifier
+@Retention(AnnotationRetention.RUNTIME)
+internal annotation class Model
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/UserName.kt b/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/UserName.kt
new file mode 100644
index 000000000..352e48cff
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/java/dagger/hilt/android/simpleKotlin/UserName.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simpleKotlin
+
+import javax.inject.Qualifier
+
+/** Qualifies bindings relating to the user name. */
+@Qualifier
+@Retention(AnnotationRetention.RUNTIME)
+internal annotation class UserName
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/res/layout/activity_main.xml b/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 000000000..0d7637d42
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/background_light">
+
+ <TextView
+ android:id="@+id/greeting"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:textColor="@android:color/primary_text_light"
+ />
+</RelativeLayout>
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/res/values/strings.xml b/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/res/values/strings.xml
new file mode 100644
index 000000000..c1e4f272f
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/main/res/values/strings.xml
@@ -0,0 +1,23 @@
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <!--The app name [CHAR_LIMIT=40]-->
+ <string name="app_name">Simple Hilt Kotlin Android App</string>
+
+ <!--The greeting message [CHAR_LIMIT=100]-->
+ <string name="welcome">Hello, %1$s! You are on build %2$s.</string>
+</resources>
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/sharedTest/java/dagger/hilt/android/simpleKotlin/InternalAccessTest.kt b/javatests/artifacts/hilt-android/simpleKotlin/app/src/sharedTest/java/dagger/hilt/android/simpleKotlin/InternalAccessTest.kt
new file mode 100644
index 000000000..43a1abc5c
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/sharedTest/java/dagger/hilt/android/simpleKotlin/InternalAccessTest.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2021 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.hilt.android.simpleKotlin
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Verifies internal Kotlin classes are accessible with classpath aggregation.
+ */
+@RunWith(AndroidJUnit4::class)
+class InternalAccessTest {
+ @Test
+ fun verifyInternalMembersAreAccessible() {
+ assertThat(DefinedStrings.morningSweet).isNotNull()
+ assertThat(DefinedStrings.getInternalSweet()).isNotNull()
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/sharedTest/java/dagger/hilt/android/simpleKotlin/TransitiveDependenciesTest.kt b/javatests/artifacts/hilt-android/simpleKotlin/app/src/sharedTest/java/dagger/hilt/android/simpleKotlin/TransitiveDependenciesTest.kt
new file mode 100644
index 000000000..10e6515fa
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/sharedTest/java/dagger/hilt/android/simpleKotlin/TransitiveDependenciesTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.hilt.android.simpleKotlin
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import dagger.hilt.android.simpleKotlin.lib.AndroidLibraryEntryPoints
+import dagger.hilt.android.simpleKotlin.lib.KotlinLibraryEntryPoints
+import dagger.hilt.android.testing.HiltAndroidRule
+import dagger.hilt.android.testing.HiltAndroidTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests that entry points in deep dependencies with `implementation` boundaries are available at
+ * runtime.
+ */
+@HiltAndroidTest
+@RunWith(AndroidJUnit4::class)
+class TransitiveDependenciesTest {
+ @get:Rule
+ val rule = HiltAndroidRule(this)
+
+ @Test
+ fun testEntryPointFromAndroidLib() {
+ AndroidLibraryEntryPoints.invokeEntryPoints(
+ InstrumentationRegistry.getInstrumentation().targetContext.applicationContext
+ )
+ }
+
+ @Test
+ fun testEntryPointFromKotlinLib() {
+ KotlinLibraryEntryPoints.invokeEntryPoints(
+ InstrumentationRegistry.getInstrumentation().targetContext.applicationContext
+ )
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/test/java/dagger/hilt/android/simpleKotlin/SimpleTest.kt b/javatests/artifacts/hilt-android/simpleKotlin/app/src/test/java/dagger/hilt/android/simpleKotlin/SimpleTest.kt
new file mode 100644
index 000000000..1e04606a7
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/test/java/dagger/hilt/android/simpleKotlin/SimpleTest.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simpleKotlin
+
+import android.os.Build
+import androidx.appcompat.app.AppCompatActivity
+import androidx.test.core.app.ActivityScenario
+import com.google.common.truth.Truth.assertThat
+import dagger.hilt.android.AndroidEntryPoint
+import dagger.hilt.android.testing.BindValue
+import dagger.hilt.android.testing.HiltAndroidRule
+import dagger.hilt.android.testing.HiltAndroidTest
+import dagger.hilt.android.testing.HiltTestApplication
+import javax.inject.Inject
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@HiltAndroidTest
+@RunWith(RobolectricTestRunner::class)
+// Robolectric requires Java9 to run API 29 and above, so use API 28 instead
+@Config(sdk = [Build.VERSION_CODES.P], application = HiltTestApplication::class)
+class SimpleTest {
+ @get:Rule val rule = HiltAndroidRule(this)
+
+ @BindValue val bindStr = "STRING_BINDING"
+
+ @Inject @JvmField
+ var str: String? = null
+
+ @AndroidEntryPoint
+ class TestActivity : AppCompatActivity()
+
+ @Test
+ fun testBindValue() {
+ assertThat(str).isNull()
+ rule.inject()
+ assertThat(str).isEqualTo(bindStr)
+ }
+
+ @Test
+ fun verifyMainActivity() {
+ ActivityScenario.launch(MainActivity::class.java).use { scenario ->
+ scenario.onActivity { activity ->
+ assertThat(activity::class.java.getSuperclass()?.getSimpleName())
+ .isEqualTo("Hilt_MainActivity")
+ assertThat(activity.viewModel.model).isNotNull()
+ assertThat(activity.viewModel.name).isNotNull()
+ }
+ }
+ }
+
+ @Test
+ fun verifyTestActivity() {
+ ActivityScenario.launch(TestActivity::class.java).use { scenario ->
+ scenario.onActivity { activity ->
+ assertThat(activity::class.java.getSuperclass()?.getSimpleName())
+ .isEqualTo("Hilt_SimpleTest_TestActivity")
+ }
+ }
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/test/resources/dagger/hilt/android/simpleKotlin/robolectric.properties b/javatests/artifacts/hilt-android/simpleKotlin/app/src/test/resources/dagger/hilt/android/simpleKotlin/robolectric.properties
new file mode 100644
index 000000000..0234ffe6f
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/test/resources/dagger/hilt/android/simpleKotlin/robolectric.properties
@@ -0,0 +1,2 @@
+sdk=28
+application=dagger.hilt.android.testing.HiltTestApplication \ No newline at end of file
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/build.gradle b/javatests/artifacts/hilt-android/simpleKotlin/build.gradle
new file mode 100644
index 000000000..d59447608
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/build.gradle
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+buildscript {
+ ext {
+ kotlin_version = '1.3.61'
+ agp_version = System.getenv('AGP_VERSION') ?: "4.2.0-beta04"
+ }
+ repositories {
+ google()
+ jcenter()
+ mavenLocal()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:$agp_version"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath 'com.google.dagger:hilt-android-gradle-plugin:LOCAL-SNAPSHOT'
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ mavenCentral()
+ mavenLocal()
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/deep-android-lib/build.gradle b/javatests/artifacts/hilt-android/simpleKotlin/deep-android-lib/build.gradle
new file mode 100644
index 000000000..3a1923aff
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/deep-android-lib/build.gradle
@@ -0,0 +1,46 @@
+plugins {
+ id 'com.android.library'
+ id 'kotlin-android'
+ id 'kotlin-kapt'
+ id 'dagger.hilt.android.plugin'
+}
+
+android {
+ compileSdkVersion 30
+ buildToolsVersion "30.0.2"
+
+ defaultConfig {
+ minSdkVersion 15
+ targetSdkVersion 30
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+ lintOptions {
+ checkReleaseBuilds = false
+ }
+}
+
+dependencies {
+ implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'
+ kapt 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'
+
+ testImplementation 'androidx.test.ext:junit:1.1.2'
+ testImplementation 'com.google.truth:truth:1.0.1'
+
+ androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+ androidTestImplementation 'androidx.test:runner:1.3.0'
+ androidTestImplementation 'com.google.truth:truth:1.0.1'
+}
+
+hilt {
+ enableExperimentalClasspathAggregation = true
+} \ No newline at end of file
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/deep-android-lib/src/androidTest/java/dagger/hilt/android/simpleKotlin/deep/InternalAccessEmulatorTest.kt b/javatests/artifacts/hilt-android/simpleKotlin/deep-android-lib/src/androidTest/java/dagger/hilt/android/simpleKotlin/deep/InternalAccessEmulatorTest.kt
new file mode 100644
index 000000000..4ea4c0942
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/deep-android-lib/src/androidTest/java/dagger/hilt/android/simpleKotlin/deep/InternalAccessEmulatorTest.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.hilt.android.simpleKotlin.deep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Verifies internal Kotlin classes are accessible with classpath aggregation in Android library.
+ */
+@RunWith(AndroidJUnit4::class)
+class InternalAccessEmulatorTest {
+ @Test
+ fun verifyInternalMembersAreAccessible() {
+ assertThat(DeepAndroidLib.internalFunction()).isNotNull()
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/deep-android-lib/src/main/AndroidManifest.xml b/javatests/artifacts/hilt-android/simpleKotlin/deep-android-lib/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..ae2a5e330
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/deep-android-lib/src/main/AndroidManifest.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.hilt.android.simpleKotlin.deep">
+
+</manifest> \ No newline at end of file
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/deep-android-lib/src/main/java/dagger/hilt/android/simpleKotlin/deep/DeepAndroidLib.kt b/javatests/artifacts/hilt-android/simpleKotlin/deep-android-lib/src/main/java/dagger/hilt/android/simpleKotlin/deep/DeepAndroidLib.kt
new file mode 100644
index 000000000..a2fcb73b1
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/deep-android-lib/src/main/java/dagger/hilt/android/simpleKotlin/deep/DeepAndroidLib.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.simpleKotlin.deep
+
+import android.content.Context
+import dagger.hilt.EntryPoint
+import dagger.hilt.InstallIn
+import dagger.hilt.android.EntryPointAccessors
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Inject
+
+class DeepAndroidLib @Inject constructor() {
+ @EntryPoint
+ @InstallIn(SingletonComponent::class)
+ internal interface LibEntryPoint {
+ fun getDeepAndroidInstance(): DeepAndroidLib
+ }
+
+ companion object {
+ fun getInstance(context: Context) =
+ EntryPointAccessors.fromApplication(context, LibEntryPoint::class.java)
+ .getDeepAndroidInstance()
+
+ internal fun internalFunction() = Any()
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/deep-android-lib/src/test/java/dagger/hilt/android/simpleKotlin/deep/InternalAccessLocalTest.kt b/javatests/artifacts/hilt-android/simpleKotlin/deep-android-lib/src/test/java/dagger/hilt/android/simpleKotlin/deep/InternalAccessLocalTest.kt
new file mode 100644
index 000000000..508e7d502
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/deep-android-lib/src/test/java/dagger/hilt/android/simpleKotlin/deep/InternalAccessLocalTest.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.hilt.android.simpleKotlin.deep
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+/**
+ * Verifies internal Kotlin classes are accessible with classpath aggregation in Android library.
+ */
+class InternalAccessLocalTest {
+ @Test
+ fun verifyInternalMembersAreAccessible() {
+ assertThat(DeepAndroidLib.internalFunction()).isNotNull()
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/deep-kotlin-lib/build.gradle b/javatests/artifacts/hilt-android/simpleKotlin/deep-kotlin-lib/build.gradle
new file mode 100644
index 000000000..306c171e7
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/deep-kotlin-lib/build.gradle
@@ -0,0 +1,16 @@
+plugins {
+ id 'java-library'
+ id 'kotlin'
+ id 'kotlin-kapt'
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation "com.google.dagger:hilt-core:LOCAL-SNAPSHOT"
+ kapt "com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT"
+} \ No newline at end of file
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/deep-kotlin-lib/src/main/java/dagger/hilt/android/simpleKotlin/deep/DeepLib.kt b/javatests/artifacts/hilt-android/simpleKotlin/deep-kotlin-lib/src/main/java/dagger/hilt/android/simpleKotlin/deep/DeepLib.kt
new file mode 100644
index 000000000..0d2bc61e5
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/deep-kotlin-lib/src/main/java/dagger/hilt/android/simpleKotlin/deep/DeepLib.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.hilt.android.simpleKotlin.deep
+
+import dagger.hilt.EntryPoint
+import dagger.hilt.EntryPoints
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Inject
+
+class DeepLib @Inject constructor() {
+ @EntryPoint
+ @InstallIn(SingletonComponent::class)
+ internal interface LibEntryPoint {
+ fun getDeepInstance(): DeepLib
+ }
+
+ companion object {
+ fun getInstance(componentManager: Any) =
+ EntryPoints.get(componentManager, LibEntryPoint::class.java)
+ .getDeepInstance()
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/gradle.properties b/javatests/artifacts/hilt-android/simpleKotlin/gradle.properties
new file mode 100644
index 000000000..646c51b97
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/gradle.properties
@@ -0,0 +1,2 @@
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/gradle/wrapper/gradle-wrapper.jar b/javatests/artifacts/hilt-android/simpleKotlin/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..5c2d1cf01
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/gradle/wrapper/gradle-wrapper.properties b/javatests/artifacts/hilt-android/simpleKotlin/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..4d9ca1649
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/gradlew b/javatests/artifacts/hilt-android/simpleKotlin/gradlew
new file mode 100755
index 000000000..b0d6d0ab5
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/gradlew
@@ -0,0 +1,188 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/kotlin-library/build.gradle b/javatests/artifacts/hilt-android/simpleKotlin/kotlin-library/build.gradle
new file mode 100644
index 000000000..7c8462f9a
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/kotlin-library/build.gradle
@@ -0,0 +1,17 @@
+plugins {
+ id 'java-library'
+ id 'kotlin'
+ id 'kotlin-kapt'
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+dependencies {
+ implementation project(':deep-kotlin-lib')
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation "com.google.dagger:hilt-core:LOCAL-SNAPSHOT"
+ kapt "com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT"
+} \ No newline at end of file
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/kotlin-library/src/main/java/dagger/hilt/android/simpleKotlin/lib/KotlinLibraryEntryPoints.kt b/javatests/artifacts/hilt-android/simpleKotlin/kotlin-library/src/main/java/dagger/hilt/android/simpleKotlin/lib/KotlinLibraryEntryPoints.kt
new file mode 100644
index 000000000..8989ebb4a
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/kotlin-library/src/main/java/dagger/hilt/android/simpleKotlin/lib/KotlinLibraryEntryPoints.kt
@@ -0,0 +1,9 @@
+package dagger.hilt.android.simpleKotlin.lib
+
+import dagger.hilt.android.simpleKotlin.deep.DeepLib
+
+object KotlinLibraryEntryPoints {
+ fun invokeEntryPoints(component: Any) {
+ DeepLib.getInstance(component)
+ }
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/settings.gradle b/javatests/artifacts/hilt-android/simpleKotlin/settings.gradle
new file mode 100644
index 000000000..cea091363
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/settings.gradle
@@ -0,0 +1,6 @@
+rootProject.name='Simple Kotlin Hilt Android'
+include ':app'
+include ':android-library'
+include ':kotlin-library'
+include ':deep-android-lib'
+include ':deep-kotlin-lib'
diff --git a/javatests/dagger/BUILD b/javatests/dagger/BUILD
index 3debec570..c6ab360b7 100644
--- a/javatests/dagger/BUILD
+++ b/javatests/dagger/BUILD
@@ -15,11 +15,11 @@
# Description:
# A JSR-330 compliant dependency injection system for android and java
-package(default_visibility = ["//:src"])
-
load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX", "DOCLINT_REFERENCES")
load("//:test_defs.bzl", "GenJavaTests")
+package(default_visibility = ["//:src"])
+
GenJavaTests(
name = "core_tests",
srcs = glob(["**/*.java"]),
@@ -27,7 +27,8 @@ GenJavaTests(
javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
deps = [
"//java/dagger:core",
- "@google_bazel_common//third_party/java/guava",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:concurrent",
"@google_bazel_common//third_party/java/jsr330_inject",
"@google_bazel_common//third_party/java/junit",
"@google_bazel_common//third_party/java/truth",
diff --git a/javatests/dagger/android/AndroidInjectionTest.java b/javatests/dagger/android/AndroidInjectionTest.java
index 19b6e8455..7e60ebd52 100644
--- a/javatests/dagger/android/AndroidInjectionTest.java
+++ b/javatests/dagger/android/AndroidInjectionTest.java
@@ -18,6 +18,7 @@ package dagger.android;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
+import static org.robolectric.annotation.LooperMode.Mode.LEGACY;
import android.app.Activity;
import android.app.Application;
@@ -27,9 +28,10 @@ import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
+import org.robolectric.annotation.LooperMode;
import org.robolectric.util.FragmentTestUtil;
-@Config(manifest = Config.NONE)
+@LooperMode(LEGACY)
@RunWith(RobolectricTestRunner.class)
public final class AndroidInjectionTest {
@@ -39,7 +41,7 @@ public final class AndroidInjectionTest {
String tag;
}
- private static AndroidInjector<Fragment> fakeFragmentInjector(String tag) {
+ private static AndroidInjector<Object> fakeFragmentInjector(String tag) {
return instance -> {
if (instance instanceof InjectableFragment) {
((InjectableFragment) instance).tag = tag;
@@ -47,15 +49,14 @@ public final class AndroidInjectionTest {
};
}
- public static class ApplicationInjectsFragment extends Application
- implements HasFragmentInjector {
+ public static class ApplicationInjectsFragment extends Application implements HasAndroidInjector {
@Override
- public AndroidInjector<Fragment> fragmentInjector() {
+ public AndroidInjector<Object> androidInjector() {
return fakeFragmentInjector("injected by app");
}
}
- @Config(manifest = Config.NONE, application = ApplicationInjectsFragment.class)
+ @Config(application = ApplicationInjectsFragment.class)
@Test
public void fragmentInjectedByApplication() {
Activity activity = Robolectric.setupActivity(Activity.class);
@@ -67,14 +68,14 @@ public final class AndroidInjectionTest {
assertThat(fragment.tag).isEqualTo("injected by app");
}
- public static class ActivityInjectsFragment extends Activity implements HasFragmentInjector {
+ public static class ActivityInjectsFragment extends Activity implements HasAndroidInjector {
@Override
- public AndroidInjector<Fragment> fragmentInjector() {
+ public AndroidInjector<Object> androidInjector() {
return fakeFragmentInjector("injected by activity");
}
}
- @Config(manifest = Config.NONE, application = ApplicationInjectsFragment.class)
+ @Config(application = ApplicationInjectsFragment.class)
@Test
public void fragmentInjectedByActivity() {
ActivityInjectsFragment activity = Robolectric.setupActivity(ActivityInjectsFragment.class);
@@ -87,14 +88,14 @@ public final class AndroidInjectionTest {
}
public static class ParentFragmentInjectsChildFragment extends Fragment
- implements HasFragmentInjector {
+ implements HasAndroidInjector {
@Override
- public AndroidInjector<Fragment> fragmentInjector() {
+ public AndroidInjector<Object> androidInjector() {
return fakeFragmentInjector("injected by parent fragment");
}
}
- @Config(manifest = Config.NONE, application = ApplicationInjectsFragment.class)
+ @Config(application = ApplicationInjectsFragment.class)
@Test
public void fragmentInjectedByParentFragment() {
ActivityInjectsFragment activity = Robolectric.setupActivity(ActivityInjectsFragment.class);
@@ -113,7 +114,7 @@ public final class AndroidInjectionTest {
}
@Test
- public void injectActivity_applicationDoesntImplementHasActivityInjector() {
+ public void injectActivity_applicationDoesntImplementHasAndroidInjector() {
Activity activity = Robolectric.setupActivity(Activity.class);
try {
@@ -139,21 +140,15 @@ public final class AndroidInjectionTest {
}
}
- private static class ApplicationReturnsNull extends Application
- implements HasActivityInjector, HasFragmentInjector {
+ private static class ApplicationReturnsNull extends Application implements HasAndroidInjector {
@Override
- public AndroidInjector<Activity> activityInjector() {
- return null;
- }
-
- @Override
- public AndroidInjector<Fragment> fragmentInjector() {
+ public AndroidInjector<Object> androidInjector() {
return null;
}
}
@Test
- @Config(manifest = Config.NONE, application = ApplicationReturnsNull.class)
+ @Config(application = ApplicationReturnsNull.class)
public void activityInjector_returnsNull() {
Activity activity = Robolectric.setupActivity(Activity.class);
@@ -161,12 +156,12 @@ public final class AndroidInjectionTest {
AndroidInjection.inject(activity);
fail();
} catch (Exception e) {
- assertThat(e).hasMessageThat().contains("activityInjector() returned null");
+ assertThat(e).hasMessageThat().contains("androidInjector() returned null");
}
}
@Test
- @Config(manifest = Config.NONE, application = ApplicationReturnsNull.class)
+ @Config(application = ApplicationReturnsNull.class)
public void fragmentInjector_returnsNull() {
Fragment fragment = new Fragment();
FragmentTestUtil.startFragment(fragment);
@@ -175,7 +170,7 @@ public final class AndroidInjectionTest {
AndroidInjection.inject(fragment);
fail();
} catch (Exception e) {
- assertThat(e).hasMessageThat().contains("fragmentInjector() returned null");
+ assertThat(e).hasMessageThat().contains("androidInjector() returned null");
}
}
diff --git a/javatests/dagger/android/AndroidProguardTest.java b/javatests/dagger/android/AndroidProguardTest.java
new file mode 100644
index 000000000..0f51e4979
--- /dev/null
+++ b/javatests/dagger/android/AndroidProguardTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.android;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.android.internal.AndroidInjectionKeys;
+import java.net.URL;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class AndroidProguardTest {
+
+ @Test
+ public void checkLegacyProguardRules() {
+ URL resUrl =
+ AndroidInjectionKeys.class
+ .getClassLoader()
+ .getResource("META-INF/proguard/dagger-android.pro");
+ assertThat(resUrl).isNotNull();
+ }
+
+ // The com.android.tools files are only used outside Google, in Gradle projects.
+ @Test
+ public void checkProguardRules() {
+ URL resUrl =
+ AndroidInjectionKeys.class
+ .getClassLoader()
+ .getResource("META-INF/com.android.tools/proguard/dagger-android.pro");
+ assertThat(resUrl).isNotNull();
+ }
+
+ @Test
+ public void checkR8Rules() {
+ URL resUrl =
+ AndroidInjectionKeys.class
+ .getClassLoader()
+ .getResource("META-INF/com.android.tools/r8/dagger-android.pro");
+ assertThat(resUrl).isNotNull();
+ }
+}
diff --git a/javatests/dagger/android/BUILD b/javatests/dagger/android/BUILD
index 5bc3f454c..38efb3afa 100644
--- a/javatests/dagger/android/BUILD
+++ b/javatests/dagger/android/BUILD
@@ -15,11 +15,11 @@
# Description:
# Tests for Dagger's Android integrations
-package(default_visibility = ["//:src"])
-
load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
load("//:test_defs.bzl", "GenRobolectricTests")
+package(default_visibility = ["//:src"])
+
GenRobolectricTests(
name = "android_tests",
srcs = glob(["*.java"]),
@@ -29,7 +29,7 @@ GenRobolectricTests(
deps = [
"//:dagger_with_compiler",
"//java/dagger/android",
- "@google_bazel_common//third_party/java/guava",
+ "//java/dagger/internal/guava:collect",
"@google_bazel_common//third_party/java/junit",
"@google_bazel_common//third_party/java/truth",
],
diff --git a/javatests/dagger/android/DispatchingAndroidInjectorTest.java b/javatests/dagger/android/DispatchingAndroidInjectorTest.java
index d0306b8ce..37d3d616d 100644
--- a/javatests/dagger/android/DispatchingAndroidInjectorTest.java
+++ b/javatests/dagger/android/DispatchingAndroidInjectorTest.java
@@ -29,9 +29,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-@Config(manifest = Config.NONE)
@RunWith(RobolectricTestRunner.class)
public final class DispatchingAndroidInjectorTest {
@Test
diff --git a/javatests/dagger/android/processor/AndroidProcessorTest.java b/javatests/dagger/android/processor/AndroidProcessorTest.java
deleted file mode 100644
index 1a45bdd05..000000000
--- a/javatests/dagger/android/processor/AndroidProcessorTest.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.android.processor;
-
-import static com.google.common.truth.Truth8.assertThat;
-import static com.google.testing.compile.CompilationSubject.assertThat;
-import static com.google.testing.compile.Compiler.javac;
-import static javax.tools.StandardLocation.CLASS_OUTPUT;
-
-import com.google.testing.compile.Compilation;
-import com.google.testing.compile.JavaFileObjects;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public final class AndroidProcessorTest {
- @Test
- public void generatedProguardFile() {
- JavaFileObject module =
- JavaFileObjects.forSourceLines(
- "test.TestModule",
- "package test;",
- "",
- "import dagger.android.AndroidInjectionKey;",
- "import dagger.Module;",
- "import dagger.multibindings.IntoMap;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class TestModule {",
- " @Provides",
- " @IntoMap",
- " @AndroidInjectionKey(\"test.TestActivity\")",
- " static int i() { ",
- " return 1;",
- " }",
- "}");
- Compilation enabled =
- javac()
- .withProcessors(new AndroidProcessor())
- .withOptions("-Adagger.android.experimentalUseStringKeys=true")
- .compile(module);
- assertThat(enabled).succeeded();
- assertThat(enabled)
- .generatedFile(CLASS_OUTPUT, "META-INF/proguard/dagger.android.AndroidInjectionKeys");
-
- Compilation disabled =
- javac()
- .withProcessors(new AndroidProcessor())
- .withOptions("-Adagger.android.experimentalUseStringKeys=false")
- .compile(module);
- assertThat(disabled).succeeded();
- assertThat(
- disabled.generatedFile(
- CLASS_OUTPUT, "META-INF/proguard/dagger.android.AndroidInjectionKeys"))
- .isEmpty();
-
- Compilation noFlag = javac().withProcessors(new AndroidProcessor()).compile(module);
- assertThat(noFlag).succeeded();
- assertThat(
- noFlag.generatedFile(
- CLASS_OUTPUT, "META-INF/proguard/dagger.android.AndroidInjectionKeys"))
- .isEmpty();
- }
-}
diff --git a/javatests/dagger/android/processor/BUILD b/javatests/dagger/android/processor/BUILD
index 8a9c16e67..d7b210c5e 100644
--- a/javatests/dagger/android/processor/BUILD
+++ b/javatests/dagger/android/processor/BUILD
@@ -15,28 +15,27 @@
# Description:
# Tests for Dagger's Android integrations
-package(default_visibility = ["//:src"])
-
load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
load("//:test_defs.bzl", "GenJavaTests")
+package(default_visibility = ["//:src"])
+
GenJavaTests(
name = "android_processor_tests",
srcs = glob(["*.java"]),
functional = False,
javacopts = DOCLINT_HTML_AND_SYNTAX,
deps = [
- "@google_bazel_common//third_party/java/guava",
- "@androidsdk//com.android.support:support-fragment-25.0.0",
- # TODO(ronshapiro): create a common location to define the current Android version
- "@androidsdk//:platforms/android-26/android.jar",
- "@google_bazel_common//third_party/java/compile_testing",
"//:dagger_with_compiler",
- "@google_bazel_common//third_party/java/junit",
- "@google_bazel_common//third_party/java/truth",
- "@google_bazel_common//third_party/java/truth:truth8",
"//java/dagger/android",
"//java/dagger/android/processor",
"//java/dagger/internal/codegen:processor",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "@androidsdk//:platforms/android-30/android.jar",
+ "@google_bazel_common//third_party/java/compile_testing",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth",
+ "@maven//:androidx_fragment_fragment",
],
)
diff --git a/javatests/dagger/android/support/AndroidSupportInjectionTest.java b/javatests/dagger/android/support/AndroidSupportInjectionTest.java
index 51e9992b7..25c5e9460 100644
--- a/javatests/dagger/android/support/AndroidSupportInjectionTest.java
+++ b/javatests/dagger/android/support/AndroidSupportInjectionTest.java
@@ -20,15 +20,15 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import android.app.Application;
-import android.support.v4.app.Fragment;
+import androidx.fragment.app.Fragment;
import dagger.android.AndroidInjector;
+import dagger.android.HasAndroidInjector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.support.v4.SupportFragmentTestUtil;
-@Config(manifest = Config.NONE)
@RunWith(RobolectricTestRunner.class)
public final class AndroidSupportInjectionTest {
@Test
@@ -45,15 +45,15 @@ public final class AndroidSupportInjectionTest {
}
private static class ApplicationReturnsNull extends Application
- implements HasSupportFragmentInjector {
+ implements HasAndroidInjector {
@Override
- public AndroidInjector<Fragment> supportFragmentInjector() {
+ public AndroidInjector<Object> androidInjector() {
return null;
}
}
@Test
- @Config(manifest = Config.NONE, application = ApplicationReturnsNull.class)
+ @Config(application = ApplicationReturnsNull.class)
public void fragmentInjector_returnsNull() {
Fragment fragment = new Fragment();
SupportFragmentTestUtil.startFragment(fragment);
@@ -62,7 +62,7 @@ public final class AndroidSupportInjectionTest {
AndroidSupportInjection.inject(fragment);
fail();
} catch (Exception e) {
- assertThat(e).hasMessageThat().contains("supportFragmentInjector() returned null");
+ assertThat(e).hasMessageThat().contains("androidInjector() returned null");
}
}
diff --git a/javatests/dagger/android/support/BUILD b/javatests/dagger/android/support/BUILD
index 6d8f43bb0..6bbfaa184 100644
--- a/javatests/dagger/android/support/BUILD
+++ b/javatests/dagger/android/support/BUILD
@@ -15,11 +15,11 @@
# Description:
# Tests for Dagger's Android and Support library integrations
-package(default_visibility = ["//:src"])
-
load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
load("//:test_defs.bzl", "GenRobolectricTests")
+package(default_visibility = ["//:src"])
+
GenRobolectricTests(
name = "android-support-tests",
srcs = glob(["*.java"]),
@@ -29,10 +29,12 @@ GenRobolectricTests(
"//:dagger_with_compiler",
"//java/dagger/android",
"//java/dagger/android/support",
- "@androidsdk//com.android.support:appcompat-v7-25.0.0",
- "@androidsdk//com.android.support:support-fragment-25.0.0",
- "@google_bazel_common//third_party/java/guava",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:concurrent",
"@google_bazel_common//third_party/java/junit",
"@google_bazel_common//third_party/java/truth",
+ "@maven//:androidx_appcompat_appcompat",
+ "@maven//:androidx_fragment_fragment",
],
)
diff --git a/javatests/dagger/android/support/functional/AndroidManifest.xml b/javatests/dagger/android/support/functional/AndroidManifest.xml
index 2e40a354f..0b5f4cd0b 100644
--- a/javatests/dagger/android/support/functional/AndroidManifest.xml
+++ b/javatests/dagger/android/support/functional/AndroidManifest.xml
@@ -16,7 +16,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="dagger.android.support.functional">
- <uses-sdk android:minSdkVersion="1" android:targetSdkVersion="26" />
+ <!-- Bump targetSdk to 29 when we update to Java 11 -->
+ <uses-sdk android:minSdkVersion="1" android:targetSdkVersion="28" />
<application android:theme="@style/Theme.AppCompat"
android:name=".UsesGeneratedModulesApplication">
diff --git a/javatests/dagger/android/support/functional/BUILD b/javatests/dagger/android/support/functional/BUILD
index 130b9718a..1ae110858 100644
--- a/javatests/dagger/android/support/functional/BUILD
+++ b/javatests/dagger/android/support/functional/BUILD
@@ -15,6 +15,8 @@
# Description:
# Functional test code for Dagger-Android
+load("//:test_defs.bzl", "GenRobolectricTests")
+
package(default_visibility = ["//:src"])
android_library(
@@ -27,9 +29,10 @@ android_library(
manifest = "AndroidManifest.xml",
resource_files = glob(["res/**"]),
deps = [
- "@androidsdk//com.android.support:support-fragment-25.0.0",
- "@androidsdk//com.android.support:appcompat-v7-25.0.0",
- "@google_bazel_common//third_party/java/guava",
+ "@maven//:androidx_fragment_fragment",
+ "@maven//:androidx_appcompat_appcompat",
+ "@maven//:androidx_annotation_annotation",
+ "//java/dagger/internal/guava:collect-android",
"//:dagger_with_compiler",
"//:android",
"//:android-support",
@@ -38,8 +41,6 @@ android_library(
],
)
-load("//:test_defs.bzl", "GenRobolectricTests")
-
GenRobolectricTests(
name = "functional_tests",
srcs = glob(["*Test.java"]),
@@ -48,9 +49,9 @@ GenRobolectricTests(
"//:android",
"//:android-support",
"//:dagger_with_compiler",
- "@androidsdk//com.android.support:support-fragment-25.0.0",
- "@google_bazel_common//third_party/java/junit",
- "@google_bazel_common//third_party/java/robolectric",
"@google_bazel_common//third_party/java/truth",
+ "@maven//:androidx_fragment_fragment",
+ "@maven//:junit_junit",
+ "@maven//:org_robolectric_robolectric",
],
)
diff --git a/javatests/dagger/android/support/functional/InjectorsTest.java b/javatests/dagger/android/support/functional/InjectorsTest.java
index c5cb15026..73bb98a02 100644
--- a/javatests/dagger/android/support/functional/InjectorsTest.java
+++ b/javatests/dagger/android/support/functional/InjectorsTest.java
@@ -20,12 +20,12 @@ import static com.google.common.truth.Truth.assertThat;
import android.content.Intent;
import android.content.res.Configuration;
+import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.annotation.Config;
@@ -59,7 +59,7 @@ public class InjectorsTest {
intentService = Robolectric.buildIntentService(TestIntentService.class).create().get();
broadcastReceiver = new TestBroadcastReceiver();
- broadcastReceiver.onReceive(RuntimeEnvironment.application, new Intent());
+ broadcastReceiver.onReceive(ApplicationProvider.getApplicationContext(), new Intent());
contentProvider = Robolectric.setupContentProvider(TestContentProvider.class);
}
diff --git a/javatests/dagger/android/support/functional/TestContentProvider.java b/javatests/dagger/android/support/functional/TestContentProvider.java
index 1668ce647..aff896168 100644
--- a/javatests/dagger/android/support/functional/TestContentProvider.java
+++ b/javatests/dagger/android/support/functional/TestContentProvider.java
@@ -19,7 +19,7 @@ package dagger.android.support.functional;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
-import android.support.annotation.Nullable;
+import androidx.annotation.Nullable;
import dagger.android.DaggerContentProvider;
import java.util.Set;
import javax.inject.Inject;
diff --git a/javatests/dagger/functional/BUILD b/javatests/dagger/functional/BUILD
index f417bec06..8e6a5551e 100644
--- a/javatests/dagger/functional/BUILD
+++ b/javatests/dagger/functional/BUILD
@@ -15,15 +15,11 @@
# Description:
# Functional tests for Dagger
-package(default_visibility = ["//:src"])
-
-load(
- "//:build_defs.bzl",
- "DOCLINT_HTML_AND_SYNTAX",
- "SOURCE_7_TARGET_7",
-)
+load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX", "SOURCE_7_TARGET_7")
load("//:test_defs.bzl", "GenJavaTests")
+package(default_visibility = ["//:src"])
+
GenJavaTests(
name = "functional_tests",
srcs = glob(
@@ -31,14 +27,16 @@ GenJavaTests(
),
javacopts = DOCLINT_HTML_AND_SYNTAX,
lib_javacopts = SOURCE_7_TARGET_7,
- # NOTE: This should not depend on Guava or jsr305 to ensure that Dagger can be
- # used without Guava and jsr305 deps.
test_only_deps = [
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:concurrent",
"@google_bazel_common//third_party/java/guava:testlib",
- "@google_bazel_common//third_party/java/guava",
"@google_bazel_common//third_party/java/truth",
"@google_bazel_common//third_party/java/junit",
],
+ # NOTE: This should not depend on Guava or jsr305 to ensure that Dagger can be
+ # used without Guava and jsr305 deps.
deps = [
"//:dagger_with_compiler",
"@google_bazel_common//third_party/java/auto:factory",
diff --git a/javatests/dagger/functional/BoundedGenerics.java b/javatests/dagger/functional/BoundedGenerics.java
index 812cd0454..d51433757 100644
--- a/javatests/dagger/functional/BoundedGenerics.java
+++ b/javatests/dagger/functional/BoundedGenerics.java
@@ -19,6 +19,7 @@ package dagger.functional;
import java.util.List;
import javax.inject.Inject;
+
class BoundedGenerics<A extends Number & Comparable<? super A>,
B extends List<? extends CharSequence>,
C extends List<? super String>,
diff --git a/javatests/dagger/functional/ComponentDependenciesTest.java b/javatests/dagger/functional/ComponentDependenciesTest.java
new file mode 100644
index 000000000..502c51d9b
--- /dev/null
+++ b/javatests/dagger/functional/ComponentDependenciesTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.Component;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests component dependencies.
+ */
+@RunWith(JUnit4.class)
+public final class ComponentDependenciesTest {
+ public interface One {
+ String getString();
+ }
+
+ public interface Two {
+ String getString();
+ }
+
+ public interface Merged extends One, Two {
+ }
+
+ @Component(dependencies = Merged.class)
+ interface TestComponent {
+ String getString();
+
+ @Component.Builder
+ interface Builder {
+ Builder dep(Merged dep);
+
+ TestComponent build();
+ }
+ }
+
+ @Test
+ public void testSameMethodTwice() throws Exception {
+ TestComponent component =
+ DaggerComponentDependenciesTest_TestComponent.builder().dep(() -> "test").build();
+ assertThat(component.getString()).isEqualTo("test");
+ }
+
+ public interface OneOverride {
+ Object getString();
+ }
+
+ public interface TwoOverride {
+ Object getString();
+ }
+
+ public interface MergedOverride extends OneOverride, TwoOverride {
+ @Override
+ String getString();
+ }
+
+ @Component(dependencies = MergedOverride.class)
+ interface TestOverrideComponent {
+ String getString();
+
+ @Component.Builder
+ interface Builder {
+ Builder dep(MergedOverride dep);
+
+ TestOverrideComponent build();
+ }
+ }
+
+ @Test
+ public void testPolymorphicOverridesStillCompiles() throws Exception {
+ TestOverrideComponent component =
+ DaggerComponentDependenciesTest_TestOverrideComponent.builder().dep(() -> "test").build();
+ assertThat(component.getString()).isEqualTo("test");
+ }
+}
diff --git a/javatests/dagger/functional/Generic2.java b/javatests/dagger/functional/Generic2.java
index f53c0f86c..40ef546db 100644
--- a/javatests/dagger/functional/Generic2.java
+++ b/javatests/dagger/functional/Generic2.java
@@ -18,6 +18,7 @@ package dagger.functional;
import javax.inject.Inject;
+
public class Generic2<T> {
final T t;
diff --git a/javatests/dagger/functional/GenericNoDeps.java b/javatests/dagger/functional/GenericNoDeps.java
index c3f38b4de..0e3d41b43 100644
--- a/javatests/dagger/functional/GenericNoDeps.java
+++ b/javatests/dagger/functional/GenericNoDeps.java
@@ -18,6 +18,7 @@ package dagger.functional;
import javax.inject.Inject;
+
class GenericNoDeps<T> {
@Inject GenericNoDeps() {}
diff --git a/javatests/dagger/functional/MultibindingTest.java b/javatests/dagger/functional/MultibindingTest.java
index d99e46d12..3da3901ce 100644
--- a/javatests/dagger/functional/MultibindingTest.java
+++ b/javatests/dagger/functional/MultibindingTest.java
@@ -80,7 +80,7 @@ public class MultibindingTest {
@Test
public void wrappedAnnotationKeyMap() {
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({"unchecked", "rawtypes"})
Class<? extends Number>[] classes = new Class[] {Long.class, Integer.class};
assertThat(multibindingComponent.wrappedAnnotationKeyMap())
.containsExactly(
diff --git a/javatests/dagger/functional/NeedsFactory.java b/javatests/dagger/functional/NeedsFactory.java
index 2ea01ec69..14ab1349d 100644
--- a/javatests/dagger/functional/NeedsFactory.java
+++ b/javatests/dagger/functional/NeedsFactory.java
@@ -19,11 +19,19 @@ package dagger.functional;
import com.google.auto.factory.AutoFactory;
import javax.inject.Inject;
-class NeedsFactory {
+public class NeedsFactory {
@Inject
- NeedsFactory(@SuppressWarnings("unused") NeedsFactory_SomethingFactory somethingFactory) {}
+ NeedsFactory(
+ @SuppressWarnings("unused") NeedsFactory_SomethingFactory somethingFactory,
+ @SuppressWarnings("unused") SomethingFactoryImpl somethingFactoryImpl) {}
- @AutoFactory
+ public interface SomethingFactory {}
+
+ @AutoFactory(implementing = SomethingFactory.class, allowSubclasses = true)
static class Something {}
+
+ public static final class SomethingFactoryImpl extends NeedsFactory_SomethingFactory {
+ @Inject SomethingFactoryImpl() {}
+ }
}
diff --git a/javatests/dagger/functional/NeedsProviderOfFactory.java b/javatests/dagger/functional/NeedsProviderOfFactory.java
index efbd73df8..1ae797e14 100644
--- a/javatests/dagger/functional/NeedsProviderOfFactory.java
+++ b/javatests/dagger/functional/NeedsProviderOfFactory.java
@@ -26,7 +26,8 @@ class NeedsProviderOfFactory {
static class InjectsProviderOfFactory {
@Inject
InjectsProviderOfFactory(
- Provider<NeedsProviderOfFactory_SomethingFactory> provider) {}
+ Provider<NeedsProviderOfFactory_SomethingFactory> somethingFactoryProvider,
+ Provider<SomethingFactoryImpl> somethingFactoryImplProvider) {}
}
@Component
@@ -34,6 +35,12 @@ class NeedsProviderOfFactory {
InjectsProviderOfFactory injectsProviderOfFactory();
}
- @AutoFactory
+ interface SomethingFactory {}
+
+ @AutoFactory(implementing = SomethingFactory.class, allowSubclasses = true)
static class Something {}
+
+ static final class SomethingFactoryImpl extends NeedsProviderOfFactory_SomethingFactory {
+ @Inject SomethingFactoryImpl() {}
+ }
}
diff --git a/javatests/dagger/functional/aot/DependsOnMissingArrayKey.java b/javatests/dagger/functional/aot/DependsOnMissingArrayKey.java
deleted file mode 100644
index 20a89d4f3..000000000
--- a/javatests/dagger/functional/aot/DependsOnMissingArrayKey.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.functional.aot;
-
-import dagger.Module;
-import dagger.Provides;
-import dagger.Subcomponent;
-
-/**
- * Regression test for an ahead-of-time subcomponents bug where generating the name for a missing
- * binding method for a key of an array type threw an exception.
- */
-final class DependsOnMissingArrayKey {
- @Module
- abstract static class ModuleArrayDependencies {
- @Provides
- static int dependsOnMissingArrayType(int[] primitive, Object[] object, String[][] doubleArray) {
- return 0;
- }
- }
-
- @Subcomponent(modules = ModuleArrayDependencies.class)
- interface HasMissingArrayBindings {
- int dependsOnMissingArrayType();
- }
-}
diff --git a/javatests/dagger/functional/aot/MapFrameworkInstanceWithContributionsInMultipleImplementationsTest.java b/javatests/dagger/functional/aot/MapFrameworkInstanceWithContributionsInMultipleImplementationsTest.java
deleted file mode 100644
index ae6b3a4d2..000000000
--- a/javatests/dagger/functional/aot/MapFrameworkInstanceWithContributionsInMultipleImplementationsTest.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2019 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.functional.aot;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import dagger.Component;
-import dagger.Module;
-import dagger.Provides;
-import dagger.Subcomponent;
-import dagger.multibindings.IntoMap;
-import dagger.multibindings.StringKey;
-import java.util.Map;
-import javax.inject.Provider;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/**
- * Tests that framework instances of map bindings are properly instantiated in ahead-of-time mode
- * when contributions are made in 3 or more implementations.
- */
-@RunWith(JUnit4.class)
-public final class MapFrameworkInstanceWithContributionsInMultipleImplementationsTest {
- @Subcomponent(modules = LeafModule.class)
- interface Leaf {
- Provider<Map<String, String>> providerOfMapOfValues();
- Provider<Map<String, Provider<String>>> providerOfMapOfProviders();
- }
-
- @Module
- interface LeafModule {
- @Provides
- @IntoMap
- @StringKey("a")
- static String fromLeaf() {
- return "a";
- }
- }
-
- @Subcomponent(modules = AncestorModule.class)
- interface Ancestor {
- Leaf leaf();
- }
-
- @Module
- interface AncestorModule {
- @Provides
- @IntoMap
- @StringKey("b")
- static String fromAncestor() {
- return "b";
- }
- }
-
- @Component(modules = RootModule.class)
- interface Root {
- Ancestor ancestor();
- }
-
- @Module
- interface RootModule {
- @Provides
- @IntoMap
- @StringKey("c")
- static String fromRoot() {
- return "c";
- }
- }
-
- @Test
- public void mapFactoryCanBeInstantiatedAcrossComponentImplementations() {
- Leaf leaf =
- DaggerMapFrameworkInstanceWithContributionsInMultipleImplementationsTest_Root.create()
- .ancestor()
- .leaf();
- assertThat(leaf.providerOfMapOfValues().get()).hasSize(3);
- assertThat(leaf.providerOfMapOfProviders().get()).hasSize(3);
- }
-}
diff --git a/javatests/dagger/functional/aot/MissingBindingReplacedWithGeneratedInstance.java b/javatests/dagger/functional/aot/MissingBindingReplacedWithGeneratedInstance.java
deleted file mode 100644
index 1813ad2ae..000000000
--- a/javatests/dagger/functional/aot/MissingBindingReplacedWithGeneratedInstance.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.functional.aot;
-
-import dagger.Subcomponent;
-import javax.inject.Inject;
-
-/**
- * This class demonstrates a regression where a missing binding method was generated in a leaf
- * component and then satisfied in an ancestor with a generated instance binding. If the ancestor's
- * generated instance method had the same name as the formerly-missing binding method, Dagger would
- * generate code without a proper {@code DaggerOuter.this} reference:
- *
- * <pre>{@code
- * public class DaggerAncestor implements Ancestor {
- * protected abstract Ancestor getAncestor();
- *
- * protected abstract class LeafImpl extends DaggerLeaf {
- * {@literal @Override}
- * protected final Ancestor getAncestor() {
- * return getAncestor();
- * // ^ should be DaggerAncestor.this.getAncestor()
- * }
- * }
- * }
- * }</pre>
- */
-final class MissingBindingReplacedWithGeneratedInstance {
- @Subcomponent
- interface Leaf {
- DependsOnGeneratedInstance dependsOnGeneratedInstance();
- }
-
- static class DependsOnGeneratedInstance {
- @Inject DependsOnGeneratedInstance(Ancestor generatedInstance) {}
- }
-
- @Subcomponent
- interface Ancestor {
- Leaf child();
- }
-}
diff --git a/javatests/dagger/functional/aot/ModifiedFrameworkInstancesTest.java b/javatests/dagger/functional/aot/ModifiedFrameworkInstancesTest.java
deleted file mode 100644
index 7084f83af..000000000
--- a/javatests/dagger/functional/aot/ModifiedFrameworkInstancesTest.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.functional.aot;
-
-import dagger.Component;
-import dagger.Module;
-import dagger.Provides;
-import dagger.Subcomponent;
-import dagger.multibindings.IntoSet;
-import java.util.Set;
-import javax.inject.Inject;
-import javax.inject.Provider;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public final class ModifiedFrameworkInstancesTest {
- static class DependsOnModifiableBinding {
- @Inject
- DependsOnModifiableBinding(Set<Integer> modifiableDependency) {}
- }
-
- @Module
- interface ChildModule {
- @Provides
- @IntoSet
- static int contribution() {
- return 1;
- }
- }
-
- @Subcomponent(modules = ChildModule.class)
- interface Child {
- Provider<DependsOnModifiableBinding> frameworkInstanceWithModifiedDependency();
- }
-
- @Module
- interface ParentModule {
- @Provides
- @IntoSet
- static int contribution() {
- return 2;
- }
- }
-
- @Component(modules = ParentModule.class)
- interface Parent {
- Child child();
- }
-
- @Test
- public void dependsOnModifiedFrameworkInstance() {
- DaggerModifiedFrameworkInstancesTest_Parent.create()
- .child()
- .frameworkInstanceWithModifiedDependency()
- // Ensure that modified framework instances that are dependencies to other framework
- // instances from superclass implementations are initialized correctly. This fixes a
- // regression where a null instance would be passed to the superclass initialization, and
- // then a NullPointerException would be thrown when the factory attempted to satisfy the
- // dependency in get(). If get() succeeds, this test should pass.
- .get();
- }
-}
diff --git a/javatests/dagger/functional/aot/PrunedBindingDependedOnInSuperInitializationTest.java b/javatests/dagger/functional/aot/PrunedBindingDependedOnInSuperInitializationTest.java
deleted file mode 100644
index 853e22b07..000000000
--- a/javatests/dagger/functional/aot/PrunedBindingDependedOnInSuperInitializationTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.functional.aot;
-
-import dagger.Component;
-import dagger.Module;
-import dagger.Provides;
-import dagger.Subcomponent;
-import javax.inject.Inject;
-import javax.inject.Provider;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class PrunedBindingDependedOnInSuperInitializationTest {
- interface PrunedDependency {}
-
- static class WillHavePrunedDependency {
- @Inject WillHavePrunedDependency(PrunedDependency pruned) {}
- }
-
- @Subcomponent
- interface Child {
- Provider<WillHavePrunedDependency> frameworkInstance();
- }
-
- @Module
- static class ParentModule {
- @Provides
- static WillHavePrunedDependency pruneDependency() {
- return new WillHavePrunedDependency(new PrunedDependency() {});
- }
- }
-
- @Component(modules = ParentModule.class)
- interface Parent {
- Child child();
- }
-
- @Test
- public void prunedFrameworkInstanceBindingUsedInInitializationDoesntThrow() {
- Parent parent = DaggerPrunedBindingDependedOnInSuperInitializationTest_Parent.create();
- // This test ensures that pruned bindings that are used during unpruned initialization
- // statements do not throw exceptions. If the subcomponent initialization succeeds, the test
- // should pass
- parent.child();
- }
-}
diff --git a/javatests/dagger/functional/aot/PrunedFrameworkInstanceWithModuleInstanceTest.java b/javatests/dagger/functional/aot/PrunedFrameworkInstanceWithModuleInstanceTest.java
deleted file mode 100644
index f995789bf..000000000
--- a/javatests/dagger/functional/aot/PrunedFrameworkInstanceWithModuleInstanceTest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2019 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.functional.aot;
-
-import dagger.Component;
-import dagger.Module;
-import dagger.Provides;
-import dagger.Subcomponent;
-import javax.inject.Inject;
-import javax.inject.Provider;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class PrunedFrameworkInstanceWithModuleInstanceTest {
- static class Pruned {}
-
- static class InjectsPruned {
- @Inject
- InjectsPruned(Provider<Pruned> pruned) {}
- }
-
- @Module
- static class InstanceStateModule {
- @Provides
- /* intentionally not static */ Pruned pruned() {
- return new Pruned();
- }
- }
-
- @Subcomponent(modules = InstanceStateModule.class)
- interface LeafWithoutCreator {
- InjectsPruned injectsPruned();
- }
-
- @Subcomponent(modules = InstanceStateModule.class)
- interface LeafWithCreator {
- InjectsPruned injectsPruned();
-
- @Subcomponent.Builder
- interface Builder {
- Builder module(InstanceStateModule module);
- LeafWithCreator build();
- }
- }
-
- @Module
- interface RootModule {
- @Provides
- static InjectsPruned pruneBindingWithInstanceState() {
- return new InjectsPruned(null);
- }
- }
-
- @Component(modules = RootModule.class)
- interface Root {
- LeafWithoutCreator leafWithoutCreator(InstanceStateModule pruned);
- LeafWithCreator.Builder leafWithCreator();
- }
-
- @Test
- public void prunedBindingWithModuleInstance_doesntThrowDuringInitialization() {
- Root root = DaggerPrunedFrameworkInstanceWithModuleInstanceTest_Root.create();
-
- Object unused = root.leafWithoutCreator(new InstanceStateModule()).injectsPruned();
- unused = root.leafWithCreator().module(new InstanceStateModule()).build().injectsPruned();
- }
-}
diff --git a/javatests/dagger/functional/aot/ScopedBindsWithMissingDependency.java b/javatests/dagger/functional/aot/ScopedBindsWithMissingDependency.java
deleted file mode 100644
index 2723ac0d0..000000000
--- a/javatests/dagger/functional/aot/ScopedBindsWithMissingDependency.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.functional.aot;
-
-import dagger.Binds;
-import dagger.Module;
-import dagger.Reusable;
-import dagger.Subcomponent;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import javax.inject.Scope;
-
-/**
- * A regression test for ahead-of-time subcomponents mode where a scoped {@link Binds} method whose
- * dependency was missing in a partial subcomponent implementation threw an exception in the
- * processor.
- */
-final class ScopedBindsWithMissingDependency {
-
- @Retention(RetentionPolicy.RUNTIME)
- @Scope
- @interface CustomScope {}
-
- @Module
- interface ScopedBindsWithMissingDependencyModule {
- @Binds
- @CustomScope
- Object bindsCustomScopeToMissingDep(String missingDependency);
-
- @Binds
- @Reusable
- CharSequence bindsReusableScopeToMissingDep(String missingDependency);
- }
-
- @CustomScope
- @Subcomponent(modules = ScopedBindsWithMissingDependencyModule.class)
- interface HasScopedBindsWithMissingDependency {
- Object customScopedBindsWithMissingDependency();
- CharSequence reusableScopedBindsWithMissingDependency();
- }
-}
diff --git a/javatests/dagger/functional/aot/SubcomponentWithInaccessibleMissingBindingMethod.java b/javatests/dagger/functional/aot/SubcomponentWithInaccessibleMissingBindingMethod.java
deleted file mode 100644
index 689689cea..000000000
--- a/javatests/dagger/functional/aot/SubcomponentWithInaccessibleMissingBindingMethod.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.functional.aot;
-
-import dagger.Subcomponent;
-import dagger.functional.aot.sub.PublicTypeWithPackagePrivateMissingDep;
-import javax.inject.Provider;
-
-@Subcomponent
-interface SubcomponentWithInaccessibleMissingBindingMethod {
- PublicTypeWithPackagePrivateMissingDep instance();
- Provider<PublicTypeWithPackagePrivateMissingDep> frameworkInstance();
-}
diff --git a/javatests/dagger/functional/aot/SubcomponentWithModifiedInaccessibleDependency.java b/javatests/dagger/functional/aot/SubcomponentWithModifiedInaccessibleDependency.java
deleted file mode 100644
index 06cebb9b3..000000000
--- a/javatests/dagger/functional/aot/SubcomponentWithModifiedInaccessibleDependency.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.functional.aot;
-
-import dagger.Subcomponent;
-import dagger.functional.aot.sub.BindsPackagePrivateModule;
-import dagger.functional.aot.sub.SubcomponentWithInaccessibleOptionalBindingMethod;
-
-/**
- * See {@link dagger.functional.aot.sub.SubcomponentWithInaccessibleOptionalBindingMethod}. This
- * subcomponent will induce a modified binding method for its single child for the key {@code
- * Optional<dagger.functional.aot.sub.PackagePrivate>}. When it tries to reimplement it, it must use
- * the publicly accessible type.
- */
-@Subcomponent(modules = BindsPackagePrivateModule.class)
-interface SubcomponentWithModifiedInaccessibleDependency {
- SubcomponentWithInaccessibleOptionalBindingMethod child();
-}
diff --git a/javatests/dagger/functional/aot/sub/BindsPackagePrivateModule.java b/javatests/dagger/functional/aot/sub/BindsPackagePrivateModule.java
deleted file mode 100644
index 2eee3c996..000000000
--- a/javatests/dagger/functional/aot/sub/BindsPackagePrivateModule.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.functional.aot.sub;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-public final class BindsPackagePrivateModule {
- @Provides
- static PackagePrivate packagePrivate() {
- return new PackagePrivate();
- }
-}
diff --git a/javatests/dagger/functional/aot/sub/PackagePrivate.java b/javatests/dagger/functional/aot/sub/PackagePrivate.java
deleted file mode 100644
index c629f6e32..000000000
--- a/javatests/dagger/functional/aot/sub/PackagePrivate.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.functional.aot.sub;
-
-final class PackagePrivate {}
diff --git a/javatests/dagger/functional/aot/sub/PublicTypeWithPackagePrivateMissingDep.java b/javatests/dagger/functional/aot/sub/PublicTypeWithPackagePrivateMissingDep.java
deleted file mode 100644
index b5ced6f61..000000000
--- a/javatests/dagger/functional/aot/sub/PublicTypeWithPackagePrivateMissingDep.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.functional.aot.sub;
-
-import javax.inject.Inject;
-
-public class PublicTypeWithPackagePrivateMissingDep {
- @Inject
- PublicTypeWithPackagePrivateMissingDep(PackagePrivate packagePrivate) {}
-}
diff --git a/javatests/dagger/functional/aot/sub/PublicTypeWithPackagePrivateOptionalDep.java b/javatests/dagger/functional/aot/sub/PublicTypeWithPackagePrivateOptionalDep.java
deleted file mode 100644
index 1d697233e..000000000
--- a/javatests/dagger/functional/aot/sub/PublicTypeWithPackagePrivateOptionalDep.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.functional.aot.sub;
-
-import java.util.Optional;
-import javax.inject.Inject;
-
-public class PublicTypeWithPackagePrivateOptionalDep {
- @Inject
- PublicTypeWithPackagePrivateOptionalDep(Optional<PackagePrivate> packagePrivateOptional) {}
-}
diff --git a/javatests/dagger/functional/aot/sub/SubcomponentWithInaccessibleOptionalBindingMethod.java b/javatests/dagger/functional/aot/sub/SubcomponentWithInaccessibleOptionalBindingMethod.java
deleted file mode 100644
index d908b1c28..000000000
--- a/javatests/dagger/functional/aot/sub/SubcomponentWithInaccessibleOptionalBindingMethod.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.functional.aot.sub;
-
-import dagger.BindsOptionalOf;
-import dagger.Module;
-import dagger.Subcomponent;
-import dagger.functional.aot.sub.SubcomponentWithInaccessibleOptionalBindingMethod.ExposesModifiablePackagePrivateBindingModule;
-import javax.inject.Provider;
-
-/**
- * This component will generate a modifiable binding method for the key {@code
- * Optional<PackagePrivate>} as a dependency of {@link PublicTypeWithPackagePrivateOptionalDep}.
- * Even though this subcomponent implementation can refer to the parameterized type, a subclass
- * implementation in another package will not be able to, and thus the return type must be reduced
- * to the publicly accessible type. This is exhibited in {@link
- * dagger.functional.aot.SubcomponentWithModifiedInaccessibleDependency}.
- */
-@Subcomponent(modules = ExposesModifiablePackagePrivateBindingModule.class)
-public interface SubcomponentWithInaccessibleOptionalBindingMethod {
- PublicTypeWithPackagePrivateOptionalDep instance();
- Provider<PublicTypeWithPackagePrivateOptionalDep> frameworkInstance();
-
- @Module
- interface ExposesModifiablePackagePrivateBindingModule {
- @BindsOptionalOf
- PackagePrivate optional();
- }
-}
diff --git a/javatests/dagger/functional/assisted/AssistedFactoryBindsTest.java b/javatests/dagger/functional/assisted/AssistedFactoryBindsTest.java
new file mode 100644
index 000000000..78495b7d8
--- /dev/null
+++ b/javatests/dagger/functional/assisted/AssistedFactoryBindsTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.assisted;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.Binds;
+import dagger.Component;
+import dagger.Module;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import javax.inject.Inject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class AssistedFactoryBindsTest {
+ @Component(modules = FooFactoryModule.class)
+ interface ParentComponent {
+ // Test using @Binds where Foo => FooImpl and FooFactory => FooFactoryImpl
+ FooFactory fooFactory();
+ }
+
+ @Module
+ interface FooFactoryModule {
+ @Binds
+ FooFactory bind(FooFactoryImpl impl);
+ }
+
+ interface Foo {}
+
+ static final class FooImpl implements Foo {
+ private final Dep dep;
+ private final AssistedDep assistedDep;
+
+ @AssistedInject
+ FooImpl(Dep dep, @Assisted AssistedDep assistedDep) {
+ this.dep = dep;
+ this.assistedDep = assistedDep;
+ }
+ }
+
+ interface FooFactory {
+ Foo create(AssistedDep assistedDep);
+ }
+
+ @AssistedFactory
+ interface FooFactoryImpl extends FooFactory {
+ @Override
+ FooImpl create(AssistedDep assistedDep);
+ }
+
+ static final class AssistedDep {}
+
+ static final class Dep {
+ @Inject
+ Dep() {}
+ }
+
+ @Test
+ public void testFooFactory() {
+ FooFactory fooFactory = DaggerAssistedFactoryBindsTest_ParentComponent.create().fooFactory();
+ assertThat(fooFactory).isInstanceOf(FooFactoryImpl.class);
+
+ AssistedDep assistedDep = new AssistedDep();
+ Foo foo = fooFactory.create(assistedDep);
+ assertThat(foo).isInstanceOf(FooImpl.class);
+
+ FooImpl fooImpl = (FooImpl) foo;
+ assertThat(fooImpl.dep).isNotNull();
+ assertThat(fooImpl.assistedDep).isEqualTo(assistedDep);
+ }
+}
diff --git a/javatests/dagger/functional/assisted/AssistedFactoryInaccessibleTest.java b/javatests/dagger/functional/assisted/AssistedFactoryInaccessibleTest.java
new file mode 100644
index 000000000..d586c619c
--- /dev/null
+++ b/javatests/dagger/functional/assisted/AssistedFactoryInaccessibleTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.assisted;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.Component;
+import dagger.assisted.AssistedFactory;
+import dagger.functional.assisted.subpackage.AccessibleFoo;
+import dagger.functional.assisted.subpackage.AssistedDep;
+import dagger.functional.assisted.subpackage.InaccessibleFooFactory;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class AssistedFactoryInaccessibleTest {
+ @Component
+ interface ParentComponent {
+ // Factory for an accessible type from another package
+ AccessibleFooFactory accessibleFooFactory();
+
+ // Factory for an inaccessible type from another package
+ InaccessibleFooFactory inaccessibleFooFactory();
+ }
+
+ @AssistedFactory
+ public interface AccessibleFooFactory {
+ // Use different parameter names than Foo to make sure we're not assuming they're the same.
+ AccessibleFoo create(AssistedDep factoryAssistedDep);
+ }
+
+ @Test
+ public void testAccessibleFooFactory() {
+ AssistedDep assistedDep = new AssistedDep();
+ AccessibleFoo accessibleFoo =
+ DaggerAssistedFactoryInaccessibleTest_ParentComponent.create()
+ .accessibleFooFactory()
+ .create(assistedDep);
+ assertThat(accessibleFoo).isNotNull();
+ assertThat(accessibleFoo.dep).isNotNull();
+ assertThat(accessibleFoo.assistedDep).isEqualTo(assistedDep);
+ }
+
+ @Test
+ public void testInaccessibleFooFactory() {
+ AssistedDep assistedDep = new AssistedDep();
+ // We can't access InaccessibleFoo directly, so just use Object instead.
+ Object inaccessibleFoo =
+ DaggerAssistedFactoryInaccessibleTest_ParentComponent.create()
+ .inaccessibleFooFactory()
+ .create(assistedDep);
+ assertThat(inaccessibleFoo).isNotNull();
+ }
+}
diff --git a/javatests/dagger/functional/assisted/AssistedFactoryParameterizedTest.java b/javatests/dagger/functional/assisted/AssistedFactoryParameterizedTest.java
new file mode 100644
index 000000000..d079430d4
--- /dev/null
+++ b/javatests/dagger/functional/assisted/AssistedFactoryParameterizedTest.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.assisted;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.Component;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class AssistedFactoryParameterizedTest {
+ @Singleton
+ @Component
+ interface ParentComponent {
+ // Tests a parameterized Factory with unique @Assisted types
+ ParameterizedFooFactory<Dep2, AssistedDep2> uniqueParameterizedFooFactory();
+
+ // Tests a parameterized Factory with duplicate @Assisted types in its resolved request type.
+ // Note: this is fine since the @Assisted types are still unique on the @AssistedInject and
+ // @AssistedFactory types, so that the generated code can correctly matches types.
+ ParameterizedFooFactory<Dep1, AssistedDep1> dupeParameterizedFooFactory();
+
+ // Tests a parameterized Factory with same type as binding
+ ParameterizedFooFactory<Dep1, Dep1> bindingParameterizedFooFactory();
+
+ // Tests a parameterized Factory with fixed type parameters
+ FixedParameterizedFooFactory fixedParameterizedFooFactory();
+
+ // Tests a parameterized Factory that extends an interface with a parameterized return type
+ ExtendedFooFactory<Dep2, AssistedDep2> extendedParameterizedFooFactory();
+
+ // Tests a request of factories from another binding.
+ SomeEntryPoint someEntryPoint();
+ }
+
+ static final class Dep1 {
+ @Inject
+ Dep1(Dep2 dep2, Dep3 dep3) {}
+ }
+
+ static final class Dep2 {
+ @Inject
+ Dep2(Dep3 dep3) {}
+ }
+
+ static final class Dep3 {
+ @Inject
+ Dep3(Dep4 dep4) {}
+ }
+
+ static final class Dep4 {
+ @Inject
+ Dep4() {}
+ }
+
+ // A base interface to test that factories can reference subclasses of the assisted parameter.
+ interface AssistedDep {}
+
+ static final class AssistedDep1 implements AssistedDep {}
+
+ static final class AssistedDep2 implements AssistedDep {}
+
+ abstract static class BaseFoo {
+ @Inject Dep4 dep4;
+ }
+
+ static final class ParameterizedFoo<DepT, AssistedDepT> extends BaseFoo {
+ private final Dep1 dep1;
+ private final Provider<DepT> depTProvider;
+ private final AssistedDep1 assistedDep1;
+ private final AssistedDepT assistedDepT;
+ private final int assistedInt;
+ private final ParameterizedFooFactory<DepT, AssistedDepT> factory;
+
+ @Inject Dep3 dep3;
+
+ @AssistedInject
+ ParameterizedFoo(
+ Dep1 dep1,
+ @Assisted AssistedDep1 assistedDep1,
+ Provider<DepT> depTProvider,
+ @Assisted AssistedDepT assistedDepT,
+ @Assisted int assistedInt,
+ ParameterizedFooFactory<DepT, AssistedDepT> factory) {
+ this.dep1 = dep1;
+ this.depTProvider = depTProvider;
+ this.assistedDep1 = assistedDep1;
+ this.assistedDepT = assistedDepT;
+ this.assistedInt = assistedInt;
+ this.factory = factory;
+ }
+ }
+
+ @AssistedFactory
+ interface ParameterizedFooFactory<DepT, AssistedDepT> {
+ ParameterizedFoo<DepT, AssistedDepT> create(
+ AssistedDep1 assistedDep1, AssistedDepT assistedDepT, int assistedInt);
+ }
+
+ @Test
+ public void testUniqueParameterizedFooFactory() {
+ AssistedDep1 assistedDep1 = new AssistedDep1();
+ AssistedDep2 assistedDep2 = new AssistedDep2();
+ int assistedInt = 7;
+ ParameterizedFoo<Dep2, AssistedDep2> parameterizedFoo =
+ DaggerAssistedFactoryParameterizedTest_ParentComponent.create()
+ .uniqueParameterizedFooFactory()
+ .create(assistedDep1, assistedDep2, assistedInt);
+ assertThat(parameterizedFoo.dep1).isNotNull();
+ assertThat(parameterizedFoo.depTProvider).isNotNull();
+ assertThat(parameterizedFoo.depTProvider.get()).isNotNull();
+ assertThat(parameterizedFoo.dep3).isNotNull();
+ assertThat(parameterizedFoo.dep4).isNotNull();
+ assertThat(parameterizedFoo.assistedDep1).isEqualTo(assistedDep1);
+ assertThat(parameterizedFoo.assistedDepT).isEqualTo(assistedDep2);
+ assertThat(parameterizedFoo.assistedInt).isEqualTo(assistedInt);
+ assertThat(parameterizedFoo.factory).isNotNull();
+ }
+
+ @Test
+ public void testDupeParameterizedFooFactory() {
+ AssistedDep1 assistedDep1 = new AssistedDep1();
+ int assistedInt = 7;
+ ParameterizedFoo<Dep1, AssistedDep1> parameterizedFoo =
+ DaggerAssistedFactoryParameterizedTest_ParentComponent.create()
+ .dupeParameterizedFooFactory()
+ .create(assistedDep1, assistedDep1, assistedInt);
+ assertThat(parameterizedFoo.dep1).isNotNull();
+ assertThat(parameterizedFoo.depTProvider).isNotNull();
+ assertThat(parameterizedFoo.depTProvider.get()).isNotNull();
+ assertThat(parameterizedFoo.dep3).isNotNull();
+ assertThat(parameterizedFoo.dep4).isNotNull();
+ assertThat(parameterizedFoo.assistedDep1).isEqualTo(assistedDep1);
+ assertThat(parameterizedFoo.assistedDepT).isEqualTo(assistedDep1);
+ assertThat(parameterizedFoo.assistedInt).isEqualTo(assistedInt);
+ assertThat(parameterizedFoo.factory).isNotNull();
+ }
+
+ @Test
+ public void testBindingParameterizedFooFactory() {
+ AssistedDep1 assistedDep1 = new AssistedDep1();
+ Dep1 dep1 = new Dep1(new Dep2(new Dep3(new Dep4())), new Dep3(new Dep4()));
+ int assistedInt = 7;
+ ParameterizedFoo<Dep1, Dep1> parameterizedFoo =
+ DaggerAssistedFactoryParameterizedTest_ParentComponent.create()
+ .bindingParameterizedFooFactory()
+ .create(assistedDep1, dep1, assistedInt);
+ assertThat(parameterizedFoo.dep1).isNotNull();
+ assertThat(parameterizedFoo.depTProvider).isNotNull();
+ assertThat(parameterizedFoo.depTProvider.get()).isNotNull();
+ assertThat(parameterizedFoo.dep3).isNotNull();
+ assertThat(parameterizedFoo.dep4).isNotNull();
+ assertThat(parameterizedFoo.assistedDep1).isEqualTo(assistedDep1);
+ assertThat(parameterizedFoo.assistedDepT).isEqualTo(dep1);
+ assertThat(parameterizedFoo.assistedInt).isEqualTo(assistedInt);
+ assertThat(parameterizedFoo.factory).isNotNull();
+ }
+
+ @AssistedFactory
+ interface FixedParameterizedFooFactory {
+ ParameterizedFoo<Dep2, AssistedDep2> create(
+ AssistedDep1 assistedDep1, AssistedDep2 assistedDep2, int assistedInt);
+ }
+
+ @Test
+ public void testFixedParameterizedFooFactory() {
+ AssistedDep1 assistedDep1 = new AssistedDep1();
+ AssistedDep2 assistedDep2 = new AssistedDep2();
+ int assistedInt = 7;
+ ParameterizedFoo<Dep2, AssistedDep2> parameterizedFoo =
+ DaggerAssistedFactoryParameterizedTest_ParentComponent.create()
+ .fixedParameterizedFooFactory()
+ .create(assistedDep1, assistedDep2, assistedInt);
+ assertThat(parameterizedFoo.dep1).isNotNull();
+ assertThat(parameterizedFoo.depTProvider).isNotNull();
+ assertThat(parameterizedFoo.depTProvider.get()).isNotNull();
+ assertThat(parameterizedFoo.dep3).isNotNull();
+ assertThat(parameterizedFoo.dep4).isNotNull();
+ assertThat(parameterizedFoo.assistedDep1).isEqualTo(assistedDep1);
+ assertThat(parameterizedFoo.assistedDepT).isEqualTo(assistedDep2);
+ assertThat(parameterizedFoo.assistedInt).isEqualTo(assistedInt);
+ assertThat(parameterizedFoo.factory).isNotNull();
+ }
+
+ interface ParameterizedFactory<ReturnT, DepT, AssistedDepT> {
+ // Use different parameter names than Foo to make sure we're not assuming they're the same.
+ ReturnT create(
+ AssistedDep1 factoryAssistedDep1, AssistedDepT factoryAssistedDepT, int factoryAssistedInt);
+ }
+
+ @AssistedFactory
+ interface ExtendedFooFactory<DepT, AssistedDepT>
+ extends ParameterizedFactory<ParameterizedFoo<DepT, AssistedDepT>, DepT, AssistedDepT> {}
+
+ @Test
+ public void testExtendedFooFactory() {
+ AssistedDep1 assistedDep1 = new AssistedDep1();
+ AssistedDep2 assistedDep2 = new AssistedDep2();
+ int assistedInt = 7;
+ ParameterizedFoo<Dep2, AssistedDep2> parameterizedFoo =
+ DaggerAssistedFactoryParameterizedTest_ParentComponent.create()
+ .extendedParameterizedFooFactory()
+ .create(assistedDep1, assistedDep2, assistedInt);
+ assertThat(parameterizedFoo.dep1).isNotNull();
+ assertThat(parameterizedFoo.depTProvider).isNotNull();
+ assertThat(parameterizedFoo.depTProvider.get()).isNotNull();
+ assertThat(parameterizedFoo.dep3).isNotNull();
+ assertThat(parameterizedFoo.dep4).isNotNull();
+ assertThat(parameterizedFoo.assistedDep1).isEqualTo(assistedDep1);
+ assertThat(parameterizedFoo.assistedDepT).isEqualTo(assistedDep2);
+ assertThat(parameterizedFoo.assistedInt).isEqualTo(assistedInt);
+ assertThat(parameterizedFoo.factory).isNotNull();
+ }
+
+ static class SomeEntryPoint {
+ private final ParameterizedFooFactory<Dep1, AssistedDep1> dupeParameterizedFooFactory;
+
+ @Inject
+ SomeEntryPoint(ParameterizedFooFactory<Dep1, AssistedDep1> dupeParameterizedFooFactory) {
+ this.dupeParameterizedFooFactory = dupeParameterizedFooFactory;
+ }
+ }
+
+ @Test
+ public void testParameterizedFooFactoryFromSomeEntryPoint() {
+ AssistedDep1 assistedDep1 = new AssistedDep1();
+ int assistedInt = 7;
+ ParameterizedFoo<Dep1, AssistedDep1> parameterizedFoo =
+ DaggerAssistedFactoryParameterizedTest_ParentComponent.create()
+ .someEntryPoint()
+ .dupeParameterizedFooFactory
+ .create(assistedDep1, assistedDep1, assistedInt);
+ assertThat(parameterizedFoo.dep1).isNotNull();
+ assertThat(parameterizedFoo.depTProvider).isNotNull();
+ assertThat(parameterizedFoo.depTProvider.get()).isNotNull();
+ assertThat(parameterizedFoo.dep3).isNotNull();
+ assertThat(parameterizedFoo.dep4).isNotNull();
+ assertThat(parameterizedFoo.assistedDep1).isEqualTo(assistedDep1);
+ assertThat(parameterizedFoo.assistedDepT).isEqualTo(assistedDep1);
+ assertThat(parameterizedFoo.assistedInt).isEqualTo(assistedInt);
+ assertThat(parameterizedFoo.factory).isNotNull();
+ }
+}
diff --git a/javatests/dagger/functional/assisted/AssistedFactoryTest.java b/javatests/dagger/functional/assisted/AssistedFactoryTest.java
new file mode 100644
index 000000000..3176add46
--- /dev/null
+++ b/javatests/dagger/functional/assisted/AssistedFactoryTest.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.assisted;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.Component;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class AssistedFactoryTest {
+ @Component
+ interface ParentComponent {
+ // Simple factory using a nested factory.
+ SimpleFoo.Factory nestedSimpleFooFactory();
+
+ // Simple factory using a non-nested factory.
+ SimpleFooFactory nonNestedSimpleFooFactory();
+
+ // Simple factory using a factory that extends a supertype.
+ ExtendedSimpleFooFactory extendedSimpleFooFactory();
+
+ // Factory as interface
+ FooFactory fooFactory();
+
+ // Factory as abstract class
+ AbstractFooFactory abstractFooFactory();
+
+ // Factory without any assisted parameters
+ NoAssistedParametersFooFactory noAssistedParametersFooFactory();
+
+ // Test injecting the factories from another class
+ SomeEntryPoint someEntryPoint();
+ }
+
+ // This class tests the request of factories from another binding.
+ static class SomeEntryPoint {
+ private final SimpleFoo.Factory nestedSimpleFooFactory;
+ private final SimpleFooFactory nonNestedSimpleFooFactory;
+ private final ExtendedSimpleFooFactory extendedSimpleFooFactory;
+ private final FooFactory fooFactory;
+ private final AbstractFooFactory abstractFooFactory;
+ private final NoAssistedParametersFooFactory noAssistedParametersFooFactory;
+
+ @Inject
+ SomeEntryPoint(
+ SimpleFoo.Factory nestedSimpleFooFactory,
+ SimpleFooFactory nonNestedSimpleFooFactory,
+ ExtendedSimpleFooFactory extendedSimpleFooFactory,
+ FooFactory fooFactory,
+ AbstractFooFactory abstractFooFactory,
+ NoAssistedParametersFooFactory noAssistedParametersFooFactory) {
+ this.nestedSimpleFooFactory = nestedSimpleFooFactory;
+ this.nonNestedSimpleFooFactory = nonNestedSimpleFooFactory;
+ this.extendedSimpleFooFactory = extendedSimpleFooFactory;
+ this.fooFactory = fooFactory;
+ this.abstractFooFactory = abstractFooFactory;
+ this.noAssistedParametersFooFactory = noAssistedParametersFooFactory;
+ }
+ }
+
+ static final class Dep1 {
+ @Inject
+ Dep1(Dep2 dep2, Dep3 dep3) {}
+ }
+
+ static final class Dep2 {
+ @Inject
+ Dep2(Dep3 dep3) {}
+ }
+
+ static final class Dep3 {
+ @Inject
+ Dep3(Dep4 dep4) {}
+ }
+
+ static final class Dep4 {
+ @Inject
+ Dep4() {}
+ }
+
+ // A base interface to test that factories can reference subclasses of the assisted parameter.
+ interface AssistedDep {}
+
+ static final class AssistedDep1 implements AssistedDep {}
+
+ static final class AssistedDep2 implements AssistedDep {}
+
+ static final class SimpleFoo {
+ private final AssistedDep assistedDep;
+
+ @AssistedInject
+ SimpleFoo(@Assisted AssistedDep assistedDep) {
+ this.assistedDep = assistedDep;
+ }
+
+ @AssistedFactory
+ interface Factory {
+ // Use different parameter names than Foo to make sure we're not assuming they're the same.
+ SimpleFoo createSimpleFoo(AssistedDep factoryAssistedDep);
+
+ // A no-op method to test static methods in assisted factories
+ static void staticMethod() {
+ return;
+ }
+
+ // A no-op method to test default methods in assisted factories
+ default void defaultMethod() {
+ return;
+ }
+ }
+ }
+
+ @AssistedFactory
+ interface SimpleFooFactory {
+ // Use different parameter names than Foo to make sure we're not assuming they're the same.
+ SimpleFoo createSimpleFoo(AssistedDep factoryAssistedDep1);
+
+ // A no-op method to test static methods are allowed
+ static void staticMethod() {
+ return;
+ }
+
+ // A no-op method to test static methods that return assisted type are allowed
+ static SimpleFoo staticSimpleFooMethod() {
+ return null;
+ }
+
+ // A no-op method to test default methods are allowed
+ default void defaultMethod() {
+ return;
+ }
+
+ // A no-op method to test default methods that return assisted type are allowed
+ default SimpleFoo defaultSimpleFooMethod() {
+ return null;
+ }
+ }
+
+ @AssistedFactory
+ interface ExtendedSimpleFooFactory extends SimpleFooFactory {}
+
+ abstract static class BaseFoo {
+ @Inject Dep4 dep4;
+ }
+
+ static final class Foo extends BaseFoo {
+ private final Dep1 dep1;
+ private final Provider<Dep2> dep2Provider;
+ private final AssistedDep1 assistedDep1;
+ private final AssistedDep2 assistedDep2;
+ private final int assistedInt;
+ private final FooFactory factory;
+
+ @Inject Dep3 dep3;
+
+ @AssistedInject
+ Foo(
+ Dep1 dep1,
+ @Assisted AssistedDep1 assistedDep1,
+ Provider<Dep2> dep2Provider,
+ @Assisted AssistedDep2 assistedDep2,
+ @Assisted int assistedInt,
+ FooFactory factory) {
+ this.dep1 = dep1;
+ this.dep2Provider = dep2Provider;
+ this.assistedDep1 = assistedDep1;
+ this.assistedDep2 = assistedDep2;
+ this.assistedInt = assistedInt;
+ this.factory = factory;
+ }
+ }
+
+ @AssistedFactory
+ interface FooFactory {
+ // Use different parameter names than Foo to make sure we're not assuming they're the same.
+ Foo createFoo(
+ AssistedDep1 factoryAssistedDep1, AssistedDep2 factoryAssistedDep2, int factoryAssistedInt);
+ }
+
+ @AssistedFactory
+ abstract static class AbstractFooFactory {
+ // Use different parameter names than Foo to make sure we're not assuming they're the same.
+ abstract Foo createFoo(
+ AssistedDep1 factoryAssistedDep1, AssistedDep2 factoryAssistedDep2, int factoryAssistedInt);
+
+ // A no-op method to test static methods are allowed
+ static void staticMethod() {
+ return;
+ }
+
+ // A no-op method to test static methods that return assisted type are allowed
+ static Foo staticFooMethod() {
+ return null;
+ }
+
+ // A no-op method to test concrete methods are allowed
+ void concreteMethod() {
+ return;
+ }
+
+ // A no-op method to test concrete methods that return assisted type are allowed
+ Foo concreteFooMethod() {
+ return null;
+ }
+ }
+
+ static final class NoAssistedParametersFoo extends BaseFoo {
+ private final Dep1 dep1;
+ private final Provider<Dep2> dep2Provider;
+ private final NoAssistedParametersFooFactory factory;
+
+ @Inject Dep3 dep3;
+
+ @AssistedInject
+ NoAssistedParametersFoo(
+ Dep1 dep1, Provider<Dep2> dep2Provider, NoAssistedParametersFooFactory factory) {
+ this.dep1 = dep1;
+ this.dep2Provider = dep2Provider;
+ this.factory = factory;
+ }
+ }
+
+ @AssistedFactory
+ interface NoAssistedParametersFooFactory {
+ NoAssistedParametersFoo createNoAssistedParametersFoo();
+ }
+
+ @Test
+ public void testNestedSimpleFooFactory() {
+ AssistedDep1 assistedDep1 = new AssistedDep1();
+ SimpleFoo simpleFoo1 =
+ DaggerAssistedFactoryTest_ParentComponent.create()
+ .nestedSimpleFooFactory()
+ .createSimpleFoo(assistedDep1);
+ assertThat(simpleFoo1.assistedDep).isEqualTo(assistedDep1);
+
+ AssistedDep2 assistedDep2 = new AssistedDep2();
+ SimpleFoo simpleFoo2 =
+ DaggerAssistedFactoryTest_ParentComponent.create()
+ .nestedSimpleFooFactory()
+ .createSimpleFoo(assistedDep2);
+ assertThat(simpleFoo2.assistedDep).isEqualTo(assistedDep2);
+ }
+
+ @Test
+ public void testNonNestedSimpleFooFactory() {
+ AssistedDep1 assistedDep1 = new AssistedDep1();
+ SimpleFoo simpleFoo =
+ DaggerAssistedFactoryTest_ParentComponent.create()
+ .nonNestedSimpleFooFactory()
+ .createSimpleFoo(assistedDep1);
+ assertThat(simpleFoo.assistedDep).isEqualTo(assistedDep1);
+ }
+
+ @Test
+ public void testExtendedSimpleFooFactory() {
+ AssistedDep1 assistedDep1 = new AssistedDep1();
+ SimpleFoo simpleFoo =
+ DaggerAssistedFactoryTest_ParentComponent.create()
+ .extendedSimpleFooFactory()
+ .createSimpleFoo(assistedDep1);
+ assertThat(simpleFoo.assistedDep).isEqualTo(assistedDep1);
+ }
+
+ @Test
+ public void testFooFactory() {
+ AssistedDep1 assistedDep1 = new AssistedDep1();
+ AssistedDep2 assistedDep2 = new AssistedDep2();
+ int assistedInt = 7;
+ Foo foo =
+ DaggerAssistedFactoryTest_ParentComponent.create()
+ .fooFactory()
+ .createFoo(assistedDep1, assistedDep2, assistedInt);
+ assertThat(foo.dep1).isNotNull();
+ assertThat(foo.dep2Provider).isNotNull();
+ assertThat(foo.dep2Provider.get()).isNotNull();
+ assertThat(foo.dep3).isNotNull();
+ assertThat(foo.dep4).isNotNull();
+ assertThat(foo.assistedDep1).isEqualTo(assistedDep1);
+ assertThat(foo.assistedDep2).isEqualTo(assistedDep2);
+ assertThat(foo.assistedInt).isEqualTo(assistedInt);
+ assertThat(foo.factory).isNotNull();
+ }
+
+ @Test
+ public void testNoAssistedParametersFooFactory() {
+ NoAssistedParametersFoo foo =
+ DaggerAssistedFactoryTest_ParentComponent.create()
+ .noAssistedParametersFooFactory()
+ .createNoAssistedParametersFoo();
+ assertThat(foo.dep1).isNotNull();
+ assertThat(foo.dep2Provider).isNotNull();
+ assertThat(foo.dep2Provider.get()).isNotNull();
+ assertThat(foo.dep3).isNotNull();
+ assertThat(foo.dep4).isNotNull();
+ assertThat(foo.factory).isNotNull();
+ }
+
+ @Test
+ public void testAssistedFactoryFromSomeEntryPoint() {
+ SomeEntryPoint someEntryPoint =
+ DaggerAssistedFactoryTest_ParentComponent.create().someEntryPoint();
+ assertThat(someEntryPoint.nestedSimpleFooFactory).isNotNull();
+ assertThat(someEntryPoint.nonNestedSimpleFooFactory).isNotNull();
+ assertThat(someEntryPoint.extendedSimpleFooFactory).isNotNull();
+ assertThat(someEntryPoint.fooFactory).isNotNull();
+ assertThat(someEntryPoint.abstractFooFactory).isNotNull();
+ assertThat(someEntryPoint.noAssistedParametersFooFactory).isNotNull();
+ }
+}
diff --git a/javatests/dagger/functional/assisted/AssistedFactoryWithArrayTypesTest.java b/javatests/dagger/functional/assisted/AssistedFactoryWithArrayTypesTest.java
new file mode 100644
index 000000000..91f7e42d8
--- /dev/null
+++ b/javatests/dagger/functional/assisted/AssistedFactoryWithArrayTypesTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.assisted;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.Component;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class AssistedFactoryWithArrayTypesTest {
+ @Component
+ interface TestComponent {
+ FooFactory fooFactory();
+ }
+
+ @AssistedFactory
+ interface FooFactory {
+ Foo create(Dep[] depArray);
+ }
+
+ static class Dep {}
+
+ static class Foo {
+ private final Dep[] depArray;
+
+ @AssistedInject
+ Foo(@Assisted Dep[] depArray) {
+ this.depArray = depArray;
+ }
+ }
+
+ @Test
+ public void testFooFactory() {
+ Dep[] depArray = {new Dep(), new Dep()};
+ Foo foo =
+ DaggerAssistedFactoryWithArrayTypesTest_TestComponent.create()
+ .fooFactory()
+ .create(depArray);
+ assertThat(foo.depArray).isEqualTo(depArray);
+ }
+}
diff --git a/javatests/dagger/functional/assisted/AssistedFactoryWithAssistedInjectParamTest.java b/javatests/dagger/functional/assisted/AssistedFactoryWithAssistedInjectParamTest.java
new file mode 100644
index 000000000..9b794fdfb
--- /dev/null
+++ b/javatests/dagger/functional/assisted/AssistedFactoryWithAssistedInjectParamTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.assisted;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.Component;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+// This is a regression test for https://github.com/google/dagger/issues/2278
+@RunWith(JUnit4.class)
+public final class AssistedFactoryWithAssistedInjectParamTest {
+ @Component
+ interface ParentComponent {
+ FooFactory fooFactory();
+
+ BarFactory barFactory();
+ }
+
+ static class Foo {
+ private final Bar bar;
+
+ @AssistedInject
+ Foo(@Assisted Bar bar) {
+ this.bar = bar;
+ }
+ }
+
+ static class Bar {
+ @AssistedInject
+ Bar() {}
+ }
+
+ @AssistedFactory
+ interface FooFactory {
+ Foo create(Bar bar);
+ }
+
+ @AssistedFactory
+ interface BarFactory {
+ Bar create();
+ }
+
+ @Test
+ public void testFooFactory() {
+ ParentComponent component =
+ DaggerAssistedFactoryWithAssistedInjectParamTest_ParentComponent.create();
+ FooFactory fooFactory = component.fooFactory();
+ BarFactory barFactory = component.barFactory();
+
+ Bar bar = barFactory.create();
+ Foo foo = fooFactory.create(bar);
+ assertThat(foo.bar).isEqualTo(bar);
+ }
+}
diff --git a/javatests/dagger/functional/assisted/AssistedFactoryWithQualifiedTypesTest.java b/javatests/dagger/functional/assisted/AssistedFactoryWithQualifiedTypesTest.java
new file mode 100644
index 000000000..952b94dd8
--- /dev/null
+++ b/javatests/dagger/functional/assisted/AssistedFactoryWithQualifiedTypesTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2021 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.assisted;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.Component;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+// See https://github.com/google/dagger/issues/2281
+@RunWith(JUnit4.class)
+public final class AssistedFactoryWithQualifiedTypesTest {
+ @Component
+ interface TestComponent {
+ // Test a factory with duplicate types with unique qualifiers.
+ DupeTypeFactory dupeTypeFactory();
+
+ // Test a factory with duplicate qualifiers with unique types.
+ DupeQualifierFactory dupeQualifierFactory();
+
+ // Test a factory with unnecessary qualifiers on the factory.
+ UnnecessaryQualifierFactory unnecessaryQualifierFactory();
+
+ // Test a factory with different parameter order than the constructor.
+ SwappedDupeTypeFactory swappedDupeTypeFactory();
+ }
+
+ static class DupeType {
+ private final String str1;
+ private final String str2;
+
+ @AssistedInject
+ DupeType(@Assisted("1") String str1, @Assisted("2") String str2) {
+ this.str1 = str1;
+ this.str2 = str2;
+ }
+ }
+
+ @AssistedFactory
+ interface DupeTypeFactory {
+ DupeType create(@Assisted("1") String str1, @Assisted("2") String str2);
+ }
+
+ @Test
+ public void testDupeTypeFactory() {
+ String str1 = "str1";
+ String str2 = "str2";
+ DupeType dupeType =
+ DaggerAssistedFactoryWithQualifiedTypesTest_TestComponent.create()
+ .dupeTypeFactory()
+ .create(str1, str2);
+ assertThat(dupeType.str1).isEqualTo(str1);
+ assertThat(dupeType.str2).isEqualTo(str2);
+ }
+
+ @AssistedFactory
+ interface SwappedDupeTypeFactory {
+ DupeType create(@Assisted("2") String str2, @Assisted("1") String str1);
+ }
+
+ @Test
+ public void testSwappedDupeTypeFactory() {
+ String str1 = "str1";
+ String str2 = "str2";
+ DupeType dupeType =
+ DaggerAssistedFactoryWithQualifiedTypesTest_TestComponent.create()
+ .swappedDupeTypeFactory()
+ .create(str2, str1);
+ assertThat(dupeType.str1).isEqualTo(str1);
+ assertThat(dupeType.str2).isEqualTo(str2);
+ }
+
+ static class DupeQualifier {
+ private final String str;
+ private final int i;
+
+ @AssistedInject
+ DupeQualifier(@Assisted("1") String str, @Assisted("1") int i) {
+ this.str = str;
+ this.i = i;
+ }
+ }
+
+ @AssistedFactory
+ interface DupeQualifierFactory {
+ DupeQualifier create(@Assisted("1") String str, @Assisted("1") int i);
+ }
+
+ @Test
+ public void testDupeQualifierFactory() {
+ String str = "str";
+ int i = 11;
+ DupeQualifier dupeQualifier =
+ DaggerAssistedFactoryWithQualifiedTypesTest_TestComponent.create()
+ .dupeQualifierFactory()
+ .create(str, i);
+ assertThat(dupeQualifier.str).isEqualTo(str);
+ assertThat(dupeQualifier.i).isEqualTo(i);
+ }
+
+ static class UnnecessaryQualifier {
+ private final String str;
+ private final double d;
+ private final int i;
+
+ @AssistedInject
+ UnnecessaryQualifier(@Assisted String str, @Assisted double d, @Assisted("") int i) {
+ this.str = str;
+ this.d = d;
+ this.i = i;
+ }
+ }
+
+ @AssistedFactory
+ interface UnnecessaryQualifierFactory {
+ UnnecessaryQualifier create(@Assisted int i, @Assisted("") String str, double d);
+ }
+
+ @Test
+ public void testUnnecessaryQualifierFactory() {
+ String str = "str";
+ double d = 2.2;
+ int i = 11;
+ UnnecessaryQualifier unnecessaryQualifier =
+ DaggerAssistedFactoryWithQualifiedTypesTest_TestComponent.create()
+ .unnecessaryQualifierFactory()
+ .create(i, str, d);
+ assertThat(unnecessaryQualifier.str).isEqualTo(str);
+ assertThat(unnecessaryQualifier.d).isEqualTo(d);
+ assertThat(unnecessaryQualifier.i).isEqualTo(i);
+ }
+}
diff --git a/javatests/dagger/functional/assisted/BUILD b/javatests/dagger/functional/assisted/BUILD
new file mode 100644
index 000000000..5e663e7e9
--- /dev/null
+++ b/javatests/dagger/functional/assisted/BUILD
@@ -0,0 +1,45 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Functional tests for Dagger
+
+load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX", "SOURCE_7_TARGET_7")
+load("//:test_defs.bzl", "GenJavaTests")
+
+package(default_visibility = ["//:src"])
+
+GenJavaTests(
+ name = "assisted",
+ srcs = glob(["*.java"]),
+ javacopts = DOCLINT_HTML_AND_SYNTAX,
+ lib_javacopts = SOURCE_7_TARGET_7,
+ test_only_deps = [
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:concurrent",
+ "@google_bazel_common//third_party/java/guava:testlib",
+ "@google_bazel_common//third_party/java/truth",
+ "@google_bazel_common//third_party/java/junit",
+ "//javatests/dagger/functional/assisted/subpackage",
+ ],
+ # NOTE: This should not depend on Guava or jsr305 to ensure that Dagger can be
+ # used without Guava and jsr305 deps.
+ deps = [
+ "//:dagger_with_compiler",
+ "@google_bazel_common//third_party/java/auto:factory",
+ "@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ ],
+)
diff --git a/javatests/dagger/functional/assisted/kotlin/BUILD b/javatests/dagger/functional/assisted/kotlin/BUILD
new file mode 100644
index 000000000..3c3421d2a
--- /dev/null
+++ b/javatests/dagger/functional/assisted/kotlin/BUILD
@@ -0,0 +1,49 @@
+# Copyright (C) 2021 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Description:
+# Tests for internal code for implementing Hilt processors.
+
+load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_library")
+load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX", "SOURCE_7_TARGET_7")
+load("//:test_defs.bzl", "GenJavaTests")
+
+package(default_visibility = ["//:src"])
+
+kt_jvm_library(
+ name = "KotlinAssistedInjectionClasses",
+ srcs = ["KotlinAssistedInjectionClasses.kt"],
+ deps = [
+ "//:dagger_with_compiler",
+ ],
+)
+
+GenJavaTests(
+ name = "kotlin",
+ srcs = glob(
+ ["*.java"],
+ exclude = ["*.kt"],
+ ),
+ javacopts = DOCLINT_HTML_AND_SYNTAX,
+ lib_javacopts = SOURCE_7_TARGET_7,
+ test_only_deps = [
+ "//:dagger_with_compiler",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth",
+ ],
+ # NOTE: This should not depend on Guava or jsr305 to ensure that Dagger can be
+ # used without Guava and jsr305 deps.
+ deps = [
+ ":KotlinAssistedInjectionClasses",
+ ],
+)
diff --git a/javatests/dagger/functional/assisted/kotlin/KotlinAssistedInjectionClasses.kt b/javatests/dagger/functional/assisted/kotlin/KotlinAssistedInjectionClasses.kt
new file mode 100644
index 000000000..e78873d69
--- /dev/null
+++ b/javatests/dagger/functional/assisted/kotlin/KotlinAssistedInjectionClasses.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.assisted.kotlin
+
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import javax.inject.Inject
+
+class Dep @Inject constructor()
+
+class AssistedDep
+
+/** Assisted injection for a kotlin class. */
+class Foo @AssistedInject constructor(val dep: Dep, @Assisted val assistedDep: AssistedDep)
+
+/** Assisted injection for a kotlin data class. */
+data class FooData @AssistedInject constructor(val dep: Dep, @Assisted val assistedDep: AssistedDep)
+
+/** Assisted factory for a kotlin class */
+@AssistedFactory
+interface FooFactory {
+ fun create(assistedDep: AssistedDep): Foo
+}
+
+/** Assisted factory for a kotlin data class */
+@AssistedFactory
+interface FooDataFactory {
+ fun create(assistedDep: AssistedDep): FooData
+}
diff --git a/javatests/dagger/functional/assisted/kotlin/KotlinAssistedInjectionTest.java b/javatests/dagger/functional/assisted/kotlin/KotlinAssistedInjectionTest.java
new file mode 100644
index 000000000..7c597f858
--- /dev/null
+++ b/javatests/dagger/functional/assisted/kotlin/KotlinAssistedInjectionTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.assisted.kotlin;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.Component;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+// This is a regression test for https://github.com/google/dagger/issues/2299
+@RunWith(JUnit4.class)
+public final class KotlinAssistedInjectionTest {
+ @Component
+ interface TestComponent {
+ FooFactory fooFactory();
+
+ FooDataFactory fooDataFactory();
+ }
+
+ @Test
+ public void testFooFactory() {
+ FooFactory fooFactory = DaggerKotlinAssistedInjectionTest_TestComponent.create().fooFactory();
+ AssistedDep assistedDep = new AssistedDep();
+ Foo foo = fooFactory.create(assistedDep);
+ assertThat(foo.getAssistedDep()).isEqualTo(assistedDep);
+ }
+
+ @Test
+ public void testFooDataFactory() {
+ FooDataFactory fooDataFactory =
+ DaggerKotlinAssistedInjectionTest_TestComponent.create().fooDataFactory();
+ AssistedDep assistedDep = new AssistedDep();
+ FooData fooData = fooDataFactory.create(assistedDep);
+ assertThat(fooData.getAssistedDep()).isEqualTo(assistedDep);
+ }
+}
diff --git a/javatests/dagger/functional/assisted/subpackage/AccessibleFoo.java b/javatests/dagger/functional/assisted/subpackage/AccessibleFoo.java
new file mode 100644
index 000000000..16d420aeb
--- /dev/null
+++ b/javatests/dagger/functional/assisted/subpackage/AccessibleFoo.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.assisted.subpackage;
+
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedInject;
+
+/** An accessible type using assisted inject from a separate package. */
+public final class AccessibleFoo {
+ // Accessible for testing
+ public final Dep dep;
+ public final AssistedDep assistedDep;
+
+ @AssistedInject
+ AccessibleFoo(Dep dep, @Assisted AssistedDep assistedDep) {
+ this.dep = dep;
+ this.assistedDep = assistedDep;
+ }
+}
diff --git a/javatests/dagger/functional/assisted/subpackage/AssistedDep.java b/javatests/dagger/functional/assisted/subpackage/AssistedDep.java
new file mode 100644
index 000000000..b27b02994
--- /dev/null
+++ b/javatests/dagger/functional/assisted/subpackage/AssistedDep.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.assisted.subpackage;
+
+/** An assisted dependency. */
+public final class AssistedDep {}
diff --git a/javatests/dagger/functional/assisted/subpackage/BUILD b/javatests/dagger/functional/assisted/subpackage/BUILD
new file mode 100644
index 000000000..644cf4410
--- /dev/null
+++ b/javatests/dagger/functional/assisted/subpackage/BUILD
@@ -0,0 +1,27 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Functional tests for Dagger
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "subpackage",
+ srcs = glob(["*.java"]),
+ deps = [
+ "//:dagger_with_compiler",
+ "@google_bazel_common//third_party/java/jsr330_inject",
+ ],
+)
diff --git a/javatests/dagger/functional/assisted/subpackage/Dep.java b/javatests/dagger/functional/assisted/subpackage/Dep.java
new file mode 100644
index 000000000..a519b3fdf
--- /dev/null
+++ b/javatests/dagger/functional/assisted/subpackage/Dep.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.assisted.subpackage;
+
+import javax.inject.Inject;
+
+/** An {@link Inject} constructor dependency. */
+public final class Dep {
+ @Inject
+ Dep() {}
+}
diff --git a/javatests/dagger/functional/assisted/subpackage/InaccessibleFoo.java b/javatests/dagger/functional/assisted/subpackage/InaccessibleFoo.java
new file mode 100644
index 000000000..e09336510
--- /dev/null
+++ b/javatests/dagger/functional/assisted/subpackage/InaccessibleFoo.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.assisted.subpackage;
+
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedInject;
+
+/** An inaccessible type using assisted inject from a separate package. */
+// TODO(bcorso): Remove public once we allow inaccessible types in fastInit.
+public final class InaccessibleFoo {
+ private final Dep dep;
+ private final AssistedDep assistedDep;
+
+ @AssistedInject
+ InaccessibleFoo(Dep dep, @Assisted AssistedDep assistedDep) {
+ this.dep = dep;
+ this.assistedDep = assistedDep;
+ }
+}
diff --git a/javatests/dagger/functional/assisted/subpackage/InaccessibleFooFactory.java b/javatests/dagger/functional/assisted/subpackage/InaccessibleFooFactory.java
new file mode 100644
index 000000000..a7efe570e
--- /dev/null
+++ b/javatests/dagger/functional/assisted/subpackage/InaccessibleFooFactory.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.assisted.subpackage;
+
+import dagger.assisted.AssistedFactory;
+
+/** An accessible factory that creates an inaccessible type. */
+@AssistedFactory
+public interface InaccessibleFooFactory {
+ InaccessibleFoo create(AssistedDep assistedDep);
+}
diff --git a/javatests/dagger/functional/binds/SimpleBindingModule.java b/javatests/dagger/functional/binds/SimpleBindingModule.java
index e1d522771..e8c3ca66f 100644
--- a/javatests/dagger/functional/binds/SimpleBindingModule.java
+++ b/javatests/dagger/functional/binds/SimpleBindingModule.java
@@ -20,6 +20,7 @@ import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.Reusable;
+import dagger.functional.NeedsFactory;
import dagger.functional.SomeQualifier;
import dagger.multibindings.ElementsIntoSet;
import dagger.multibindings.IntKey;
@@ -35,6 +36,12 @@ import javax.inject.Singleton;
@Module(includes = InterfaceModule.class)
abstract class SimpleBindingModule {
+
+ // Regression test for b/161853413 that binds an implementation that extends a generated class
+ // that is processed in the same build unit as the @Binds method.
+ @Binds
+ abstract NeedsFactory.SomethingFactory bindFooFactory(NeedsFactory.SomethingFactoryImpl impl);
+
@Binds
abstract Object bindObject(FooOfStrings impl);
@@ -147,7 +154,7 @@ abstract class SimpleBindingModule {
@IntKey(123)
@SomeQualifier
abstract Object bindFooOfStringsIntoQualifiedMap(FooOfStrings fooOfStrings);
-
+
@Provides
@Named("For-123")
static String provide123String() {
diff --git a/javatests/dagger/functional/builder/BuildMethodCovariantReturnInherited.java b/javatests/dagger/functional/builder/BuildMethodCovariantReturnInherited.java
index 07ebbc8e6..9b1447c19 100644
--- a/javatests/dagger/functional/builder/BuildMethodCovariantReturnInherited.java
+++ b/javatests/dagger/functional/builder/BuildMethodCovariantReturnInherited.java
@@ -18,6 +18,7 @@ package dagger.functional.builder;
import dagger.Component;
+
interface BuildMethodCovariantReturnInherited {
@Component
interface Simple {
diff --git a/javatests/dagger/functional/builder/BuilderBindsInstanceParameterTest.java b/javatests/dagger/functional/builder/BuilderBindsInstanceParameterTest.java
index 33d381623..d47c892ad 100644
--- a/javatests/dagger/functional/builder/BuilderBindsInstanceParameterTest.java
+++ b/javatests/dagger/functional/builder/BuilderBindsInstanceParameterTest.java
@@ -18,6 +18,7 @@ package dagger.functional.builder;
import static com.google.common.truth.Truth.assertThat;
+
import dagger.BindsInstance;
import dagger.Component;
import org.junit.Test;
diff --git a/javatests/dagger/functional/builder/GenericParent.java b/javatests/dagger/functional/builder/GenericParent.java
index 9563103dd..949514ec5 100644
--- a/javatests/dagger/functional/builder/GenericParent.java
+++ b/javatests/dagger/functional/builder/GenericParent.java
@@ -16,6 +16,7 @@
package dagger.functional.builder;
+
interface GenericParent<B> {
B subcomponentBuilder();
}
diff --git a/javatests/dagger/functional/cycle/Cycles.java b/javatests/dagger/functional/cycle/Cycles.java
index f4faeab30..e17d619be 100644
--- a/javatests/dagger/functional/cycle/Cycles.java
+++ b/javatests/dagger/functional/cycle/Cycles.java
@@ -97,7 +97,7 @@ final class Cycles {
this.sProvider = sProvider;
}
}
-
+
static class X {
public final Y y;
@@ -143,10 +143,10 @@ final class Cycles {
A a();
C c();
-
+
ChildCycleComponent child();
}
-
+
@Module
static class CycleModule {
@Provides
@@ -160,12 +160,12 @@ final class Cycles {
interface SelfCycleComponent {
S s();
}
-
+
@Subcomponent
interface ChildCycleComponent {
@SuppressWarnings("dependency-cycle")
A a();
-
+
@SuppressWarnings("dependency-cycle")
Object object();
}
diff --git a/javatests/dagger/functional/cycle/DoubleCheckCycleTest.java b/javatests/dagger/functional/cycle/DoubleCheckCycleTest.java
index b77ee3ea1..d77a713d3 100644
--- a/javatests/dagger/functional/cycle/DoubleCheckCycleTest.java
+++ b/javatests/dagger/functional/cycle/DoubleCheckCycleTest.java
@@ -181,6 +181,7 @@ public class DoubleCheckCycleTest {
})
.build();
+
int numThreads = 10;
CountDownLatch remainingTasks = new CountDownLatch(numThreads);
List<Thread> tasks = new ArrayList<>(numThreads);
diff --git a/javatests/dagger/functional/guava/BUILD b/javatests/dagger/functional/guava/BUILD
index 66492eb74..ed8efd37e 100644
--- a/javatests/dagger/functional/guava/BUILD
+++ b/javatests/dagger/functional/guava/BUILD
@@ -15,19 +15,20 @@
# Description:
# Functional tests for Dagger that depend on Guava
-package(default_visibility = ["//:src"])
-
load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
load("//:test_defs.bzl", "GenJavaTests")
+package(default_visibility = ["//:src"])
+
GenJavaTests(
name = "guava_tests",
srcs = glob(["**/*.java"]),
javacopts = DOCLINT_HTML_AND_SYNTAX,
deps = [
"//:dagger_with_compiler",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
"@google_bazel_common//third_party/java/auto:value",
- "@google_bazel_common//third_party/java/guava",
"@google_bazel_common//third_party/java/jsr305_annotations",
"@google_bazel_common//third_party/java/jsr330_inject",
"@google_bazel_common//third_party/java/junit",
diff --git a/javatests/dagger/functional/jdk8/BUILD b/javatests/dagger/functional/jdk8/BUILD
index 6b76ba3a7..5e35c753f 100644
--- a/javatests/dagger/functional/jdk8/BUILD
+++ b/javatests/dagger/functional/jdk8/BUILD
@@ -15,19 +15,20 @@
# Description:
# Functional tests for Dagger that depend on Guava
-package(default_visibility = ["//:src"])
-
load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
load("//:test_defs.bzl", "GenJavaTests")
+package(default_visibility = ["//:src"])
+
GenJavaTests(
name = "jdk8_tests",
srcs = glob(["**/*.java"]),
javacopts = DOCLINT_HTML_AND_SYNTAX,
test_only_deps = [
- "@google_bazel_common//third_party/java/guava",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
"@google_bazel_common//third_party/java/junit",
- "@google_bazel_common//third_party/java/truth:truth8",
+ "@google_bazel_common//third_party/java/truth",
],
deps = [
"//:dagger_with_compiler",
diff --git a/javatests/dagger/functional/kotlin/BUILD b/javatests/dagger/functional/kotlin/BUILD
new file mode 100644
index 000000000..529eb6992
--- /dev/null
+++ b/javatests/dagger/functional/kotlin/BUILD
@@ -0,0 +1,79 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Description:
+# Functional test code for Dagger-Android
+
+load("@rules_java//java:defs.bzl", "java_library")
+load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_library")
+load("//:test_defs.bzl", "GenJavaTests")
+
+package(default_visibility = ["//:src"])
+
+kt_jvm_library(
+ name = "kotlin",
+ srcs = glob(
+ [
+ "*.java",
+ "*.kt",
+ ],
+ exclude = [
+ "*Test.java",
+ "JavaTestQualifier.java",
+ "FooWithInjectedQualifier.kt",
+ ],
+ ),
+ # TODO(danysantiago): Remove 'plugins' once kt_jvm_library supports 'exported_plugins'.
+ plugins = ["//javatests/dagger/functional/kotlin/processor:plugin"],
+ deps = [
+ ":foo_with_injected_qualifier",
+ ":java_qualifier",
+ "//:dagger_with_compiler",
+ "//javatests/dagger/functional/kotlin/processor:annotation",
+ ],
+)
+
+kt_jvm_library(
+ name = "foo_with_injected_qualifier",
+ srcs = ["FooWithInjectedQualifier.kt"],
+ deps = [
+ ":java_qualifier",
+ "//:dagger_with_compiler",
+ ],
+)
+
+java_library(
+ name = "java_qualifier",
+ srcs = ["JavaTestQualifier.java"],
+ deps = [
+ "//:dagger_with_compiler",
+ ],
+)
+
+GenJavaTests(
+ name = "kotlin_tests",
+ srcs = glob(["*Test.java"]),
+ functional = True,
+ test_only_deps = [
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth",
+ ],
+ deps = [
+ ":foo_with_injected_qualifier",
+ ":kotlin",
+ "//:dagger_with_compiler",
+ ],
+)
diff --git a/javatests/dagger/functional/kotlin/CompanionModuleTest.java b/javatests/dagger/functional/kotlin/CompanionModuleTest.java
new file mode 100644
index 000000000..591c5d640
--- /dev/null
+++ b/javatests/dagger/functional/kotlin/CompanionModuleTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.kotlin;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class CompanionModuleTest {
+
+ @Test
+ public void verifyCompanionModule() {
+ TestKotlinComponentWithCompanionModule component =
+ DaggerTestKotlinComponentWithCompanionModule.create();
+ assertThat(component.getDataA()).isNotNull();
+ assertThat(component.getDataB()).isNotNull();
+ assertThat(component.getBoolean()).isTrue();
+ assertThat(component.getStringType()).isNotNull();
+ assertThat(component.getCatNamedStringType()).isEqualTo("Cat");
+ assertThat(component.getDogNamedStringType()).isEqualTo("Dog");
+ assertThat(component.getInterface()).isNotNull();
+ assertThat(component.getLong()).isEqualTo(4L);
+ assertThat(component.getDouble()).isEqualTo(1.0);
+ assertThat(component.getInteger()).isEqualTo(2);
+ }
+}
diff --git a/javatests/dagger/functional/kotlin/FooWithInjectedQualifier.kt b/javatests/dagger/functional/kotlin/FooWithInjectedQualifier.kt
new file mode 100644
index 000000000..6602f107f
--- /dev/null
+++ b/javatests/dagger/functional/kotlin/FooWithInjectedQualifier.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.kotlin
+
+import javax.inject.Inject
+
+class FooWithInjectedQualifier {
+ @Inject
+ @JavaTestQualifier
+ lateinit var qualifiedString: String
+
+ @Inject
+ @field:JavaTestQualifier
+ @NotAQualifier
+ lateinit var qualifiedStringWithPropertyAnnotaion: String
+}
+
+@Target(AnnotationTarget.PROPERTY)
+annotation class NotAQualifier
diff --git a/javatests/dagger/functional/kotlin/JavaTestQualifier.java b/javatests/dagger/functional/kotlin/JavaTestQualifier.java
new file mode 100644
index 000000000..156f43616
--- /dev/null
+++ b/javatests/dagger/functional/kotlin/JavaTestQualifier.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.kotlin;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import javax.inject.Qualifier;
+
+@Qualifier
+@Retention(RUNTIME)
+public @interface JavaTestQualifier {}
diff --git a/javatests/dagger/functional/kotlin/JavaTestQualifierWithTarget.java b/javatests/dagger/functional/kotlin/JavaTestQualifierWithTarget.java
new file mode 100644
index 000000000..3bc2e4ea3
--- /dev/null
+++ b/javatests/dagger/functional/kotlin/JavaTestQualifierWithTarget.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.kotlin;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import javax.inject.Qualifier;
+
+@Qualifier
+@Retention(RUNTIME)
+@Target({ElementType.FIELD, ElementType.METHOD})
+public @interface JavaTestQualifierWithTarget {}
diff --git a/javatests/dagger/functional/kotlin/ObjectModuleTest.java b/javatests/dagger/functional/kotlin/ObjectModuleTest.java
new file mode 100644
index 000000000..a37be44ec
--- /dev/null
+++ b/javatests/dagger/functional/kotlin/ObjectModuleTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.kotlin;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ObjectModuleTest {
+
+ @Test
+ public void verifyObjectModule() {
+ TestKotlinComponentWithObjectModule component =
+ DaggerTestKotlinComponentWithObjectModule.create();
+ assertThat(component.getDataA()).isNotNull();
+ assertThat(component.getDataAFromNestedModule()).isNotNull();
+ assertThat(component.getDataB()).isNotNull();
+ assertThat(component.getSetOfDataA()).isNotNull();
+ assertThat(component.getSetOfDataA()).hasSize(1);
+ assertThat(component.getPrimitiveType()).isTrue();
+ }
+}
diff --git a/javatests/dagger/functional/kotlin/PropertyQualifierTest.java b/javatests/dagger/functional/kotlin/PropertyQualifierTest.java
new file mode 100644
index 000000000..e0ebf3c44
--- /dev/null
+++ b/javatests/dagger/functional/kotlin/PropertyQualifierTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.kotlin;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class PropertyQualifierTest {
+
+ @Test
+ public void verifyQualifiedBinding() {
+ TestMemberInjectedClassWithQualifier injectedClass = new TestMemberInjectedClassWithQualifier();
+ DaggerTestKotlinComponentWithQualifier.create().inject(injectedClass);
+
+ assertThat(injectedClass.javaDataA).isNotNull();
+ assertThat(injectedClass.javaDataB).isNotNull();
+ assertThat(injectedClass.javaWithTargetDataA).isNotNull();
+ assertThat(injectedClass.kotlinDataA).isNotNull();
+ assertThat(injectedClass.dataWithConstructionInjection).isNotNull();
+ assertThat(injectedClass.dataWithConstructionInjection.getData()).isNotNull();
+ }
+
+ @Test
+ public void verifyQualifiedBinding_acrossCompilation() {
+ FooWithInjectedQualifier injectedClass = new FooWithInjectedQualifier();
+ DaggerTestKotlinComponentWithQualifier.create().inject(injectedClass);
+
+ assertThat(injectedClass.getQualifiedString()).isEqualTo("qualified string");
+ assertThat(injectedClass.getQualifiedStringWithPropertyAnnotaion())
+ .isEqualTo("qualified string");
+ }
+}
diff --git a/javatests/dagger/functional/kotlin/PublicModuleWithNonPublicInclude.java b/javatests/dagger/functional/kotlin/PublicModuleWithNonPublicInclude.java
new file mode 100644
index 000000000..f927186be
--- /dev/null
+++ b/javatests/dagger/functional/kotlin/PublicModuleWithNonPublicInclude.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.kotlin;
+
+import dagger.Module;
+
+/** Verifies that a non-public included Kotlin object module does not fail compilation. */
+@Module(includes = {NonPublicObjectModule.class})
+public class PublicModuleWithNonPublicInclude {}
diff --git a/javatests/dagger/functional/kotlin/TestComponentWithCompanionModule.kt b/javatests/dagger/functional/kotlin/TestComponentWithCompanionModule.kt
new file mode 100644
index 000000000..1eaa3f5f7
--- /dev/null
+++ b/javatests/dagger/functional/kotlin/TestComponentWithCompanionModule.kt
@@ -0,0 +1,97 @@
+package dagger.functional.kotlin
+
+import dagger.Binds
+import dagger.Component
+import dagger.Module
+import dagger.Provides
+import javax.inject.Named
+
+@Component(
+ modules = [
+ TestKotlinModuleWithCompanion::class,
+ TestKotlinModuleWithNamedCompanion::class,
+ TestKotlinAbstractModuleWithCompanion::class,
+ TestKotlinWorkaroundModuleWithCompanion::class,
+ TestKotlinModuleWithPrivateCompanion::class
+ ]
+)
+interface TestKotlinComponentWithCompanionModule {
+ fun getDataA(): TestDataA
+ fun getDataB(): TestDataB
+ fun getBoolean(): Boolean
+ fun getStringType(): String
+ @Named("Cat")
+ fun getCatNamedStringType(): String
+ @Named("Dog")
+ fun getDogNamedStringType(): String
+
+ fun getInterface(): TestInterface
+ fun getLong(): Long
+ fun getDouble(): Double
+ fun getInteger(): Int
+}
+
+@Module
+class TestKotlinModuleWithCompanion {
+ @Provides
+ fun provideDataA() = TestDataA("test")
+
+ companion object {
+ @Provides
+ fun provideDataB() = TestDataB("test")
+
+ @Provides
+ fun provideBoolean(): Boolean = true
+ }
+}
+
+@Module
+class TestKotlinModuleWithNamedCompanion {
+
+ @Provides
+ @Named("Cat")
+ fun provideNamedString() = "Cat"
+
+ companion object Foo {
+ @Provides
+ fun provideStringType(): String = ""
+ }
+}
+
+@Module
+abstract class TestKotlinAbstractModuleWithCompanion {
+
+ @Binds
+ abstract fun bindInterface(injectable: TestInjectable): TestInterface
+
+ companion object {
+ @Provides
+ fun provideLong() = 4L
+ }
+}
+
+@Module
+class TestKotlinWorkaroundModuleWithCompanion {
+
+ @Provides
+ fun provideDouble() = 1.0
+
+ @Module
+ companion object {
+ @Provides
+ @JvmStatic
+ fun provideInteger() = 2
+ }
+}
+
+@Module
+class TestKotlinModuleWithPrivateCompanion {
+
+ @Provides
+ @Named("Dog")
+ fun getNamedStringType() = "Dog"
+
+ private companion object {
+ fun randomFunction() = ""
+ }
+}
diff --git a/javatests/dagger/functional/kotlin/TestComponentWithObjectModule.kt b/javatests/dagger/functional/kotlin/TestComponentWithObjectModule.kt
new file mode 100644
index 000000000..e390abaea
--- /dev/null
+++ b/javatests/dagger/functional/kotlin/TestComponentWithObjectModule.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.kotlin
+
+import dagger.Component
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.IntoSet
+import javax.inject.Named
+
+@Component(
+ modules = [
+ TestKotlinObjectModule::class,
+ TestModuleForNesting.TestNestedKotlinObjectModule::class
+ ]
+)
+interface TestKotlinComponentWithObjectModule {
+ fun getDataA(): TestDataA
+ @Named("nested-data-a")
+ fun getDataAFromNestedModule(): TestDataA
+ fun getDataB(): TestDataB
+ fun getSetOfDataA(): Set<TestDataA>
+ fun getPrimitiveType(): Boolean
+}
+
+@Module
+object TestKotlinObjectModule {
+ @Provides
+ fun provideDataA() = TestDataA("test")
+
+ @Provides
+ fun providePrimitiveType(): Boolean = true
+
+ @Provides
+ @JvmStatic
+ fun provideDataB() = TestDataB("test")
+
+ @Provides
+ @IntoSet
+ fun provideIntoMapDataA() = TestDataA("set-test")
+}
+
+class TestModuleForNesting {
+ @Module
+ object TestNestedKotlinObjectModule {
+ @Provides
+ @Named("nested-data-a")
+ fun provideDataA() = TestDataA("test")
+ }
+}
+
+@Module
+private object NonPublicObjectModule {
+ @Provides
+ fun provideInt() = 42
+}
diff --git a/javatests/dagger/functional/kotlin/TestComponentWithQualifier.kt b/javatests/dagger/functional/kotlin/TestComponentWithQualifier.kt
new file mode 100644
index 000000000..9aeb2d8be
--- /dev/null
+++ b/javatests/dagger/functional/kotlin/TestComponentWithQualifier.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.kotlin
+
+import dagger.Component
+import dagger.Module
+import dagger.Provides
+import dagger.functional.kotlin.processor.TriggerGeneratedTypeProcessor
+import javax.inject.Inject
+
+@Component(modules = [TestKotlinModuleWithQualifier::class])
+interface TestKotlinComponentWithQualifier {
+ fun inject(testInjectedClassWithQualifier: TestMemberInjectedClassWithQualifier)
+ fun inject(fooWithInjectedQualifier: FooWithInjectedQualifier)
+}
+
+@Module
+class TestKotlinModuleWithQualifier {
+ @Provides
+ @JavaTestQualifier
+ fun provideJavaDataA() = TestDataA("test")
+
+ @Provides
+ @JavaTestQualifier
+ fun provideJavaDataB() = TestDataB("test")
+
+ @Provides
+ @JavaTestQualifierWithTarget
+ fun provideJavaWithTargetDataA() = TestDataA("test")
+
+ @Provides
+ @KotlinTestQualifier
+ fun provideKotlinDataA() = TestDataA("test")
+
+ @Provides
+ @JavaTestQualifier
+ fun provideString() = "qualified string"
+}
+
+class TestConstructionInjectedClassWithQualifier @Inject constructor(
+ @JavaTestQualifier val data: TestDataA
+)
+
+@TriggerGeneratedTypeProcessor
+class TestMemberInjectedClassWithQualifier {
+ @Inject
+ @JavaTestQualifier
+ lateinit var javaDataA: TestDataA
+
+ @Inject
+ @field:JavaTestQualifier
+ lateinit var javaDataB: TestDataB
+
+ @Inject
+ @JavaTestQualifierWithTarget
+ lateinit var javaWithTargetDataA: TestDataA
+
+ @Inject
+ @JavaTestQualifier
+ lateinit var kotlinDataA: TestDataA
+
+ @Inject
+ lateinit var dataWithConstructionInjection: TestConstructionInjectedClassWithQualifier
+
+ val noBackingFieldProperty: Int
+ get() = 0
+
+ val delegatedProperty by lazy { "" }
+
+ val generatedTypeProperty = dagger.functional.kotlin.GeneratedType()
+
+ val generatedTypeDelegatedProperty by lazy { dagger.functional.kotlin.GeneratedType() }
+}
diff --git a/javatests/dagger/functional/kotlin/TestKotlinClasses.kt b/javatests/dagger/functional/kotlin/TestKotlinClasses.kt
new file mode 100644
index 000000000..5d151fc59
--- /dev/null
+++ b/javatests/dagger/functional/kotlin/TestKotlinClasses.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.kotlin
+
+import javax.inject.Inject
+import javax.inject.Qualifier
+
+data class TestDataA(val data: String)
+data class TestDataB(val data: String)
+
+interface TestInterface
+class TestInjectable @Inject constructor() : TestInterface
+
+@Qualifier
+@Retention(AnnotationRetention.RUNTIME)
+annotation class KotlinTestQualifier
diff --git a/javatests/dagger/functional/kotlin/processor/BUILD b/javatests/dagger/functional/kotlin/processor/BUILD
new file mode 100644
index 000000000..31a45c75a
--- /dev/null
+++ b/javatests/dagger/functional/kotlin/processor/BUILD
@@ -0,0 +1,37 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_library")
+
+package(default_visibility = ["//:src"])
+
+java_plugin(
+ name = "plugin",
+ generates_api = 1,
+ processor_class = "dagger.functional.kotlin.processor.TestGeneratedTypeProcessor",
+ deps = [":processor"],
+)
+
+kt_jvm_library(
+ name = "processor",
+ srcs = ["TestGeneratedTypeProcessor.kt"],
+ deps = [
+ "@google_bazel_common//third_party/java/auto:service",
+ "@google_bazel_common//third_party/java/javapoet",
+ ],
+)
+
+kt_jvm_library(
+ name = "annotation",
+ srcs = ["TriggerGeneratedTypeProcessor.kt"],
+)
diff --git a/javatests/dagger/functional/kotlin/processor/TestGeneratedTypeProcessor.kt b/javatests/dagger/functional/kotlin/processor/TestGeneratedTypeProcessor.kt
new file mode 100644
index 000000000..4a1d6e20a
--- /dev/null
+++ b/javatests/dagger/functional/kotlin/processor/TestGeneratedTypeProcessor.kt
@@ -0,0 +1,48 @@
+package dagger.functional.kotlin.processor
+
+import com.google.auto.service.AutoService
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.TypeSpec
+import javax.annotation.processing.AbstractProcessor
+import javax.annotation.processing.Processor
+import javax.annotation.processing.RoundEnvironment
+import javax.lang.model.SourceVersion
+import javax.lang.model.element.Modifier
+import javax.lang.model.element.TypeElement
+
+/**
+ * A processor to be used in functional tests that will generate a simple class with fqname
+ * 'dagger.functional.kotlin.GeneratedType'. This processor is useful for testing situations
+ * with KAPT where a type is not resolvable and for which KAPT will generate stubs containing
+ * the 'error.NonExistentClass' type.
+ */
+@AutoService(Processor::class)
+class TestGeneratedTypeProcessor : AbstractProcessor() {
+
+ private var isSourceGenerated = false
+
+ override fun getSupportedAnnotationTypes() =
+ mutableSetOf("dagger.functional.kotlin.processor.TriggerGeneratedTypeProcessor")
+
+ override fun getSupportedSourceVersion() = SourceVersion.latestSupported()
+
+ override fun process(
+ annotations: MutableSet<out TypeElement>,
+ roundEnv: RoundEnvironment
+ ): Boolean {
+ if (isSourceGenerated) {
+ return false
+ }
+
+ JavaFile.builder(
+ "dagger.functional.kotlin",
+ TypeSpec.classBuilder("GeneratedType")
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .build()
+ ).build().writeTo(processingEnv.filer)
+
+ isSourceGenerated = true
+
+ return false
+ }
+}
diff --git a/javatests/dagger/functional/kotlin/processor/TriggerGeneratedTypeProcessor.kt b/javatests/dagger/functional/kotlin/processor/TriggerGeneratedTypeProcessor.kt
new file mode 100644
index 000000000..c240fd3b7
--- /dev/null
+++ b/javatests/dagger/functional/kotlin/processor/TriggerGeneratedTypeProcessor.kt
@@ -0,0 +1,4 @@
+package dagger.functional.kotlin.processor
+
+@Retention(AnnotationRetention.BINARY)
+annotation class TriggerGeneratedTypeProcessor
diff --git a/javatests/dagger/functional/membersinject/MembersInjectGenericParent.java b/javatests/dagger/functional/membersinject/MembersInjectGenericParent.java
index 351934898..4a084555e 100644
--- a/javatests/dagger/functional/membersinject/MembersInjectGenericParent.java
+++ b/javatests/dagger/functional/membersinject/MembersInjectGenericParent.java
@@ -18,6 +18,7 @@ package dagger.functional.membersinject;
import javax.inject.Inject;
+
class MembersInjectGenericParent<T> {
@Inject T t;
diff --git a/javatests/dagger/functional/membersinject/MembersInjectModule.java b/javatests/dagger/functional/membersinject/MembersInjectModule.java
index d60a7d1e8..6e182dd7f 100644
--- a/javatests/dagger/functional/membersinject/MembersInjectModule.java
+++ b/javatests/dagger/functional/membersinject/MembersInjectModule.java
@@ -26,7 +26,7 @@ class MembersInjectModule {
@Provides int[] provideIntArray() { return new int[10]; }
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({"unchecked", "rawtypes"})
@Provides MembersInjectGenericParent<String[]>[] provideFooArrayOfStringArray() { return new MembersInjectGenericParent[10]; }
}
diff --git a/javatests/dagger/functional/membersinject/MembersInjectTest.java b/javatests/dagger/functional/membersinject/MembersInjectTest.java
index 0617fd5e5..31e4f4595 100644
--- a/javatests/dagger/functional/membersinject/MembersInjectTest.java
+++ b/javatests/dagger/functional/membersinject/MembersInjectTest.java
@@ -18,12 +18,15 @@ package dagger.functional.membersinject;
import static com.google.common.truth.Truth.assertThat;
+import dagger.BindsInstance;
+import dagger.Component;
import dagger.MembersInjector;
import dagger.functional.multipackage.DaggerMembersInjectionVisibilityComponent;
import dagger.functional.multipackage.MembersInjectionVisibilityComponent;
import dagger.functional.multipackage.a.AGrandchild;
import dagger.functional.multipackage.a.AParent;
import dagger.functional.multipackage.b.BChild;
+import javax.inject.Inject;
import javax.inject.Provider;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -93,4 +96,35 @@ public class MembersInjectTest {
injector.injectMembers(child);
assertThat(child.t).isEqualTo("field!");
}
+
+ public static final class A extends B {
+ // No injected members
+ }
+
+ public static class B extends C {
+ // No injected members
+ }
+
+ public static class C {
+ @Inject String value;
+ }
+
+ @Component
+ interface NonLocalMembersComponent {
+ MembersInjector<A> getAMembersInjector();
+
+ @Component.Factory
+ interface Factory {
+ NonLocalMembersComponent create(@BindsInstance String value);
+ }
+ }
+
+ @Test
+ public void testNonLocalMembersInjection() {
+ MembersInjector<A> membersInjector = DaggerMembersInjectTest_NonLocalMembersComponent.factory()
+ .create("test").getAMembersInjector();
+ A testA = new A();
+ membersInjector.injectMembers(testA);
+ assertThat(testA.value).isEqualTo("test");
+ }
}
diff --git a/javatests/dagger/functional/membersinject/MembersWithInstanceNameTest.java b/javatests/dagger/functional/membersinject/MembersWithInstanceNameTest.java
new file mode 100644
index 000000000..7cd97d96b
--- /dev/null
+++ b/javatests/dagger/functional/membersinject/MembersWithInstanceNameTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.membersinject;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import javax.inject.Inject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class MembersWithInstanceNameTest {
+
+ static final class Foo {
+ // Checks that member injection fields can use "instance" as a name (b/175818837).
+ @Inject String instance;
+
+ @Inject Foo() {}
+ }
+
+ @Module
+ interface TestModule {
+ @Provides
+ static String provideString() {
+ return "test";
+ }
+ }
+
+ @Component(modules = TestModule.class)
+ interface TestComponent {
+ Foo foo();
+ }
+
+ @Test
+ public void testMemberWithInstanceName() {
+ TestComponent component = DaggerMembersWithInstanceNameTest_TestComponent.create();
+ Foo foo = component.foo();
+ assertThat(foo).isNotNull();
+ assertThat(foo.instance).isEqualTo("test");
+ }
+}
diff --git a/javatests/dagger/functional/modules/ModuleIncludesTest.java b/javatests/dagger/functional/modules/ModuleIncludesTest.java
index 38f7d9795..474cb805d 100644
--- a/javatests/dagger/functional/modules/ModuleIncludesTest.java
+++ b/javatests/dagger/functional/modules/ModuleIncludesTest.java
@@ -19,6 +19,7 @@ package dagger.functional.modules;
import static com.google.common.truth.Truth.assertThat;
import dagger.Component;
+import dagger.functional.modules.subpackage.FooForProvision;
import dagger.functional.modules.subpackage.PublicModule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -30,6 +31,8 @@ public final class ModuleIncludesTest {
@Component(modules = PublicModule.class)
interface TestComponent {
Object object();
+
+ FooForProvision fooForProvision();
}
@Test
diff --git a/javatests/dagger/functional/modules/subpackage/FooForProvision.java b/javatests/dagger/functional/modules/subpackage/FooForProvision.java
new file mode 100644
index 000000000..60b4482db
--- /dev/null
+++ b/javatests/dagger/functional/modules/subpackage/FooForProvision.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.modules.subpackage;
+
+/** This object is provisioned by NonAbstractPackagePrivate module */
+public class FooForProvision {}
diff --git a/javatests/dagger/functional/modules/subpackage/NonAbstractPackagePrivateModule.java b/javatests/dagger/functional/modules/subpackage/NonAbstractPackagePrivateModule.java
new file mode 100644
index 000000000..47d24411a
--- /dev/null
+++ b/javatests/dagger/functional/modules/subpackage/NonAbstractPackagePrivateModule.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.modules.subpackage;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * We needed a separate test for non-abstract transitively included pkg-private modules. The reason
+ * is that this caused a build failures when the component was generated in a separate package
+ * because the generated no-op method references the inaccessible package-private type, so we
+ * omitted those no-op methods to support such modules.
+ */
+@Module
+class NonAbstractPackagePrivateModule {
+ @Provides
+ static FooForProvision provideFoo() {
+ return new FooForProvision();
+ }
+
+ private NonAbstractPackagePrivateModule() {}
+}
diff --git a/javatests/dagger/functional/modules/subpackage/PublicModule.java b/javatests/dagger/functional/modules/subpackage/PublicModule.java
index 7c5fd29a9..9eade5290 100644
--- a/javatests/dagger/functional/modules/subpackage/PublicModule.java
+++ b/javatests/dagger/functional/modules/subpackage/PublicModule.java
@@ -19,7 +19,7 @@ package dagger.functional.modules.subpackage;
import dagger.Module;
import dagger.Provides;
-@Module(includes = PackagePrivateModule.class)
+@Module(includes = {PackagePrivateModule.class, NonAbstractPackagePrivateModule.class})
public abstract class PublicModule {
@Provides
static int provideInt() {
diff --git a/javatests/dagger/functional/multibindings/ComplexMapKeysInDifferentOrderTest.java b/javatests/dagger/functional/multibindings/ComplexMapKeysInDifferentOrderTest.java
index f2b5598ac..801d120bb 100644
--- a/javatests/dagger/functional/multibindings/ComplexMapKeysInDifferentOrderTest.java
+++ b/javatests/dagger/functional/multibindings/ComplexMapKeysInDifferentOrderTest.java
@@ -17,6 +17,7 @@
package dagger.functional.multibindings;
import static com.google.common.truth.Truth.assertThat;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.google.auto.value.AutoAnnotation;
import dagger.Component;
@@ -24,6 +25,7 @@ import dagger.MapKey;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
+import java.lang.annotation.Retention;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,6 +33,7 @@ import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public final class ComplexMapKeysInDifferentOrderTest {
+ @Retention(RUNTIME)
@MapKey(unwrapValue = false)
@interface ComplexMapKey {
int i();
diff --git a/javatests/dagger/functional/multibindings/MapKeyWithDefaultTest.java b/javatests/dagger/functional/multibindings/MapKeyWithDefaultTest.java
index f1884052a..70260e748 100644
--- a/javatests/dagger/functional/multibindings/MapKeyWithDefaultTest.java
+++ b/javatests/dagger/functional/multibindings/MapKeyWithDefaultTest.java
@@ -17,6 +17,7 @@
package dagger.functional.multibindings;
import static com.google.common.truth.Truth.assertThat;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.google.auto.value.AutoAnnotation;
import dagger.Component;
@@ -24,6 +25,7 @@ import dagger.MapKey;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoMap;
+import java.lang.annotation.Retention;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,6 +33,7 @@ import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public final class MapKeyWithDefaultTest {
+ @Retention(RUNTIME)
@MapKey(unwrapValue = false)
@interface MapKeyWithDefault {
boolean hasDefault() default true;
diff --git a/javatests/dagger/functional/producers/BUILD b/javatests/dagger/functional/producers/BUILD
index 36c970197..f69b3cb85 100644
--- a/javatests/dagger/functional/producers/BUILD
+++ b/javatests/dagger/functional/producers/BUILD
@@ -15,8 +15,6 @@
# Description:
# Functional tests for Dagger Producers
-package(default_visibility = ["//:src"])
-
load(
"//:build_defs.bzl",
"DOCLINT_HTML_AND_SYNTAX",
@@ -25,20 +23,24 @@ load(
)
load("//:test_defs.bzl", "GenJavaTests")
+package(default_visibility = ["//:src"])
+
GenJavaTests(
name = "producers-functional-tests",
srcs = glob(["**/*.java"]),
- javacopts = SOURCE_7_TARGET_7 + DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
+ javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
+ lib_javacopts = SOURCE_7_TARGET_7,
deps = [
"//:producers_with_compiler",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:concurrent",
"@google_bazel_common//third_party/java/auto:value",
- "@google_bazel_common//third_party/java/guava",
"@google_bazel_common//third_party/java/jsr305_annotations",
"@google_bazel_common//third_party/java/jsr330_inject",
"@google_bazel_common//third_party/java/junit",
"@google_bazel_common//third_party/java/mockito",
"@google_bazel_common//third_party/java/truth",
- "@google_bazel_common//third_party/java/truth:truth8",
],
)
diff --git a/javatests/dagger/functional/producers/ComponentDependenciesTest.java b/javatests/dagger/functional/producers/ComponentDependenciesTest.java
new file mode 100644
index 000000000..d4ad9f01a
--- /dev/null
+++ b/javatests/dagger/functional/producers/ComponentDependenciesTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.functional.producers;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import dagger.producers.ProductionComponent;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests component dependencies.
+ */
+@RunWith(JUnit4.class)
+public final class ComponentDependenciesTest {
+ public interface One {
+ ListenableFuture<String> getString();
+ }
+
+ public interface Two {
+ ListenableFuture<String> getString();
+ }
+
+ public interface Merged extends One, Two {
+ }
+
+ @ProductionComponent(dependencies = Merged.class)
+ interface TestProductionComponent {
+ ListenableFuture<String> getString();
+
+ @ProductionComponent.Builder
+ interface Builder {
+ Builder dep(Merged dep);
+
+ TestProductionComponent build();
+ }
+ }
+
+ @Test
+ public void testSameMethodTwiceProduction() throws Exception {
+ TestProductionComponent component =
+ DaggerComponentDependenciesTest_TestProductionComponent.builder().dep(
+ () -> Futures.immediateFuture("test")).build();
+ assertThat(component.getString().get()).isEqualTo("test");
+ }
+
+ public interface OneOverride {
+ ListenableFuture<?> getString();
+ }
+
+ public interface TwoOverride {
+ ListenableFuture<?> getString();
+ }
+
+ public interface MergedOverride extends OneOverride, TwoOverride {
+ @Override
+ ListenableFuture<String> getString();
+ }
+
+ @ProductionComponent(dependencies = MergedOverride.class)
+ interface TestOverrideComponent {
+ ListenableFuture<String> getString();
+
+ @ProductionComponent.Builder
+ interface Builder {
+ Builder dep(MergedOverride dep);
+
+ TestOverrideComponent build();
+ }
+ }
+
+ @Test
+ public void testPolymorphicOverridesStillCompiles() throws Exception {
+ TestOverrideComponent component =
+ DaggerComponentDependenciesTest_TestOverrideComponent.builder().dep(
+ () -> Futures.immediateFuture("test")).build();
+ assertThat(component.getString().get()).isEqualTo("test");
+ }
+}
diff --git a/javatests/dagger/functional/producers/ProducerFactoryTest.java b/javatests/dagger/functional/producers/ProducerFactoryTest.java
index c85e34231..6df526e25 100644
--- a/javatests/dagger/functional/producers/ProducerFactoryTest.java
+++ b/javatests/dagger/functional/producers/ProducerFactoryTest.java
@@ -18,7 +18,7 @@ package dagger.functional.producers;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.when;
diff --git a/javatests/dagger/functional/producers/monitoring/MonitoringTest.java b/javatests/dagger/functional/producers/monitoring/MonitoringTest.java
index 543835f60..32007a975 100644
--- a/javatests/dagger/functional/producers/monitoring/MonitoringTest.java
+++ b/javatests/dagger/functional/producers/monitoring/MonitoringTest.java
@@ -18,7 +18,7 @@ package dagger.functional.producers.monitoring;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
-import static org.mockito.Mockito.any;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyNoMoreInteractions;
diff --git a/javatests/dagger/functional/producers/multibindings/MultibindingComponent.java b/javatests/dagger/functional/producers/multibindings/MultibindingComponent.java
index fa5c6ee76..a5eb8a3d5 100644
--- a/javatests/dagger/functional/producers/multibindings/MultibindingComponent.java
+++ b/javatests/dagger/functional/producers/multibindings/MultibindingComponent.java
@@ -30,6 +30,7 @@ import dagger.producers.ProductionComponent;
import java.util.Map;
import java.util.Set;
+
@ProductionComponent(
modules = {ExecutorModule.class, MultibindingProducerModule.class, MultibindingModule.class}
)
diff --git a/javatests/dagger/functional/spi/BUILD b/javatests/dagger/functional/spi/BUILD
index fffaddbe9..8119bdd7f 100644
--- a/javatests/dagger/functional/spi/BUILD
+++ b/javatests/dagger/functional/spi/BUILD
@@ -15,18 +15,19 @@
# Description:
# Functional tests for the experimental Dagger SPI
-package(default_visibility = ["//:src"])
-
+load("@rules_java//java:defs.bzl", "java_library", "java_plugin")
load("//:test_defs.bzl", "GenJavaTests")
+package(default_visibility = ["//:src"])
+
java_plugin(
name = "test_plugin",
srcs = ["TestPlugin.java"],
deps = [
- "//java/dagger/model",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
"//java/dagger/spi",
"@google_bazel_common//third_party/java/auto:service",
- "@google_bazel_common//third_party/java/guava",
"@google_bazel_common//third_party/java/javapoet",
],
)
diff --git a/javatests/dagger/functional/subcomponent/GenericParentComponent.java b/javatests/dagger/functional/subcomponent/GenericParentComponent.java
index f49083b76..10745d705 100644
--- a/javatests/dagger/functional/subcomponent/GenericParentComponent.java
+++ b/javatests/dagger/functional/subcomponent/GenericParentComponent.java
@@ -16,6 +16,7 @@
package dagger.functional.subcomponent;
+
interface GenericParentComponent<B> {
B subcomponent();
}
diff --git a/javatests/dagger/functional/subcomponent/SubcomponentTest.java b/javatests/dagger/functional/subcomponent/SubcomponentTest.java
index c34de0a2b..380322be4 100644
--- a/javatests/dagger/functional/subcomponent/SubcomponentTest.java
+++ b/javatests/dagger/functional/subcomponent/SubcomponentTest.java
@@ -50,6 +50,7 @@ public class SubcomponentTest {
this.childComponent = childComponent;
}
+
@Test
public void scopePropagatesUpward_class() {
assertThat(childComponent.requiresSingleton().singletonType())
diff --git a/javatests/dagger/functional/subcomponent/hiding/ChildComponent.java b/javatests/dagger/functional/subcomponent/hiding/ChildComponent.java
index 7cb4fcec5..846ceb063 100644
--- a/javatests/dagger/functional/subcomponent/hiding/ChildComponent.java
+++ b/javatests/dagger/functional/subcomponent/hiding/ChildComponent.java
@@ -16,6 +16,7 @@
package dagger.functional.subcomponent.hiding;
+
import dagger.Subcomponent;
@Subcomponent(modules = dagger.functional.subcomponent.hiding.b.CommonModuleName.class)
diff --git a/javatests/dagger/functional/tck/BUILD b/javatests/dagger/functional/tck/BUILD
index 7526bf097..9cad2a017 100644
--- a/javatests/dagger/functional/tck/BUILD
+++ b/javatests/dagger/functional/tck/BUILD
@@ -15,8 +15,6 @@
# Description:
# TCK tests for Dagger
-package(default_visibility = ["//:src"])
-
load(
"//:build_defs.bzl",
"DOCLINT_HTML_AND_SYNTAX",
@@ -24,6 +22,8 @@ load(
)
load("//:test_defs.bzl", "GenJavaTests")
+package(default_visibility = ["//:src"])
+
GenJavaTests(
name = "tck_tests",
srcs = glob(["*.java"]),
diff --git a/javatests/dagger/grpc/functional/server/BUILD b/javatests/dagger/grpc/functional/server/BUILD
index 1e8c2e6e8..0603801fe 100644
--- a/javatests/dagger/grpc/functional/server/BUILD
+++ b/javatests/dagger/grpc/functional/server/BUILD
@@ -1,9 +1,8 @@
# Functional tests for Dagger-gRPC
-package(default_visibility = ["//:src"])
+load("@rules_java//java:defs.bzl", "java_proto_library")
-load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX", "DOCLINT_REFERENCES")
-load("//:test_defs.bzl", "GenJavaTests")
+package(default_visibility = ["//:src"])
# TODO(dpb): enable tests once java_grpc_library is ready in bazel:
# https://github.com/grpc/grpc-java/issues/2756
diff --git a/javatests/dagger/hilt/android/processor/AndroidCompilers.java b/javatests/dagger/hilt/android/processor/AndroidCompilers.java
new file mode 100644
index 000000000..5ce0a2ad8
--- /dev/null
+++ b/javatests/dagger/hilt/android/processor/AndroidCompilers.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor;
+
+import static java.util.stream.Collectors.toMap;
+
+import com.google.common.collect.ImmutableList;
+import com.google.testing.compile.Compiler;
+import dagger.hilt.android.processor.internal.androidentrypoint.AndroidEntryPointProcessor;
+import dagger.hilt.android.processor.internal.customtestapplication.CustomTestApplicationProcessor;
+import dagger.hilt.android.processor.internal.uninstallmodules.UninstallModulesProcessor;
+import dagger.hilt.processor.internal.aggregateddeps.AggregatedDepsProcessor;
+import dagger.hilt.processor.internal.definecomponent.DefineComponentProcessor;
+import dagger.hilt.processor.internal.generatesrootinput.GeneratesRootInputProcessor;
+import dagger.hilt.processor.internal.originatingelement.OriginatingElementProcessor;
+import dagger.hilt.processor.internal.root.RootProcessor;
+import dagger.internal.codegen.ComponentProcessor;
+import dagger.testing.compile.CompilerTests;
+import java.util.Arrays;
+import java.util.Map;
+import javax.annotation.processing.Processor;
+import com.tschuchort.compiletesting.KotlinCompilation;
+
+/** {@link Compiler} instances for testing Android Hilt. */
+public final class AndroidCompilers {
+
+ public static Compiler compiler(Processor... extraProcessors) {
+ Map<Class<?>, Processor> processors =
+ defaultProcessors().stream()
+ .collect(toMap((Processor e) -> e.getClass(), (Processor e) -> e));
+
+ // Adds extra processors, and allows overriding any processors of the same class.
+ Arrays.stream(extraProcessors)
+ .forEach(processor -> processors.put(processor.getClass(), processor));
+
+ return CompilerTests.compiler().withProcessors(processors.values());
+ }
+
+ public static KotlinCompilation kotlinCompiler() {
+ KotlinCompilation compilation = new KotlinCompilation();
+ compilation.setAnnotationProcessors(defaultProcessors());
+ compilation.setClasspaths(
+ ImmutableList.<java.io.File>builder()
+ .addAll(compilation.getClasspaths())
+ .add(CompilerTests.compilerDepsJar())
+ .build()
+ );
+ return compilation;
+ }
+
+ private static ImmutableList<Processor> defaultProcessors() {
+ return ImmutableList.of(
+ new AggregatedDepsProcessor(),
+ new AndroidEntryPointProcessor(),
+ new ComponentProcessor(),
+ new DefineComponentProcessor(),
+ new GeneratesRootInputProcessor(),
+ new OriginatingElementProcessor(),
+ new CustomTestApplicationProcessor(),
+ new UninstallModulesProcessor(),
+ new RootProcessor());
+ }
+
+ private AndroidCompilers() {}
+}
diff --git a/javatests/dagger/hilt/android/processor/BUILD b/javatests/dagger/hilt/android/processor/BUILD
new file mode 100644
index 000000000..7fb23201b
--- /dev/null
+++ b/javatests/dagger/hilt/android/processor/BUILD
@@ -0,0 +1,38 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Description:
+# Tests for internal code for implementing Hilt processors.
+
+package(default_visibility = ["//:src"])
+
+java_library(
+ name = "android_compilers",
+ srcs = ["AndroidCompilers.java"],
+ deps = [
+ "//java/dagger/hilt/android/processor/internal/androidentrypoint:processor_lib",
+ "//java/dagger/hilt/android/processor/internal/customtestapplication:processor_lib",
+ "//java/dagger/hilt/android/processor/internal/uninstallmodules:processor_lib",
+ "//java/dagger/hilt/android/processor/internal/viewmodel:processor_lib",
+ "//java/dagger/hilt/processor/internal/aggregateddeps:processor_lib",
+ "//java/dagger/hilt/processor/internal/definecomponent:processor_lib",
+ "//java/dagger/hilt/processor/internal/generatesrootinput:processor_lib",
+ "//java/dagger/hilt/processor/internal/originatingelement:processor_lib",
+ "//java/dagger/hilt/processor/internal/root:processor_lib",
+ "//java/dagger/internal/codegen:processor",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/testing/compile",
+ "@google_bazel_common//third_party/java/compile_testing",
+ "@maven//:com_github_tschuchortdev_kotlin_compile_testing",
+ ],
+)
diff --git a/javatests/dagger/hilt/android/processor/internal/aggregateddeps/BUILD b/javatests/dagger/hilt/android/processor/internal/aggregateddeps/BUILD
new file mode 100644
index 000000000..bf2ed4c8a
--- /dev/null
+++ b/javatests/dagger/hilt/android/processor/internal/aggregateddeps/BUILD
@@ -0,0 +1,51 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Description:
+# Tests for internal code for implementing Hilt processors.
+
+load("//java/dagger/testing/compile:macros.bzl", "compiler_test")
+
+package(default_visibility = ["//:src"])
+
+compiler_test(
+ name = "TestInstallInTest",
+ srcs = ["TestInstallInTest.java"],
+ compiler_deps = [
+ ":InstallInModule",
+ "//:dagger_with_compiler",
+ "//java/dagger/hilt:install_in",
+ "//java/dagger/hilt:entry_point",
+ "//java/dagger/hilt/components",
+ "//java/dagger/hilt/android/internal/modules",
+ "//java/dagger/hilt/testing:test_install_in",
+ "//java/dagger/hilt/android/testing:hilt_android_test",
+ "@androidsdk//:platforms/android-30/android.jar",
+ ],
+ deps = [
+ "@google_bazel_common//third_party/java/compile_testing",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth",
+ "//javatests/dagger/hilt/android/processor:android_compilers",
+ ],
+)
+
+java_library(
+ name = "InstallInModule",
+ srcs = ["InstallInModule.java"],
+ deps = [
+ "//:dagger_with_compiler",
+ "//java/dagger/hilt:install_in",
+ "//java/dagger/hilt/components",
+ ],
+)
diff --git a/javatests/dagger/hilt/android/processor/internal/aggregateddeps/InstallInModule.java b/javatests/dagger/hilt/android/processor/internal/aggregateddeps/InstallInModule.java
new file mode 100644
index 000000000..af535f58f
--- /dev/null
+++ b/javatests/dagger/hilt/android/processor/internal/aggregateddeps/InstallInModule.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.aggregateddeps;
+
+import dagger.Module;
+import dagger.hilt.InstallIn;
+import dagger.hilt.components.SingletonComponent;
+
+/**
+ * This is used in TestInstallInTest to test that the wrapper module, HiltWrapper_InstallInModule,
+ * cannot be replaced. This needs to be compiled in a separate library because
+ * AggregatedDepsProcesor does not defer modules that have not been generated yet.
+ */
+@Module
+@InstallIn(SingletonComponent.class)
+interface InstallInModule {}
diff --git a/javatests/dagger/hilt/android/processor/internal/aggregateddeps/TestInstallInTest.java b/javatests/dagger/hilt/android/processor/internal/aggregateddeps/TestInstallInTest.java
new file mode 100644
index 000000000..3af5be6fd
--- /dev/null
+++ b/javatests/dagger/hilt/android/processor/internal/aggregateddeps/TestInstallInTest.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.aggregateddeps;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.hilt.android.processor.AndroidCompilers.compiler;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TestInstallInTest {
+
+ @Test
+ public void testMissingValues() {
+ JavaFileObject testInstallInModule =
+ JavaFileObjects.forSourceLines(
+ "test.TestInstallInModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.hilt.testing.TestInstallIn;",
+ "",
+ "@Module",
+ "@TestInstallIn",
+ "interface TestInstallInModule {}");
+ Compilation compilation = compiler().compile(testInstallInModule);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@dagger.hilt.testing.TestInstallIn is missing default values for elements "
+ + "components,replaces")
+ .inFile(testInstallInModule)
+ .onLine(7);
+ }
+
+ @Test
+ public void testEmptyComponentValues() {
+ JavaFileObject installInModule =
+ JavaFileObjects.forSourceLines(
+ "test.InstallInModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.hilt.InstallIn;",
+ "import dagger.hilt.components.SingletonComponent;",
+ "",
+ "@Module",
+ "@InstallIn(SingletonComponent.class)",
+ "interface InstallInModule {}");
+ JavaFileObject testInstallInModule =
+ JavaFileObjects.forSourceLines(
+ "test.TestInstallInModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.hilt.testing.TestInstallIn;",
+ "",
+ "@Module",
+ "@TestInstallIn(components = {}, replaces = InstallInModule.class)",
+ "interface TestInstallInModule {}");
+ Compilation compilation = compiler().compile(installInModule, testInstallInModule);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ // TODO(bcorso): Add inFile().onLine() whenever we've fixed Processors.getAnnotationClassValues
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@TestInstallIn, 'components' class is invalid or missing: "
+ + "@dagger.hilt.testing.TestInstallIn("
+ + "components={}, replaces={test.InstallInModule.class})");
+ }
+
+ @Test
+ public void testEmptyReplacesValues() {
+ JavaFileObject testInstallInModule =
+ JavaFileObjects.forSourceLines(
+ "test.TestInstallInModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.hilt.testing.TestInstallIn;",
+ "import dagger.hilt.components.SingletonComponent;",
+ "",
+ "@Module",
+ "@TestInstallIn(components = SingletonComponent.class, replaces = {})",
+ "interface TestInstallInModule {}");
+ Compilation compilation = compiler().compile(testInstallInModule);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ // TODO(bcorso): Add inFile().onLine() whenever we've fixed Processors.getAnnotationClassValues
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@TestInstallIn, 'replaces' class is invalid or missing: "
+ + "@dagger.hilt.testing.TestInstallIn("
+ + "components={dagger.hilt.components.SingletonComponent.class}, replaces={})");
+ }
+
+ @Test
+ public void testMissingModuleAnnotation() {
+ JavaFileObject installInModule =
+ JavaFileObjects.forSourceLines(
+ "test.InstallInModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.hilt.InstallIn;",
+ "import dagger.hilt.components.SingletonComponent;",
+ "",
+ "@Module",
+ "@InstallIn(SingletonComponent.class)",
+ "interface InstallInModule {}");
+ JavaFileObject testInstallInModule =
+ JavaFileObjects.forSourceLines(
+ "test.TestInstallInModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.hilt.components.SingletonComponent;",
+ "import dagger.hilt.testing.TestInstallIn;",
+ "",
+ "@TestInstallIn(",
+ " components = SingletonComponent.class,",
+ " replaces = InstallInModule.class)",
+ "interface TestInstallInModule {}");
+ Compilation compilation = compiler().compile(installInModule, testInstallInModule);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@TestInstallIn-annotated classes must also be annotated with @Module or @EntryPoint: "
+ + "test.TestInstallInModule")
+ .inFile(testInstallInModule)
+ .onLine(10);
+ }
+
+ @Test
+ public void testInvalidUsageOnEntryPoint() {
+ JavaFileObject installInModule =
+ JavaFileObjects.forSourceLines(
+ "test.InstallInModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.hilt.InstallIn;",
+ "import dagger.hilt.components.SingletonComponent;",
+ "",
+ "@Module",
+ "@InstallIn(SingletonComponent.class)",
+ "interface InstallInModule {}");
+ JavaFileObject testInstallInEntryPoint =
+ JavaFileObjects.forSourceLines(
+ "test.TestInstallInEntryPoint",
+ "package test;",
+ "",
+ "import dagger.hilt.EntryPoint;",
+ "import dagger.hilt.components.SingletonComponent;",
+ "import dagger.hilt.testing.TestInstallIn;",
+ "",
+ "@EntryPoint",
+ "@TestInstallIn(",
+ " components = SingletonComponent.class,",
+ " replaces = InstallInModule.class)",
+ "interface TestInstallInEntryPoint {}");
+ Compilation compilation = compiler().compile(installInModule, testInstallInEntryPoint);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining("@TestInstallIn can only be used with modules")
+ .inFile(testInstallInEntryPoint)
+ .onLine(11);
+ }
+
+ @Test
+ public void testInvalidReplaceModules() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines("test.Foo", "package test;", "", "class Foo {}");
+ JavaFileObject testInstallInModule =
+ JavaFileObjects.forSourceLines(
+ "test.TestInstallInModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.hilt.components.SingletonComponent;",
+ "import dagger.hilt.testing.TestInstallIn;",
+ "",
+ "@Module",
+ "@TestInstallIn(",
+ " components = SingletonComponent.class,",
+ " replaces = Foo.class)",
+ "interface TestInstallInModule {}");
+ Compilation compilation = compiler().compile(foo, testInstallInModule);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@TestInstallIn#replaces() can only contain @InstallIn modules, but found: [test.Foo]")
+ .inFile(testInstallInModule)
+ .onLine(11);
+ }
+
+ @Test
+ public void testInternalDaggerReplaceModules() {
+ JavaFileObject testInstallInModule =
+ JavaFileObjects.forSourceLines(
+ "test.TestInstallInModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.hilt.components.SingletonComponent;",
+ "import dagger.hilt.testing.TestInstallIn;",
+ "",
+ "@Module",
+ "@TestInstallIn(",
+ " components = SingletonComponent.class,",
+ " replaces = dagger.hilt.android.internal.modules.ApplicationContextModule.class)",
+ "interface TestInstallInModule {}");
+ Compilation compilation = compiler().compile(testInstallInModule);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@TestInstallIn#replaces() cannot contain internal Hilt modules, but found: "
+ + "[dagger.hilt.android.internal.modules.ApplicationContextModule]")
+ .inFile(testInstallInModule)
+ .onLine(11);
+ }
+
+ @Test
+ public void testHiltWrapperDaggerReplaceModules() {
+ JavaFileObject testInstallInModule =
+ JavaFileObjects.forSourceLines(
+ "test.TestInstallInModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.hilt.components.SingletonComponent;",
+ "import dagger.hilt.testing.TestInstallIn;",
+ "import"
+ + " dagger.hilt.android.processor.internal.aggregateddeps.HiltWrapper_InstallInModule;",
+ "",
+ "@Module",
+ "@TestInstallIn(",
+ " components = SingletonComponent.class,",
+ // Note: this module is built in a separate library since AggregatedDepsProcessor can't
+ // handle modules generated in the same round.
+ " replaces = HiltWrapper_InstallInModule.class)",
+ "interface TestInstallInModule {}");
+ Compilation compilation = compiler().compile(testInstallInModule);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@TestInstallIn#replaces() cannot contain Hilt generated public wrapper modules, "
+ + "but found: [dagger.hilt.android.processor.internal.aggregateddeps."
+ + "HiltWrapper_InstallInModule]")
+ .inFile(testInstallInModule)
+ .onLine(12);
+ }
+
+ @Test
+ public void testCannotReplaceLocalInstallInModule() {
+ JavaFileObject test =
+ JavaFileObjects.forSourceLines(
+ "test.MyTest",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.hilt.InstallIn;",
+ "import dagger.hilt.components.SingletonComponent;",
+ "import dagger.hilt.testing.TestInstallIn;",
+ "import dagger.hilt.android.testing.HiltAndroidTest;",
+ "",
+ "@HiltAndroidTest",
+ "public class MyTest {",
+ " @Module",
+ " @InstallIn(SingletonComponent.class)",
+ " interface LocalInstallInModule {}",
+ "}");
+ JavaFileObject testInstallIn =
+ JavaFileObjects.forSourceLines(
+ "test.TestInstallInModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.hilt.components.SingletonComponent;",
+ "import dagger.hilt.testing.TestInstallIn;",
+ "",
+ "@Module",
+ "@TestInstallIn(",
+ " components = SingletonComponent.class,",
+ " replaces = MyTest.LocalInstallInModule.class)",
+ "interface TestInstallInModule {}");
+ Compilation compilation = compiler().compile(test, testInstallIn);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "TestInstallIn#replaces() cannot replace test specific @InstallIn modules, but found: "
+ + "[test.MyTest.LocalInstallInModule].")
+ .inFile(testInstallIn)
+ .onLine(11);
+ }
+
+ @Test
+ public void testThatTestInstallInCannotOriginateFromTest() {
+ JavaFileObject installInModule =
+ JavaFileObjects.forSourceLines(
+ "test.InstallInModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.hilt.InstallIn;",
+ "import dagger.hilt.components.SingletonComponent;",
+ "",
+ "@Module",
+ "@InstallIn(SingletonComponent.class)",
+ "interface InstallInModule {}");
+ JavaFileObject test =
+ JavaFileObjects.forSourceLines(
+ "test.MyTest",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.hilt.components.SingletonComponent;",
+ "import dagger.hilt.testing.TestInstallIn;",
+ "import dagger.hilt.android.testing.HiltAndroidTest;",
+ "",
+ "@HiltAndroidTest",
+ "public class MyTest {",
+ " @Module",
+ " @TestInstallIn(",
+ " components = SingletonComponent.class,",
+ " replaces = InstallInModule.class)",
+ " interface TestInstallInModule {}",
+ "}");
+ Compilation compilation = compiler().compile(test, installInModule);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@TestInstallIn modules cannot be nested in (or originate from) a "
+ + "@HiltAndroidTest-annotated class: test.MyTest")
+ .inFile(test)
+ .onLine(14);
+ }
+}
diff --git a/javatests/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGeneratorTest.java b/javatests/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGeneratorTest.java
new file mode 100644
index 000000000..25a1abdeb
--- /dev/null
+++ b/javatests/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGeneratorTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.androidentrypoint;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.hilt.android.processor.AndroidCompilers.compiler;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ActivityGeneratorTest {
+
+ @Test
+ public void generate_componentActivity() {
+ JavaFileObject myActivity =
+ JavaFileObjects.forSourceLines(
+ "test.MyActivity",
+ "package test;",
+ "",
+ "import androidx.activity.ComponentActivity;",
+ "import dagger.hilt.android.AndroidEntryPoint;",
+ "",
+ "@AndroidEntryPoint(ComponentActivity.class)",
+ "public class MyActivity extends Hilt_MyActivity {",
+ "}");
+ Compilation compilation = compiler().compile(myActivity);
+ assertThat(compilation).succeeded();
+ }
+
+ @Test
+ public void generate_baseHiltComponentActivity() {
+ JavaFileObject baseActivity =
+ JavaFileObjects.forSourceLines(
+ "test.BaseActivity",
+ "package test;",
+ "",
+ "import androidx.activity.ComponentActivity;",
+ "import dagger.hilt.android.AndroidEntryPoint;",
+ "",
+ "@AndroidEntryPoint(ComponentActivity.class)",
+ "public class BaseActivity extends Hilt_BaseActivity {",
+ "}");
+ JavaFileObject myActivity =
+ JavaFileObjects.forSourceLines(
+ "test.MyActivity",
+ "package test;",
+ "",
+ "import androidx.activity.ComponentActivity;",
+ "import dagger.hilt.android.AndroidEntryPoint;",
+ "",
+ "@AndroidEntryPoint(BaseActivity.class)",
+ "public class MyActivity extends Hilt_MyActivity {",
+ "}");
+ Compilation compilation = compiler().compile(baseActivity, myActivity);
+ assertThat(compilation).succeeded();
+ }
+}
diff --git a/javatests/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointProcessorTest.java b/javatests/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointProcessorTest.java
new file mode 100644
index 000000000..c024a9c18
--- /dev/null
+++ b/javatests/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointProcessorTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.androidentrypoint;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.hilt.android.processor.AndroidCompilers.compiler;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class AndroidEntryPointProcessorTest {
+
+ @Test
+ public void missingBaseClass() {
+ JavaFileObject testActivity =
+ JavaFileObjects.forSourceLines(
+ "test.MyActivity",
+ "package test;",
+ "",
+ "import androidx.activity.ComponentActivity;",
+ "import dagger.hilt.android.AndroidEntryPoint;",
+ "",
+ "@AndroidEntryPoint",
+ "public class MyActivity extends ComponentActivity { }");
+ Compilation compilation = compiler().compile(testActivity);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Expected @AndroidEntryPoint to have a value.")
+ ;
+ }
+
+ @Test
+ public void incorrectSuperclass() {
+ JavaFileObject testActivity =
+ JavaFileObjects.forSourceLines(
+ "test.MyActivity",
+ "package test;",
+ "",
+ "import androidx.activity.ComponentActivity;",
+ "import dagger.hilt.android.AndroidEntryPoint;",
+ "",
+ "@AndroidEntryPoint(ComponentActivity.class)",
+ "public class MyActivity extends ComponentActivity { }");
+ Compilation compilation = compiler().compile(testActivity);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@AndroidEntryPoint class expected to extend Hilt_MyActivity. "
+ + "Found: ComponentActivity")
+ ;
+ }
+
+ @Test
+ public void disableSuperclassValidation_activity() {
+ JavaFileObject testActivity =
+ JavaFileObjects.forSourceLines(
+ "test.MyActivity",
+ "package test;",
+ "",
+ "import androidx.activity.ComponentActivity;",
+ "import dagger.hilt.android.AndroidEntryPoint;",
+ "",
+ "@AndroidEntryPoint",
+ "public class MyActivity extends ComponentActivity { }");
+ Compilation compilation =
+ compiler()
+ .withOptions("-Adagger.hilt.android.internal.disableAndroidSuperclassValidation=true")
+ .compile(testActivity);
+ assertThat(compilation).succeeded();
+ }
+
+ @Test
+ public void disableSuperclassValidation_application() {
+ JavaFileObject testApplication =
+ JavaFileObjects.forSourceLines(
+ "test.MyApp",
+ "package test;",
+ "",
+ "import android.app.Application;",
+ "import dagger.hilt.android.HiltAndroidApp;",
+ "",
+ "@HiltAndroidApp",
+ "public class MyApp extends Application { }");
+ Compilation compilation =
+ compiler()
+ .withOptions("-Adagger.hilt.android.internal.disableAndroidSuperclassValidation=true")
+ .compile(testApplication);
+ assertThat(compilation).succeeded();
+ }
+
+ @Test
+ public void checkBaseActivityExtendsComponentActivity() {
+ JavaFileObject testActivity =
+ JavaFileObjects.forSourceLines(
+ "test.MyActivity",
+ "package test;",
+ "",
+ "import android.app.Activity;",
+ "import dagger.hilt.android.AndroidEntryPoint;",
+ "",
+ "@AndroidEntryPoint(Activity.class)",
+ "public class MyActivity extends Hilt_MyActivity { }");
+ Compilation compilation = compiler().compile(testActivity);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Activities annotated with @AndroidEntryPoint must be a subclass of "
+ + "androidx.activity.ComponentActivity. (e.g. FragmentActivity, AppCompatActivity, "
+ + "etc.)");
+ }
+
+ @Test
+ public void checkBaseActivityWithTypeParameters() {
+ JavaFileObject testActivity =
+ JavaFileObjects.forSourceLines(
+ "test.BaseActivity",
+ "package test;",
+ "",
+ "import androidx.activity.ComponentActivity;",
+ "import dagger.hilt.android.AndroidEntryPoint;",
+ "",
+ "@AndroidEntryPoint(ComponentActivity.class)",
+ "public class BaseActivity<T> extends Hilt_BaseActivity {}");
+ Compilation compilation = compiler().compile(testActivity);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(2);
+ assertThat(compilation).hadErrorContaining(
+ "cannot find symbol\n symbol: class Hilt_BaseActivity");
+ assertThat(compilation).hadErrorContaining(
+ "@AndroidEntryPoint-annotated classes cannot have type parameters.");
+ }
+
+ @Test
+ public void checkAndroidEntryPointOnApplicationRecommendsHiltAndroidApp() {
+ JavaFileObject testActivity =
+ JavaFileObjects.forSourceLines(
+ "test.MyApplication",
+ "package test;",
+ "",
+ "import android.app.Application;",
+ "import dagger.hilt.android.AndroidEntryPoint;",
+ "",
+ "@AndroidEntryPoint(Application.class)",
+ "public class MyApplication extends Hilt_MyApplication { }");
+ Compilation compilation = compiler().compile(testActivity);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("@AndroidEntryPoint cannot be used on an Application. "
+ + "Use @HiltAndroidApp instead.");
+ }
+}
diff --git a/javatests/dagger/hilt/android/processor/internal/androidentrypoint/BUILD b/javatests/dagger/hilt/android/processor/internal/androidentrypoint/BUILD
new file mode 100644
index 000000000..e53d9e26f
--- /dev/null
+++ b/javatests/dagger/hilt/android/processor/internal/androidentrypoint/BUILD
@@ -0,0 +1,67 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Description:
+# Tests for internal code for implementing Hilt processors.
+
+load("//java/dagger/testing/compile:macros.bzl", "compiler_test")
+
+package(default_visibility = ["//:src"])
+
+compiler_test(
+ name = "ActivityGeneratorTest",
+ srcs = ["ActivityGeneratorTest.java"],
+ compiler_deps = [
+ "//java/dagger/hilt/android:android_entry_point",
+ "@androidsdk//:platforms/android-30/android.jar",
+ ],
+ deps = [
+ "@google_bazel_common//third_party/java/compile_testing",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth",
+ "//javatests/dagger/hilt/android/processor:android_compilers",
+ ],
+)
+
+compiler_test(
+ name = "AndroidEntryPointProcessorTest",
+ srcs = ["AndroidEntryPointProcessorTest.java"],
+ compiler_deps = [
+ "//java/dagger/hilt/android:hilt_android_app",
+ "//java/dagger/hilt/android:android_entry_point",
+ "@androidsdk//:platforms/android-30/android.jar",
+ ],
+ deps = [
+ "@google_bazel_common//third_party/java/compile_testing",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth",
+ "//javatests/dagger/hilt/android/processor:android_compilers",
+ ],
+)
+
+compiler_test(
+ name = "KotlinAndroidEntryPointProcessorTest",
+ srcs = ["KotlinAndroidEntryPointProcessorTest.java"],
+ compiler_deps = [
+ "//java/dagger/hilt/android:hilt_android_app",
+ "//java/dagger/hilt/android:android_entry_point",
+ "@androidsdk//:platforms/android-30/android.jar",
+ ],
+ deps = [
+ "//java/dagger/internal/guava:collect",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth",
+ "//javatests/dagger/hilt/android/processor:android_compilers",
+ "@maven//:com_github_tschuchortdev_kotlin_compile_testing",
+ ],
+)
diff --git a/javatests/dagger/hilt/android/processor/internal/androidentrypoint/KotlinAndroidEntryPointProcessorTest.java b/javatests/dagger/hilt/android/processor/internal/androidentrypoint/KotlinAndroidEntryPointProcessorTest.java
new file mode 100644
index 000000000..739c87c73
--- /dev/null
+++ b/javatests/dagger/hilt/android/processor/internal/androidentrypoint/KotlinAndroidEntryPointProcessorTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.androidentrypoint;
+
+import static dagger.hilt.android.processor.AndroidCompilers.kotlinCompiler;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.truth.Truth;
+import com.tschuchort.compiletesting.KotlinCompilation;
+import com.tschuchort.compiletesting.KotlinCompilation.ExitCode;
+import com.tschuchort.compiletesting.SourceFile;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class KotlinAndroidEntryPointProcessorTest {
+ @Test
+ public void checkBaseClassConstructorHasNotDefaultParameters() {
+ SourceFile fragmentSrc = SourceFile.Companion.kotlin("MyFragment.kt",
+ String.join("\n",
+ "package test",
+ "",
+ "import dagger.hilt.android.AndroidEntryPoint",
+ "",
+ "@AndroidEntryPoint",
+ "class MyFragment : BaseFragment()"
+ ),
+ false);
+ SourceFile baseFragmentSrc = SourceFile.Companion.kotlin("BaseFragment.kt",
+ String.join("\n",
+ "package test",
+ "",
+ "import androidx.fragment.app.Fragment",
+ "",
+ "abstract class BaseFragment(layoutId: Int = 0) : Fragment()"
+ ),
+ false);
+ KotlinCompilation compilation = kotlinCompiler();
+ compilation.setSources(ImmutableList.of(fragmentSrc, baseFragmentSrc));
+ compilation.setKaptArgs(ImmutableMap.of(
+ "dagger.hilt.android.internal.disableAndroidSuperclassValidation", "true"));
+ KotlinCompilation.Result result = compilation.compile();
+ Truth.assertThat(result.getExitCode()).isEqualTo(ExitCode.COMPILATION_ERROR);
+ Truth.assertThat(result.getMessages()).contains("The base class, 'test.BaseFragment', of the "
+ + "@AndroidEntryPoint, 'test.MyFragment', contains a constructor with default parameters. "
+ + "This is currently not supported by the Gradle plugin. Either specify the base class as "
+ + "described at https://dagger.dev/hilt/gradle-setup#why-use-the-plugin or remove the "
+ + "default value declaration.");
+ }
+}
diff --git a/javatests/dagger/hilt/android/processor/internal/viewmodel/BUILD b/javatests/dagger/hilt/android/processor/internal/viewmodel/BUILD
new file mode 100644
index 000000000..030efd918
--- /dev/null
+++ b/javatests/dagger/hilt/android/processor/internal/viewmodel/BUILD
@@ -0,0 +1,112 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Tests for internal code for implementing Hilt processors.
+
+load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_library")
+load("//java/dagger/testing/compile:macros.bzl", "kt_compiler_test")
+
+package(default_visibility = ["//:src"])
+
+java_test(
+ name = "ViewModelProcessorTest",
+ runtime_deps = [
+ ":ViewModelProcessorTestLib",
+ "//java/dagger/hilt/android/lifecycle",
+ "@androidsdk//:platforms/android-30/android.jar",
+ "@google_bazel_common//third_party/java/compile_testing",
+ "@google_bazel_common//third_party/java/truth",
+ "@maven//:androidx_lifecycle_lifecycle_viewmodel",
+ "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate",
+ ],
+)
+
+kt_jvm_library(
+ name = "ViewModelProcessorTestLib",
+ srcs = [
+ "ViewModelProcessorTest.kt",
+ ],
+ deps = [
+ ":test_utils",
+ "//java/dagger/hilt/android/processor/internal/viewmodel:processor_lib",
+ "@google_bazel_common//third_party/java/compile_testing",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth",
+ ],
+)
+
+java_test(
+ name = "ViewModelGeneratorTest",
+ runtime_deps = [
+ ":ViewModelGeneratorTestLib",
+ "//java/dagger/hilt/android/lifecycle",
+ "@androidsdk//:platforms/android-30/android.jar",
+ "@google_bazel_common//third_party/java/compile_testing",
+ "@google_bazel_common//third_party/java/truth",
+ "@maven//:androidx_lifecycle_lifecycle_viewmodel",
+ "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate",
+ ],
+)
+
+kt_jvm_library(
+ name = "ViewModelGeneratorTestLib",
+ srcs = [
+ "ViewModelGeneratorTest.kt",
+ ],
+ deps = [
+ ":test_utils",
+ "//java/dagger/hilt/android/processor/internal/viewmodel:processor_lib",
+ "@google_bazel_common//third_party/java/compile_testing",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth",
+ ],
+)
+
+kt_compiler_test(
+ name = "ViewModelValidationPluginTest",
+ srcs = [
+ "ViewModelValidationPluginTest.kt",
+ ],
+ compiler_deps = [
+ "@androidsdk//:platforms/android-30/android.jar",
+ "@maven//:androidx_lifecycle_lifecycle_viewmodel",
+ "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate",
+ "@google_bazel_common//third_party/java/compile_testing",
+ "@google_bazel_common//third_party/java/truth",
+ "//java/dagger/hilt/android/lifecycle",
+ "//java/dagger/hilt/android:android_entry_point",
+ "//java/dagger/hilt/android:hilt_android_app",
+ ],
+ deps = [
+ ":test_utils",
+ "//:compiler_internals",
+ "//java/dagger/hilt/android/processor/internal/viewmodel:processor_lib",
+ "//java/dagger/hilt/android/processor/internal/viewmodel:validation_plugin_lib",
+ "//javatests/dagger/hilt/android/processor:android_compilers",
+ "@google_bazel_common//third_party/java/compile_testing",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth",
+ ],
+)
+
+kt_jvm_library(
+ name = "test_utils",
+ srcs = [
+ "TestUtils.kt",
+ ],
+ deps = [
+ "@google_bazel_common//third_party/java/compile_testing",
+ ],
+)
diff --git a/javatests/dagger/hilt/android/processor/internal/viewmodel/TestUtils.kt b/javatests/dagger/hilt/android/processor/internal/viewmodel/TestUtils.kt
new file mode 100644
index 000000000..713cd22b8
--- /dev/null
+++ b/javatests/dagger/hilt/android/processor/internal/viewmodel/TestUtils.kt
@@ -0,0 +1,15 @@
+package dagger.hilt.android.processor.internal.viewmodel
+
+import com.google.testing.compile.JavaFileObjects
+
+val GENERATED_TYPE = try {
+ Class.forName("javax.annotation.processing.Generated")
+ "javax.annotation.processing.Generated"
+} catch (_: ClassNotFoundException) {
+ "javax.annotation.Generated"
+}
+
+const val GENERATED_ANNOTATION =
+ "@Generated(\"dagger.hilt.android.processor.internal.viewmodel.ViewModelProcessor\")"
+
+fun String.toJFO(qName: String) = JavaFileObjects.forSourceString(qName, this.trimIndent())
diff --git a/javatests/dagger/hilt/android/processor/internal/viewmodel/ViewModelGeneratorTest.kt b/javatests/dagger/hilt/android/processor/internal/viewmodel/ViewModelGeneratorTest.kt
new file mode 100644
index 000000000..7c3e45fd7
--- /dev/null
+++ b/javatests/dagger/hilt/android/processor/internal/viewmodel/ViewModelGeneratorTest.kt
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.viewmodel
+
+import com.google.testing.compile.CompilationSubject.assertThat
+import com.google.testing.compile.Compiler
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class ViewModelGeneratorTest {
+
+ private fun compiler(): Compiler = Compiler.javac().withProcessors(ViewModelProcessor())
+
+ @Test
+ fun verifyModule_noArg() {
+ val myViewModel = """
+ package dagger.hilt.android.test;
+
+ import dagger.hilt.android.lifecycle.HiltViewModel;
+ import androidx.lifecycle.ViewModel;
+ import javax.inject.Inject;
+
+ @HiltViewModel
+ class MyViewModel extends ViewModel {
+ @Inject
+ MyViewModel() { }
+ }
+ """.toJFO("dagger.hilt.android.test.MyViewModel")
+
+ val expected = """
+ package dagger.hilt.android.test;
+
+ import androidx.lifecycle.ViewModel;
+ import dagger.Binds;
+ import dagger.Module;
+ import dagger.Provides;
+ import dagger.hilt.InstallIn;
+ import dagger.hilt.android.components.ActivityRetainedComponent;
+ import dagger.hilt.android.components.ViewModelComponent;
+ import dagger.hilt.android.internal.lifecycle.HiltViewModelMap;
+ import dagger.hilt.codegen.OriginatingElement;
+ import dagger.multibindings.IntoMap;
+ import dagger.multibindings.IntoSet;
+ import dagger.multibindings.StringKey;
+ import java.lang.String;
+ import $GENERATED_TYPE
+
+ $GENERATED_ANNOTATION
+ @OriginatingElement(
+ topLevelClass = MyViewModel.class
+ )
+ public final class MyViewModel_HiltModules {
+ private MyViewModel_HiltModules() {
+ }
+
+ @Module
+ @InstallIn(ViewModelComponent.class)
+ public static abstract class BindsModule {
+ @Binds
+ @IntoMap
+ @StringKey("dagger.hilt.android.test.MyViewModel")
+ @HiltViewModelMap
+ public abstract ViewModel binds(MyViewModel vm);
+ }
+
+ @Module
+ @InstallIn(ActivityRetainedComponent.class)
+ public static final class KeyModule {
+ private KeyModule() {
+ }
+
+ @Provides
+ @IntoSet
+ @HiltViewModelMap.KeySet
+ public static String provide() {
+ return "dagger.hilt.android.test.MyViewModel";
+ }
+ }
+ }
+ """.toJFO("dagger.hilt.android.test.MyViewModel_HiltModule")
+
+ val compilation = compiler()
+ .compile(myViewModel)
+ assertThat(compilation).apply {
+ succeeded()
+ generatedSourceFile("dagger.hilt.android.test.MyViewModel_HiltModules")
+ .hasSourceEquivalentTo(expected)
+ }
+ }
+
+ @Test
+ fun verifyModule_savedStateOnlyArg() {
+ val myViewModel = """
+ package dagger.hilt.android.test;
+
+ import dagger.hilt.android.lifecycle.HiltViewModel;
+ import androidx.lifecycle.ViewModel;
+ import androidx.lifecycle.SavedStateHandle;
+ import javax.inject.Inject;
+
+ @HiltViewModel
+ class MyViewModel extends ViewModel {
+ @Inject
+ MyViewModel(SavedStateHandle savedState) { }
+ }
+ """.toJFO("dagger.hilt.android.test.MyViewModel")
+
+ val expected = """
+ package dagger.hilt.android.test;
+
+ import androidx.lifecycle.ViewModel;
+ import dagger.Binds;
+ import dagger.Module;
+ import dagger.Provides;
+ import dagger.hilt.InstallIn;
+ import dagger.hilt.android.components.ActivityRetainedComponent;
+ import dagger.hilt.android.components.ViewModelComponent;
+ import dagger.hilt.android.internal.lifecycle.HiltViewModelMap;
+ import dagger.hilt.codegen.OriginatingElement;
+ import dagger.multibindings.IntoMap;
+ import dagger.multibindings.IntoSet;
+ import dagger.multibindings.StringKey;
+ import java.lang.String;
+ import $GENERATED_TYPE
+
+ $GENERATED_ANNOTATION
+ @OriginatingElement(
+ topLevelClass = MyViewModel.class
+ )
+ public final class MyViewModel_HiltModules {
+ private MyViewModel_HiltModules() {
+ }
+
+ @Module
+ @InstallIn(ViewModelComponent.class)
+ public static abstract class BindsModule {
+ @Binds
+ @IntoMap
+ @StringKey("dagger.hilt.android.test.MyViewModel")
+ @HiltViewModelMap
+ public abstract ViewModel binds(MyViewModel vm);
+ }
+
+ @Module
+ @InstallIn(ActivityRetainedComponent.class)
+ public static final class KeyModule {
+ private KeyModule() {
+ }
+
+ @Provides
+ @IntoSet
+ @HiltViewModelMap.KeySet
+ public static String provide() {
+ return "dagger.hilt.android.test.MyViewModel";
+ }
+ }
+ }
+ """.toJFO("dagger.hilt.android.test.MyViewModel_HiltModule")
+
+ val compilation = compiler()
+ .compile(myViewModel)
+ assertThat(compilation).apply {
+ succeeded()
+ generatedSourceFile("dagger.hilt.android.test.MyViewModel_HiltModules")
+ .hasSourceEquivalentTo(expected)
+ }
+ }
+
+ @Test
+ fun verifyModule_mixedArgs() {
+ val foo = """
+ package dagger.hilt.android.test;
+
+ public class Foo { }
+ """.toJFO("dagger.hilt.android.test.Foo")
+
+ val myViewModel = """
+ package dagger.hilt.android.test;
+
+ import dagger.hilt.android.lifecycle.HiltViewModel;
+ import androidx.lifecycle.ViewModel;
+ import androidx.lifecycle.SavedStateHandle;
+ import java.lang.String;
+ import javax.inject.Inject;
+
+ @HiltViewModel
+ class MyViewModel extends ViewModel {
+ @Inject
+ MyViewModel(String s, Foo f, SavedStateHandle savedState, long l) { }
+ }
+ """.toJFO("dagger.hilt.android.test.MyViewModel")
+
+ val expected = """
+ package dagger.hilt.android.test;
+
+ import androidx.lifecycle.ViewModel;
+ import dagger.Binds;
+ import dagger.Module;
+ import dagger.Provides;
+ import dagger.hilt.InstallIn;
+ import dagger.hilt.android.components.ActivityRetainedComponent;
+ import dagger.hilt.android.components.ViewModelComponent;
+ import dagger.hilt.android.internal.lifecycle.HiltViewModelMap;
+ import dagger.hilt.codegen.OriginatingElement;
+ import dagger.multibindings.IntoMap;
+ import dagger.multibindings.IntoSet;
+ import dagger.multibindings.StringKey;
+ import java.lang.String;
+ import $GENERATED_TYPE
+
+ $GENERATED_ANNOTATION
+ @OriginatingElement(
+ topLevelClass = MyViewModel.class
+ )
+ public final class MyViewModel_HiltModules {
+ private MyViewModel_HiltModules() {
+ }
+
+ @Module
+ @InstallIn(ViewModelComponent.class)
+ public static abstract class BindsModule {
+ @Binds
+ @IntoMap
+ @StringKey("dagger.hilt.android.test.MyViewModel")
+ @HiltViewModelMap
+ public abstract ViewModel binds(MyViewModel vm);
+ }
+
+ @Module
+ @InstallIn(ActivityRetainedComponent.class)
+ public static final class KeyModule {
+ private KeyModule() {
+ }
+
+ @Provides
+ @IntoSet
+ @HiltViewModelMap.KeySet
+ public static String provide() {
+ return "dagger.hilt.android.test.MyViewModel";
+ }
+ }
+ }
+ """.toJFO("dagger.hilt.android.test.MyViewModel_HiltModule")
+
+ val compilation = compiler()
+ .compile(foo, myViewModel)
+ assertThat(compilation).apply {
+ succeeded()
+ generatedSourceFile("dagger.hilt.android.test.MyViewModel_HiltModules")
+ .hasSourceEquivalentTo(expected)
+ }
+ }
+
+ @Test
+ fun verifyModule_mixedAndProviderArgs() {
+ val foo = """
+ package dagger.hilt.android.test;
+
+ public class Foo { }
+ """.toJFO("dagger.hilt.android.test.Foo")
+
+ val myViewModel = """
+ package dagger.hilt.android.test;
+
+ import dagger.hilt.android.lifecycle.HiltViewModel;
+ import androidx.lifecycle.ViewModel;
+ import androidx.lifecycle.SavedStateHandle;
+ import java.lang.String;
+ import javax.inject.Inject;
+ import javax.inject.Provider;
+
+ @HiltViewModel
+ class MyViewModel extends ViewModel {
+ @Inject
+ MyViewModel(String s, Provider<Foo> f, SavedStateHandle savedState) { }
+ }
+ """.toJFO("dagger.hilt.android.test.MyViewModel")
+
+ val expected = """
+ package dagger.hilt.android.test;
+
+ import androidx.lifecycle.ViewModel;
+ import dagger.Binds;
+ import dagger.Module;
+ import dagger.Provides;
+ import dagger.hilt.InstallIn;
+ import dagger.hilt.android.components.ActivityRetainedComponent;
+ import dagger.hilt.android.components.ViewModelComponent;
+ import dagger.hilt.android.internal.lifecycle.HiltViewModelMap;
+ import dagger.hilt.codegen.OriginatingElement;
+ import dagger.multibindings.IntoMap;
+ import dagger.multibindings.IntoSet;
+ import dagger.multibindings.StringKey;
+ import java.lang.String;
+ import $GENERATED_TYPE;
+
+ $GENERATED_ANNOTATION
+ @OriginatingElement(
+ topLevelClass = MyViewModel.class
+ )
+ public final class MyViewModel_HiltModules {
+ private MyViewModel_HiltModules() {
+ }
+
+ @Module
+ @InstallIn(ViewModelComponent.class)
+ public static abstract class BindsModule {
+ @Binds
+ @IntoMap
+ @StringKey("dagger.hilt.android.test.MyViewModel")
+ @HiltViewModelMap
+ public abstract ViewModel binds(MyViewModel vm);
+ }
+
+ @Module
+ @InstallIn(ActivityRetainedComponent.class)
+ public static final class KeyModule {
+ private KeyModule() {
+ }
+
+ @Provides
+ @IntoSet
+ @HiltViewModelMap.KeySet
+ public static String provide() {
+ return "dagger.hilt.android.test.MyViewModel";
+ }
+ }
+ }
+ """.toJFO("dagger.hilt.android.test.MyViewModel_HiltModules")
+
+ val compilation = compiler()
+ .compile(foo, myViewModel)
+ assertThat(compilation).apply {
+ succeeded()
+ generatedSourceFile("dagger.hilt.android.test.MyViewModel_HiltModules")
+ .hasSourceEquivalentTo(expected)
+ }
+ }
+
+ @Test
+ fun verifyModule_qualifiedArgs() {
+ val myQualifier = """
+ package dagger.hilt.android.test;
+
+ import javax.inject.Qualifier;
+
+ @Qualifier
+ public @interface MyQualifier { }
+ """.toJFO("dagger.hilt.android.test.MyQualifier")
+
+ val myViewModel = """
+ package dagger.hilt.android.test;
+
+ import dagger.hilt.android.lifecycle.HiltViewModel;
+ import androidx.lifecycle.ViewModel;
+ import androidx.lifecycle.SavedStateHandle;
+ import java.lang.Long;
+ import java.lang.String;
+ import javax.inject.Inject;
+ import javax.inject.Named;
+ import javax.inject.Provider;
+
+ @HiltViewModel
+ class MyViewModel extends ViewModel {
+ @Inject
+ MyViewModel(@Named("TheString") String s, @MyQualifier Provider<Long> l,
+ SavedStateHandle savedState) {
+ }
+ }
+ """.toJFO("dagger.hilt.android.test.MyViewModel")
+
+ val expected = """
+ package dagger.hilt.android.test;
+
+ import androidx.lifecycle.ViewModel;
+ import dagger.Binds;
+ import dagger.Module;
+ import dagger.Provides;
+ import dagger.hilt.InstallIn;
+ import dagger.hilt.android.components.ActivityRetainedComponent;
+ import dagger.hilt.android.components.ViewModelComponent;
+ import dagger.hilt.android.internal.lifecycle.HiltViewModelMap;
+ import dagger.hilt.codegen.OriginatingElement;
+ import dagger.multibindings.IntoMap;
+ import dagger.multibindings.IntoSet;
+ import dagger.multibindings.StringKey;
+ import java.lang.String;
+ import $GENERATED_TYPE;
+
+ $GENERATED_ANNOTATION
+ @OriginatingElement(
+ topLevelClass = MyViewModel.class
+ )
+ public final class MyViewModel_HiltModules {
+ private MyViewModel_HiltModules() {
+ }
+
+ @Module
+ @InstallIn(ViewModelComponent.class)
+ public static abstract class BindsModule {
+ @Binds
+ @IntoMap
+ @StringKey("dagger.hilt.android.test.MyViewModel")
+ @HiltViewModelMap
+ public abstract ViewModel binds(MyViewModel vm);
+ }
+
+ @Module
+ @InstallIn(ActivityRetainedComponent.class)
+ public static final class KeyModule {
+ private KeyModule() {
+ }
+
+ @Provides
+ @IntoSet
+ @HiltViewModelMap.KeySet
+ public static String provide() {
+ return "dagger.hilt.android.test.MyViewModel";
+ }
+ }
+ }
+ """.toJFO("dagger.hilt.android.test.MyViewModel_HiltModules")
+
+ val compilation = compiler()
+ .compile(myQualifier, myViewModel)
+ assertThat(compilation).apply {
+ succeeded()
+ generatedSourceFile("dagger.hilt.android.test.MyViewModel_HiltModules")
+ .hasSourceEquivalentTo(expected)
+ }
+ }
+
+ @Test
+ fun verifyInnerClass() {
+ val viewModel = """
+ package dagger.hilt.android.test;
+
+ import dagger.hilt.android.lifecycle.HiltViewModel;
+ import androidx.lifecycle.ViewModel;
+ import javax.inject.Inject;
+
+ class Outer {
+ @HiltViewModel
+ static class InnerViewModel extends ViewModel {
+ @Inject
+ InnerViewModel() { }
+ }
+ }
+ """.toJFO("dagger.hilt.android.test.Outer")
+
+ val expectedModule = """
+ package dagger.hilt.android.test;
+
+ import androidx.lifecycle.ViewModel;
+ import dagger.Binds;
+ import dagger.Module;
+ import dagger.Provides;
+ import dagger.hilt.InstallIn;
+ import dagger.hilt.android.components.ActivityRetainedComponent;
+ import dagger.hilt.android.components.ViewModelComponent;
+ import dagger.hilt.android.internal.lifecycle.HiltViewModelMap;
+ import dagger.hilt.codegen.OriginatingElement;
+ import dagger.multibindings.IntoMap;
+ import dagger.multibindings.IntoSet;
+ import dagger.multibindings.StringKey;
+ import java.lang.String;
+ import $GENERATED_TYPE
+
+ $GENERATED_ANNOTATION
+ @OriginatingElement(
+ topLevelClass = Outer.class
+ )
+ public final class Outer_InnerViewModel_HiltModules {
+ private Outer_InnerViewModel_HiltModules() {
+ }
+
+ @Module
+ @InstallIn(ViewModelComponent.class)
+ public static abstract class BindsModule {
+ @Binds
+ @IntoMap
+ @StringKey("dagger.hilt.android.test.Outer${'$'}InnerViewModel")
+ @HiltViewModelMap
+ public abstract ViewModel binds(Outer.InnerViewModel vm);
+ }
+
+ @Module
+ @InstallIn(ActivityRetainedComponent.class)
+ public static final class KeyModule {
+ private KeyModule() {
+ }
+
+ @Provides
+ @IntoSet
+ @HiltViewModelMap.KeySet
+ public static String provide() {
+ return "dagger.hilt.android.test.Outer${'$'}InnerViewModel";
+ }
+ }
+ }
+ """.toJFO("dagger.hilt.android.test.Outer_InnerViewModel_HiltModules")
+
+ val compilation = compiler()
+ .compile(viewModel)
+ assertThat(compilation).apply {
+ succeeded()
+ generatedSourceFile("dagger.hilt.android.test.Outer_InnerViewModel_HiltModules")
+ .hasSourceEquivalentTo(expectedModule)
+ }
+ }
+}
diff --git a/javatests/dagger/hilt/android/processor/internal/viewmodel/ViewModelProcessorTest.kt b/javatests/dagger/hilt/android/processor/internal/viewmodel/ViewModelProcessorTest.kt
new file mode 100644
index 000000000..b3eaeabbc
--- /dev/null
+++ b/javatests/dagger/hilt/android/processor/internal/viewmodel/ViewModelProcessorTest.kt
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.viewmodel
+
+import com.google.testing.compile.CompilationSubject.assertThat
+import com.google.testing.compile.Compiler
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class ViewModelProcessorTest {
+
+ private fun compiler(): Compiler = Compiler.javac().withProcessors(ViewModelProcessor())
+
+ @Test
+ fun validViewModel() {
+ val myViewModel = """
+ package dagger.hilt.android.test;
+
+ import androidx.lifecycle.ViewModel;
+ import dagger.hilt.android.lifecycle.HiltViewModel;
+ import javax.inject.Inject;
+
+ @HiltViewModel
+ class MyViewModel extends ViewModel {
+ @Inject MyViewModel() { }
+ }
+ """.toJFO("dagger.hilt.android.test.MyViewModel")
+
+ val compilation = compiler().compile(myViewModel)
+ assertThat(compilation).succeeded()
+ }
+
+ @Test
+ fun verifyEnclosingElementExtendsViewModel() {
+ val myViewModel = """
+ package dagger.hilt.android.test;
+
+ import dagger.hilt.android.lifecycle.HiltViewModel;
+ import javax.inject.Inject;
+
+ @HiltViewModel
+ class MyViewModel {
+ @Inject
+ MyViewModel() { }
+ }
+ """.toJFO("dagger.hilt.android.test.MyViewModel")
+
+ val compilation = compiler().compile(myViewModel)
+ assertThat(compilation).apply {
+ failed()
+ hadErrorCount(1)
+ hadErrorContainingMatch(
+ "@HiltViewModel is only supported on types that subclass androidx.lifecycle.ViewModel."
+ )
+ }
+ }
+
+ @Test
+ fun verifySingleAnnotatedConstructor() {
+ val myViewModel = """
+ package dagger.hilt.android.test;
+
+ import androidx.lifecycle.ViewModel;
+ import dagger.hilt.android.lifecycle.HiltViewModel;
+ import javax.inject.Inject;
+
+ @HiltViewModel
+ class MyViewModel extends ViewModel {
+ @Inject
+ MyViewModel() { }
+
+ @Inject
+ MyViewModel(String s) { }
+ }
+ """.toJFO("dagger.hilt.android.test.MyViewModel")
+
+ val compilation = compiler().compile(myViewModel)
+ assertThat(compilation).apply {
+ failed()
+ hadErrorCount(1)
+ hadErrorContainingMatch(
+ "@HiltViewModel annotated class should contain exactly one @Inject annotated constructor."
+ )
+ }
+ }
+
+ @Test
+ fun verifyNonPrivateConstructor() {
+ val myViewModel = """
+ package dagger.hilt.android.test;
+
+ import androidx.lifecycle.ViewModel;
+ import dagger.hilt.android.lifecycle.HiltViewModel;
+ import javax.inject.Inject;
+
+ @HiltViewModel
+ class MyViewModel extends ViewModel {
+ @Inject
+ private MyViewModel() { }
+ }
+ """.toJFO("dagger.hilt.android.test.MyViewModel")
+
+ val compilation = compiler().compile(myViewModel)
+ assertThat(compilation).apply {
+ failed()
+ hadErrorCount(1)
+ hadErrorContainingMatch(
+ "@Inject annotated constructors must not be " +
+ "private."
+ )
+ }
+ }
+
+ @Test
+ fun verifyInnerClassIsStatic() {
+ val myViewModel = """
+ package dagger.hilt.android.test;
+
+ import androidx.lifecycle.ViewModel;
+ import dagger.hilt.android.lifecycle.HiltViewModel;
+ import javax.inject.Inject;
+
+ class Outer {
+ @HiltViewModel
+ class MyViewModel extends ViewModel {
+ @Inject
+ MyViewModel() { }
+ }
+ }
+ """.toJFO("dagger.hilt.android.test.Outer")
+
+ val compilation = compiler().compile(myViewModel)
+ assertThat(compilation).apply {
+ failed()
+ hadErrorCount(1)
+ hadErrorContainingMatch(
+ "@HiltViewModel may only be used on inner classes if they are static."
+ )
+ }
+ }
+
+ @Test
+ fun verifyNoScopeAnnotation() {
+ val myViewModel = """
+ package dagger.hilt.android.test;
+
+ import androidx.lifecycle.ViewModel;
+ import dagger.hilt.android.lifecycle.HiltViewModel;
+ import javax.inject.Inject;
+ import javax.inject.Singleton;
+
+ @Singleton
+ @HiltViewModel
+ class MyViewModel extends ViewModel {
+ @Inject MyViewModel() { }
+ }
+ """.toJFO("dagger.hilt.android.test.MyViewModel")
+
+ val compilation = compiler().compile(myViewModel)
+ assertThat(compilation).apply {
+ failed()
+ hadErrorCount(1)
+ hadErrorContainingMatch(
+ "@HiltViewModel classes should not be scoped. Found: @javax.inject.Singleton"
+ )
+ }
+ }
+}
diff --git a/javatests/dagger/hilt/android/processor/internal/viewmodel/ViewModelValidationPluginTest.kt b/javatests/dagger/hilt/android/processor/internal/viewmodel/ViewModelValidationPluginTest.kt
new file mode 100644
index 000000000..b5c22c1a3
--- /dev/null
+++ b/javatests/dagger/hilt/android/processor/internal/viewmodel/ViewModelValidationPluginTest.kt
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.android.processor.internal.viewmodel
+
+import com.google.testing.compile.CompilationSubject.assertThat
+import com.google.testing.compile.Compiler
+import dagger.hilt.android.processor.AndroidCompilers.compiler
+import dagger.internal.codegen.ComponentProcessor
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class ViewModelValidationPluginTest {
+
+ private fun testCompiler(): Compiler = compiler(
+ ComponentProcessor.forTesting(ViewModelValidationPlugin()),
+ ViewModelProcessor()
+ )
+
+ private val hiltAndroidApp = """
+ package test;
+
+ import android.app.Application;
+ import dagger.hilt.android.HiltAndroidApp;
+
+ @HiltAndroidApp(Application.class)
+ public class TestApplication extends Hilt_TestApplication {}
+ """.toJFO("test.TestApplication")
+
+ @Test
+ fun injectViewModelIsProhibited() {
+ val hiltActivity = """
+ package test;
+
+ import androidx.fragment.app.FragmentActivity;
+ import dagger.hilt.android.AndroidEntryPoint;
+ import javax.inject.Inject;
+
+ @AndroidEntryPoint(FragmentActivity.class)
+ public class TestActivity extends Hilt_TestActivity {
+ @Inject Foo foo;
+ }
+ """.toJFO("test.TestActivity")
+ val hiltViewModel = """
+ package test;
+
+ import androidx.lifecycle.ViewModel;
+ import dagger.hilt.android.lifecycle.HiltViewModel;
+ import javax.inject.Inject;
+
+ @HiltViewModel
+ class MyViewModel extends ViewModel {
+ @Inject MyViewModel() { }
+ }
+ """.toJFO("test.MyViewModel")
+ val foo = """
+ package test;
+
+ import javax.inject.Inject;
+
+ final class Foo {
+ @Inject Foo(MyViewModel viewModel) {}
+ }
+ """.toJFO("test.Foo")
+
+ val compilation = testCompiler().compile(foo, hiltViewModel, hiltAndroidApp, hiltActivity)
+ assertThat(compilation).apply {
+ failed()
+ hadErrorCount(1)
+ hadErrorContainingMatch(
+ "Injection of an @HiltViewModel class is prohibited"
+ )
+ }
+ }
+
+ @Test
+ fun fieldInjectedViewModelIsProhibited() {
+ val hiltActivity = """
+ package test;
+
+ import androidx.fragment.app.FragmentActivity;
+ import dagger.hilt.android.AndroidEntryPoint;
+ import javax.inject.Inject;
+
+ @AndroidEntryPoint(FragmentActivity.class)
+ public class TestActivity extends Hilt_TestActivity {
+ @Inject MyViewModel viewModel;
+ }
+ """.toJFO("test.TestActivity")
+ val hiltViewModel = """
+ package test;
+
+ import androidx.lifecycle.ViewModel;
+ import dagger.hilt.android.lifecycle.HiltViewModel;
+ import javax.inject.Inject;
+
+ @HiltViewModel
+ class MyViewModel extends ViewModel {
+ @Inject MyViewModel() { }
+ }
+ """.toJFO("test.MyViewModel")
+
+ val compilation = testCompiler().compile(hiltViewModel, hiltAndroidApp, hiltActivity)
+ assertThat(compilation).apply {
+ failed()
+ hadErrorCount(1)
+ hadErrorContainingMatch(
+ "Injection of an @HiltViewModel class is prohibited"
+ )
+ }
+ }
+
+ @Test
+ fun injectViewModelFromViewModelComponentIsProhibited() {
+ // Use an @HiltViewModel that injects a Foo to get the binding inside the ViewModelComponent
+ val hiltViewModel = """
+ package test;
+
+ import androidx.lifecycle.ViewModel;
+ import dagger.hilt.android.lifecycle.HiltViewModel;
+ import javax.inject.Inject;
+
+ @HiltViewModel
+ class MyViewModel extends ViewModel {
+ @Inject MyViewModel(Foo foo) { }
+ }
+ """.toJFO("test.MyViewModel")
+
+ val foo = """
+ package test;
+
+ import javax.inject.Inject;
+ import javax.inject.Provider;
+
+ final class Foo {
+ @Inject Foo(Provider<MyViewModel> viewModelProvider) {}
+ }
+ """.toJFO("test.Foo")
+
+ val compilation = testCompiler().compile(foo, hiltViewModel, hiltAndroidApp)
+ assertThat(compilation).apply {
+ failed()
+ hadErrorCount(1)
+ hadErrorContainingMatch(
+ "Injection of an @HiltViewModel class is prohibited"
+ )
+ }
+ }
+
+ @Test
+ fun injectOverriddenViewModelBindingIsAllowed() {
+ val hiltActivity = """
+ package test;
+
+ import androidx.fragment.app.FragmentActivity;
+ import dagger.hilt.android.AndroidEntryPoint;
+ import javax.inject.Inject;
+
+ @AndroidEntryPoint(FragmentActivity.class)
+ public class TestActivity extends Hilt_TestActivity {
+ @Inject Foo foo;
+ }
+ """.toJFO("test.TestActivity")
+ val hiltViewModel = """
+ package test;
+
+ import androidx.lifecycle.ViewModel;
+ import dagger.hilt.android.lifecycle.HiltViewModel;
+ import javax.inject.Inject;
+
+ @HiltViewModel
+ class MyViewModel extends ViewModel {
+ @Inject MyViewModel() { }
+ }
+ """.toJFO("test.MyViewModel")
+ val foo = """
+ package test;
+
+ import javax.inject.Inject;
+
+ final class Foo {
+ @Inject Foo(MyViewModel viewModel) {}
+ }
+ """.toJFO("test.Foo")
+ val activityModule = """
+ package test;
+
+ import dagger.Module;
+ import dagger.Provides;
+ import dagger.hilt.InstallIn;
+ import dagger.hilt.android.components.ActivityComponent;
+
+ @InstallIn(ActivityComponent.class)
+ @Module
+ public final class ActivityModule {
+ @Provides static MyViewModel provideMyViewModel() {
+ // Normally you'd expect this to use a ViewModelProvider or something but
+ // since this test is just testing the binding graph, for simplicity just return
+ // null.
+ return null;
+ }
+ }
+ """.toJFO("test.ActivityModule")
+
+ val compilation = testCompiler().compile(
+ foo, activityModule, hiltViewModel, hiltAndroidApp, hiltActivity
+ )
+ assertThat(compilation).succeeded()
+ }
+
+ @Test
+ fun injectQualifiedViewModelBindingIsAllowed() {
+ val hiltActivity = """
+ package test;
+
+ import androidx.fragment.app.FragmentActivity;
+ import dagger.hilt.android.AndroidEntryPoint;
+ import javax.inject.Inject;
+
+ @AndroidEntryPoint(FragmentActivity.class)
+ public class TestActivity extends Hilt_TestActivity {
+ @Inject Foo foo;
+ }
+ """.toJFO("test.TestActivity")
+ val hiltViewModel = """
+ package test;
+
+ import androidx.lifecycle.ViewModel;
+ import dagger.hilt.android.lifecycle.HiltViewModel;
+ import javax.inject.Inject;
+
+ @HiltViewModel
+ class MyViewModel extends ViewModel {
+ @Inject MyViewModel() { }
+ }
+ """.toJFO("test.MyViewModel")
+ val foo = """
+ package test;
+
+ import javax.inject.Inject;
+
+ final class Foo {
+ @Inject Foo(@ActivityModule.MyQualifier MyViewModel viewModel) {}
+ }
+ """.toJFO("test.Foo")
+ val activityModule = """
+ package test;
+
+ import dagger.Module;
+ import dagger.Provides;
+ import dagger.hilt.InstallIn;
+ import dagger.hilt.android.components.ActivityComponent;
+ import javax.inject.Qualifier;
+
+ @InstallIn(ActivityComponent.class)
+ @Module
+ public final class ActivityModule {
+ @Qualifier
+ public @interface MyQualifier {}
+
+ @Provides
+ @MyQualifier
+ static MyViewModel provideMyViewModel() {
+ // Normally you'd expect this to use a ViewModelProvider or something but
+ // since this test is just testing the binding graph, for simplicity just return
+ // null.
+ return null;
+ }
+ }
+ """.toJFO("test.ActivityModule")
+
+ val compilation = testCompiler().compile(
+ foo, activityModule, hiltViewModel, hiltAndroidApp, hiltActivity
+ )
+ assertThat(compilation).succeeded()
+ }
+
+ // Regression test for not handling array types properly
+ @Test
+ fun correctlyAllowsOtherBindings() {
+ val hiltActivity = """
+ package test;
+
+ import androidx.fragment.app.FragmentActivity;
+ import dagger.hilt.android.AndroidEntryPoint;
+ import javax.inject.Inject;
+
+ @AndroidEntryPoint(FragmentActivity.class)
+ public class TestActivity extends Hilt_TestActivity {
+ @Inject Foo foo;
+ }
+ """.toJFO("test.TestActivity")
+ val hiltViewModel = """
+ package test;
+
+ import androidx.lifecycle.ViewModel;
+ import dagger.hilt.android.lifecycle.HiltViewModel;
+ import javax.inject.Inject;
+
+ @HiltViewModel
+ class MyViewModel extends ViewModel {
+ @Inject MyViewModel() { }
+ }
+ """.toJFO("test.MyViewModel")
+ val foo = """
+ package test;
+
+ import javax.inject.Inject;
+
+ final class Foo {
+ @Inject Foo(Long[] longArray) {}
+ }
+ """.toJFO("test.Foo")
+ val activityModule = """
+ package test;
+
+ import dagger.Module;
+ import dagger.Provides;
+ import dagger.hilt.InstallIn;
+ import dagger.hilt.android.components.ActivityComponent;
+
+ @InstallIn(ActivityComponent.class)
+ @Module
+ public final class ActivityModule {
+ @Provides
+ static Long[] provideLongArray() {
+ return null;
+ }
+ }
+ """.toJFO("test.ActivityModule")
+
+ val compilation = testCompiler().compile(
+ foo, activityModule, hiltViewModel, hiltAndroidApp, hiltActivity
+ )
+ assertThat(compilation).succeeded()
+ }
+}
diff --git a/javatests/dagger/hilt/processor/internal/BUILD b/javatests/dagger/hilt/processor/internal/BUILD
new file mode 100644
index 000000000..e6e1cfb4e
--- /dev/null
+++ b/javatests/dagger/hilt/processor/internal/BUILD
@@ -0,0 +1,40 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Tests for internal code for implementing Hilt processors.
+
+package(default_visibility = ["//:src"])
+
+java_test(
+ name = "ProcessorsTest",
+ size = "small",
+ srcs = ["ProcessorsTest.java"],
+ deps = [
+ "//java/dagger/hilt/processor/internal:processors",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth",
+ ],
+)
+
+java_library(
+ name = "generated_import",
+ srcs = ["GeneratedImport.java"],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["**/*"]),
+)
diff --git a/javatests/dagger/hilt/processor/internal/GeneratedImport.java b/javatests/dagger/hilt/processor/internal/GeneratedImport.java
new file mode 100644
index 000000000..0feaa4e6d
--- /dev/null
+++ b/javatests/dagger/hilt/processor/internal/GeneratedImport.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal;
+
+/** Utility class for @Generated usage in tests. */
+public final class GeneratedImport {
+ public static final String IMPORT_GENERATED_ANNOTATION =
+ isBeforeJava9()
+ ? "import javax.annotation.Generated;"
+ : "import javax.annotation.processing.Generated;";
+
+ private static boolean isBeforeJava9() {
+ try {
+ Class.forName("java.lang.Module");
+ return false;
+ } catch (ClassNotFoundException e) {
+ return true;
+ }
+ }
+
+ private GeneratedImport() {}
+}
diff --git a/javatests/dagger/hilt/processor/internal/ProcessorsTest.java b/javatests/dagger/hilt/processor/internal/ProcessorsTest.java
new file mode 100644
index 000000000..cad41aab8
--- /dev/null
+++ b/javatests/dagger/hilt/processor/internal/ProcessorsTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeVariableName;
+import java.util.List;
+import javax.lang.model.element.Modifier;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ProcessorsTest {
+
+ @Test
+ public void testCopyMethodSpecWithoutBodyForMethod() throws Exception {
+ TypeVariableName t = TypeVariableName.get("T", Number.class);
+ ClassName list = ClassName.get(List.class);
+ MethodSpec.Builder builder = MethodSpec.methodBuilder("myMethod")
+ .addJavadoc("This is a test line 1 in the java doc \n"
+ + "This is a test line 2 in the java doc \n"
+ + "<p>Test Use of links {@link $T} \n", list)
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC)
+ .addParameter(ParameterizedTypeName.get(list, t), "list", Modifier.FINAL)
+ .returns(ParameterizedTypeName.get(list, t))
+ .addException(IllegalArgumentException.class)
+ .addTypeVariable(t);
+
+ // Test that method copy is the same as original
+ MethodSpec method = builder.build();
+ MethodSpec methodCopy = Processors.copyMethodSpecWithoutBody(method).build();
+ assertThat(method.toString()).contains(methodCopy.toString());
+
+ // Test that method copy removes the body, compare with the old copy
+ MethodSpec methodWithBody = builder.addStatement("return list").build();
+ MethodSpec methodCopyWithBody = Processors.copyMethodSpecWithoutBody(methodWithBody).build();
+ assertThat(methodCopyWithBody.toString()).isEqualTo(methodCopy.toString());
+
+ }
+
+ @Test
+ public void testCopyMethodSpecWithoutBodyForConstructor() throws Exception {
+ TypeVariableName t = TypeVariableName.get("T", Number.class);
+ ClassName list = ClassName.get(List.class);
+ MethodSpec.Builder builder = MethodSpec.constructorBuilder()
+ .addJavadoc("This is a test line 1 in the java doc \n"
+ + "This is a test line 2 in the java doc \n"
+ + "<p>Test Use of links {@link $T} \n", list)
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PUBLIC)
+ .addParameter(ParameterizedTypeName.get(list, t), "list", Modifier.FINAL)
+ .addException(IllegalArgumentException.class)
+ .addTypeVariable(t);
+
+ // Test that method copy is the same as original
+ MethodSpec method = builder.build();
+ MethodSpec methodCopy = Processors.copyMethodSpecWithoutBody(method).build();
+ assertThat(method.toString()).contains(methodCopy.toString());
+
+ // Test that method copy removes the body, compare with the old copy
+ MethodSpec methodWithBody = builder.addStatement("this.list = list").build();
+ MethodSpec methodCopyWithBody = Processors.copyMethodSpecWithoutBody(methodWithBody).build();
+ assertThat(methodCopyWithBody.toString()).isEqualTo(methodCopy.toString());
+ }
+}
diff --git a/javatests/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsProcessorErrorsTest.java b/javatests/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsProcessorErrorsTest.java
new file mode 100644
index 000000000..b36a71d8c
--- /dev/null
+++ b/javatests/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsProcessorErrorsTest.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.aggregateddeps;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+
+import com.google.common.base.Joiner;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import dagger.hilt.processor.internal.GeneratedImport;
+import dagger.testing.compile.CompilerTests;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for errors generated by {@link AggregatedDepsProcessor} */
+@RunWith(JUnit4.class)
+public class AggregatedDepsProcessorErrorsTest {
+ private static final Joiner LINES = Joiner.on("\n");
+
+ @Test
+ public void reportMultipleAnnotationTypeKindErrors() {
+ JavaFileObject source =
+ JavaFileObjects.forSourceString(
+ "foo.bar.AnnotationsOnWrongTypeKind",
+ LINES.join(
+ "package foo.bar;",
+ "",
+ "import dagger.hilt.EntryPoint;",
+ "import dagger.hilt.InstallIn;",
+ "import dagger.Module;",
+ "import dagger.hilt.components.SingletonComponent;",
+ "import dagger.hilt.internal.ComponentEntryPoint;",
+ "import dagger.hilt.internal.GeneratedEntryPoint;",
+ "",
+ "@InstallIn(SingletonComponent.class)",
+ "@Module",
+ "enum FooModule { VALUE }",
+ "",
+ "@InstallIn(SingletonComponent.class)",
+ "@EntryPoint",
+ "final class BarEntryPoint {}",
+ "",
+ "@InstallIn(SingletonComponent.class)",
+ "@ComponentEntryPoint",
+ "final class BazComponentEntryPoint {}",
+ "",
+ "@EntryPoint",
+ "interface QuxEntryPoint {}",
+ "",
+ "@EntryPoint",
+ "@Module",
+ "interface DontMix{}",
+ ""));
+
+ Compilation compilation =
+ CompilerTests.compiler().withProcessors(new AggregatedDepsProcessor()).compile(source);
+
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Only classes and interfaces can be annotated with @Module")
+ .inFile(source)
+ .onLine(12);
+ assertThat(compilation)
+ .hadErrorContaining("Only interfaces can be annotated with @EntryPoint")
+ .inFile(source)
+ .onLine(16);
+ assertThat(compilation)
+ .hadErrorContaining("Only interfaces can be annotated with @ComponentEntryPoint")
+ .inFile(source)
+ .onLine(20);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@EntryPoint foo.bar.QuxEntryPoint must also be annotated with @InstallIn")
+ .inFile(source)
+ .onLine(23);
+ assertThat(compilation)
+ .hadErrorContaining("@Module and @EntryPoint cannot be used on the same interface")
+ .inFile(source)
+ .onLine(27);
+ }
+
+ @Test
+ public void testInvalidComponentInInstallInAnnotation() {
+ JavaFileObject module = JavaFileObjects.forSourceLines(
+ "test.FooModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.hilt.InstallIn;",
+ "import dagger.hilt.android.qualifiers.ApplicationContext;",
+ "",
+ "@InstallIn(ApplicationContext.class)", // Error: Not a Hilt component
+ "@Module",
+ "final class FooModule {}");
+
+ Compilation compilation =
+ CompilerTests.compiler().withProcessors(new AggregatedDepsProcessor()).compile(module);
+
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@InstallIn, can only be used with @DefineComponent-annotated classes, but found: "
+ + "[dagger.hilt.android.qualifiers.ApplicationContext]")
+ .inFile(module)
+ .onLine(9);
+ }
+
+ @Test
+ public void testMissingInstallInAnnotation() {
+ JavaFileObject source = JavaFileObjects.forSourceString(
+ "foo.bar.AnnotationsOnWrongTypeKind",
+ LINES.join(
+ "package foo.bar;",
+ "",
+ "import dagger.Module;",
+ "",
+ "@Module", // Error: Doesn't have InstallIn annotation
+ "final class FooModule {}"));
+
+ Compilation compilation =
+ CompilerTests.compiler().withProcessors(new AggregatedDepsProcessor()).compile(source);
+
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("foo.bar.FooModule is missing an @InstallIn annotation")
+ .inFile(source)
+ .onLine(6);
+ }
+
+ @Test
+ public void testNoErrorOnDaggerGeneratedModules() {
+ JavaFileObject source =
+ JavaFileObjects.forSourceString(
+ "foo.bar",
+ LINES.join(
+ "package foo.bar;",
+ "",
+ GeneratedImport.IMPORT_GENERATED_ANNOTATION,
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "@Generated(value = \"something\")", // Error: Isn't Dagger-generated but missing
+ // InstallIn
+ "final class FooModule {}",
+ "",
+ "@Module",
+ "@Generated(value = \"dagger\")", // No error because the module is dagger generated
+ "final class BarModule {}"));
+
+ Compilation compilation =
+ CompilerTests.compiler().withProcessors(new AggregatedDepsProcessor()).compile(source);
+
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining("foo.bar.FooModule is missing an @InstallIn annotation")
+ .inFile(source)
+ .onLine(8);
+ }
+
+ @Test
+ public void testModuleWithOnlyParamConstructor_fails() {
+ JavaFileObject source = JavaFileObjects.forSourceString("foo.bar", LINES.join(
+ "package foo.bar;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.hilt.InstallIn;",
+ "import dagger.hilt.components.SingletonComponent;",
+ "",
+ "@Module",
+ "@InstallIn(SingletonComponent.class)",
+ "final class FooModule {",
+ " FooModule(String arg) {}",
+ "",
+ " @Provides",
+ " String provideString() {",
+ " return \"\";",
+ " }",
+ "}"));
+
+ Compilation compilation =
+ CompilerTests.compiler().withProcessors(new AggregatedDepsProcessor()).compile(source);
+
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "Modules that need to be instantiated by Hilt must have a visible, empty constructor.");
+ }
+
+ @Test
+ public void testInnerModule() {
+ JavaFileObject source = JavaFileObjects.forSourceString("foo.bar", LINES.join(
+ "package foo.bar;",
+ "",
+ "import dagger.Module;",
+ "import dagger.hilt.InstallIn;",
+ "import dagger.hilt.components.SingletonComponent;",
+ "",
+ "final class Outer {",
+ " @Module",
+ " @InstallIn(SingletonComponent.class)",
+ " final class InnerModule {}",
+ "}"));
+
+ Compilation compilation =
+ CompilerTests.compiler().withProcessors(new AggregatedDepsProcessor()).compile(source);
+
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "Nested @InstallIn modules must be static unless they are directly nested within a "
+ + "test. Found: foo.bar.Outer.InnerModule");
+ }
+
+ @Test
+ public void testInnerModuleInTest() {
+ JavaFileObject source = JavaFileObjects.forSourceString("foo.bar", LINES.join(
+ "package foo.bar;",
+ "",
+ "import dagger.Module;",
+ "import dagger.hilt.InstallIn;",
+ "import dagger.hilt.components.SingletonComponent;",
+ "import dagger.hilt.android.testing.HiltAndroidTest;",
+ "",
+ "@HiltAndroidTest",
+ "final class Outer {",
+ " static class Nested {",
+ " @Module",
+ " @InstallIn(SingletonComponent.class)",
+ " final class InnerModule {}",
+ " }",
+ "}"));
+
+ Compilation compilation =
+ CompilerTests.compiler().withProcessors(new AggregatedDepsProcessor()).compile(source);
+
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "Nested @InstallIn modules must be static unless they are directly nested within a "
+ + "test. Found: foo.bar.Outer.Nested.InnerModule");
+ }
+
+ @Test
+ public void testInnerModuleInTest_succeeds() {
+ JavaFileObject source = JavaFileObjects.forSourceString("foo.bar", LINES.join(
+ "package foo.bar;",
+ "",
+ "import dagger.Module;",
+ "import dagger.hilt.InstallIn;",
+ "import dagger.hilt.components.SingletonComponent;",
+ "import dagger.hilt.android.testing.HiltAndroidTest;",
+ "",
+ "@HiltAndroidTest",
+ "final class Outer {",
+ " @Module",
+ " @InstallIn(SingletonComponent.class)",
+ " final class InnerModule {}",
+ "}"));
+
+ Compilation compilation =
+ CompilerTests.compiler().withProcessors(new AggregatedDepsProcessor()).compile(source);
+
+ assertThat(compilation).succeeded();
+ }
+}
diff --git a/javatests/dagger/hilt/processor/internal/aggregateddeps/BUILD b/javatests/dagger/hilt/processor/internal/aggregateddeps/BUILD
new file mode 100644
index 000000000..f52b051d7
--- /dev/null
+++ b/javatests/dagger/hilt/processor/internal/aggregateddeps/BUILD
@@ -0,0 +1,49 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Description:
+# Builds and run tests related to AggregatedDepsProcessor.
+
+load("//java/dagger/testing/compile:macros.bzl", "compiler_test")
+
+package(default_visibility = ["//:src"])
+
+compiler_test(
+ name = "AggregatedDepsProcessorErrorsTest",
+ size = "small",
+ srcs = ["AggregatedDepsProcessorErrorsTest.java"],
+ compiler_deps = [
+ "//java/dagger/hilt/internal:component_entry_point",
+ "//java/dagger/hilt/internal:generated_entry_point",
+ "//:dagger_with_compiler",
+ "@google_bazel_common//third_party/java/jsr250_annotations",
+ "//java/dagger/hilt:entry_point",
+ "//java/dagger/hilt:install_in",
+ "//java/dagger/hilt/android/testing:hilt_android_test",
+ "//java/dagger/hilt/android/components",
+ ],
+ deps = [
+ "//java/dagger/hilt/processor/internal/aggregateddeps:processor_lib",
+ "//java/dagger/internal/guava:base",
+ "//javatests/dagger/hilt/processor/internal:generated_import",
+ "@google_bazel_common//third_party/java/compile_testing",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/javatests/dagger/hilt/processor/internal/definecomponent/BUILD b/javatests/dagger/hilt/processor/internal/definecomponent/BUILD
new file mode 100644
index 000000000..ce5cc0aef
--- /dev/null
+++ b/javatests/dagger/hilt/processor/internal/definecomponent/BUILD
@@ -0,0 +1,46 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Description:
+# Tests for Hilt's DefineComponentProcessor
+
+load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
+load("//:test_defs.bzl", "GenJavaTests")
+
+package(default_visibility = ["//:src"])
+
+GenJavaTests(
+ name = "hilt_processor_tests",
+ srcs = glob(["*.java"]),
+ functional = False,
+ javacopts = DOCLINT_HTML_AND_SYNTAX,
+ deps = [
+ "//:dagger_with_compiler",
+ "//java/dagger/hilt:entry_point",
+ "//java/dagger/hilt:install_in",
+ "//java/dagger/hilt/android/components",
+ "//java/dagger/hilt/android/qualifiers",
+ "//java/dagger/hilt/processor/internal/definecomponent:define_components",
+ "//java/dagger/hilt/processor/internal/definecomponent:processor_lib",
+ "//javatests/dagger/hilt/processor/internal:generated_import",
+ "@google_bazel_common//third_party/java/compile_testing",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/javatests/dagger/hilt/processor/internal/definecomponent/DefineComponentProcessorTest.java b/javatests/dagger/hilt/processor/internal/definecomponent/DefineComponentProcessorTest.java
new file mode 100644
index 000000000..901c7a3b0
--- /dev/null
+++ b/javatests/dagger/hilt/processor/internal/definecomponent/DefineComponentProcessorTest.java
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.definecomponent;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.Compiler.javac;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.Compiler;
+import com.google.testing.compile.JavaFileObjects;
+import dagger.hilt.processor.internal.GeneratedImport;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class DefineComponentProcessorTest {
+
+ @Test
+ public void testDefineComponentOutput() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.FooComponent",
+ "package test;",
+ "",
+ "import dagger.hilt.components.SingletonComponent;",
+ "import dagger.hilt.DefineComponent;",
+ "",
+ "@DefineComponent(parent = SingletonComponent.class)",
+ "interface FooComponent {",
+ " static int staticField = 1;",
+ " static int staticMethod() { return staticField; }",
+ "}");
+
+ JavaFileObject builder =
+ JavaFileObjects.forSourceLines(
+ "test.FooComponentBuilder",
+ "package test;",
+ "",
+ "import dagger.hilt.DefineComponent;",
+ "",
+ "@DefineComponent.Builder",
+ "interface FooComponentBuilder {",
+ " static int staticField = 1;",
+ " static int staticMethod() { return staticField; }",
+ "",
+ " FooComponent create();",
+ "}");
+
+ JavaFileObject componentOutput =
+ JavaFileObjects.forSourceLines(
+ "dagger.hilt.processor.internal.definecomponent.codegen.test_FooComponent",
+ "package dagger.hilt.processor.internal.definecomponent.codegen;",
+ "",
+ "import dagger.hilt.internal.definecomponent.DefineComponentClasses;",
+ GeneratedImport.IMPORT_GENERATED_ANNOTATION,
+ "",
+ "@DefineComponentClasses(component = \"test.FooComponent\")",
+ "@Generated(\"" + DefineComponentProcessor.class.getName() + "\")",
+ "interface test_FooComponent {}");
+
+ JavaFileObject builderOutput =
+ JavaFileObjects.forSourceLines(
+ "dagger.hilt.processor.internal.definecomponent.codegen.test_FooComponentBuilder",
+ "package dagger.hilt.processor.internal.definecomponent.codegen;",
+ "",
+ "import dagger.hilt.internal.definecomponent.DefineComponentClasses;",
+ GeneratedImport.IMPORT_GENERATED_ANNOTATION,
+ "",
+ "@DefineComponentClasses(builder = \"test.FooComponentBuilder\")",
+ "@Generated(\"" + DefineComponentProcessor.class.getName() + "\")",
+ "interface test_FooComponentBuilder {}");
+
+ Compilation compilation = compiler().compile(component, builder);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile(sourceName(componentOutput))
+ .hasSourceEquivalentTo(componentOutput);
+ assertThat(compilation)
+ .generatedSourceFile(sourceName(builderOutput))
+ .hasSourceEquivalentTo(builderOutput);
+ }
+
+ private static String sourceName(JavaFileObject fileObject) {
+ return fileObject.getName().replace(".java", "").replace('.', '/');
+ }
+
+ @Test
+ public void testDefineComponentClass_fails() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.FooComponent",
+ "package test;",
+ "",
+ "import dagger.hilt.components.SingletonComponent;",
+ "import dagger.hilt.DefineComponent;",
+ "",
+ "@DefineComponent( parent = SingletonComponent.class )",
+ "abstract class FooComponent {}");
+
+ Compilation compilation = compiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@DefineComponent is only allowed on interfaces. Found: test.FooComponent");
+ }
+
+ @Test
+ public void testDefineComponentWithTypeParameters_fails() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.FooComponent",
+ "package test;",
+ "",
+ "import dagger.hilt.components.SingletonComponent;",
+ "import dagger.hilt.DefineComponent;",
+ "",
+ "@DefineComponent( parent = SingletonComponent.class )",
+ "interface FooComponent<T> {}");
+
+ Compilation compilation = compiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining("@DefineComponent test.FooComponent<T>, cannot have type parameters.");
+ }
+
+ @Test
+ public void testDefineComponentWithInvalidComponent_fails() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.FooComponent",
+ "package test;",
+ "",
+ "import dagger.hilt.DefineComponent;",
+ "import dagger.hilt.android.qualifiers.ApplicationContext;",
+ "",
+ "@DefineComponent( parent = ApplicationContext.class )",
+ "interface FooComponent {}");
+
+ Compilation compilation = compiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@DefineComponent test.FooComponent, references a type not annotated with "
+ + "@DefineComponent: dagger.hilt.android.qualifiers.ApplicationContext")
+ .inFile(component);
+ }
+
+ @Test
+ public void testDefineComponentExtendsInterface_fails() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.FooComponent",
+ "package test;",
+ "",
+ "import dagger.hilt.components.SingletonComponent;",
+ "import dagger.hilt.DefineComponent;",
+ "",
+ "interface Foo {}",
+ "",
+ "@DefineComponent( parent = SingletonComponent.class )",
+ "interface FooComponent extends Foo {}");
+
+ Compilation compilation = compiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@DefineComponent test.FooComponent, cannot extend a super class or interface."
+ + " Found: test.Foo");
+ }
+
+ @Test
+ public void testDefineComponentNonStaticMethod_fails() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.FooComponent",
+ "package test;",
+ "",
+ "import dagger.hilt.components.SingletonComponent;",
+ "import dagger.hilt.DefineComponent;",
+ "",
+ "@DefineComponent( parent = SingletonComponent.class )",
+ "interface FooComponent {",
+ " int nonStaticMethod();",
+ "}");
+
+ Compilation compilation = compiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@DefineComponent test.FooComponent, cannot have non-static methods. "
+ + "Found: [nonStaticMethod()]");
+ }
+
+ @Test
+ public void testDefineComponentDependencyCycle_fails() {
+ JavaFileObject component1 =
+ JavaFileObjects.forSourceLines(
+ "test.Component1",
+ "package test;",
+ "",
+ "import dagger.hilt.DefineComponent;",
+ "",
+ "@DefineComponent(parent = Component2.class)",
+ "interface Component1 {}");
+
+ JavaFileObject component2 =
+ JavaFileObjects.forSourceLines(
+ "test.Component2",
+ "package test;",
+ "",
+ "import dagger.hilt.DefineComponent;",
+ "",
+ "@DefineComponent(parent = Component1.class)",
+ "interface Component2 {}");
+
+ Compilation compilation = compiler().compile(component1, component2);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(2);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@DefineComponent cycle: test.Component1 -> test.Component2 -> test.Component1");
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@DefineComponent cycle: test.Component2 -> test.Component1 -> test.Component2");
+ }
+
+ @Test
+ public void testDefineComponentNoParent_fails() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.FooComponent",
+ "package test;",
+ "",
+ "import dagger.hilt.DefineComponent;",
+ "",
+ "@DefineComponent",
+ "interface FooComponent {}");
+ Compilation compilation = compiler().compile(component);
+ assertThat(compilation)
+ .hadErrorContaining("@DefineComponent test.FooComponent is missing a parent declaration.");
+ }
+
+ @Test
+ public void testDefineComponentBuilderClass_fails() {
+ JavaFileObject builder =
+ JavaFileObjects.forSourceLines(
+ "test.FooComponentBuilder",
+ "package test;",
+ "",
+ "import dagger.hilt.DefineComponent;",
+ "",
+ "@DefineComponent.Builder",
+ "abstract class FooComponentBuilder {}");
+
+ Compilation compilation = compiler().compile(builder);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@DefineComponent.Builder is only allowed on interfaces. "
+ + "Found: test.FooComponentBuilder");
+ }
+
+ @Test
+ public void testDefineComponentBuilderWithTypeParameters_fails() {
+ JavaFileObject builder =
+ JavaFileObjects.forSourceLines(
+ "test.FooComponentBuilder",
+ "package test;",
+ "",
+ "import dagger.hilt.DefineComponent;",
+ "",
+ "@DefineComponent.Builder",
+ "interface FooComponentBuilder<T> {}");
+
+ Compilation compilation = compiler().compile(builder);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@DefineComponent.Builder test.FooComponentBuilder<T>, cannot have type "
+ + "parameters.");
+ }
+
+ @Test
+ public void testDefineComponentBuilderExtendsInterface_fails() {
+ JavaFileObject builder =
+ JavaFileObjects.forSourceLines(
+ "test.FooComponentBuilder",
+ "package test;",
+ "",
+ "import dagger.hilt.DefineComponent;",
+ "",
+ "interface Foo {}",
+ "",
+ "@DefineComponent.Builder",
+ "interface FooComponentBuilder extends Foo {}");
+
+ Compilation compilation = compiler().compile(builder);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@DefineComponent.Builder test.FooComponentBuilder, cannot extend a super class "
+ + "or interface. Found: test.Foo");
+ }
+
+ @Test
+ public void testDefineComponentBuilderNoBuilderMethod_fails() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.FooComponent",
+ "package test;",
+ "",
+ "import dagger.hilt.DefineComponent;",
+ "",
+ "@DefineComponent.Builder",
+ "interface FooComponentBuilder {}");
+
+ Compilation compilation = compiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@DefineComponent.Builder test.FooComponentBuilder, must have exactly 1 build "
+ + "method that takes no parameters. Found: []");
+ }
+
+ @Test
+ public void testDefineComponentBuilderPrimitiveReturnType_fails() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.FooComponent",
+ "package test;",
+ "",
+ "import dagger.hilt.DefineComponent;",
+ "",
+ "@DefineComponent.Builder",
+ "interface FooComponentBuilder {",
+ " int nonStaticMethod();",
+ "}");
+
+ Compilation compilation = compiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@DefineComponent.Builder method, test.FooComponentBuilder#nonStaticMethod(), "
+ + "must return a @DefineComponent type. Found: int");
+ }
+
+ @Test
+ public void testDefineComponentBuilderWrongReturnType_fails() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.FooComponent",
+ "package test;",
+ "",
+ "import dagger.hilt.DefineComponent;",
+ "",
+ "interface Foo {}",
+ "",
+ "@DefineComponent.Builder",
+ "interface FooComponentBuilder {",
+ " Foo build();",
+ "}");
+
+ Compilation compilation = compiler().compile(component);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@DefineComponent.Builder method, test.FooComponentBuilder#build(), must return "
+ + "a @DefineComponent type. Found: test.Foo");
+ }
+
+ private static Compiler compiler() {
+ return javac().withProcessors(new DefineComponentProcessor());
+ }
+}
diff --git a/javatests/dagger/hilt/processor/internal/disableinstallincheck/BUILD b/javatests/dagger/hilt/processor/internal/disableinstallincheck/BUILD
new file mode 100644
index 000000000..90b0596b6
--- /dev/null
+++ b/javatests/dagger/hilt/processor/internal/disableinstallincheck/BUILD
@@ -0,0 +1,43 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Description:
+# Builds and run tests related to DisableInstallInCheckProcessor.
+
+load("//java/dagger/testing/compile:macros.bzl", "compiler_test")
+
+package(default_visibility = ["//:src"])
+
+compiler_test(
+ name = "DisableInstallInCheckProcessorErrorsTest",
+ size = "small",
+ srcs = ["DisableInstallInCheckProcessorErrorsTest.java"],
+ compiler_deps = [
+ "//java/dagger/hilt/migration:disable_install_in_check",
+ "//:dagger_with_compiler",
+ "@google_bazel_common//third_party/java/jsr250_annotations",
+ "//java/dagger/hilt:entry_point",
+ ],
+ deps = [
+ "//java/dagger/hilt/processor/internal/disableinstallincheck:processor_lib",
+ "@google_bazel_common//third_party/java/compile_testing",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["*"]),
+)
diff --git a/javatests/dagger/hilt/processor/internal/disableinstallincheck/DisableInstallInCheckProcessorErrorsTest.java b/javatests/dagger/hilt/processor/internal/disableinstallincheck/DisableInstallInCheckProcessorErrorsTest.java
new file mode 100644
index 000000000..8ea715d64
--- /dev/null
+++ b/javatests/dagger/hilt/processor/internal/disableinstallincheck/DisableInstallInCheckProcessorErrorsTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.disableinstallincheck;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import dagger.testing.compile.CompilerTests;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for errors generated by {@link DisableInstallInCheckProcessor} */
+@RunWith(JUnit4.class)
+public class DisableInstallInCheckProcessorErrorsTest {
+
+ @Test
+ public void testIllegalCombinationInstallIn() {
+ JavaFileObject source =
+ JavaFileObjects.forSourceLines(
+ "foo.bar",
+ "package foo.bar;",
+ "",
+ "import dagger.hilt.migration.DisableInstallInCheck;",
+ "import dagger.hilt.EntryPoint;",
+ "",
+ "@DisableInstallInCheck",
+ "final class NotModule {}",
+ "",
+ "@DisableInstallInCheck",
+ "@EntryPoint",
+ "interface FooEntryPoint {}");
+
+ Compilation compilation =
+ CompilerTests.compiler()
+ .withProcessors(new DisableInstallInCheckProcessor())
+ .compile(source);
+
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@DisableInstallInCheck should only be used on modules. However, it was found"
+ + " annotating foo.bar.NotModule")
+ .inFile(source)
+ .onLine(7);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@DisableInstallInCheck should only be used on modules. However, it was found"
+ + " annotating foo.bar.FooEntryPoint")
+ .inFile(source)
+ .onLine(11);
+ }
+}
diff --git a/javatests/dagger/hilt/processor/internal/generatesrootinput/BUILD b/javatests/dagger/hilt/processor/internal/generatesrootinput/BUILD
new file mode 100644
index 000000000..c5dacd9f8
--- /dev/null
+++ b/javatests/dagger/hilt/processor/internal/generatesrootinput/BUILD
@@ -0,0 +1,45 @@
+# Copyright (C) 2017 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Description:
+# Tests the functionality of GeneratesRootInputProcessor.
+
+load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
+load("//:test_defs.bzl", "GenJavaTests")
+
+package(default_visibility = ["//:src"])
+
+GenJavaTests(
+ name = "GeneratesRootInputProcessorTest",
+ srcs = glob(["*.java"]),
+ functional = False,
+ javacopts = DOCLINT_HTML_AND_SYNTAX,
+ deps = [
+ "//:dagger_with_compiler",
+ "//java/dagger/hilt:generates_root_input",
+ "//java/dagger/hilt/processor/internal:base_processor",
+ "//java/dagger/hilt/processor/internal/generatesrootinput:generates_root_inputs",
+ "//java/dagger/hilt/processor/internal/generatesrootinput:processor_lib",
+ "@google_bazel_common//third_party/java/compile_testing",
+ "@google_bazel_common//third_party/java/javapoet",
+ "@google_bazel_common//third_party/java/junit",
+ "@google_bazel_common//third_party/java/truth",
+ "@maven//:com_google_auto_auto_common",
+ ],
+)
+
+filegroup(
+ name = "srcs_filegroup",
+ srcs = glob(["**/*"]),
+)
diff --git a/javatests/dagger/hilt/processor/internal/generatesrootinput/GeneratesRootInputProcessorTest.java b/javatests/dagger/hilt/processor/internal/generatesrootinput/GeneratesRootInputProcessorTest.java
new file mode 100644
index 000000000..fb62c3d12
--- /dev/null
+++ b/javatests/dagger/hilt/processor/internal/generatesrootinput/GeneratesRootInputProcessorTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.hilt.processor.internal.generatesrootinput;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.Compiler.javac;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.truth.Correspondence;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.TypeSpec;
+import dagger.hilt.processor.internal.BaseProcessor;
+import java.util.ArrayList;
+import java.util.List;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.element.Element;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests that {@link GeneratesRootInputs} returns the elements to wait for. */
+@RunWith(JUnit4.class)
+public final class GeneratesRootInputProcessorTest {
+ private static final int GENERATED_CLASSES = 5;
+ private static final ClassName TEST_ANNOTATION = ClassName.get("test", "TestAnnotation");
+
+ private final List<Element> elementsToWaitFor = new ArrayList<>();
+ private int generatedClasses = 0;
+
+ @SupportedAnnotationTypes("*")
+ public final class TestAnnotationProcessor extends BaseProcessor {
+ private GeneratesRootInputs generatesRootInputs;
+
+ @Override
+ public synchronized void init(ProcessingEnvironment processingEnv) {
+ super.init(processingEnv);
+ generatesRootInputs = new GeneratesRootInputs(processingEnv);
+ }
+
+ @Override
+ protected void postRoundProcess(RoundEnvironment roundEnv) throws Exception {
+ if (generatedClasses > 0) {
+ elementsToWaitFor.addAll(generatesRootInputs.getElementsToWaitFor(roundEnv));
+ }
+ if (generatedClasses < GENERATED_CLASSES) {
+ TypeSpec typeSpec =
+ TypeSpec.classBuilder("Foo" + generatedClasses++)
+ .addAnnotation(TEST_ANNOTATION)
+ .build();
+ JavaFile.builder("foo", typeSpec).build().writeTo(processingEnv.getFiler());
+ }
+ }
+ }
+
+ @Test
+ public void succeeds_ComponentProcessorWaitsForAnnotationsWithgeneratesstinginput() {
+ JavaFileObject testAnnotation =
+ JavaFileObjects.forSourceLines(
+ "test.TestAnnotation",
+ "package test;",
+ "@dagger.hilt.GeneratesRootInput",
+ "public @interface TestAnnotation {}");
+
+ Compilation compilation =
+ javac()
+ .withProcessors(new TestAnnotationProcessor(), new GeneratesRootInputProcessor())
+ .compile(testAnnotation);
+
+ assertThat(compilation).succeeded();
+ assertThat(elementsToWaitFor)
+ .comparingElementsUsing(
+ Correspondence.<Element, String>transforming(
+ element -> MoreElements.asType(element).getQualifiedName().toString(),
+ "has qualified name of"))
+ .containsExactly("foo.Foo0", "foo.Foo1", "foo.Foo2", "foo.Foo3", "foo.Foo4")
+ .inOrder();
+ }
+}
diff --git a/javatests/dagger/internal/MapProviderFactoryTest.java b/javatests/dagger/internal/MapProviderFactoryTest.java
index 5598ff223..c55bee3ab 100644
--- a/javatests/dagger/internal/MapProviderFactoryTest.java
+++ b/javatests/dagger/internal/MapProviderFactoryTest.java
@@ -46,6 +46,7 @@ public class MapProviderFactoryTest {
MapProviderFactory.<String, Integer>builder(1).put("Hello", null);
}
+
@Test
public void iterationOrder() {
Provider<Integer> p1 = incrementingIntegerProvider(10);
@@ -74,6 +75,7 @@ public class MapProviderFactoryTest {
.inOrder();
}
+
private static Provider<Integer> incrementingIntegerProvider(int seed) {
return new AtomicInteger(seed)::getAndIncrement;
}
diff --git a/javatests/dagger/internal/codegen/AheadOfTimeSubcomponentsMultibindingsTest.java b/javatests/dagger/internal/codegen/AheadOfTimeSubcomponentsMultibindingsTest.java
deleted file mode 100644
index bb967b177..000000000
--- a/javatests/dagger/internal/codegen/AheadOfTimeSubcomponentsMultibindingsTest.java
+++ /dev/null
@@ -1,2838 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.testing.compile.CompilationSubject.assertThat;
-import static dagger.internal.codegen.CompilerMode.AHEAD_OF_TIME_SUBCOMPONENTS_MODE;
-import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
-import static dagger.internal.codegen.Compilers.CLASS_PATH_WITHOUT_GUAVA_OPTION;
-import static dagger.internal.codegen.Compilers.compilerWithOptions;
-import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
-import static dagger.internal.codegen.GeneratedLines.GENERATION_OPTIONS_ANNOTATION;
-import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
-
-import com.google.common.collect.ImmutableList;
-import com.google.testing.compile.Compilation;
-import com.google.testing.compile.JavaFileObjects;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public final class AheadOfTimeSubcomponentsMultibindingsTest {
- @Test
- public void setMultibindings_contributionsInLeaf() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "InLeaf");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Set;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " Set<InLeaf> contributionsInLeaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoSet;",
- "",
- "@Module",
- "class LeafModule {",
- " @Provides",
- " @IntoSet",
- " static InLeaf provideInLeaf() {",
- " return new InLeaf();",
- " }",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableSet;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public Set<InLeaf> contributionsInLeaf() {",
- " return ImmutableSet.<InLeaf>of(",
- " LeafModule_ProvideInLeafFactory.provideInLeaf());",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
- }
-
- @Test
- public void setMultibindings_contributionsInAncestorOnly() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "InAncestor");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Set;",
- "",
- "@Subcomponent",
- "interface Leaf {",
- " Set<InAncestor> contributionsInAncestor();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public abstract Set<InAncestor> contributionsInAncestor();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Set;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableSet;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.ElementsIntoSet;",
- "import java.util.Set;",
- "",
- "@Module",
- "class AncestorModule {",
- " @Provides",
- " @ElementsIntoSet",
- " static Set<InAncestor> provideInAncestors() {",
- " return ImmutableSet.of(new InAncestor(), new InAncestor());",
- " }",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableSet;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " public Set<InAncestor> contributionsInAncestor() {",
- " return ImmutableSet.<InAncestor>copyOf(",
- " AncestorModule_ProvideInAncestorsFactory.provideInAncestors());",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void setMultibindings_contributionsInLeafAndAncestor() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "InEachSubcomponent");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Set;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " Set<InEachSubcomponent> contributionsInEachSubcomponent();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoSet;",
- "",
- "@Module",
- "class LeafModule {",
- " @Provides",
- " @IntoSet",
- " static InEachSubcomponent provideInLeaf() {",
- " return new InEachSubcomponent();",
- " }",
- "",
- " @Provides",
- " @IntoSet",
- " static InEachSubcomponent provideAnotherInLeaf() {",
- " return new InEachSubcomponent();",
- " }",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableSet;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public Set<InEachSubcomponent> contributionsInEachSubcomponent() {",
- " return ImmutableSet.<InEachSubcomponent>of(",
- " LeafModule_ProvideInLeafFactory.provideInLeaf(),",
- " LeafModule_ProvideAnotherInLeafFactory.provideAnotherInLeaf());",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Set;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableSet;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.ElementsIntoSet;",
- "import java.util.Set;",
- "",
- "@Module",
- "class AncestorModule {",
- " @Provides",
- " @ElementsIntoSet",
- " static Set<InEachSubcomponent> provideInAncestor() {",
- " return ImmutableSet.of(new InEachSubcomponent(), new InEachSubcomponent());",
- " }",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableSet;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " public Set<InEachSubcomponent> contributionsInEachSubcomponent() {",
- " return ImmutableSet.<InEachSubcomponent>builderWithExpectedSize(3)",
- " .addAll(AncestorModule_ProvideInAncestorFactory.provideInAncestor())",
- " .addAll(super.contributionsInEachSubcomponent())",
- " .build();",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void setMultibindings_contributionsInLeafAndGrandAncestor() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "InLeafAndGrandAncestor");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Set;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " Set<InLeafAndGrandAncestor> contributionsInLeafAndGrandAncestor();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoSet;",
- "",
- "@Module",
- "class LeafModule {",
- " @Provides",
- " @IntoSet",
- " static InLeafAndGrandAncestor provideInLeaf() {",
- " return new InLeafAndGrandAncestor();",
- " }",
- "",
- " @Provides",
- " @IntoSet",
- " static InLeafAndGrandAncestor provideAnotherInLeaf() {",
- " return new InLeafAndGrandAncestor();",
- " }",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableSet;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public Set<InLeafAndGrandAncestor> contributionsInLeafAndGrandAncestor() {",
- " return ImmutableSet.<InLeafAndGrandAncestor>of(",
- " LeafModule_ProvideInLeafFactory.provideInLeaf(),",
- " LeafModule_ProvideAnotherInLeafFactory.provideAnotherInLeaf());",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Set;",
- "",
- "@Subcomponent",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.GrandAncestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Set;",
- "",
- "@Subcomponent(modules = GrandAncestorModule.class)",
- "interface GrandAncestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.GrandAncestorModule",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableSet;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.ElementsIntoSet;",
- "import java.util.Set;",
- "",
- "@Module",
- "class GrandAncestorModule {",
- " @Provides",
- " @ElementsIntoSet",
- " static Set<InLeafAndGrandAncestor> provideInGrandAncestor() {",
- " return ImmutableSet.of(new InLeafAndGrandAncestor(),",
- " new InLeafAndGrandAncestor());",
- " }",
- "}"));
- JavaFileObject generatedGrandAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerGrandAncestor",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableSet;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerGrandAncestor implements GrandAncestor {",
- " protected DaggerGrandAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " public Set<InLeafAndGrandAncestor> contributionsInLeafAndGrandAncestor() {",
- " return ImmutableSet.<InLeafAndGrandAncestor>builderWithExpectedSize(3)",
- " .addAll(GrandAncestorModule_ProvideInGrandAncestorFactory",
- " .provideInGrandAncestor())",
- " .addAll(super.contributionsInLeafAndGrandAncestor())",
- " .build();",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerGrandAncestor")
- .hasSourceEquivalentTo(generatedGrandAncestor);
- }
-
- @Test
- public void setMultibindings_nonComponentMethodDependency() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(
- filesToCompile, "InAllSubcomponents", "RequresInAllSubcomponentsSet");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Set;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " RequresInAllSubcomponentsSet requiresNonComponentMethod();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoSet;",
- "import java.util.Set;",
- "",
- "@Module",
- "class LeafModule {",
- " @Provides",
- " @IntoSet",
- " static InAllSubcomponents provideInAllSubcomponents() {",
- " return new InAllSubcomponents();",
- " }",
- "",
- " @Provides",
- " static RequresInAllSubcomponentsSet providesRequresInAllSubcomponentsSet(",
- " Set<InAllSubcomponents> inAllSubcomponents) {",
- " return new RequresInAllSubcomponentsSet();",
- " }",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableSet;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public RequresInAllSubcomponentsSet requiresNonComponentMethod() {",
- " return LeafModule_ProvidesRequresInAllSubcomponentsSetFactory",
- " .providesRequresInAllSubcomponentsSet(getSetOfInAllSubcomponents());",
- " }",
- "",
- " protected Set getSetOfInAllSubcomponents() {",
- " return ImmutableSet.<InAllSubcomponents>of(",
- " LeafModule_ProvideInAllSubcomponentsFactory",
- " .provideInAllSubcomponents());",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoSet;",
- "",
- "@Module",
- "class AncestorModule {",
- " @Provides",
- " @IntoSet",
- " static InAllSubcomponents provideInAllSubcomponents() {",
- " return new InAllSubcomponents();",
- " }",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableSet;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " protected Set getSetOfInAllSubcomponents() {",
- " return ImmutableSet.<InAllSubcomponents>builderWithExpectedSize(2)",
- " .add(AncestorModule_ProvideInAllSubcomponentsFactory",
- " .provideInAllSubcomponents())",
- " .addAll(super.getSetOfInAllSubcomponents())",
- " .build();",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void setMultibindings_newSubclass() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "InAncestor", "RequiresInAncestorSet");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface Leaf {",
- " RequiresInAncestorSet missingWithSetDependency();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public abstract RequiresInAncestorSet missingWithSetDependency();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoSet;",
- "import java.util.Set;",
- "",
- "@Module",
- "class AncestorModule {",
- "",
- " @Provides",
- " static RequiresInAncestorSet provideRequiresInAncestorSet(",
- " Set<InAncestor> inAncestors) {",
- " return new RequiresInAncestorSet();",
- " }",
- "",
- " @Provides",
- " @IntoSet",
- " static InAncestor provideInAncestor() {",
- " return new InAncestor();",
- " }",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableSet;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " private RequiresInAncestorSet getRequiresInAncestorSet() {",
- " return AncestorModule_ProvideRequiresInAncestorSetFactory",
- " .provideRequiresInAncestorSet(getSetOfInAncestor());",
- " }",
- "",
- " protected Set getSetOfInAncestor() {",
- " return ImmutableSet.<InAncestor>of(",
- " AncestorModule_ProvideInAncestorFactory.provideInAncestor());",
- " }",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " public final RequiresInAncestorSet missingWithSetDependency() {",
- " return DaggerAncestor.this.getRequiresInAncestorSet();",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void setMultibinding_requestedAsInstanceInLeaf_requestedAsFrameworkInstanceFromAncestor() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(
- filesToCompile, "Multibound", "MissingInLeaf_WillDependOnFrameworkInstance");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Set;",
- "import javax.inject.Provider;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " Set<Multibound> instance();",
- " MissingInLeaf_WillDependOnFrameworkInstance willDependOnFrameworkInstance();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoSet;",
- "import java.util.Set;",
- "",
- "@Module",
- "class LeafModule {",
- " @Provides",
- " @IntoSet",
- " static Multibound contribution() {",
- " return new Multibound();",
- " }",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableSet;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public Set<Multibound> instance() {",
- " return ImmutableSet.<Multibound>of(",
- " LeafModule_ContributionFactory.contribution());",
- " }",
- "",
- " @Override",
- " public abstract MissingInLeaf_WillDependOnFrameworkInstance",
- " willDependOnFrameworkInstance();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.Multibinds;",
- "import java.util.Set;",
- "import javax.inject.Provider;",
- "",
- "@Module",
- "interface AncestorModule {",
- " @Provides",
- " static MissingInLeaf_WillDependOnFrameworkInstance providedInAncestor(",
- " Provider<Set<Multibound>> frameworkInstance) {",
- " return null;",
- " }",
- "",
- " @Multibinds Set<Multibound> multibinds();",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import dagger.internal.SetFactory;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " private Provider<Set<Multibound>> setOfMultiboundProvider;",
- "",
- " protected LeafImpl() {}",
- "",
- " protected void configureInitialization() { ",
- " initialize();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize() { ",
- " this.setOfMultiboundProvider =",
- " SetFactory.<Multibound>builder(1, 0)",
- " .addProvider(LeafModule_ContributionFactory.create())",
- " .build();",
- " }",
- "",
- " protected Provider getSetOfMultiboundProvider() {",
- " return setOfMultiboundProvider;",
- " }",
- "",
- " @Override",
- " public final MissingInLeaf_WillDependOnFrameworkInstance ",
- " willDependOnFrameworkInstance() {",
- " return AncestorModule_ProvidedInAncestorFactory.providedInAncestor(",
- " getSetOfMultiboundProvider());",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void missingMultibindingInLeaf_onlyContributionsInAncestor_notReModifiedInRoot() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Set;",
- "import javax.inject.Provider;",
- "",
- "@Subcomponent",
- "interface Leaf {",
- " Set<Object> set();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public abstract Set<Object> set();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Set;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoSet;",
- "",
- "@Module",
- "class AncestorModule {",
- " @Provides",
- " @IntoSet",
- " static Object onlyContribution() {",
- " return new Object();",
- " }",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableSet;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " public Set<Object> set() {",
- " return ImmutableSet.<Object>of(",
- " AncestorModule_OnlyContributionFactory.onlyContribution());",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Root",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface Root {",
- " Ancestor ancestor();",
- "}"));
- JavaFileObject generatedRoot =
- JavaFileObjects.forSourceLines(
- "test.DaggerRoot",
- "package test;",
- "",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATED_ANNOTATION,
- "final class DaggerRoot implements Root {",
- " private DaggerRoot() {}",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static Root create() {",
- " return new Builder().build();",
- " }",
- "",
- " @Override",
- " public Ancestor ancestor() {",
- " return new AncestorImpl();",
- " }",
- "",
- " static final class Builder {",
- " private Builder() {}",
- "",
- " public Root build() {",
- " return new DaggerRoot();",
- " }",
- " }",
- "",
- " protected final class AncestorImpl extends DaggerAncestor {",
- " private AncestorImpl() {}",
- "",
- " @Override",
- " public Leaf leaf() {",
- " return new LeafImpl();",
- " }",
- "",
- " protected final class LeafImpl extends DaggerAncestor.LeafImpl {",
- " private LeafImpl() {}",
- // This tests a regression case where Dagger used to reimplement Leaf.set(), even though
- // there were no new contributions, because the state change from missing ->
- // multibinding wasn't properly recorded
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerRoot")
- .hasSourceEquivalentTo(generatedRoot);
- }
-
- @Test
- public void setMultibindings_contributionsInLeafAndAncestor_frameworkInstances() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "InEachSubcomponent");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Set;",
- "import javax.inject.Provider;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " Provider<Set<InEachSubcomponent>> contributionsInEachSubcomponent();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoSet;",
- "",
- "@Module",
- "class LeafModule {",
- " @Provides",
- " @IntoSet",
- " static InEachSubcomponent provideInLeaf() {",
- " return new InEachSubcomponent();",
- " }",
- "",
- " @Provides",
- " @IntoSet",
- " static InEachSubcomponent provideAnotherInLeaf() {",
- " return new InEachSubcomponent();",
- " }",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import dagger.internal.SetFactory;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " private Provider<Set<InEachSubcomponent>> setOfInEachSubcomponentProvider;",
- "",
- " protected DaggerLeaf() {}",
- "",
- " protected void configureInitialization() {",
- " initialize();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize() {",
- " this.setOfInEachSubcomponentProvider =",
- " SetFactory.<InEachSubcomponent>builder(2, 0)",
- " .addProvider(LeafModule_ProvideInLeafFactory.create())",
- " .addProvider(LeafModule_ProvideAnotherInLeafFactory.create())",
- " .build();",
- " }",
- "",
- " @Override",
- " public Provider<Set<InEachSubcomponent>> contributionsInEachSubcomponent() {",
- " return setOfInEachSubcomponentProvider;",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Set;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableSet;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.ElementsIntoSet;",
- "import java.util.Set;",
- "",
- "@Module",
- "class AncestorModule {",
- " @Provides",
- " @ElementsIntoSet",
- " static Set<InEachSubcomponent> provideInAncestor() {",
- " return ImmutableSet.of(new InEachSubcomponent(), new InEachSubcomponent());",
- " }",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import dagger.internal.DelegateFactory;",
- "import dagger.internal.GenerationOptions;",
- "import dagger.internal.SetFactory;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " private Provider<Set<InEachSubcomponent>> setOfInEachSubcomponentProvider = ",
- " new DelegateFactory<>();",
- "",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " protected void configureInitialization() {",
- " super.configureInitialization();",
- " initialize();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize() {",
- " DelegateFactory.setDelegate(",
- " setOfInEachSubcomponentProvider,",
- " SetFactory.<InEachSubcomponent>builder(0, 2)",
- " .addCollectionProvider(super.contributionsInEachSubcomponent())",
- " .addCollectionProvider(",
- " AncestorModule_ProvideInAncestorFactory.create())",
- " .build());",
- " }",
- "",
- " @Override",
- " public Provider<Set<InEachSubcomponent>> contributionsInEachSubcomponent() {",
- " return setOfInEachSubcomponentProvider;",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void mapMultibindings_contributionsInLeaf() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "InLeaf");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Map;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " Map<String, InLeaf> contributionsInLeaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoMap;",
- "import dagger.multibindings.StringKey;",
- "import java.util.Map;",
- "",
- "@Module",
- "class LeafModule {",
- " @Provides",
- " @IntoMap",
- " @StringKey(\"leafmodule\")",
- " static InLeaf provideInLeaf() {",
- " return new InLeaf();",
- " }",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableMap;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Map;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public Map<String, InLeaf> contributionsInLeaf() {",
- " return ImmutableMap.<String, InLeaf>of(",
- " \"leafmodule\",",
- " LeafModule_ProvideInLeafFactory.provideInLeaf());",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
- }
-
- @Test
- public void mapMultibindings_contributionsInAncestorOnly() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "InAncestor");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Map;",
- "",
- "@Subcomponent",
- "interface Leaf {",
- " Map<String, InAncestor> contributionsInAncestor();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Map;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public abstract Map<String, InAncestor> contributionsInAncestor();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoMap;",
- "import dagger.multibindings.StringKey;",
- "import java.util.Map;",
- "",
- "@Module",
- "class AncestorModule {",
- " @Provides",
- " @IntoMap",
- " @StringKey(\"ancestormodule\")",
- " static InAncestor provideInAncestor() {",
- " return new InAncestor();",
- " }",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableMap;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Map;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " public Map<String, InAncestor> contributionsInAncestor() {",
- " return ImmutableMap.<String, InAncestor>of(\"ancestormodule\",",
- " AncestorModule_ProvideInAncestorFactory.provideInAncestor());",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void mapMultibindings_contributionsInLeafAndAncestor() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "InEachSubcomponent");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Map;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " Map<String, InEachSubcomponent> contributionsInEachSubcomponent();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoMap;",
- "import dagger.multibindings.StringKey;",
- "import java.util.Map;",
- "",
- "@Module",
- "class LeafModule {",
- " @Provides",
- " @IntoMap",
- " @StringKey(\"leafmodule\")",
- " static InEachSubcomponent provideInLeaf() {",
- " return new InEachSubcomponent();",
- " }",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableMap;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Map;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public Map<String, InEachSubcomponent> contributionsInEachSubcomponent() {",
- " return ImmutableMap.<String, InEachSubcomponent>of(",
- " \"leafmodule\", LeafModule_ProvideInLeafFactory.provideInLeaf());",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoMap;",
- "import dagger.multibindings.StringKey;",
- "import java.util.Map;",
- "",
- "@Module",
- "class AncestorModule {",
- " @Provides",
- " @IntoMap",
- " @StringKey(\"ancestormodule\")",
- " static InEachSubcomponent provideInAncestor() {",
- " return new InEachSubcomponent();",
- " }",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableMap;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Map;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " public Map<String, InEachSubcomponent> contributionsInEachSubcomponent() {",
- " return ImmutableMap.<String, InEachSubcomponent>builderWithExpectedSize(2)",
- " .put(\"ancestormodule\",",
- " AncestorModule_ProvideInAncestorFactory.provideInAncestor())",
- " .putAll(super.contributionsInEachSubcomponent())",
- " .build();",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void mapMultibindings_contributionsInLeafAndAncestor_frameworkInstance() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "InEachSubcomponent");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Map;",
- "import javax.inject.Provider;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " Provider<Map<String, InEachSubcomponent>> contributionsInEachSubcomponent();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoMap;",
- "import dagger.multibindings.StringKey;",
- "import java.util.Map;",
- "",
- "@Module",
- "class LeafModule {",
- " @Provides",
- " @IntoMap",
- " @StringKey(\"leafmodule\")",
- " static InEachSubcomponent provideInLeaf() {",
- " return new InEachSubcomponent();",
- " }",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import dagger.internal.MapFactory;",
- "import java.util.Map;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " private Provider<Map<String, InEachSubcomponent>> ",
- " mapOfStringAndInEachSubcomponentProvider;",
- "",
- " protected DaggerLeaf() {}",
- "",
- " protected void configureInitialization() {",
- " initialize();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize() {",
- " this.mapOfStringAndInEachSubcomponentProvider =",
- " MapFactory.<String, InEachSubcomponent>builder(1)",
- " .put(\"leafmodule\", LeafModule_ProvideInLeafFactory.create())",
- " .build();",
- " }",
- "",
- " @Override",
- " public Provider<Map<String, InEachSubcomponent>> ",
- " contributionsInEachSubcomponent() {",
- " return mapOfStringAndInEachSubcomponentProvider;",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoMap;",
- "import dagger.multibindings.StringKey;",
- "import java.util.Map;",
- "",
- "@Module",
- "class AncestorModule {",
- " @Provides",
- " @IntoMap",
- " @StringKey(\"ancestormodule\")",
- " static InEachSubcomponent provideInAncestor() {",
- " return new InEachSubcomponent();",
- " }",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import dagger.internal.DelegateFactory;",
- "import dagger.internal.GenerationOptions;",
- "import dagger.internal.MapFactory;",
- "import java.util.Map;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " private Provider<Map<String, InEachSubcomponent>> ",
- " mapOfStringAndInEachSubcomponentProvider = new DelegateFactory<>();",
- "",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " protected void configureInitialization() { ",
- " super.configureInitialization();",
- " initialize();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize() { ",
- " DelegateFactory.setDelegate(",
- " mapOfStringAndInEachSubcomponentProvider,",
- " MapFactory.<String, InEachSubcomponent>builder(2)",
- " .putAll(super.contributionsInEachSubcomponent())",
- " .put(",
- " \"ancestormodule\",",
- " AncestorModule_ProvideInAncestorFactory.create())",
- " .build());",
- " }",
- "",
- " @Override",
- " public Provider<Map<String, InEachSubcomponent>> ",
- " contributionsInEachSubcomponent() {",
- " return mapOfStringAndInEachSubcomponentProvider;",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void mapMultibindings_contributionsInLeafAndGrandAncestor() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "InLeafAndGrandAncestor");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Map;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " Map<String, InLeafAndGrandAncestor> contributionsInLeafAndGrandAncestor();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoMap;",
- "import dagger.multibindings.StringKey;",
- "import java.util.Map;",
- "",
- "@Module",
- "class LeafModule {",
- " @Provides",
- " @IntoMap",
- " @StringKey(\"leafmodule\")",
- " static InLeafAndGrandAncestor provideInLeaf() {",
- " return new InLeafAndGrandAncestor();",
- " }",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableMap;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Map;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public Map<String, InLeafAndGrandAncestor> contributionsInLeafAndGrandAncestor() {",
- " return ImmutableMap.<String, InLeafAndGrandAncestor>of(",
- " \"leafmodule\", LeafModule_ProvideInLeafFactory.provideInLeaf());",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.GrandAncestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = GrandAncestorModule.class)",
- "interface GrandAncestor {",
- " Ancestor ancestor();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.GrandAncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoMap;",
- "import dagger.multibindings.StringKey;",
- "import java.util.Map;",
- "",
- "@Module",
- "class GrandAncestorModule {",
- " @Provides",
- " @IntoMap",
- " @StringKey(\"grandancestormodule\")",
- " static InLeafAndGrandAncestor provideInGrandAncestor() {",
- " return new InLeafAndGrandAncestor();",
- " }",
- "}"));
- JavaFileObject generatedGrandAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerGrandAncestor",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableMap;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Map;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerGrandAncestor implements GrandAncestor {",
- " protected DaggerGrandAncestor() {}",
- "",
- " protected abstract class AncestorImpl extends DaggerAncestor {",
- " protected AncestorImpl() {}",
- "",
- " protected abstract class LeafImpl extends DaggerAncestor.LeafImpl {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " public Map<String, InLeafAndGrandAncestor>",
- " contributionsInLeafAndGrandAncestor() {",
- " return",
- " ImmutableMap.<String, InLeafAndGrandAncestor>builderWithExpectedSize(2)",
- " .put(\"grandancestormodule\",",
- " GrandAncestorModule_ProvideInGrandAncestorFactory",
- " .provideInGrandAncestor())",
- " .putAll(super.contributionsInLeafAndGrandAncestor())",
- " .build();",
- " }",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerGrandAncestor")
- .hasSourceEquivalentTo(generatedGrandAncestor);
- }
-
- @Test
- public void mapMultibindings_contributionsInLeafAndAncestorWithoutGuava() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "InEachSubcomponent");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Map;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " Map<String, InEachSubcomponent> contributionsInEachSubcomponent();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoMap;",
- "import dagger.multibindings.StringKey;",
- "import java.util.Map;",
- "",
- "@Module",
- "class LeafModule {",
- " @Provides",
- " @IntoMap",
- " @StringKey(\"leafmodule\")",
- " static InEachSubcomponent provideInLeaf() {",
- " return new InEachSubcomponent();",
- " }",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Collections;",
- "import java.util.Map",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public Map<String, InEachSubcomponent> contributionsInEachSubcomponent() {",
- " return Collections.<String, InEachSubcomponent>singletonMap(",
- " \"leafmodule\", LeafModule_ProvideInLeafFactory.provideInLeaf());",
- " }",
- "}");
- Compilation compilation = compileWithoutGuava(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoMap;",
- "import dagger.multibindings.StringKey;",
- "import java.util.Map;",
- "",
- "@Module",
- "class AncestorModule {",
- " @Provides",
- " @IntoMap",
- " @StringKey(\"ancestormodule\")",
- " static InEachSubcomponent provideInAncestor() {",
- " return new InEachSubcomponent();",
- " }",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import dagger.internal.MapBuilder;",
- "import java.util.Map;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " public Map<String, InEachSubcomponent> contributionsInEachSubcomponent() {",
- " return MapBuilder.<String, InEachSubcomponent>newMapBuilder(2)",
- " .put(\"ancestormodule\",",
- " AncestorModule_ProvideInAncestorFactory.provideInAncestor())",
- " .putAll(super.contributionsInEachSubcomponent())",
- " .build();",
- " }",
- " }",
- "}");
- compilation = compileWithoutGuava(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void mapMultibinding_requestedAsInstanceInLeaf_requestedAsFrameworkInstanceFromAncestor() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(
- filesToCompile, "Multibound", "MissingInLeaf_WillDependOnFrameworkInstance");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Map;",
- "import javax.inject.Provider;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " Map<Integer, Multibound> instance();",
- " MissingInLeaf_WillDependOnFrameworkInstance willDependOnFrameworkInstance();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntKey;",
- "import dagger.multibindings.IntoMap;",
- "import java.util.Map;",
- "",
- "@Module",
- "class LeafModule {",
- " @Provides",
- " @IntoMap",
- " @IntKey(111)",
- " static Multibound contribution() {",
- " return new Multibound();",
- " }",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableMap;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Map;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public Map<Integer, Multibound> instance() {",
- " return ImmutableMap.<Integer, Multibound>of(",
- " 111, LeafModule_ContributionFactory.contribution());",
- " }",
- "",
- " @Override",
- " public abstract MissingInLeaf_WillDependOnFrameworkInstance",
- " willDependOnFrameworkInstance();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.Multibinds;",
- "import java.util.Map;",
- "import javax.inject.Provider;",
- "",
- "@Module",
- "interface AncestorModule {",
- " @Provides",
- " static MissingInLeaf_WillDependOnFrameworkInstance providedInAncestor(",
- " Provider<Map<Integer, Multibound>> frameworkInstance) {",
- " return null;",
- " }",
- "",
- " @Multibinds Map<Integer, Multibound> multibinds();",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import dagger.internal.MapFactory;",
- "import java.util.Map;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " private Provider<Map<Integer, Multibound>> mapOfIntegerAndMultiboundProvider;",
- "",
- " protected LeafImpl() {}",
- "",
- " protected void configureInitialization() { ",
- " initialize();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize() { ",
- " this.mapOfIntegerAndMultiboundProvider =",
- " MapFactory.<Integer, Multibound>builder(1)",
- " .put(111, LeafModule_ContributionFactory.create())",
- " .build();",
- " }",
- "",
- " protected Provider getMapOfIntegerAndMultiboundProvider() {",
- " return mapOfIntegerAndMultiboundProvider;",
- " }",
- "",
- " @Override",
- " public final MissingInLeaf_WillDependOnFrameworkInstance ",
- " willDependOnFrameworkInstance() {",
- " return AncestorModule_ProvidedInAncestorFactory.providedInAncestor(",
- " getMapOfIntegerAndMultiboundProvider());",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void emptyMultibinds_set() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "Multibound");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.multibindings.Multibinds;",
- "import java.util.Set;",
- "",
- "@Module",
- "interface LeafModule {",
- " @Multibinds",
- " Set<Multibound> set();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Set;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " Set<Multibound> set();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableSet;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public Set<Multibound> set() {",
- " return ImmutableSet.<Multibound>of();",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoSet;",
- "",
- "@Module",
- "class AncestorModule {",
- " @Provides",
- " @IntoSet",
- " static Multibound fromAncestor() {",
- " return new Multibound();",
- " }",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableSet;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " public Set<Multibound> set() {",
- " return ImmutableSet.<Multibound>of(",
- " AncestorModule_FromAncestorFactory.fromAncestor());",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void emptyMultibinds_set_frameworkInstance() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "Multibound");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.multibindings.Multibinds;",
- "import java.util.Set;",
- "",
- "@Module",
- "interface LeafModule {",
- " @Multibinds",
- " Set<Multibound> set();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Set;",
- "import javax.inject.Provider;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " Provider<Set<Multibound>> set();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import dagger.internal.SetFactory;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public Provider<Set<Multibound>> set() {",
- " return SetFactory.<Multibound>empty();",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoSet;",
- "",
- "@Module",
- "class AncestorModule {",
- " @Provides",
- " @IntoSet",
- " static Multibound fromAncestor() {",
- " return new Multibound();",
- " }",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import dagger.internal.DelegateFactory;",
- "import dagger.internal.GenerationOptions;",
- "import dagger.internal.SetFactory;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " private Provider<Set<Multibound>> setOfMultiboundProvider =",
- " new DelegateFactory<>();",
- "",
- " protected LeafImpl() {}",
- "",
- " protected void configureInitialization() {",
- " initialize();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize() {",
- " DelegateFactory.setDelegate(",
- " setOfMultiboundProvider,",
- " SetFactory.<Multibound>builder(1, 0)",
- " .addProvider(AncestorModule_FromAncestorFactory.create())",
- " .build());",
- " }",
- "",
- " @Override",
- " public Provider<Set<Multibound>> set() {",
- " return setOfMultiboundProvider;",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void emptyMultibinds_map() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "Multibound");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.multibindings.Multibinds;",
- "import java.util.Map;",
- "",
- "@Module",
- "interface LeafModule {",
- " @Multibinds",
- " Map<Integer, Multibound> map();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Map;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " Map<Integer, Multibound> map();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableMap;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Map;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public Map<Integer, Multibound> map() {",
- " return ImmutableMap.<Integer, Multibound>of();",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntKey;",
- "import dagger.multibindings.IntoMap;",
- "",
- "@Module",
- "class AncestorModule {",
- " @Provides",
- " @IntoMap",
- " @IntKey(111)",
- " static Multibound fromAncestor() {",
- " return new Multibound();",
- " }",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableMap;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Map;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " public Map<Integer, Multibound> map() {",
- " return ImmutableMap.<Integer, Multibound>of(",
- " 111, AncestorModule_FromAncestorFactory.fromAncestor());",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void emptyMultibinds_map_frameworkInstance() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "Multibound");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.multibindings.Multibinds;",
- "import java.util.Map;",
- "",
- "@Module",
- "interface LeafModule {",
- " @Multibinds",
- " Map<Integer, Multibound> map();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Map;",
- "import javax.inject.Provider;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " Provider<Map<Integer, Multibound>> map();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import dagger.internal.MapFactory;",
- "import java.util.Map;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public Provider<Map<Integer, Multibound>> map() {",
- " return MapFactory.<Integer, Multibound>emptyMapProvider();",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntKey;",
- "import dagger.multibindings.IntoMap;",
- "",
- "@Module",
- "class AncestorModule {",
- " @Provides",
- " @IntoMap",
- " @IntKey(111)",
- " static Multibound fromAncestor() {",
- " return new Multibound();",
- " }",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import dagger.internal.DelegateFactory;",
- "import dagger.internal.GenerationOptions;",
- "import dagger.internal.MapFactory;",
- "import java.util.Map;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " private Provider<Map<Integer, Multibound>> mapOfIntegerAndMultiboundProvider =",
- " new DelegateFactory<>()",
- "",
- " protected LeafImpl() {}",
- "",
- " protected void configureInitialization() {",
- " initialize();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize() {",
- " DelegateFactory.setDelegate(",
- " mapOfIntegerAndMultiboundProvider,",
- " MapFactory.<Integer, Multibound>builder(1)",
- " .put(111, AncestorModule_FromAncestorFactory.create())",
- " .build());",
- " }",
- "",
- " @Override",
- " public Provider<Map<Integer, Multibound>> map() {",
- " return mapOfIntegerAndMultiboundProvider;",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void bindsMissingDep_Multibindings() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Binds;",
- "import dagger.Module;",
- "import dagger.multibindings.IntoSet;",
- "",
- "@Module",
- "interface LeafModule {",
- " @Binds",
- " @IntoSet",
- " CharSequence bindsMultibindingWithMissingDep(String string);",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Set;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " Set<CharSequence> set();",
- "}"));
-
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableSet;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public Set<CharSequence> set() {",
- " return ImmutableSet.<CharSequence>of(getBindsMultibindingWithMissingDep());",
- " }",
- "",
- // The expected output here is subtle: the Key of
- // LeafModule.bindsMultibindingWithMissingDep() is Set<CharSequence>, but the binding
- // method should only be returning an individual CharSequence. Otherwise the
- // ImmutableSet factory method above will fail.
- " protected abstract CharSequence getBindsMultibindingWithMissingDep();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
- }
-
- @Test
- public void multibindingsAndFastInit() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "PackagePrivate");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.MultibindingModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntKey;",
- "import dagger.multibindings.IntoMap;",
- "import dagger.multibindings.IntoSet;",
- "",
- "@Module",
- "interface MultibindingModule {",
- " @Provides",
- " @IntoSet",
- " @LeafScope",
- " static PackagePrivate setContribution() {",
- " return new PackagePrivate();",
- " }",
- "",
- " @Provides",
- " @IntoMap",
- " @IntKey(1)",
- " @LeafScope",
- " static PackagePrivate mapContribution() {",
- " return new PackagePrivate();",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.LeafScope",
- "package test;",
- "",
- "import javax.inject.Scope;",
- "",
- "@Scope",
- "@interface LeafScope {}"),
- JavaFileObjects.forSourceLines(
- "test.UsesMultibindings",
- "package test;",
- "",
- "import java.util.Map;",
- "import java.util.Set;",
- "import javax.inject.Inject;",
- "",
- "class UsesMultibindings {",
- " @Inject",
- " UsesMultibindings(Set<PackagePrivate> set, Map<Integer, PackagePrivate> map) {}",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Map;",
- "import java.util.Set;",
- "",
- "@LeafScope",
- "@Subcomponent(modules = MultibindingModule.class)",
- "interface Leaf {",
- " UsesMultibindings entryPoint();",
- "}"));
-
- Compilation compilation =
- compilerWithOptions(AHEAD_OF_TIME_SUBCOMPONENTS_MODE, FAST_INIT_MODE)
- .compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
-
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "@GenerationOptions(fastInit = true)",
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " private PackagePrivate getSetContribution() {",
- " Object local = setContribution;",
- " if (local instanceof MemoizedSentinel) {",
- " synchronized (local) {",
- " local = setContribution;",
- " if (local instanceof MemoizedSentinel) {",
- " local = MultibindingModule_SetContributionFactory.setContribution();",
- " setContribution = DoubleCheck.reentrantCheck(setContribution, local);",
- " }",
- " }",
- " }",
- " return (PackagePrivate) local;",
- " }",
- "",
- " private PackagePrivate getMapContribution() {",
- " Object local = mapContribution;",
- " if (local instanceof MemoizedSentinel) {",
- " synchronized (local) {",
- " local = mapContribution;",
- " if (local instanceof MemoizedSentinel) {",
- " local = MultibindingModule_MapContributionFactory.mapContribution();",
- " mapContribution = DoubleCheck.reentrantCheck(mapContribution, local);",
- " }",
- " }",
- " }",
- " return (PackagePrivate) local;",
- " }",
- "",
- " @Override",
- " public UsesMultibindings entryPoint() {",
- " return new UsesMultibindings(",
- " getSetOfPackagePrivate(), getMapOfIntegerAndPackagePrivate());",
- " }",
- "",
- " protected Set getSetOfPackagePrivate() {",
- " return ImmutableSet.<PackagePrivate>of(getSetContribution());",
- " }",
- "",
- " protected Map getMapOfIntegerAndPackagePrivate() {",
- " return ImmutableMap.<Integer, PackagePrivate>of(1, getMapContribution());",
- " }",
- "}");
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .containsElementsIn(generatedLeaf);
- }
-
- // TODO(ronshapiro): remove copies from AheadOfTimeSubcomponents*Test classes
- private void createSimplePackagePrivateClasses(
- ImmutableList.Builder<JavaFileObject> filesBuilder, String... ancillaryClasses) {
- for (String className : ancillaryClasses) {
- filesBuilder.add(
- JavaFileObjects.forSourceLines(
- String.format("test.%s", className),
- "package test;",
- "",
- String.format("class %s { }", className)));
- }
- }
-
- private static Compilation compile(Iterable<JavaFileObject> files) {
- return compilerWithOptions(AHEAD_OF_TIME_SUBCOMPONENTS_MODE).compile(files);
- }
-
- private static Compilation compileWithoutGuava(Iterable<JavaFileObject> files) {
- return daggerCompiler()
- .withOptions(
- AHEAD_OF_TIME_SUBCOMPONENTS_MODE.javacopts().append(CLASS_PATH_WITHOUT_GUAVA_OPTION))
- .compile(files);
- }
-}
diff --git a/javatests/dagger/internal/codegen/AheadOfTimeSubcomponentsTest.java b/javatests/dagger/internal/codegen/AheadOfTimeSubcomponentsTest.java
deleted file mode 100644
index 1bd221a61..000000000
--- a/javatests/dagger/internal/codegen/AheadOfTimeSubcomponentsTest.java
+++ /dev/null
@@ -1,5677 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.testing.compile.CompilationSubject.assertThat;
-import static dagger.internal.codegen.CompilerMode.AHEAD_OF_TIME_SUBCOMPONENTS_MODE;
-import static dagger.internal.codegen.Compilers.compilerWithOptions;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
-import static dagger.internal.codegen.GeneratedLines.GENERATION_OPTIONS_ANNOTATION;
-import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ObjectArrays;
-import com.google.testing.compile.Compilation;
-import com.google.testing.compile.JavaFileObjects;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public final class AheadOfTimeSubcomponentsTest {
- private static final String PRUNED_METHOD_BODY =
- "throw new UnsupportedOperationException(\"This binding is not part of the final binding "
- + "graph. The key was requested by a binding that was believed to possibly be part of "
- + "the graph, but is no longer requested. If this exception is thrown, it is the result "
- + "of a Dagger bug.\");";
-
- @Test
- public void missingBindings_fromComponentMethod() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "MissingInLeaf");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface Leaf {",
- " MissingInLeaf missingFromComponentMethod();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public abstract MissingInLeaf missingFromComponentMethod();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class AncestorModule {",
- " @Provides",
- " static MissingInLeaf satisfiedInAncestor() { return new MissingInLeaf(); }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " public final MissingInLeaf missingFromComponentMethod() {",
- " return AncestorModule_SatisfiedInAncestorFactory.satisfiedInAncestor();",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void missingBindings_dependsOnBindingWithMatchingComponentMethod() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "MissingInLeaf");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface Leaf {",
- " MissingInLeaf missingComponentMethod();",
- " DependsOnComponentMethod dependsOnComponentMethod();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.DependsOnComponentMethod",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class DependsOnComponentMethod {",
- " @Inject DependsOnComponentMethod(MissingInLeaf missingInLeaf) {}",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public abstract MissingInLeaf missingComponentMethod();",
- "",
- " @Override",
- " public DependsOnComponentMethod dependsOnComponentMethod() {",
- " return new DependsOnComponentMethod(missingComponentMethod());",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
- }
-
- @Test
- public void missingBindings_dependsOnMissingBinding() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "MissingInLeaf");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface Leaf {",
- " DependsOnMissingBinding dependsOnMissingBinding();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.DependsOnMissingBinding",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class DependsOnMissingBinding {",
- " @Inject DependsOnMissingBinding(MissingInLeaf missing) {}",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public DependsOnMissingBinding dependsOnMissingBinding() {",
- " return new DependsOnMissingBinding((MissingInLeaf) getMissingInLeaf());",
- " }",
- "",
- " protected abstract Object getMissingInLeaf();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class AncestorModule {",
- " @Provides",
- " static MissingInLeaf satisfiedInAncestor() { return new MissingInLeaf(); }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " protected final Object getMissingInLeaf() {",
- " return AncestorModule_SatisfiedInAncestorFactory.satisfiedInAncestor();",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void missingBindings_satisfiedInGreatAncestor() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "MissingInLeaf");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface Leaf {",
- " DependsOnMissingBinding dependsOnMissingBinding();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.DependsOnMissingBinding",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class DependsOnMissingBinding {",
- " @Inject DependsOnMissingBinding(MissingInLeaf missing) {}",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.GreatAncestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = SatisfiesMissingBindingModule.class)",
- "interface GreatAncestor {",
- " Ancestor ancestor();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.SatisfiesMissingBindingModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class SatisfiesMissingBindingModule {",
- " @Provides",
- " static MissingInLeaf satisfy() { return new MissingInLeaf(); }",
- "}"));
- // DaggerLeaf+DaggerAncestor generated types are ignored - they're not the focus of this test
- // and are tested elsewhere
- JavaFileObject generatedGreatAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerGreatAncestor implements GreatAncestor {",
- " protected DaggerGreatAncestor() {}",
- "",
- " protected abstract class AncestorImpl extends DaggerAncestor {",
- " protected AncestorImpl() {}",
- "",
- " protected abstract class LeafImpl extends DaggerAncestor.LeafImpl {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " protected final Object getMissingInLeaf() {",
- " return SatisfiesMissingBindingModule_SatisfyFactory.satisfy();",
- " }",
- " }",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerGreatAncestor")
- .hasSourceEquivalentTo(generatedGreatAncestor);
- }
-
- @Test
- public void moduleInstanceDependency() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = TestModule.class)",
- "interface Leaf {",
- " String string();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class TestModule {",
- " @Provides String provideString() { return \"florp\"; }",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public String string() {",
- " return TestModule_ProvideStringFactory.provideString(testModule());",
- " }",
- "",
- " protected abstract TestModule testModule();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Root",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface Root {",
- " Ancestor ancestor();",
- "}"));
- JavaFileObject generatedRoot =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATED_ANNOTATION,
- "final class DaggerRoot implements Root {",
- " private DaggerRoot() {}",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static Root create() {",
- " return new Builder().build();",
- " }",
- "",
- " @Override",
- " public Ancestor ancestor() {",
- " return new AncestorImpl();",
- " }",
- "",
- " static final class Builder {",
- " private Builder() {}",
- "",
- " public Root build() {",
- " return new DaggerRoot();",
- " }",
- " }",
- "",
- " protected final class AncestorImpl extends DaggerAncestor {",
- " private AncestorImpl() {}",
- "",
- " @Override",
- " public Leaf leaf() {",
- " return new LeafImpl();",
- " }",
- "",
- " protected final class LeafImpl extends DaggerAncestor.LeafImpl {",
- " private final TestModule testModule;",
- "",
- " private LeafImpl() {",
- " this.testModule = new TestModule();",
- " }",
- "",
- " @Override",
- " protected TestModule testModule() {",
- " return testModule;",
- " }",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerRoot")
- .hasSourceEquivalentTo(generatedRoot);
- }
-
- @Test
- public void moduleInstanceDependency_withModuleParams() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = TestModule.class)",
- "interface Leaf {",
- " int getInt();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.TestModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "final class TestModule {",
- " private int i;",
- "",
- " TestModule(int i) {}",
- "",
- " @Provides int provideInt() {",
- " return i++;",
- " }",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public int getInt() {",
- " return testModule().provideInt();",
- " }",
- "",
- " protected abstract TestModule testModule();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface Ancestor {",
- " Leaf leaf(TestModule module);",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Root",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface Root {",
- " Ancestor ancestor();",
- "}"));
- JavaFileObject generatedRoot =
- JavaFileObjects.forSourceLines(
- "test.DaggerRoot",
- "package test;",
- "",
- "import dagger.internal.Preconditions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATED_ANNOTATION,
- "final class DaggerRoot implements Root {",
- " private DaggerRoot() {}",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static Root create() {",
- " return new Builder().build();",
- " }",
- "",
- " @Override",
- " public Ancestor ancestor() {",
- " return new AncestorImpl();",
- " }",
- "",
- " static final class Builder {",
- " private Builder() {}",
- "",
- " public Root build() {",
- " return new DaggerRoot();",
- " }",
- " }",
- "",
- " protected final class AncestorImpl extends DaggerAncestor {",
- " private AncestorImpl() {}",
- "",
- " @Override",
- " public Leaf leaf(TestModule module) {",
- " Preconditions.checkNotNull(module);",
- " return new LeafImpl(module);",
- " }",
- "",
- " protected final class LeafImpl extends DaggerAncestor.LeafImpl {",
- " private final TestModule testModule;",
- "",
- " private LeafImpl(TestModule module) {",
- " this.testModule = module;",
- " }",
- "",
- " @Override",
- " protected TestModule testModule() {",
- " return testModule;",
- " }",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerRoot")
- .hasSourceEquivalentTo(generatedRoot);
- }
-
- @Test
- public void generatedInstanceBinding() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface Leaf {",
- " @Subcomponent.Builder",
- " interface Builder {",
- " Leaf build();",
- " }",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface Ancestor {",
- " Leaf.Builder leaf();",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " @Override",
- " public abstract Leaf.Builder leaf();",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Root",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface Root {",
- " Ancestor ancestor();",
- "}"));
- JavaFileObject generatedRoot =
- JavaFileObjects.forSourceLines(
- "test.DaggerRoot",
- "package test;",
- "",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATED_ANNOTATION,
- "final class DaggerRoot implements Root {",
- " private DaggerRoot() {}",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static Root create() {",
- " return new Builder().build();",
- " }",
- "",
- " @Override",
- " public Ancestor ancestor() {",
- " return new AncestorImpl();",
- " }",
- "",
- " static final class Builder {",
- " private Builder() {}",
- "",
- " public Root build() {",
- " return new DaggerRoot();",
- " }",
- " }",
- "",
- " protected final class AncestorImpl extends DaggerAncestor {",
- " private AncestorImpl() {}",
- "",
- " @Override",
- " public Leaf.Builder leaf() {",
- " return new LeafBuilder();",
- " }",
- "",
- " private final class LeafBuilder implements Leaf.Builder {",
- " @Override",
- " public Leaf build() {",
- " return new LeafImpl();",
- " }",
- " }",
- "",
- " protected final class LeafImpl extends DaggerAncestor.LeafImpl {",
- " private LeafImpl() {}",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerRoot")
- .hasSourceEquivalentTo(generatedRoot);
- }
-
- @Test
- public void prunedGeneratedInstanceBinding() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.PrunedSubcomponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface PrunedSubcomponent {",
- " @Subcomponent.Builder",
- " interface Builder {",
- " PrunedSubcomponent build();",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.InstallsPrunedSubcomponentModule",
- "package test;",
- "",
- "import dagger.Module;",
- "",
- "@Module(subcomponents = PrunedSubcomponent.class)",
- "interface InstallsPrunedSubcomponentModule {}"),
- JavaFileObjects.forSourceLines(
- "test.DependsOnPrunedSubcomponentBuilder",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class DependsOnPrunedSubcomponentBuilder {",
- " @Inject DependsOnPrunedSubcomponentBuilder(PrunedSubcomponent.Builder builder) {}",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.MaybeLeaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = InstallsPrunedSubcomponentModule.class)",
- "interface MaybeLeaf {",
- " DependsOnPrunedSubcomponentBuilder dependsOnPrunedSubcomponentBuilder();",
- "}"));
- JavaFileObject generatedMaybeLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerMaybeLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerMaybeLeaf implements MaybeLeaf {",
- " protected DaggerMaybeLeaf() {}",
- "",
- " @Override",
- " public DependsOnPrunedSubcomponentBuilder dependsOnPrunedSubcomponentBuilder() {",
- " return new DependsOnPrunedSubcomponentBuilder(",
- " (PrunedSubcomponent.Builder) getPrunedSubcomponentBuilder());",
- " }",
- "",
- " protected abstract Object getPrunedSubcomponentBuilder();",
- "",
- " protected abstract class PrunedSubcomponentImpl extends DaggerPrunedSubcomponent {",
- " protected PrunedSubcomponentImpl() {}",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerMaybeLeaf")
- .hasSourceEquivalentTo(generatedMaybeLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.PrunesGeneratedInstanceModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "interface PrunesGeneratedInstanceModule {",
- " @Provides",
- " static DependsOnPrunedSubcomponentBuilder pruneGeneratedInstance() {",
- " return new DependsOnPrunedSubcomponentBuilder(null);",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Root",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = PrunesGeneratedInstanceModule.class)",
- "interface Root {",
- " MaybeLeaf actuallyLeaf();",
- "}"));
- JavaFileObject generatedRoot =
- JavaFileObjects.forSourceLines(
- "test.DaggerRoot",
- "package test;",
- "",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATED_ANNOTATION,
- "final class DaggerRoot implements Root {",
- " private DaggerRoot() {}",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static Root create() {",
- " return new Builder().build();",
- " }",
- "",
- " @Override",
- " public MaybeLeaf actuallyLeaf() {",
- " return new MaybeLeafImpl();",
- " }",
- "",
- " static final class Builder {",
- " private Builder() {}",
- "",
- " public Root build() {",
- " return new DaggerRoot();",
- " }",
- " }",
- "",
- " protected final class MaybeLeafImpl extends DaggerMaybeLeaf {",
- " private MaybeLeafImpl() {}",
- "",
- " @Override",
- " protected Object getPrunedSubcomponentBuilder() {",
- " " + PRUNED_METHOD_BODY,
- " }",
- "",
- " @Override",
- " public DependsOnPrunedSubcomponentBuilder dependsOnPrunedSubcomponentBuilder() {",
- " return PrunesGeneratedInstanceModule_PruneGeneratedInstanceFactory",
- " .pruneGeneratedInstance();",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerRoot")
- .hasSourceEquivalentTo(generatedRoot);
- }
-
- @Test
- public void optionalBindings_boundAndSatisfiedInSameSubcomponent() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "SatisfiedInSub");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Sub",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Optional;",
- "",
- "@Subcomponent(modules = {SubModule.class, BindsSatisfiedInSubModule.class})",
- "interface Sub {",
- " Optional<SatisfiedInSub> satisfiedInSub();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.SubModule",
- "package test;",
- "",
- "import dagger.BindsOptionalOf;",
- "import dagger.Module;",
- "",
- "@Module",
- "abstract class SubModule {",
- " @BindsOptionalOf abstract SatisfiedInSub optionalSatisfiedInSub();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.BindsSatisfiedInSubModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "abstract class BindsSatisfiedInSubModule {",
- " @Provides static SatisfiedInSub provideSatisfiedInSub() {",
- " return new SatisfiedInSub();",
- " }",
- "}"));
- JavaFileObject generatedSubcomponent =
- JavaFileObjects.forSourceLines(
- "test.DaggerSub",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Optional;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerSub implements Sub {",
- " protected DaggerSub() {}",
- "",
- " @Override",
- " public Optional<SatisfiedInSub> satisfiedInSub() {",
- " return Optional.of(",
- " BindsSatisfiedInSubModule_ProvideSatisfiedInSubFactory",
- " .provideSatisfiedInSub());",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerSub")
- .hasSourceEquivalentTo(generatedSubcomponent);
- }
-
- @Test
- public void optionalBindings_satisfiedInAncestor() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "SatisfiedInAncestor");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Optional;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " Optional<SatisfiedInAncestor> satisfiedInAncestor();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.BindsOptionalOf;",
- "import dagger.Module;",
- "",
- "@Module",
- "abstract class LeafModule {",
- " @BindsOptionalOf abstract SatisfiedInAncestor optionalSatisfiedInAncestor();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Optional;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public Optional<SatisfiedInAncestor> satisfiedInAncestor() {",
- " return Optional.<SatisfiedInAncestor>empty();",
- " }",
- "}");
- Compilation compilation =
- compile(
- filesToCompile.build()
- , CompilerMode.JAVA7
- );
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "abstract class AncestorModule {",
- " @Provides",
- " static SatisfiedInAncestor satisfiedInAncestor(){",
- " return new SatisfiedInAncestor();",
- " }",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Optional;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " public final Optional<SatisfiedInAncestor> satisfiedInAncestor() {",
- " return Optional.of(AncestorModule_SatisfiedInAncestorFactory",
- " .satisfiedInAncestor());",
- " }",
- "",
- " }",
- "}");
- compilation =
- compile(
- filesToCompile.build()
- , CompilerMode.JAVA7
- );
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void optionalBindings_satisfiedInGrandAncestor() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "SatisfiedInGrandAncestor");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Optional;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " Optional<SatisfiedInGrandAncestor> satisfiedInGrandAncestor();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.BindsOptionalOf;",
- "import dagger.Module;",
- "",
- "@Module",
- "abstract class LeafModule {",
- " @BindsOptionalOf",
- " abstract SatisfiedInGrandAncestor optionalSatisfiedInGrandAncestor();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Optional;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public Optional<SatisfiedInGrandAncestor> satisfiedInGrandAncestor() {",
- " return Optional.<SatisfiedInGrandAncestor>empty();",
- " }",
- "}");
- Compilation compilation =
- compile(
- filesToCompile.build()
- , CompilerMode.JAVA7
- );
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- " }",
- "}");
- compilation =
- compile(
- filesToCompile.build()
- , CompilerMode.JAVA7
- );
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.GreatAncestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = GreatAncestorModule.class)",
- "interface GreatAncestor {",
- " Ancestor ancestor();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.GreatAncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "abstract class GreatAncestorModule {",
- " @Provides",
- " static SatisfiedInGrandAncestor satisfiedInGrandAncestor(){",
- " return new SatisfiedInGrandAncestor();",
- " }",
- "}"));
- JavaFileObject generatedGreatAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerGreatAncestor",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Optional;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerGreatAncestor implements GreatAncestor {",
- " protected DaggerGreatAncestor() {}",
- "",
- " protected abstract class AncestorImpl extends DaggerAncestor {",
- " protected AncestorImpl() {}",
- "",
- " protected abstract class LeafImpl extends DaggerAncestor.LeafImpl {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " public final Optional<SatisfiedInGrandAncestor> satisfiedInGrandAncestor() {",
- " return Optional.of(",
- " GreatAncestorModule_SatisfiedInGrandAncestorFactory",
- " .satisfiedInGrandAncestor());",
- " }",
- " }",
- " }",
- "}");
- compilation =
- compile(
- filesToCompile.build()
- , CompilerMode.JAVA7
- );
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerGreatAncestor")
- .hasSourceEquivalentTo(generatedGreatAncestor);
- }
-
- @Test
- public void optionalBindings_nonComponentMethodDependencySatisfiedInAncestor() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(
- filesToCompile, "SatisfiedInAncestor", "RequiresOptionalSatisfiedInAncestor");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Optional;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " RequiresOptionalSatisfiedInAncestor requiresOptionalSatisfiedInAncestor();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.BindsOptionalOf;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import java.util.Optional;",
- "",
- "@Module",
- "abstract class LeafModule {",
- " @Provides static RequiresOptionalSatisfiedInAncestor",
- " provideRequiresOptionalSatisfiedInAncestor(",
- " Optional<SatisfiedInAncestor> satisfiedInAncestor) {",
- " return new RequiresOptionalSatisfiedInAncestor();",
- " }",
- "",
- " @BindsOptionalOf abstract SatisfiedInAncestor optionalSatisfiedInAncestor();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Optional;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public RequiresOptionalSatisfiedInAncestor",
- " requiresOptionalSatisfiedInAncestor() {",
- " return LeafModule_ProvideRequiresOptionalSatisfiedInAncestorFactory",
- " .provideRequiresOptionalSatisfiedInAncestor(",
- " getOptionalOfSatisfiedInAncestor());",
- " }",
- "",
- " protected Optional getOptionalOfSatisfiedInAncestor() {",
- " return Optional.<SatisfiedInAncestor>empty();",
- " }",
- "}");
- Compilation compilation =
- compile(
- filesToCompile.build()
- , CompilerMode.JAVA7
- );
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "abstract class AncestorModule {",
- " @Provides",
- " static SatisfiedInAncestor satisfiedInAncestor(){",
- " return new SatisfiedInAncestor();",
- " }",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Optional;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " protected final Optional getOptionalOfSatisfiedInAncestor() {",
- " return Optional.of(",
- " AncestorModule_SatisfiedInAncestorFactory.satisfiedInAncestor());",
- " }",
- " }",
- "}");
- compilation =
- compile(
- filesToCompile.build()
- , CompilerMode.JAVA7
- );
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void optionalBindings_boundInAncestorAndSatisfiedInGrandAncestor() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "SatisfiedInGrandAncestor");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import java.util.Optional;",
- "",
- "@Subcomponent",
- "interface Leaf {",
- " Optional<SatisfiedInGrandAncestor> boundInAncestorSatisfiedInGrandAncestor();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Optional;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public abstract Optional<SatisfiedInGrandAncestor>",
- " boundInAncestorSatisfiedInGrandAncestor();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.BindsOptionalOf;",
- "import dagger.Module;",
- "",
- "@Module",
- "abstract class AncestorModule {",
- " @BindsOptionalOf",
- " abstract SatisfiedInGrandAncestor optionalSatisfiedInGrandAncestor();",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Optional;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " public Optional<SatisfiedInGrandAncestor>",
- " boundInAncestorSatisfiedInGrandAncestor() {",
- " return Optional.<SatisfiedInGrandAncestor>empty();",
- " }",
- " }",
- "}");
- compilation =
- compile(
- filesToCompile.build()
- , CompilerMode.JAVA7
- );
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.GrandAncestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = GrandAncestorModule.class)",
- "interface GrandAncestor {",
- " Ancestor ancestor();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.GrandAncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class GrandAncestorModule {",
- " @Provides static SatisfiedInGrandAncestor provideSatisfiedInGrandAncestor() {",
- " return new SatisfiedInGrandAncestor();",
- " }",
- "}"));
- JavaFileObject generatedGrandAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerGrandAncestor",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Optional;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerGrandAncestor implements GrandAncestor {",
- " protected DaggerGrandAncestor() {}",
- "",
- " protected abstract class AncestorImpl extends DaggerAncestor {",
- " protected AncestorImpl() {}",
- "",
- " protected abstract class LeafImpl extends DaggerAncestor.LeafImpl {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " public final Optional<SatisfiedInGrandAncestor>",
- " boundInAncestorSatisfiedInGrandAncestor() {",
- " return Optional.of(",
- " GrandAncestorModule_ProvideSatisfiedInGrandAncestorFactory",
- " .provideSatisfiedInGrandAncestor());",
- " }",
- " }",
- " }",
- "}");
- compilation =
- compile(
- filesToCompile.build()
- , CompilerMode.JAVA7
- );
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerGrandAncestor")
- .hasSourceEquivalentTo(generatedGrandAncestor);
- }
-
- @Test
- public void provisionOverInjection_providedInAncestor() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.ProvidedInAncestor",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class ProvidedInAncestor {",
- " @Inject",
- " ProvidedInAncestor(String string) {}",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface Leaf {",
- " ProvidedInAncestor injectedInLeaf();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public ProvidedInAncestor injectedInLeaf() {",
- " return new ProvidedInAncestor(getString());",
- " }",
- "",
- " protected abstract String getString();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class AncestorModule {",
- " @Provides",
- " static ProvidedInAncestor provideProvidedInAncestor() {",
- " return new ProvidedInAncestor(\"static\");",
- " }",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " public final ProvidedInAncestor injectedInLeaf() {",
- " return AncestorModule_ProvideProvidedInAncestorFactory",
- " .provideProvidedInAncestor();",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void provisionOverInjection_providedInGrandAncestor() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.ProvidedInGrandAncestor",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class ProvidedInGrandAncestor {",
- " @Inject",
- " ProvidedInGrandAncestor(String string) {}",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface Leaf {",
- " ProvidedInGrandAncestor injectedInLeaf();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public ProvidedInGrandAncestor injectedInLeaf() {",
- " return new ProvidedInGrandAncestor(getString());",
- " }",
- "",
- " protected abstract String getString();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.GrandAncestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = GrandAncestorModule.class)",
- "interface GrandAncestor {",
- " Ancestor ancestor();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.GrandAncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class GrandAncestorModule {",
- " @Provides",
- " static ProvidedInGrandAncestor provideProvidedInGrandAncestor() {",
- " return new ProvidedInGrandAncestor(\"static\");",
- " }",
- "}"));
- JavaFileObject generatedGrandAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerGrandAncestor",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerGrandAncestor implements GrandAncestor {",
- " protected DaggerGrandAncestor() {}",
- "",
- " protected abstract class AncestorImpl extends DaggerAncestor {",
- " protected AncestorImpl() {}",
- "",
- " protected abstract class LeafImpl extends DaggerAncestor.LeafImpl {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " public final ProvidedInGrandAncestor injectedInLeaf() {",
- " return GrandAncestorModule_ProvideProvidedInGrandAncestorFactory",
- " .provideProvidedInGrandAncestor();",
- " }",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerGrandAncestor")
- .hasSourceEquivalentTo(generatedGrandAncestor);
- }
-
- @Test
- public void provisionOverInjection_indirectDependency() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.ProvidedInAncestor",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class ProvidedInAncestor {",
- " @Inject",
- " ProvidedInAncestor(String string) {}",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.InjectedInLeaf",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class InjectedInLeaf {",
- " @Inject",
- " InjectedInLeaf(ProvidedInAncestor providedInAncestor) {}",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface Leaf {",
- " InjectedInLeaf injectedInLeaf();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public InjectedInLeaf injectedInLeaf() {",
- " return new InjectedInLeaf((ProvidedInAncestor) getProvidedInAncestor());",
- " }",
- "",
- " protected abstract String getString();",
- "",
- " protected Object getProvidedInAncestor() {",
- " return new ProvidedInAncestor(getString());",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class AncestorModule {",
- " @Provides",
- " static ProvidedInAncestor provideProvidedInAncestor() {",
- " return new ProvidedInAncestor(\"static\");",
- " }",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " protected final Object getProvidedInAncestor() {",
- " return AncestorModule_ProvideProvidedInAncestorFactory",
- " .provideProvidedInAncestor();",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void provisionOverInjection_prunedIndirectDependency() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "PrunedDependency");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.InjectsPrunedDependency",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class InjectsPrunedDependency {",
- " @Inject",
- " InjectsPrunedDependency(PrunedDependency prunedDependency) {}",
- "",
- " private InjectsPrunedDependency() { }",
- "",
- " static InjectsPrunedDependency create() { return new InjectsPrunedDependency(); }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface Leaf {",
- " InjectsPrunedDependency injectsPrunedDependency();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public InjectsPrunedDependency injectsPrunedDependency() {",
- " return new InjectsPrunedDependency((PrunedDependency) getPrunedDependency());",
- " }",
- "",
- " protected abstract Object getPrunedDependency();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Root",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = RootModule.class)",
- "interface Root {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.RootModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class RootModule {",
- " @Provides",
- " static InjectsPrunedDependency injectsPrunedDependency() {",
- " return InjectsPrunedDependency.create();",
- " }",
- "}"));
- JavaFileObject generatedRoot =
- JavaFileObjects.forSourceLines(
- "test.DaggerRoot",
- "package test;",
- "",
- "import dagger.internal.Preconditions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATED_ANNOTATION,
- "final class DaggerRoot implements Root {",
- " private DaggerRoot() {}",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static Root create() {",
- " return new Builder().build();",
- " }",
- "",
- " @Override",
- " public Leaf leaf() {",
- " return new LeafImpl();",
- " }",
- "",
- " static final class Builder {",
- " private Builder() {}",
- "",
- " @Deprecated",
- " public Builder rootModule(RootModule rootModule) {",
- " Preconditions.checkNotNull(rootModule);",
- " return this;",
- " }",
- "",
- " public Root build() {",
- " return new DaggerRoot();",
- " }",
- " }",
- "",
- " protected final class LeafImpl extends DaggerLeaf {",
- " private LeafImpl() {}",
- "",
- " @Override",
- " protected Object getPrunedDependency() {",
- " " + PRUNED_METHOD_BODY,
- " }",
- "",
- " @Override",
- " public InjectsPrunedDependency injectsPrunedDependency() {",
- " return RootModule_InjectsPrunedDependencyFactory",
- " .injectsPrunedDependency();",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerRoot")
- .hasSourceEquivalentTo(generatedRoot);
- }
-
- @Test
- public void provisionOverInjection_prunedDirectDependency_prunedInConcreteImplementation() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- filesToCompile.add(
- // The binding for PrunedDependency will always exist, but will change from
- // ModifiableBindingType.INJECTION to ModifiableBindingType.MISSING. We should correctly
- // ignore this change leave the modifiable binding method alone
- JavaFileObjects.forSourceLines(
- "test.PrunedDependency",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class PrunedDependency {",
- " @Inject PrunedDependency() {}",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.InjectsPrunedDependency",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class InjectsPrunedDependency {",
- " @Inject",
- " InjectsPrunedDependency(PrunedDependency prunedDependency) {}",
- "",
- " private InjectsPrunedDependency() { }",
- "",
- " static InjectsPrunedDependency create() { return new InjectsPrunedDependency(); }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface Leaf {",
- " InjectsPrunedDependency injectsPrunedDependency();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public InjectsPrunedDependency injectsPrunedDependency() {",
- " return new InjectsPrunedDependency((PrunedDependency) getPrunedDependency());",
- " }",
- "",
- " protected Object getPrunedDependency() {",
- " return new PrunedDependency();",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Root",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = RootModule.class)",
- "interface Root {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.RootModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class RootModule {",
- " @Provides",
- " static InjectsPrunedDependency injectsPrunedDependency() {",
- " return InjectsPrunedDependency.create();",
- " }",
- "}"));
- JavaFileObject generatedRoot =
- JavaFileObjects.forSourceLines(
- "test.DaggerRoot",
- "package test;",
- "",
- "import dagger.internal.Preconditions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATED_ANNOTATION,
- "final class DaggerRoot implements Root {",
- " private DaggerRoot() {}",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static Root create() {",
- " return new Builder().build();",
- " }",
- "",
- " @Override",
- " public Leaf leaf() {",
- " return new LeafImpl();",
- " }",
- "",
- " static final class Builder {",
- " private Builder() {}",
- "",
- " @Deprecated",
- " public Builder rootModule(RootModule rootModule) {",
- " Preconditions.checkNotNull(rootModule);",
- " return this;",
- " }",
- "",
- " public Root build() {",
- " return new DaggerRoot();",
- " }",
- " }",
- "",
- " protected final class LeafImpl extends DaggerLeaf {",
- " private LeafImpl() {}",
- "",
- " @Override",
- " public InjectsPrunedDependency injectsPrunedDependency() {",
- " return RootModule_InjectsPrunedDependencyFactory",
- " .injectsPrunedDependency();",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerRoot")
- .hasSourceEquivalentTo(generatedRoot);
- }
-
- @Test
- public void provisionOverInjection_prunedDirectDependency_prunedInAbstractImplementation() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- filesToCompile.add(
- // The binding for PrunedDependency will always exist, but will change from
- // ModifiableBindingType.INJECTION to ModifiableBindingType.MISSING. We should correctly
- // ignore this change leave the modifiable binding method alone
- JavaFileObjects.forSourceLines(
- "test.PrunedDependency",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class PrunedDependency {",
- " @Inject PrunedDependency() {}",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.InjectsPrunedDependency",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class InjectsPrunedDependency {",
- " @Inject",
- " InjectsPrunedDependency(PrunedDependency prunedDependency) {}",
- "",
- " private InjectsPrunedDependency() { }",
- "",
- " static InjectsPrunedDependency create() { return new InjectsPrunedDependency(); }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface Leaf {",
- " InjectsPrunedDependency injectsPrunedDependency();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public InjectsPrunedDependency injectsPrunedDependency() {",
- " return new InjectsPrunedDependency((PrunedDependency) getPrunedDependency());",
- " }",
- "",
- " protected Object getPrunedDependency() {",
- " return new PrunedDependency();",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class AncestorModule {",
- " @Provides",
- " static InjectsPrunedDependency injectsPrunedDependency() {",
- " return InjectsPrunedDependency.create();",
- " }",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " public final InjectsPrunedDependency injectsPrunedDependency() {",
- " return AncestorModule_InjectsPrunedDependencyFactory",
- " .injectsPrunedDependency();",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Root",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface Root {",
- " Ancestor ancestor();",
- "}"));
- JavaFileObject generatedRoot =
- JavaFileObjects.forSourceLines(
- "test.DaggerRoot",
- "package test;",
- "",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATED_ANNOTATION,
- "final class DaggerRoot implements Root {",
- " private DaggerRoot() {}",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static Root create() {",
- " return new Builder().build();",
- " }",
- "",
- " @Override",
- " public Ancestor ancestor() {",
- " return new AncestorImpl();",
- " }",
- "",
- " static final class Builder {",
- " private Builder() {}",
- "",
- " public Root build() {",
- " return new DaggerRoot();",
- " }",
- " }",
- "",
- " protected final class AncestorImpl extends DaggerAncestor {",
- " private AncestorImpl() {}",
- "",
- " @Override",
- " public Leaf leaf() {",
- " return new LeafImpl();",
- " }",
- "",
- " protected final class LeafImpl extends DaggerAncestor.LeafImpl {",
- " private LeafImpl() {}",
- // even though DaggerAncestor.LeafImpl.getPrunedDependency() was
- // ModifiableBindingType.MISSING, it doesn't need to be reimplemented because there was
- // a base implementation
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerRoot")
- .hasSourceEquivalentTo(generatedRoot);
- }
-
- @Test
- public void productionSubcomponentAndModifiableFrameworkInstance() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "Response", "ResponseDependency");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import com.google.common.util.concurrent.ListenableFuture;",
- "import dagger.producers.ProductionSubcomponent;",
- "import java.util.Set;",
- "",
- "@ProductionSubcomponent(modules = ResponseProducerModule.class)",
- "interface Leaf {",
- " ListenableFuture<Set<Response>> responses();",
- "",
- " @ProductionSubcomponent.Builder",
- " interface Builder {",
- " Leaf build();",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.ResponseProducerModule",
- "package test;",
- "",
- "import dagger.multibindings.IntoSet;",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "",
- "@ProducerModule",
- "final class ResponseProducerModule {",
- " @Produces",
- " @IntoSet",
- " static Response response(ResponseDependency responseDependency) {",
- " return new Response();",
- " }",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import com.google.common.util.concurrent.ListenableFuture;",
- "import dagger.internal.GenerationOptions;",
- "import dagger.producers.Producer;",
- "import dagger.producers.internal.CancellationListener;",
- "import dagger.producers.internal.Producers;",
- "import dagger.producers.internal.SetProducer;",
- "import dagger.producers.monitoring.ProductionComponentMonitor;",
- "import java.util.Set;",
- "import java.util.concurrent.Executor;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf, CancellationListener {",
- " private Producer<Set<Response>> responsesEntryPoint;",
- " private Producer<Response> responseProducer;",
- " private Producer<Set<Response>> setOfResponseProducer;",
- "",
- " protected DaggerLeaf() {}",
- "",
- " protected void configureInitialization() {",
- " initialize();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize() {",
- " this.responseProducer =",
- " ResponseProducerModule_ResponseFactory.create(",
- " getProductionImplementationExecutorProvider(),",
- " getProductionComponentMonitorProvider(),",
- " getResponseDependencyProducer());",
- " this.setOfResponseProducer =",
- " SetProducer.<Response>builder(1, 0)",
- " .addProducer(responseProducer).build();",
- " this.responsesEntryPoint =",
- " Producers.entryPointViewOf(getSetOfResponseProducer(), this);",
- " }",
- "",
- " @Override",
- " public ListenableFuture<Set<Response>> responses() {",
- " return responsesEntryPoint.get();",
- " }",
- "",
- " protected abstract Provider<Executor>",
- " getProductionImplementationExecutorProvider();",
- "",
- " protected abstract Provider<ProductionComponentMonitor>",
- " getProductionComponentMonitorProvider();",
- "",
- " protected abstract Producer getResponseDependencyProducer();",
- "",
- " protected Producer getSetOfResponseProducer() {",
- " return setOfResponseProducer;",
- " }",
- "",
- " @Override",
- " public void onProducerFutureCancelled(boolean mayInterruptIfRunning) {",
- " Producers.cancel(getSetOfResponseProducer(), mayInterruptIfRunning);",
- " Producers.cancel(responseProducer, mayInterruptIfRunning);",
- " }",
- "}");
-
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.ExecutorModule",
- "package test;",
- "",
- "import com.google.common.util.concurrent.MoreExecutors;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.producers.Production;",
- "import java.util.concurrent.Executor;",
- "",
- "@Module",
- "final class ExecutorModule {",
- " @Provides",
- " @Production",
- " static Executor executor() {",
- " return MoreExecutors.directExecutor();",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Root",
- "package test;",
- "",
- "import com.google.common.util.concurrent.ListenableFuture;",
- "import dagger.producers.ProductionComponent;",
- "",
- "@ProductionComponent(",
- " modules = {",
- " ExecutorModule.class,",
- " ResponseDependencyProducerModule.class,",
- " RootMultibindingModule.class,",
- " })",
- "interface Root {",
- " Leaf.Builder leaf();",
- "",
- " @ProductionComponent.Builder",
- " interface Builder {",
- " Root build();",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.ResponseDependencyProducerModule",
- "package test;",
- "",
- "import com.google.common.util.concurrent.Futures;",
- "import com.google.common.util.concurrent.ListenableFuture;",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "",
- "@ProducerModule",
- "final class ResponseDependencyProducerModule {",
- " @Produces",
- " static ListenableFuture<ResponseDependency> responseDependency() {",
- " return Futures.immediateFuture(new ResponseDependency());",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.RootMultibindingModule",
- "package test;",
- "",
- "import dagger.multibindings.IntoSet;",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "",
- "@ProducerModule",
- "final class RootMultibindingModule {",
- " @Produces",
- " @IntoSet",
- " static Response response() {",
- " return new Response();",
- " }",
- "}"));
- JavaFileObject generatedRoot =
- JavaFileObjects.forSourceLines(
- "test.DaggerRoot",
- "package test;",
- "",
- "import dagger.internal.DoubleCheck;",
- "import dagger.internal.InstanceFactory;",
- "import dagger.internal.SetFactory;",
- "import dagger.producers.Producer;",
- "import dagger.producers.internal.CancellationListener;",
- "import dagger.producers.internal.DelegateProducer;",
- "import dagger.producers.internal.Producers;",
- "import dagger.producers.internal.SetProducer;",
- "import dagger.producers.monitoring.ProductionComponentMonitor;",
- "import java.util.Set;",
- "import java.util.concurrent.Executor;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATED_ANNOTATION,
- "final class DaggerRoot implements Root, CancellationListener {",
- " private Provider<Executor> productionImplementationExecutorProvider;",
- " private Provider<Root> rootProvider;",
- " private Provider<ProductionComponentMonitor> monitorProvider;",
- " private Producer<ResponseDependency> responseDependencyProducer;",
- " private Producer<Response> responseProducer;",
- "",
- " private DaggerRoot() {",
- " initialize();",
- " }",
- "",
- " public static Root.Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static Root create() {",
- " return new Builder().build();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize() {",
- " this.productionImplementationExecutorProvider =",
- " DoubleCheck.provider((Provider) ExecutorModule_ExecutorFactory.create());",
- " this.rootProvider = InstanceFactory.create((Root) this);",
- " this.monitorProvider =",
- " DoubleCheck.provider(",
- " Root_MonitoringModule_MonitorFactory.create(",
- " rootProvider,",
- " SetFactory.<ProductionComponentMonitor.Factory>empty()));",
- " this.responseDependencyProducer =",
- " ResponseDependencyProducerModule_ResponseDependencyFactory.create(",
- " productionImplementationExecutorProvider, monitorProvider);",
- " this.responseProducer =",
- " RootMultibindingModule_ResponseFactory.create(",
- " productionImplementationExecutorProvider, monitorProvider);",
- " }",
- "",
- " @Override",
- " public Leaf.Builder leaf() {",
- " return new LeafBuilder();",
- " }",
- "",
- " @Override",
- " public void onProducerFutureCancelled(boolean mayInterruptIfRunning) {",
- " Producers.cancel(responseProducer, mayInterruptIfRunning);",
- " Producers.cancel(responseDependencyProducer, mayInterruptIfRunning);",
- " }",
- "",
- " private static final class Builder implements Root.Builder {",
- " @Override",
- " public Root build() {",
- " return new DaggerRoot();",
- " }",
- " }",
- "",
- " private final class LeafBuilder implements Leaf.Builder {",
- " @Override",
- " public Leaf build() {",
- " return new LeafImpl();",
- " }",
- " }",
- "",
- " protected final class LeafImpl extends DaggerLeaf implements CancellationListener {",
- " private Producer<Set<Response>> setOfResponseProducer = new DelegateProducer<>();",
- "",
- " private LeafImpl() {",
- " configureInitialization();",
- " initialize();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize() {",
- " DelegateProducer.setDelegate(",
- " setOfResponseProducer,",
- " SetProducer.<Response>builder(1, 1)",
- " .addCollectionProducer(super.getSetOfResponseProducer())",
- " .addProducer(DaggerRoot.this.responseProducer)",
- " .build());",
- " }",
- "",
- " @Override",
- " protected Provider<Executor> getProductionImplementationExecutorProvider() {",
- " return DaggerRoot.this.productionImplementationExecutorProvider;",
- " }",
- "",
- " @Override",
- " protected Provider<ProductionComponentMonitor>",
- " getProductionComponentMonitorProvider() {",
- " return DaggerRoot.this.monitorProvider;",
- " }",
- "",
- " @Override",
- " protected Producer getResponseDependencyProducer() {",
- " return DaggerRoot.this.responseDependencyProducer;",
- " }",
- "",
- " @Override",
- " protected Producer getSetOfResponseProducer() {",
- " return setOfResponseProducer;",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerRoot")
- .hasSourceEquivalentTo(generatedRoot);
- }
-
- @Test
- public void lazyOfModifiableBinding() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "MissingInLeaf");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Lazy;",
- "import dagger.Subcomponent;",
- "import javax.inject.Provider;",
- "",
- "@Subcomponent",
- "interface Leaf {",
- " Lazy<MissingInLeaf> lazy();",
- " Provider<Lazy<MissingInLeaf>> providerOfLazy();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.Lazy;",
- "import dagger.internal.DoubleCheck;",
- "import dagger.internal.GenerationOptions;",
- "import dagger.internal.ProviderOfLazy;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public Lazy<MissingInLeaf> lazy() {",
- " return DoubleCheck.lazy(getMissingInLeafProvider());",
- " }",
- "",
- " @Override",
- " public Provider<Lazy<MissingInLeaf>> providerOfLazy() {",
- " return ProviderOfLazy.create(getMissingInLeafProvider());",
- " }",
- "",
- " protected abstract Provider getMissingInLeafProvider();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class AncestorModule {",
- " @Provides",
- " static MissingInLeaf satisfiedInAncestor() { return new MissingInLeaf(); }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " protected final Provider getMissingInLeafProvider() {",
- " return AncestorModule_SatisfiedInAncestorFactory.create();",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void missingBindingAccessInLeafAndAncestor() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(
- filesToCompile, "Missing", "DependsOnMissing", "ProvidedInAncestor_InducesSetBinding");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.multibindings.IntoSet;",
- "import dagger.Provides;",
- "import javax.inject.Provider;",
- "",
- "@Module",
- "class LeafModule {",
- " @Provides",
- " static DependsOnMissing test(",
- " Missing missing,",
- " Provider<Missing> missingProvider,",
- " ProvidedInAncestor_InducesSetBinding missingInLeaf) {",
- " return new DependsOnMissing();",
- " }",
- "",
- " @Provides",
- " @IntoSet",
- " static Object unresolvedSetBinding(",
- " Missing missing, Provider<Missing> missingProvider) {",
- " return new Object();",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import javax.inject.Provider;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " DependsOnMissing instance();",
- " Provider<DependsOnMissing> frameworkInstance();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " private Provider<DependsOnMissing> testProvider;",
- "",
- " protected DaggerLeaf() {}",
- "",
- " protected void configureInitialization() {",
- " initialize();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize() {",
- " this.testProvider =",
- " LeafModule_TestFactory.create(",
- " getMissingProvider(), getProvidedInAncestor_InducesSetBindingProvider());",
- " }",
- "",
- " @Override",
- " public DependsOnMissing instance() {",
- " return LeafModule_TestFactory.test(",
- // TODO(b/117833324): remove these unnecessary casts
- " (Missing) getMissing(),",
- " getMissingProvider(),",
- " (ProvidedInAncestor_InducesSetBinding)",
- " getProvidedInAncestor_InducesSetBinding());",
- " }",
- "",
- " @Override",
- " public Provider<DependsOnMissing> frameworkInstance() {",
- " return testProvider;",
- " }",
- "",
- " protected abstract Object getMissing();",
- " protected abstract Provider getMissingProvider();",
- " protected abstract Object getProvidedInAncestor_InducesSetBinding();",
- " protected abstract Provider getProvidedInAncestor_InducesSetBindingProvider();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.multibindings.IntoSet;",
- "import dagger.Provides;",
- "import java.util.Set;",
- "",
- "@Module",
- "interface AncestorModule {",
- " @Provides",
- " static ProvidedInAncestor_InducesSetBinding providedInAncestor(",
- " Set<Object> setThatInducesMissingBindingInChildSubclassImplementation) {",
- " return new ProvidedInAncestor_InducesSetBinding();",
- " }",
- "",
- " @Provides",
- " @IntoSet",
- " static Object setContribution() {",
- " return new Object();",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableSet;",
- "import dagger.internal.DelegateFactory;",
- "import dagger.internal.GenerationOptions;",
- "import dagger.internal.SetFactory;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " private Provider<Object> unresolvedSetBindingProvider;",
- " private Provider<Set<Object>> setOfObjectProvider;",
- " private Provider<ProvidedInAncestor_InducesSetBinding> ",
- " providedInAncestorProvider = ",
- " new DelegateFactory<>();",
- "",
- " protected LeafImpl() {}",
- "",
- " @Override",
- " protected void configureInitialization() {",
- " super.configureInitialization();",
- " initialize();",
- " }",
- "",
- " private Object getUnresolvedSetBinding() {",
- " return LeafModule_UnresolvedSetBindingFactory.unresolvedSetBinding(",
- // TODO(b/117833324): remove this unnecessary cast
- " (Missing) getMissing(), getMissingProvider());",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize() {",
- " this.unresolvedSetBindingProvider =",
- " LeafModule_UnresolvedSetBindingFactory.create(getMissingProvider());",
- " this.setOfObjectProvider =",
- " SetFactory.<Object>builder(2, 0)",
- " .addProvider(AncestorModule_SetContributionFactory.create())",
- " .addProvider(unresolvedSetBindingProvider)",
- " .build();",
- " DelegateFactory.setDelegate(",
- " providedInAncestorProvider,",
- " AncestorModule_ProvidedInAncestorFactory.create(getSetOfObjectProvider()));",
- " }",
- "",
- " protected Set<Object> getSetOfObject() {",
- " return ImmutableSet.<Object>of(",
- " AncestorModule_SetContributionFactory.setContribution(),",
- " getUnresolvedSetBinding());",
- " }",
- "",
- " @Override",
- " protected final Object getProvidedInAncestor_InducesSetBinding() {",
- " return AncestorModule_ProvidedInAncestorFactory.providedInAncestor(",
- " getSetOfObject());",
- " }",
- "",
- " protected Provider<Set<Object>> getSetOfObjectProvider() {",
- " return setOfObjectProvider;",
- " }",
- "",
- " @Override",
- " protected final Provider getProvidedInAncestor_InducesSetBindingProvider() {",
- " return providedInAncestorProvider;",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void subcomponentBuilders() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "InducesDependenciesOnBuilderFields");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class LeafModule {",
- " private final Object object;",
- "",
- " LeafModule(Object object) {",
- " this.object = object;",
- " }",
- "",
- " @Provides",
- " Object fromModule() {",
- " return object;",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.MultibindingsModule",
- "package test;",
- "",
- "import dagger.Binds;",
- "import dagger.Module;",
- "import dagger.multibindings.IntoSet;",
- "",
- "@Module",
- "interface MultibindingsModule {",
- " @Binds",
- " @IntoSet",
- " String string(String string);",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.BindsInstance;",
- "import dagger.Subcomponent;",
- "import javax.inject.Provider;",
- "",
- "@Subcomponent(modules = {LeafModule.class, MultibindingsModule.class})",
- "interface Leaf {",
- " int bindsInstance();",
- " Object fromModule();",
- " InducesDependenciesOnBuilderFields inducesDependenciesOnBuilderFields();",
- "",
- " @Subcomponent.Builder",
- " interface Builder {",
- " @BindsInstance Builder bindsInstance(int boundInstance);",
- " @BindsInstance Builder inducedInSubclass(String induced);",
- " Builder module(LeafModule module);",
- "",
- " Leaf build();",
- " }",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import dagger.internal.Preconditions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " private Integer bindsInstance;",
- " private LeafModule leafModule;",
- "",
- " protected DaggerLeaf() {}",
- "",
- " protected void configureInitialization(",
- " LeafModule leafModuleParam, Integer bindsInstanceParam) {",
- " this.bindsInstance = bindsInstanceParam;",
- " this.leafModule = leafModuleParam;",
- " }",
- "",
- " @Override",
- " public int bindsInstance() {",
- " return bindsInstance;",
- " }",
- "",
- " @Override",
- " public Object fromModule() {",
- " return LeafModule_FromModuleFactory.fromModule(leafModule());",
- " }",
- "",
- " @Override",
- " public abstract InducesDependenciesOnBuilderFields",
- " inducesDependenciesOnBuilderFields();",
- "",
- " protected LeafModule leafModule() {",
- " return leafModule;",
- " }",
- "",
- " public abstract static class Builder implements Leaf.Builder {",
- " protected Integer bindsInstance;",
- " protected String inducedInSubclass;",
- " protected LeafModule leafModule;",
- "",
- " @Override",
- " public Builder bindsInstance(int boundInstance) {",
- " this.bindsInstance = Preconditions.checkNotNull(boundInstance);",
- " return this;",
- " }",
- "",
- " @Override",
- " public Builder inducedInSubclass(String induced) {",
- " this.inducedInSubclass = Preconditions.checkNotNull(induced);",
- " return this;",
- " }",
- "",
- " @Override",
- " public Builder module(LeafModule module) {",
- " this.leafModule = Preconditions.checkNotNull(module);",
- " return this;",
- " }",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = MultibindingInducingModule.class)",
- "interface Ancestor {",
- " Leaf.Builder leaf();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.MultibindingInducingModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.multibindings.Multibinds;",
- "import dagger.Provides;",
- "import java.util.Set;",
- "",
- "@Module",
- "interface MultibindingInducingModule {",
- " @Provides",
- " static InducesDependenciesOnBuilderFields induce(",
- " Set<String> multibindingWithBuilderFieldDeps) { ",
- " return new InducesDependenciesOnBuilderFields();",
- " }",
- "",
- " @Multibinds",
- " Set<String> multibinding();",
- "}"));
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableSet;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " @Override",
- " public abstract Leaf.Builder leaf();",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " private String inducedInSubclass;",
- "",
- " protected LeafImpl() {}",
- "",
- " protected void configureInitialization(",
- " LeafModule leafModule,",
- " Integer bindsInstance,",
- " String inducedInSubclassParam) {",
- " this.inducedInSubclass = inducedInSubclassParam;",
- " configureInitialization(leafModule, bindsInstance);",
- " }",
- "",
- " protected Set<String> getSetOfString() {",
- " return ImmutableSet.<String>of(inducedInSubclass);",
- " }",
- "",
- " @Override",
- " public final InducesDependenciesOnBuilderFields",
- " inducesDependenciesOnBuilderFields() {",
- " return MultibindingInducingModule_InduceFactory.induce(getSetOfString());",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Root",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface Root {",
- " Ancestor ancestor();",
- "}"));
- JavaFileObject generatedRoot =
- JavaFileObjects.forSourceLines(
- "test.DaggerRoot",
- "package test;",
- "",
- "import dagger.internal.Preconditions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATED_ANNOTATION,
- "final class DaggerRoot implements Root {",
- " private DaggerRoot() {}",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static Root create() {",
- " return new Builder().build();",
- " }",
- "",
- " @Override",
- " public Ancestor ancestor() {",
- " return new AncestorImpl();",
- " }",
- "",
- " static final class Builder {",
- " private Builder() {}",
- "",
- " public Root build() {",
- " return new DaggerRoot();",
- " }",
- " }",
- "",
- " protected final class AncestorImpl extends DaggerAncestor {",
- " private AncestorImpl() {}",
- "",
- " @Override",
- " public Leaf.Builder leaf() {",
- " return new LeafBuilder();",
- " }",
- "",
- " private final class LeafBuilder extends DaggerLeaf.Builder {",
- " @Override",
- " public Leaf build() {",
- // TODO(b/117833324): Can we stick the validations into a method on the base class
- // builder so that the contents of this method are just call to that and then new
- // FooImpl? But repeated modules may make this more complicated, since those *should*
- // be null
- " Preconditions.checkBuilderRequirement(bindsInstance, Integer.class);",
- " Preconditions.checkBuilderRequirement(inducedInSubclass, String.class);",
- " Preconditions.checkBuilderRequirement(leafModule, LeafModule.class);",
- " return new LeafImpl(leafModule, bindsInstance, inducedInSubclass);",
- " }",
- " }",
- "",
- " protected final class LeafImpl extends DaggerAncestor.LeafImpl {",
- " private final LeafModule leafModule;",
- "",
- " private LeafImpl(",
- " LeafModule leafModuleParam,",
- " Integer bindsInstance,",
- " String inducedInSubclass) {",
- " this.leafModule = leafModuleParam;",
- " configureInitialization(leafModuleParam, bindsInstance, inducedInSubclass);",
- " }",
- "",
- " @Override",
- " protected LeafModule leafModule() {",
- " return leafModule;",
- " }",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerRoot")
- .hasSourceEquivalentTo(generatedRoot);
- }
-
- @Test
- public void subcomponentBuilders_moduleWithUnusedInstanceBindings() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "Used", "Unused");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.ModuleWithUsedBinding",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class ModuleWithUsedBinding {",
- " @Provides",
- " Used used() {",
- " return new Used();",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.ModuleWithUnusedBinding",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class ModuleWithUnusedBinding {",
- " @Provides",
- " Unused unused() {",
- " return new Unused();",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = {ModuleWithUsedBinding.class, ModuleWithUnusedBinding.class})",
- "interface Leaf {",
- " Used used();",
- "",
- " @Subcomponent.Builder",
- " interface Builder {",
- " Builder setUsed(ModuleWithUsedBinding module);",
- " Builder setUnused(ModuleWithUnusedBinding module);",
- " Leaf build();",
- " }",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import dagger.internal.Preconditions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " private ModuleWithUsedBinding moduleWithUsedBinding;",
- "",
- " protected DaggerLeaf() {}",
- "",
- " protected void configureInitialization(",
- " ModuleWithUsedBinding moduleWithUsedBindingParam) {",
- " this.moduleWithUsedBinding = moduleWithUsedBindingParam;",
- " }",
- "",
- " @Override",
- " public Used used() {",
- " return ModuleWithUsedBinding_UsedFactory.used(",
- " moduleWithUsedBinding());",
- " }",
- "",
- " protected ModuleWithUsedBinding moduleWithUsedBinding() {",
- " return moduleWithUsedBinding;",
- " }",
- "",
- " public abstract static class Builder implements Leaf.Builder {",
- " protected ModuleWithUsedBinding moduleWithUsedBinding;",
- " protected ModuleWithUnusedBinding moduleWithUnusedBinding;",
- "",
- " @Override",
- " public Builder setUsed(ModuleWithUsedBinding module) {",
- " this.moduleWithUsedBinding = Preconditions.checkNotNull(module);",
- " return this;",
- " }",
- "",
- " @Override",
- " public Builder setUnused(ModuleWithUnusedBinding module) {",
- " this.moduleWithUnusedBinding = Preconditions.checkNotNull(module);",
- " return this;",
- " }",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Root",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface Root {",
- " Leaf.Builder leaf();",
- "}"));
- JavaFileObject generatedRoot =
- JavaFileObjects.forSourceLines(
- "test.DaggerRoot",
- "package test;",
- "",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATED_ANNOTATION,
- "final class DaggerRoot implements Root {",
- " private DaggerRoot() {}",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static Root create() {",
- " return new Builder().build();",
- " }",
- "",
- " @Override",
- " public Leaf.Builder leaf() {",
- " return new LeafBuilder();",
- " }",
- "",
- " static final class Builder {",
- " private Builder() {}",
- "",
- " public Root build() {",
- " return new DaggerRoot();",
- " }",
- " }",
- "",
- " private final class LeafBuilder extends DaggerLeaf.Builder {",
- " @Override",
- " public Leaf build() {",
- " if (moduleWithUsedBinding == null) {",
- " this.moduleWithUsedBinding = new ModuleWithUsedBinding();",
- " }",
- // ModuleWithUnusedBinding is not verified since it's not used
- " return new LeafImpl(moduleWithUsedBinding);",
- " }",
- " }",
- "",
- " protected final class LeafImpl extends DaggerLeaf {",
- " private final ModuleWithUsedBinding moduleWithUsedBinding;",
- "",
- " private LeafImpl(ModuleWithUsedBinding moduleWithUsedBindingParam) {",
- " this.moduleWithUsedBinding = moduleWithUsedBindingParam;",
- " configureInitialization(moduleWithUsedBindingParam);",
- " }",
- "",
- " @Override",
- " protected ModuleWithUsedBinding moduleWithUsedBinding() {",
- " return moduleWithUsedBinding;",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerRoot")
- .hasSourceEquivalentTo(generatedRoot);
- }
-
- @Test
- public void subcomponentBuilders_repeatedModule() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.RepeatedModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class RepeatedModule {",
- " @Provides",
- " int i() {",
- " return 1;",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = RepeatedModule.class)",
- "interface Leaf {",
- " int i();",
- "",
- " @Subcomponent.Builder",
- " interface Builder {",
- " Builder repeatedModule(RepeatedModule repeatedModule);",
- " Leaf build();",
- " }",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import dagger.internal.Preconditions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " private RepeatedModule repeatedModule;",
- "",
- " protected DaggerLeaf() {}",
- "",
- " protected void configureInitialization(RepeatedModule repeatedModuleParam) {",
- " this.repeatedModule = repeatedModuleParam;",
- " }",
- "",
- " @Override",
- " public int i() {",
- " return repeatedModule().i();",
- " }",
- "",
- " protected RepeatedModule repeatedModule() {",
- " return repeatedModule;",
- " }",
- "",
- " public abstract static class Builder implements Leaf.Builder {",
- " protected RepeatedModule repeatedModule;",
- "",
- " @Override",
- " public Builder repeatedModule(RepeatedModule repeatedModule) {",
- " this.repeatedModule = Preconditions.checkNotNull(repeatedModule);",
- " return this;",
- " }",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Root",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = RepeatedModule.class)",
- "interface Root {",
- " Leaf.Builder leaf();",
- "}"));
- JavaFileObject generatedRoot =
- JavaFileObjects.forSourceLines(
- "test.DaggerRoot",
- "package test;",
- "",
- "import dagger.internal.Preconditions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATED_ANNOTATION,
- "final class DaggerRoot implements Root {",
- " private final RepeatedModule repeatedModule;",
- "",
- " private DaggerRoot(RepeatedModule repeatedModuleParam) {",
- " this.repeatedModule = repeatedModuleParam;",
- " }",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static Root create() {",
- " return new Builder().build();",
- " }",
- "",
- " @Override",
- " public Leaf.Builder leaf() {",
- " return new LeafBuilder();",
- " }",
- "",
- " static final class Builder {",
- " private RepeatedModule repeatedModule;",
- "",
- " private Builder() {}",
- "",
- " public Builder repeatedModule(RepeatedModule repeatedModule) {",
- " this.repeatedModule = Preconditions.checkNotNull(repeatedModule);",
- " return this;",
- " }",
- "",
- " public Root build() {",
- " if (repeatedModule == null) {",
- " this.repeatedModule = new RepeatedModule();",
- " }",
- " return new DaggerRoot(repeatedModule);",
- " }",
- " }",
- "",
- " private final class LeafBuilder extends DaggerLeaf.Builder {",
- " @Override",
- " public LeafBuilder repeatedModule(RepeatedModule repeatedModule) {",
- " throw new UnsupportedOperationException(",
- " String.format(",
- " \"%s cannot be set because it is inherited from the enclosing component\",",
- " RepeatedModule.class.getCanonicalName()));",
- " }",
- "",
- " @Override",
- " public Leaf build() {",
- " return new LeafImpl();",
- " }",
- " }",
- "",
- " protected final class LeafImpl extends DaggerLeaf {",
- " private LeafImpl() {",
- " configureInitialization(null);",
- " }",
- "",
- " @Override",
- " protected RepeatedModule repeatedModule() {",
- " return DaggerRoot.this.repeatedModule;",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerRoot")
- .hasSourceEquivalentTo(generatedRoot);
- }
-
- @Test
- public void bindsWithMissingDependency() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "MissingInLeaf");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Binds;",
- "",
- "@Module",
- "interface LeafModule {",
- " @Binds Object missingBindsDependency(MissingInLeaf missing);",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " Object bindsWithMissingDependencyInLeaf();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public abstract Object bindsWithMissingDependencyInLeaf();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.MissingInLeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "interface MissingInLeafModule {",
- " @Provides",
- " static MissingInLeaf boundInRoot() {",
- " return new MissingInLeaf();",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Root",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = MissingInLeafModule.class)",
- "interface Root {",
- " Leaf leaf();",
- "}"));
- JavaFileObject generatedRoot =
- JavaFileObjects.forSourceLines(
- "test.DaggerRoot",
- "package test;",
- "",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATED_ANNOTATION,
- "final class DaggerRoot implements Root {",
- " private DaggerRoot() {}",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static Root create() {",
- " return new Builder().build();",
- " }",
- "",
- " @Override",
- " public Leaf leaf() {",
- " return new LeafImpl();",
- " }",
- "",
- " static final class Builder {",
- " private Builder() {}",
- "",
- " public Root build() {",
- " return new DaggerRoot();",
- " }",
- " }",
- "",
- " protected final class LeafImpl extends DaggerLeaf {",
- " private LeafImpl() {}",
- "",
- " @Override",
- " public Object bindsWithMissingDependencyInLeaf() {",
- " return MissingInLeafModule_BoundInRootFactory.boundInRoot();",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerRoot")
- .hasSourceEquivalentTo(generatedRoot);
- }
-
- @Test
- public void bindsWithMissingDependency_pruned() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "MissingInLeaf");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Binds;",
- "",
- "@Module",
- "interface LeafModule {",
- " @Binds Object missingBindsDependency(MissingInLeaf missing);",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.DependsOnBindsWithMissingDep",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class DependsOnBindsWithMissingDep {",
- " @Inject DependsOnBindsWithMissingDep(Object bindsWithMissingDep) {}",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " DependsOnBindsWithMissingDep DependsOnBindsWithMissingDep();",
- "}"));
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public DependsOnBindsWithMissingDep DependsOnBindsWithMissingDep() {",
- " return new DependsOnBindsWithMissingDep(getObject());",
- " }",
- "",
- " protected abstract Object getObject();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.PrunesInjectConstructorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "interface PrunesInjectConstructorModule {",
- " @Provides",
- " static DependsOnBindsWithMissingDep pruneInjectConstructor() {",
- " return new DependsOnBindsWithMissingDep(new Object());",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Root",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = PrunesInjectConstructorModule.class)",
- "interface Root {",
- " Leaf leaf();",
- "}"));
- JavaFileObject generatedRoot =
- JavaFileObjects.forSourceLines(
- "test.DaggerRoot",
- "package test;",
- "",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATED_ANNOTATION,
- "final class DaggerRoot implements Root {",
- " private DaggerRoot() {}",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static Root create() {",
- " return new Builder().build();",
- " }",
- "",
- " @Override",
- " public Leaf leaf() {",
- " return new LeafImpl();",
- " }",
- "",
- " static final class Builder {",
- " private Builder() {}",
- "",
- " public Root build() {",
- " return new DaggerRoot();",
- " }",
- " }",
- "",
- " protected final class LeafImpl extends DaggerLeaf {",
- " private LeafImpl() {}",
- "",
- " @Override",
- " protected Object getObject() {",
- " " + PRUNED_METHOD_BODY,
- " }",
- "",
- " @Override",
- " public DependsOnBindsWithMissingDep DependsOnBindsWithMissingDep() {",
- " return PrunesInjectConstructorModule_PruneInjectConstructorFactory",
- " .pruneInjectConstructor();",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerRoot")
- .hasSourceEquivalentTo(generatedRoot);
- }
-
- @Test
- public void modifiedProducerFromProvider() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "DependsOnModifiedProducerFromProvider");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.multibindings.IntoSet;",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "import dagger.Provides;",
- "import java.util.Set;",
- "",
- "@ProducerModule",
- "interface LeafModule {",
- " @Produces",
- " static DependsOnModifiedProducerFromProvider dependsOnModified(Set<String> set) {",
- " return new DependsOnModifiedProducerFromProvider();",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.producers.Producer;",
- "import dagger.producers.ProductionSubcomponent;",
- "import java.util.Set;",
- "",
- "@ProductionSubcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " Producer<DependsOnModifiedProducerFromProvider>",
- " dependsOnModifiedProducerFromProvider();",
- "}"));
-
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import dagger.producers.Producer;",
- "import dagger.producers.internal.CancellationListener;",
- "import dagger.producers.internal.Producers;",
- "import dagger.producers.monitoring.ProductionComponentMonitor;",
- "import java.util.Set;",
- "import java.util.concurrent.Executor;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf, CancellationListener {",
- " private Producer<DependsOnModifiedProducerFromProvider>",
- " dependsOnModifiedProducerFromProviderEntryPoint;",
- " private Producer<DependsOnModifiedProducerFromProvider> dependsOnModifiedProducer;",
- "",
- " protected DaggerLeaf() {}",
- "",
- " protected void configureInitialization() {",
- " initialize();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize() {",
- " this.dependsOnModifiedProducer =",
- " LeafModule_DependsOnModifiedFactory.create(",
- " getProductionImplementationExecutorProvider(),",
- " getProductionComponentMonitorProvider(),",
- " getSetOfStringProducer());",
- " this.dependsOnModifiedProducerFromProviderEntryPoint =",
- " Producers.entryPointViewOf(dependsOnModifiedProducer, this);",
- " }",
- "",
- " @Override",
- " public Producer<DependsOnModifiedProducerFromProvider> ",
- " dependsOnModifiedProducerFromProvider() {",
- " return dependsOnModifiedProducerFromProviderEntryPoint;",
- " }",
- "",
- " protected abstract Provider<Executor> ",
- " getProductionImplementationExecutorProvider();",
- "",
- " protected abstract Provider<ProductionComponentMonitor>",
- " getProductionComponentMonitorProvider();",
- "",
- " protected abstract Producer<Set<String>> getSetOfStringProducer();",
- "",
- " @Override",
- " public void onProducerFutureCancelled(boolean mayInterruptIfRunning) {",
- " Producers.cancel(dependsOnModifiedProducer, mayInterruptIfRunning);",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.RootModule",
- "package test;",
- "",
- "import dagger.multibindings.IntoSet;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.producers.Production;",
- "import java.util.Set;",
- "import java.util.concurrent.Executor;",
- "",
- "@Module",
- "interface RootModule {",
- " @Provides",
- " @IntoSet",
- " static String induceModificationInLeaf() {",
- " return new String();",
- " }",
- "",
- " @Provides",
- " @Production",
- " static Executor productionExecutor() {",
- " return null;",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Root",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = RootModule.class)",
- "interface Root {",
- " Leaf leaf();",
- "}"));
- JavaFileObject generatedRoot =
- JavaFileObjects.forSourceLines(
- "test.DaggerRoot",
- "package test;",
- "",
- "import dagger.internal.DelegateFactory;",
- "import dagger.internal.DoubleCheck;",
- "import dagger.internal.InstanceFactory;",
- "import dagger.internal.SetFactory;",
- "import dagger.producers.Producer;",
- "import dagger.producers.internal.CancellationListener;",
- "import dagger.producers.internal.DelegateProducer;",
- "import dagger.producers.internal.Producers;",
- "import dagger.producers.monitoring.ProductionComponentMonitor;",
- "import java.util.Set;",
- "import java.util.concurrent.Executor;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATED_ANNOTATION,
- "final class DaggerRoot implements Root {",
- " private DaggerRoot() {}",
- "",
- " public static Builder builder() {",
- " return new Builder();",
- " }",
- "",
- " public static Root create() {",
- " return new Builder().build();",
- " }",
- "",
- " @Override",
- " public Leaf leaf() {",
- " return new LeafImpl();",
- " }",
- "",
- " static final class Builder {",
- " private Builder() {}",
- "",
- " public Root build() {",
- " return new DaggerRoot();",
- " }",
- " }",
- "",
- " protected final class LeafImpl extends DaggerLeaf implements CancellationListener {",
- " private Provider<Executor> productionImplementationExecutorProvider =",
- " new DelegateFactory<>();",
- " private Provider<Leaf> leafProvider;",
- " private Provider<ProductionComponentMonitor> monitorProvider =",
- " new DelegateFactory<>();",
- " private Provider<Set<String>> setOfStringProvider;",
- " private Producer<Set<String>> setOfStringProducer = new DelegateProducer<>();",
- "",
- " private LeafImpl() {",
- " configureInitialization();",
- " initialize();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize() {",
- " DelegateFactory.setDelegate(",
- " productionImplementationExecutorProvider,",
- " DoubleCheck.provider(",
- " (Provider) RootModule_ProductionExecutorFactory.create()));",
- " this.leafProvider = InstanceFactory.create((Leaf) this);",
- " DelegateFactory.setDelegate(",
- " monitorProvider,",
- " DoubleCheck.provider(",
- " Leaf_MonitoringModule_MonitorFactory.create(",
- " leafProvider,",
- " getSetOfProductionComponentMonitorFactoryProvider())));",
- " this.setOfStringProvider =",
- " SetFactory.<String>builder(1, 0)",
- " .addProvider(RootModule_InduceModificationInLeafFactory.create())",
- " .build();",
- " DelegateProducer.setDelegate(",
- " setOfStringProducer,",
- " Producers.producerFromProvider(getSetOfStringProvider()));",
- " }",
- "",
- " @Override",
- " protected Provider<Executor> getProductionImplementationExecutorProvider() {",
- " return productionImplementationExecutorProvider;",
- " }",
- "",
- " protected Provider<Set<ProductionComponentMonitor.Factory>> ",
- " getSetOfProductionComponentMonitorFactoryProvider() {",
- " return SetFactory.<ProductionComponentMonitor.Factory>empty();",
- " }",
- "",
- " @Override",
- " protected Provider<ProductionComponentMonitor> ",
- " getProductionComponentMonitorProvider() {",
- " return monitorProvider;",
- " }",
- "",
- " protected Provider<Set<String>> getSetOfStringProvider() {",
- " return setOfStringProvider;",
- " }",
- "",
- " @Override",
- " protected Producer<Set<String>> getSetOfStringProducer() {",
- " return setOfStringProducer;",
- " }",
- "",
- " @Override",
- " public void onProducerFutureCancelled(boolean mayInterruptIfRunning) {",
- " super.onProducerFutureCancelled(mayInterruptIfRunning);",
- " Producers.cancel(getSetOfStringProducer(), mayInterruptIfRunning);",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerRoot")
- .hasSourceEquivalentTo(generatedRoot);
- }
-
- @Test
- public void modifiableBindingMethods_namesDedupedAcrossImplementations() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "foo.Thing",
- "package foo;",
- "", // force multi-line format
- "public interface Thing extends CharSequence {}"),
- JavaFileObjects.forSourceLines(
- "bar.Thing",
- "package bar;",
- "", // force multi-line format
- "public interface Thing extends Runnable {}"),
- JavaFileObjects.forSourceLines(
- "test.WillInduceSetOfRunnable",
- "package test;",
- "", // force multi-line format
- "class WillInduceSetOfRunnable {}"),
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoSet;",
- "",
- "@Module",
- "interface LeafModule {",
- " @Provides",
- " static CharSequence depOnFooThing(foo.Thing thing) {",
- " return thing.toString();",
- " }",
- "",
- " @Provides",
- " @IntoSet",
- " static Runnable depOnBarThing(bar.Thing thing) {",
- " return () -> {};",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " CharSequence inducesFoo();",
- " WillInduceSetOfRunnable willInduceSetOfRunnable();",
- "}"));
-
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- "import foo.Thing;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public CharSequence inducesFoo() {",
- " return LeafModule_DepOnFooThingFactory.depOnFooThing(getThing());",
- " }",
- "",
- " @Override",
- " public abstract WillInduceSetOfRunnable willInduceSetOfRunnable();",
- "",
- " protected abstract Thing getThing();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.Multibinds;",
- "import java.util.Set;",
- "",
- "@Module",
- "interface AncestorModule {",
- " @Provides",
- " static WillInduceSetOfRunnable induce(Set<Runnable> set) {",
- " return null;",
- " }",
- "",
- " @Multibinds Set<Runnable> runnables();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " Leaf leaf();",
- "}"));
-
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import bar.Thing;",
- "import com.google.common.collect.ImmutableSet;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class LeafImpl extends DaggerLeaf {",
- " protected LeafImpl() {}",
- "",
- " private Runnable getDepOnBarThing() {",
- " return LeafModule_DepOnBarThingFactory.depOnBarThing(getThing2());",
- " }",
- "",
- " protected abstract Thing getThing2();",
- "",
- " protected Set<Runnable> getSetOfRunnable() {",
- " return ImmutableSet.<Runnable>of(getDepOnBarThing());",
- " }",
- "",
- " @Override",
- " public final WillInduceSetOfRunnable willInduceSetOfRunnable() {",
- " return AncestorModule_InduceFactory.induce(getSetOfRunnable());",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- /**
- * This test verifies that Dagger can find the appropriate child subcomponent
- * super-implementation, even if it is not enclosed in the current component's
- * super-implementation. This can happen if a subcomponent is installed with a module's {@code
- * subcomponents} attribute, but the binding is not accessed in a super-implementation. To exhibit
- * this, we use multibindings that reference the pruned subcomponent, but make the multibinding
- * also unresolved in the base implementation. An ancestor component defines a binding that
- * depends on the multibinding, which induces the previously unresolved multibinding
- * contributions, which itself induces the previously unresolved subcomponent.
- */
- @Test
- public void subcomponentInducedFromAncestor() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "Inducer");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.InducedSubcomponent",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface InducedSubcomponent {",
- " @Subcomponent.Builder",
- " interface Builder {",
- " InducedSubcomponent build();",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.MaybeLeaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = InducedSubcomponentModule.class)",
- "interface MaybeLeaf {",
- " Inducer inducer();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.MaybeLeaf",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.IntoSet;",
- "",
- "@Module(subcomponents = InducedSubcomponent.class)",
- "interface InducedSubcomponentModule {",
- " @Provides",
- " @IntoSet",
- " static Object inducedSet(InducedSubcomponent.Builder builder) {",
- " return new Object();",
- " }",
- "}"));
-
- JavaFileObject generatedMaybeLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerMaybeLeaf implements MaybeLeaf {",
- " protected DaggerMaybeLeaf() {}",
- "",
- " @Override",
- " public abstract Inducer inducer();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerMaybeLeaf")
- .hasSourceEquivalentTo(generatedMaybeLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import dagger.multibindings.Multibinds;",
- "import java.util.Set;",
- "",
- "@Module",
- "interface AncestorModule {",
- " @Provides",
- " static Inducer inducer(Set<Object> set) {",
- " return null;",
- " }",
- "",
- " @Multibinds Set<Object> set();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = AncestorModule.class)",
- "interface Ancestor {",
- " MaybeLeaf noLongerLeaf();",
- "}"));
-
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- "import com.google.common.collect.ImmutableSet;",
- "import dagger.internal.GenerationOptions;",
- "import java.util.Set;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected DaggerAncestor() {}",
- "",
- " protected abstract class MaybeLeafImpl extends DaggerMaybeLeaf {",
- " protected MaybeLeafImpl() {}",
- "",
- " private Object getInducedSet() {",
- " return InducedSubcomponentModule_InducedSetFactory.inducedSet(",
- // TODO(b/117833324): remove this unnecessary cast
- " (InducedSubcomponent.Builder) getInducedSubcomponentBuilder());",
- " }",
- "",
- " protected abstract Object getInducedSubcomponentBuilder();",
- "",
- " protected Set<Object> getSetOfObject() {",
- " return ImmutableSet.<Object>of(getInducedSet());",
- " }",
- "",
- " @Override",
- " public final Inducer inducer() {",
- " return AncestorModule_InducerFactory.inducer(getSetOfObject());",
- " }",
- "",
- " protected abstract class InducedSubcomponentImpl extends",
- " DaggerInducedSubcomponent {",
- // ^ Note that this is DaggerInducedSubcomponent, not
- // DaggerMaybeLeaf.InducedSubcomponentImpl
- " protected InducedSubcomponentImpl() {}",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .hasSourceEquivalentTo(generatedAncestor);
- }
-
- @Test
- public void rootScopedAtInjectConstructor_effectivelyMissingInSubcomponent() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "ProvidesMethodRootScoped");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.RootScope",
- "package test;",
- "",
- "import javax.inject.Scope;",
- "",
- "@Scope",
- "public @interface RootScope {}"),
- JavaFileObjects.forSourceLines(
- "test.AtInjectRootScoped",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "@RootScope",
- "class AtInjectRootScoped {",
- " @Inject AtInjectRootScoped() {}",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface Leaf {",
- " AtInjectRootScoped shouldBeEffectivelyMissingInLeaf();",
- "}"));
-
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public abstract AtInjectRootScoped shouldBeEffectivelyMissingInLeaf();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@RootScope",
- "@Component",
- "interface Root {",
- " Leaf leaf();",
- "}"));
-
- JavaFileObject generatedRoot =
- JavaFileObjects.forSourceLines(
- "test.DaggerRoot",
- "package test;",
- "",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATED_ANNOTATION,
- "final class DaggerRoot implements Root {",
- " protected final class LeafImpl extends DaggerLeaf {",
- " @Override",
- " public AtInjectRootScoped shouldBeEffectivelyMissingInLeaf() {",
- " return DaggerRoot.this.atInjectRootScopedProvider.get();",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerRoot")
- .containsElementsIn(generatedRoot);
- }
-
- @Test
- public void prunedModuleWithInstanceState() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "Pruned");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Modified",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class Modified {",
- " @Inject Modified(Pruned pruned) {}",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class LeafModule {",
- " @Provides",
- " Pruned pruned() {",
- " return new Pruned();",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " Modified modified();",
- "}"));
-
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected DaggerLeaf() {}",
- "",
- " @Override",
- " public Modified modified() {",
- " return new Modified(LeafModule_PrunedFactory.pruned(leafModule()));",
- " }",
- "",
- " protected abstract LeafModule leafModule();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.RootModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "class RootModule {",
- " @Provides",
- " static Modified modified() {",
- " return new Modified(null);",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Root",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = RootModule.class)",
- "interface Root {",
- " Leaf leaf();",
- "}"));
-
- JavaFileObject generatedRoot =
- JavaFileObjects.forSourceLines(
- "test.DaggerRoot",
- "package test;",
- "",
- IMPORT_GENERATED_ANNOTATION,
- "",
- GENERATED_ANNOTATION,
- "final class DaggerRoot implements Root {",
- " protected final class LeafImpl extends DaggerLeaf {",
- " @Override",
- " public Modified modified() {",
- " return RootModule_ModifiedFactory.modified();",
- " }",
- "",
- " @Override",
- " protected LeafModule leafModule() {",
- " return null;",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerRoot")
- .containsElementsIn(generatedRoot);
- }
-
- @Test
- public void modifiableCycles() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.A",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class A {",
- " @Inject A(B b) {}",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.B",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "import javax.inject.Provider;",
- "",
- "class B {",
- " @Inject B(Provider<A> a) {}",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Subcomponent",
- "interface Leaf {",
- " Provider<A> frameworkInstanceCycle();",
- "}"));
-
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import dagger.internal.DelegateFactory;",
- "import dagger.internal.GenerationOptions;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " private Provider<A> aProvider;",
- " private Provider<B> bProvider;",
- "",
- " protected DaggerLeaf() {}",
- "",
- " protected void configureInitialization() {",
- " initialize();",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize() {",
- " this.aProvider = new DelegateFactory<>();",
- " this.bProvider = B_Factory.create(frameworkInstanceCycle());",
- " DelegateFactory.setDelegate(aProvider, A_Factory.create(getBProvider()));",
- " }",
- "",
- " @Override",
- " public Provider<A> frameworkInstanceCycle() {",
- " return aProvider;",
- " }",
- "",
- " protected Provider getBProvider() {",
- " return bProvider;",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .hasSourceEquivalentTo(generatedLeaf);
- }
-
- /**
- * This tests a regression case where the component builder in the base implementation used one
- * set of disambiguated names from all of the {@link ComponentDescriptor#requirements()}, and the
- * final implementation used a different set of disambiguated names from the resolved {@link
- * BindingGraph#componentRequirements()}. This resulted in generated output that didn't compile,
- * as the builder implementation attempted to use the new names in validation, which didn't line
- * up with the old names.
- */
- @Test
- public void componentBuilderFields_consistencyAcrossImplementations() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "a.Mod",
- "package a;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import javax.inject.Named;",
- "",
- "@Module",
- "public class Mod {",
- " @Provides",
- " @Named(\"a\")",
- " int i() { return 0; }",
- "}"),
- JavaFileObjects.forSourceLines(
- "b.Mod",
- "package b;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import javax.inject.Named;",
- "",
- "@Module",
- "public class Mod {",
- " @Provides",
- " @Named(\"b\")",
- " int i() { return 0; }",
- "}"),
- JavaFileObjects.forSourceLines(
- "c.Mod",
- "package c;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "import javax.inject.Named;",
- "",
- "@Module",
- "public class Mod {",
- " @Provides",
- " @Named(\"c\")",
- " int i() { return 0; }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.HasUnusedModuleLeaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "import javax.inject.Named;",
- "",
- "@Subcomponent(modules = {a.Mod.class, b.Mod.class, c.Mod.class})",
- "interface HasUnusedModuleLeaf {",
- " @Named(\"a\") int a();",
- // b omitted intentionally
- " @Named(\"c\") int c();",
- "",
- " @Subcomponent.Builder",
- " interface Builder {",
- " Builder setAMod(a.Mod mod);",
- " Builder setBMod(b.Mod mod);",
- " Builder setCMod(c.Mod mod);",
- " HasUnusedModuleLeaf build();",
- " }",
- "}"));
-
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import a.Mod;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerHasUnusedModuleLeaf implements HasUnusedModuleLeaf {",
- " public abstract static class Builder implements HasUnusedModuleLeaf.Builder {",
- " protected Mod mod;",
- " protected b.Mod mod2;",
- " protected c.Mod mod3;",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerHasUnusedModuleLeaf")
- .containsElementsIn(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Root",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component",
- "interface Root {",
- " HasUnusedModuleLeaf.Builder leaf();",
- "}"));
-
- JavaFileObject generatedRoot =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- "import a.Mod;",
- "",
- GENERATED_ANNOTATION,
- "final class DaggerRoot implements Root {",
- " private final class HasUnusedModuleLeafBuilder",
- " extends DaggerHasUnusedModuleLeaf.Builder {",
- " @Override",
- " public HasUnusedModuleLeaf build() {",
- " if (mod == null) {",
- " this.mod = new Mod();",
- " }",
- // Before this regression was fixed, `mod3` was instead `mod2`, since the `b.Mod` was
- // pruned from the graph and did not need validation.
- " if (mod3 == null) {",
- " this.mod3 = new c.Mod();",
- " }",
- " return new HasUnusedModuleLeafImpl(mod, mod3);",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerRoot")
- .containsElementsIn(generatedRoot);
- }
-
- @Test
- public void dependencyExpressionCasting() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.PublicType",
- "package test;",
- "", //
- "public class PublicType {}"),
- JavaFileObjects.forSourceLines(
- "test.ModifiableNonPublicSubclass",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class ModifiableNonPublicSubclass extends PublicType {",
- " @Inject ModifiableNonPublicSubclass() {}",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Parameterized",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class Parameterized<T extends PublicType> {",
- " @Inject Parameterized(T t) {}",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "import javax.inject.Provider;",
- "",
- "@Subcomponent",
- "interface Leaf {",
- " Parameterized<ModifiableNonPublicSubclass> parameterizedWithNonPublicSubtype();",
- "}"));
-
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " @Override",
- " public Parameterized<ModifiableNonPublicSubclass> ",
- " parameterizedWithNonPublicSubtype() {",
- " return Parameterized_Factory.newInstance(",
- " (ModifiableNonPublicSubclass) getModifiableNonPublicSubclass());",
- " }",
- "",
- " protected Object getModifiableNonPublicSubclass() {",
- " return new ModifiableNonPublicSubclass();",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .containsElementsIn(generatedLeaf);
- }
-
- @Test
- public void multipleComponentMethodsForSameBindingRequest() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface Leaf {",
- " String string1();",
- " String string2();",
- "}"));
-
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " @Override",
- " public final String string2() {",
- " return string1();",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .containsElementsIn(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.RootModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "interface RootModule {",
- " @Provides",
- " static String string() {",
- " return new String();",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Root",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = RootModule.class)",
- "interface Root {",
- " Leaf leaf();",
- "}"));
-
- JavaFileObject generatedRoot =
- JavaFileObjects.forSourceLines(
- "test.DaggerRoot",
- "package test;",
- "",
- GENERATED_ANNOTATION,
- "final class DaggerRoot implements Root {",
- " protected final class LeafImpl extends DaggerLeaf {",
- " private LeafImpl() {}",
- "",
- " @Override",
- " public String string1() {",
- " return RootModule_StringFactory.string();",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerRoot")
- .containsElementsIn(generatedRoot);
- }
-
- @Test
- public void boundInstanceUsedOnlyInInitialize() {
- JavaFileObject subcomponent =
- JavaFileObjects.forSourceLines(
- "test.Sub",
- "package test;",
- "",
- "import dagger.BindsInstance;",
- "import dagger.Subcomponent;",
- "import javax.inject.Provider;",
- "",
- "@Subcomponent",
- "interface Sub {",
- " Provider<String> stringProvider();",
- "",
- " @Subcomponent.Builder",
- " interface Builder {",
- " @BindsInstance",
- " Builder string(String string);",
- " Sub build();",
- " }",
- "}");
-
- JavaFileObject generated =
- JavaFileObjects.forSourceLines(
- "test.Sub",
- "package test;",
- "",
- "import dagger.internal.InstanceFactory;",
- "import dagger.internal.Preconditions;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerSub implements Sub {",
- " private Provider<String> stringProvider;",
- "",
- " protected DaggerSub() {}",
- "",
- " protected void configureInitialization(String stringParam) {",
- " initialize(stringParam);",
- " }",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize(final String stringParam) {",
- " this.stringProvider = InstanceFactory.create(stringParam);",
- " }",
- "",
- " @Override",
- " public Provider<String> stringProvider() {",
- " return stringProvider;",
- " }",
- "}");
-
- Compilation compilation = compile(ImmutableList.of(subcomponent));
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerSub")
- .containsElementsIn(generated);
- }
-
- @Test
- public void packagePrivate_derivedFromFrameworkInstance_ComponentMethod() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.PackagePrivate",
- "package test;",
- "",
- "import dagger.Reusable;",
- "import javax.inject.Inject;",
- "",
- "@Reusable", // Use @Reusable to force a framework field
- "class PackagePrivate {",
- " @Inject PackagePrivate() {}",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent",
- "interface Leaf {",
- " PackagePrivate packagePrivate();",
- "}"));
-
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " private Provider<PackagePrivate> packagePrivateProvider;",
- "",
- " @Override",
- " public PackagePrivate packagePrivate() {",
- " return (PackagePrivate) getPackagePrivateProvider().get();",
- " }",
- "",
- " protected Provider getPackagePrivateProvider() {",
- " return packagePrivateProvider;",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .containsElementsIn(generatedLeaf);
- }
-
- @Test
- public void castModifiableMethodAccessedInFinalImplementation() {
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "PackagePrivate");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.PublicBaseType",
- "package test;",
- "", //
- "public class PublicBaseType {}"),
- JavaFileObjects.forSourceLines(
- "test.PackagePrivateSubtype",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- // Force this to be a modifiable binding resolved in the ancestor even though the
- // binding is requested in the leaf.
- "@AncestorScope",
- "class PackagePrivateSubtype extends PublicBaseType {",
- " @Inject PackagePrivateSubtype() {}",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorScope",
- "package test;",
- "",
- "import javax.inject.Scope;",
- "",
- "@Scope @interface AncestorScope {}"),
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.Binds;",
- "import dagger.BindsOptionalOf;",
- "import dagger.Module;",
- "",
- "@Module",
- "interface LeafModule {",
- " @Binds PublicBaseType publicBaseType(PackagePrivateSubtype subtype);",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.InjectsOptionalOfModifiable",
- "package test;",
- "",
- "import java.util.Optional;",
- "import javax.inject.Inject;",
- "",
- "class InjectsOptionalOfModifiable {",
- " @Inject InjectsOptionalOfModifiable(",
- " Optional<PublicBaseType> optionalOfModifiable) {}",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@Subcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " InjectsOptionalOfModifiable injectsOptionalOfModifiable();",
- "}"));
-
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf {",
- " protected abstract Optional<PublicBaseType> getOptionalOfPublicBaseType();",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .containsElementsIn(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.InjectsPackagePrivateSubtype",
- "package test;",
- "",
- "import java.util.Optional;",
- "import javax.inject.Inject;",
- "",
- "class InjectsPackagePrivateSubtype {",
- " @Inject InjectsPackagePrivateSubtype(",
- // Force a modifiable binding method for PackagePrivateSubtype in Ancestor. The
- // final Leaf implementation will refer to this method, but will need to cast it
- // since the PackagePrivateSubtype is accessible from the current package, but the
- // method returns Object
- " PackagePrivateSubtype packagePrivateSubtype) {}",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.AncestorModule",
- "package test;",
- "",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "interface AncestorModule {",
- " @Provides",
- " static PackagePrivateSubtype packagePrivateSubtype() {",
- " return new PackagePrivateSubtype();",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Ancestor",
- "package test;",
- "",
- "import dagger.Subcomponent;",
- "",
- "@AncestorScope",
- "@Subcomponent",
- "interface Ancestor {",
- " InjectsPackagePrivateSubtype injectsPackagePrivateSubtype();",
- " Leaf leaf();",
- "}"));
-
- JavaFileObject generatedAncestor =
- JavaFileObjects.forSourceLines(
- "test.DaggerAncestor",
- "package test;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerAncestor implements Ancestor {",
- " protected Object getPackagePrivateSubtype() {",
- " return getPackagePrivateSubtypeProvider().get();",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerAncestor")
- .containsElementsIn(generatedAncestor);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.RootModule",
- "package test;",
- "",
- "import dagger.BindsOptionalOf;",
- "import dagger.Module;",
- "",
- "@Module",
- "interface RootModule {",
- " @BindsOptionalOf",
- " PublicBaseType optionalPublicBaseType();",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Root",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = RootModule.class)",
- "interface Root {",
- " Ancestor ancestor();",
- "}"));
-
- JavaFileObject generatedRoot =
- JavaFileObjects.forSourceLines(
- "test.DaggerRoot",
- "package test;",
- "",
- GENERATED_ANNOTATION,
- "final class DaggerRoot implements Root {",
- " protected final class AncestorImpl extends DaggerAncestor {",
- " protected final class LeafImpl extends DaggerAncestor.LeafImpl {",
- " @Override",
- " protected Optional<PublicBaseType> getOptionalOfPublicBaseType() {",
- " return Optional.of(",
- " (PublicBaseType) AncestorImpl.this.getPackagePrivateSubtype());",
- " }",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerRoot")
- .containsElementsIn(generatedRoot);
- }
-
- @Test
- public void injectInLeaf_ProductionInRoot() {
- // most of this is also covered in ProducesMethodShadowsInjectConstructorTest, but this test
- // asserts that the correct PrunedConcreteBindingExpression is used
- ImmutableList.Builder<JavaFileObject> filesToCompile = ImmutableList.builder();
- createSimplePackagePrivateClasses(filesToCompile, "Dependency", "Missing");
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.Injected",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "class Injected {",
- " @Inject Injected(Dependency dependency, Missing missing) {}",
- "",
- " Injected(Dependency dependency) {}",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.LeafModule",
- "package test;",
- "",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "",
- "@ProducerModule",
- "interface LeafModule {",
- " @Produces",
- " static Object dependsOnInjectReplacedWithProduces(Injected injected) {",
- " return new Object();",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Leaf",
- "package test;",
- "",
- "import dagger.producers.Producer;",
- "import dagger.producers.ProductionSubcomponent;",
- "",
- "@ProductionSubcomponent(modules = LeafModule.class)",
- "interface Leaf {",
- " Producer<Object> objectProducer();",
- "}"));
-
- JavaFileObject generatedLeaf =
- JavaFileObjects.forSourceLines(
- "test.DaggerLeaf",
- "package test;",
- "",
- GENERATION_OPTIONS_ANNOTATION,
- GENERATED_ANNOTATION,
- "public abstract class DaggerLeaf implements Leaf, CancellationListener {",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize() {",
- " this.injectedProvider = Injected_Factory.create(",
- " getDependencyProvider(), getMissingProvider());",
- " this.injectedProducer = Producers.producerFromProvider(getInjectedProvider());",
- " this.dependsOnInjectReplacedWithProducesProducer =",
- " LeafModule_DependsOnInjectReplacedWithProducesFactory.create(",
- " getProductionImplementationExecutorProvider(),",
- " getProductionComponentMonitorProvider(),",
- " getInjectedProducer());",
- " this.objectProducerEntryPoint =",
- " Producers.entryPointViewOf(",
- " dependsOnInjectReplacedWithProducesProducer, this);",
- " }",
- "",
- " protected abstract Provider getDependencyProvider();",
- " protected abstract Provider getMissingProvider();",
- "",
- " protected Provider getInjectedProvider() {",
- " return injectedProvider;",
- " }",
- "",
- " protected Producer getInjectedProducer() {",
- " return injectedProducer;",
- " }",
- "}");
- Compilation compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerLeaf")
- .containsElementsIn(generatedLeaf);
-
- filesToCompile.add(
- JavaFileObjects.forSourceLines(
- "test.RootModule",
- "package test;",
- "",
- "import com.google.common.util.concurrent.MoreExecutors;",
- "import dagger.Provides;",
- "import dagger.producers.ProducerModule;",
- "import dagger.producers.Produces;",
- "import dagger.producers.Production;",
- "import java.util.concurrent.Executor;",
- "",
- "@ProducerModule",
- "interface RootModule {",
- " @Produces",
- " static Injected replaceInjectWithProduces(Dependency dependency) {",
- " return new Injected(dependency);",
- " }",
- "",
- " @Produces",
- " static Dependency dependency() {",
- " return new Dependency();",
- " }",
- "",
- " @Provides",
- " @Production",
- " static Executor executor() {",
- " return MoreExecutors.directExecutor();",
- " }",
- "}"),
- JavaFileObjects.forSourceLines(
- "test.Root",
- "package test;",
- "",
- "import dagger.producers.ProductionComponent;",
- "",
- "@ProductionComponent(modules = RootModule.class)",
- "interface Root {",
- " Leaf leaf();",
- "}"));
-
- JavaFileObject generatedRoot =
- JavaFileObjects.forSourceLines(
- "test.DaggerRoot",
- "package test;",
- "",
- GENERATED_ANNOTATION,
- "final class DaggerRoot implements Root, CancellationListener {",
- " private Producer<Dependency> dependencyProducer;",
- " private Producer<Injected> replaceInjectWithProducesProducer;",
- "",
- " @SuppressWarnings(\"unchecked\")",
- " private void initialize() {",
- " this.productionImplementationExecutorProvider =",
- " DoubleCheck.provider((Provider) RootModule_ExecutorFactory.create());",
- " this.rootProvider = InstanceFactory.create((Root) this);",
- " this.monitorProvider =",
- " DoubleCheck.provider(",
- " Root_MonitoringModule_MonitorFactory.create(",
- " rootProvider,",
- " SetFactory.<ProductionComponentMonitor.Factory>empty()));",
- " this.dependencyProducer =",
- " RootModule_DependencyFactory.create(",
- " productionImplementationExecutorProvider, monitorProvider);",
- " this.replaceInjectWithProducesProducer =",
- " RootModule_ReplaceInjectWithProducesFactory.create(",
- " productionImplementationExecutorProvider,",
- " monitorProvider,",
- " dependencyProducer);",
- " }",
- "",
- " protected final class LeafImpl extends DaggerLeaf",
- " implements CancellationListener {",
- " @Override",
- " protected Provider getDependencyProvider() {",
- " return MissingBindingFactory.create();",
- " }",
- "",
- " @Override",
- " protected Provider getMissingProvider() {",
- " return MissingBindingFactory.create();",
- " }",
- "",
- " @Override",
- " protected Producer getInjectedProducer() {",
- " return DaggerRoot.this.replaceInjectWithProducesProducer;",
- " }",
- " }",
- "}");
- compilation = compile(filesToCompile.build());
- assertThat(compilation).succeededWithoutWarnings();
- assertThat(compilation)
- .generatedSourceFile("test.DaggerRoot")
- .containsElementsIn(generatedRoot);
- }
-
- // TODO(ronshapiro): remove copies from AheadOfTimeSubcomponents*Test classes
- private void createSimplePackagePrivateClasses(
- ImmutableList.Builder<JavaFileObject> filesBuilder, String... ancillaryClasses) {
- for (String className : ancillaryClasses) {
- filesBuilder.add(
- JavaFileObjects.forSourceLines(
- String.format("test.%s", className),
- "package test;",
- "",
- String.format("class %s { }", className)));
- }
- }
-
- private static Compilation compile(Iterable<JavaFileObject> files, CompilerMode... modes) {
- return compilerWithOptions(
- ObjectArrays.concat(
- new CompilerMode[] {AHEAD_OF_TIME_SUBCOMPONENTS_MODE}, modes, CompilerMode.class))
- .compile(files);
- }
-}
diff --git a/javatests/dagger/internal/codegen/AnnotationProtoConverterTest.java b/javatests/dagger/internal/codegen/AnnotationProtoConverterTest.java
deleted file mode 100644
index fda58599f..000000000
--- a/javatests/dagger/internal/codegen/AnnotationProtoConverterTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2019 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.auto.common.AnnotationMirrors;
-import com.google.testing.compile.CompilationRule;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import javax.lang.model.element.AnnotationMirror;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/** Tests {@link TypeProtoConverter}. */
-@RunWith(JUnit4.class)
-public class AnnotationProtoConverterTest {
- @Rule public CompilationRule compilationRule = new CompilationRule();
-
- private DaggerElements elements;
- private DaggerTypes types;
- private AnnotationProtoConverter annotationProtoConverter;
-
- @Before
- public void setUp() {
- this.elements = new DaggerElements(compilationRule.getElements(), compilationRule.getTypes());
- this.types = new DaggerTypes(compilationRule.getTypes(), elements);
- this.annotationProtoConverter =
- new AnnotationProtoConverter(new TypeProtoConverter(types, elements));
- }
-
- @Retention(RetentionPolicy.RUNTIME)
- @interface TestAnnotation {
- byte b();
- boolean bool();
- short s();
- char c();
- int i();
- long l();
- double d();
- float f();
-
- String string();
- RetentionPolicy enumValue();
- Class<?> classValue();
- HasDefaults[] nestedAnnotations();
- }
-
- @Retention(RetentionPolicy.RUNTIME)
- @interface HasDefaults {
- int value() default 2;
- }
-
- @TestAnnotation(
- b = 1,
- bool = true,
- s = 2,
- c = 'c',
- i = 4,
- l = 5,
- d = 6.0d,
- f = 7.0f,
- string = "hello, world",
- enumValue = RetentionPolicy.CLASS,
- classValue = AnnotationProtoConverter.class,
- nestedAnnotations = {@HasDefaults, @HasDefaults(8)})
- static class TestSubject {}
-
- @Test
- public void conversion() {
- AnnotationMirror actual =
- getOnlyElement(elements.getTypeElement(TestSubject.class).getAnnotationMirrors());
- AnnotationMirror translated =
- annotationProtoConverter.fromProto(AnnotationProtoConverter.toProto(actual));
- assertThat(AnnotationMirrors.equivalence().equivalent(actual, translated)).isTrue();
- }
-}
diff --git a/javatests/dagger/internal/codegen/AssistedErrorsTest.java b/javatests/dagger/internal/codegen/AssistedErrorsTest.java
new file mode 100644
index 000000000..bf61412d9
--- /dev/null
+++ b/javatests/dagger/internal/codegen/AssistedErrorsTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
+
+import com.google.common.collect.ImmutableCollection;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class AssistedErrorsTest {
+ @Parameters(name = "{0}")
+ public static ImmutableCollection<Object[]> parameters() {
+ return CompilerMode.TEST_PARAMETERS;
+ }
+
+ private final CompilerMode compilerMode;
+
+ public AssistedErrorsTest(CompilerMode compilerMode) {
+ this.compilerMode = compilerMode;
+ }
+
+ @Test
+ public void testAssistedNotWithAssistedInjectionConstructor() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "",
+ "final class Foo {",
+ " Foo(",
+ " @Assisted String str",
+ " ) {}",
+ "",
+ " void someMethod(",
+ " @Assisted int i",
+ " ) {}",
+ "}");
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(foo);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(2);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@Assisted parameters can only be used within an @AssistedInject-annotated constructor")
+ .inFile(foo)
+ .onLine(7);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@Assisted parameters can only be used within an @AssistedInject-annotated constructor")
+ .inFile(foo)
+ .onLine(11);
+ }
+
+ @Test
+ public void testNestedFactoryNotStatic() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "import javax.inject.Qualifier;",
+ "",
+ "class Foo {",
+ " @Qualifier @interface FooQualifier {}",
+ "",
+ " @AssistedInject",
+ " Foo(",
+ " @FooQualifier @Assisted int i",
+ " ) {}",
+ "}");
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(foo);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining("Qualifiers cannot be used with @Assisted parameters.")
+ .inFile(foo)
+ .onLine(12);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/AssistedFactoryErrorsTest.java b/javatests/dagger/internal/codegen/AssistedFactoryErrorsTest.java
new file mode 100644
index 000000000..2bcfedcc8
--- /dev/null
+++ b/javatests/dagger/internal/codegen/AssistedFactoryErrorsTest.java
@@ -0,0 +1,839 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
+
+import com.google.common.collect.ImmutableCollection;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class AssistedFactoryErrorsTest {
+ @Parameters(name = "{0}")
+ public static ImmutableCollection<Object[]> parameters() {
+ return CompilerMode.TEST_PARAMETERS;
+ }
+
+ private final CompilerMode compilerMode;
+
+ public AssistedFactoryErrorsTest(CompilerMode compilerMode) {
+ this.compilerMode = compilerMode;
+ }
+
+ @Test
+ public void testFactoryNotAbstract() {
+ JavaFileObject factory =
+ JavaFileObjects.forSourceLines(
+ "test.Factory",
+ "package test;",
+ "",
+ "import dagger.assisted.AssistedFactory;",
+ "",
+ "@AssistedFactory class Factory {}");
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(factory);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "The @AssistedFactory-annotated type must be either an abstract class or interface.");
+ }
+
+ @Test
+ public void testNestedFactoryNotStatic() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "import dagger.assisted.AssistedFactory;",
+ "",
+ "class Foo {",
+ " @AssistedInject",
+ " Foo(@Assisted int i) {}",
+ "",
+ " @AssistedFactory",
+ " abstract class Factory {",
+ " abstract Foo create(int i);",
+ " }",
+ "}");
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(foo);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining("Nested @AssistedFactory-annotated types must be static.");
+ }
+
+ @Test
+ public void testFactoryMissingAbstractMethod() {
+ JavaFileObject factory =
+ JavaFileObjects.forSourceLines(
+ "test.Factory",
+ "package test;",
+ "",
+ "import dagger.assisted.AssistedFactory;",
+ "",
+ "@AssistedFactory interface Factory {}");
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(factory);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "The @AssistedFactory-annotated type is missing an abstract, non-default method whose"
+ + " return type matches the assisted injection type.");
+ }
+
+ @Test
+ public void testFactoryReturnsNonDeclaredType() {
+ JavaFileObject noInject =
+ JavaFileObjects.forSourceLines(
+ "test.NoInject", "package test;", "", "final class NoInject {}");
+ JavaFileObject noAssistedParam =
+ JavaFileObjects.forSourceLines(
+ "test.NoAssistedParam",
+ "package test;",
+ "",
+ "import dagger.assisted.AssistedInject;",
+ "",
+ "final class NoAssistedParam {",
+ " @AssistedInject NoAssistedParam() {}",
+ "}");
+ JavaFileObject factory =
+ JavaFileObjects.forSourceLines(
+ "test.Factory",
+ "package test;",
+ "",
+ "import dagger.assisted.AssistedFactory;",
+ "",
+ "@AssistedFactory",
+ "interface Factory<T> {",
+ " int createInt();", // Fails return type not @AssistedInject
+ "",
+ " NoInject createNoInject();", // Fails return type not @AssistedInject
+ "",
+ " NoAssistedParam createNoAssistedParam();", // Succeeds
+ "",
+ " T createT();", // Fails return type not @AssistedInject
+ "}");
+ Compilation compilation =
+ compilerWithOptions(compilerMode.javacopts()).compile(factory, noInject, noAssistedParam);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(4);
+
+ assertThat(compilation)
+ .hadErrorContaining(
+ "The @AssistedFactory-annotated type should contain a single abstract, non-default "
+ + "method but found multiple: ["
+ + "createInt(), createNoInject(), createNoAssistedParam(), createT()]")
+ .inFile(factory)
+ .onLine(6);
+
+ assertThat(compilation)
+ .hadErrorContaining(
+ "Invalid return type: int. "
+ + "An assisted factory's abstract method must return a type with an "
+ + "@AssistedInject-annotated constructor.")
+ .inFile(factory)
+ .onLine(7);
+
+ assertThat(compilation)
+ .hadErrorContaining(
+ "Invalid return type: test.NoInject. "
+ + "An assisted factory's abstract method must return a type with an "
+ + "@AssistedInject-annotated constructor.")
+ .inFile(factory)
+ .onLine(9);
+
+ assertThat(compilation)
+ .hadErrorContaining(
+ "Invalid return type: T. "
+ + "An assisted factory's abstract method must return a type with an "
+ + "@AssistedInject-annotated constructor.")
+ .inFile(factory)
+ .onLine(13);
+ }
+
+ @Test
+ public void testFactoryMultipleAbstractMethods() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "",
+ "class Foo {",
+ " @AssistedInject Foo(@Assisted int i) {}",
+ "}");
+
+ JavaFileObject fooFactoryInterface =
+ JavaFileObjects.forSourceLines(
+ "test.FooFactoryInterface",
+ "package test;",
+ "",
+ "interface FooFactoryInterface {",
+ " Foo createFoo1(int i);",
+ "}");
+
+ JavaFileObject fooFactory =
+ JavaFileObjects.forSourceLines(
+ "test.FooFactory",
+ "package test;",
+ "",
+ "import dagger.assisted.AssistedFactory;",
+ "",
+ "@AssistedFactory",
+ "interface FooFactory extends FooFactoryInterface {",
+ " Foo createFoo2(int i);",
+ "",
+ " Foo createFoo3(int i);",
+ "}");
+
+ Compilation compilation =
+ compilerWithOptions(compilerMode.javacopts()).compile(foo, fooFactory, fooFactoryInterface);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "The @AssistedFactory-annotated type should contain a single abstract, non-default "
+ + "method but found multiple: [createFoo1(int), createFoo2(int), createFoo3(int)]");
+ }
+
+ @Test
+ public void testFactoryMismatchingParameter() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "",
+ "class Foo {",
+ " @AssistedInject Foo(@Assisted int i) {}",
+ "}");
+
+ JavaFileObject fooFactory =
+ JavaFileObjects.forSourceLines(
+ "test.FooFactory",
+ "package test;",
+ "",
+ "import dagger.assisted.AssistedFactory;",
+ "",
+ "@AssistedFactory",
+ "interface FooFactory {",
+ " Foo create(String i);",
+ "}");
+ Compilation compilation =
+ compilerWithOptions(compilerMode.javacopts()).compile(foo, fooFactory);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "The parameters in the factory method must match the @Assisted parameters in "
+ + "test.Foo.\n"
+ + " Actual: test.FooFactory#create(java.lang.String)\n"
+ + " Expected: test.FooFactory#create(int)");
+ }
+
+ @Test
+ public void testFactoryMismatchingGenericParameter() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "",
+ "class Foo<T> {",
+ " @AssistedInject Foo(@Assisted T t) {}",
+ "}");
+
+ JavaFileObject fooFactory =
+ JavaFileObjects.forSourceLines(
+ "test.FooFactory",
+ "package test;",
+ "",
+ "import dagger.assisted.AssistedFactory;",
+ "",
+ "@AssistedFactory",
+ "interface FooFactory<T> {",
+ " Foo<T> create(String str);",
+ "}");
+ Compilation compilation =
+ compilerWithOptions(compilerMode.javacopts()).compile(foo, fooFactory);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "The parameters in the factory method must match the @Assisted parameters in "
+ + "test.Foo<T>.\n"
+ + " Actual: test.FooFactory#create(java.lang.String)\n"
+ + " Expected: test.FooFactory#create(T)");
+ }
+
+ @Test
+ public void testFactoryDuplicateGenericParameter() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "",
+ "class Foo<T> {",
+ " @AssistedInject Foo(@Assisted String str, @Assisted T t) {}",
+ "}");
+
+ JavaFileObject fooFactory =
+ JavaFileObjects.forSourceLines(
+ "test.FooFactory",
+ "package test;",
+ "",
+ "import dagger.assisted.AssistedFactory;",
+ "",
+ "@AssistedFactory",
+ "interface FooFactory {",
+ " Foo<String> create(String str1, String str2);",
+ "}");
+ Compilation compilation =
+ compilerWithOptions(compilerMode.javacopts()).compile(foo, fooFactory);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@AssistedFactory method has duplicate @Assisted types: @Assisted java.lang.String");
+ }
+
+ @Test
+ public void testAssistedInjectionRequest() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "",
+ "class Foo {",
+ " @AssistedInject Foo(@Assisted String str) {}",
+ "}");
+
+ JavaFileObject bar =
+ JavaFileObjects.forSourceLines(
+ "test.Bar",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "class Bar {",
+ " @Inject",
+ " Bar(Foo foo, Provider<Foo> fooProvider) {}",
+ "}");
+
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.FooModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Provider;",
+ "",
+ "@Module",
+ "class FooModule {",
+ " @Provides",
+ " static int provideInt(Foo foo, Provider<Foo> fooProvider) {",
+ " return 0;",
+ " }",
+ "}");
+
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.FooComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "",
+ "@Component",
+ "interface FooComponent {",
+ " Foo foo();",
+ "",
+ " Provider<Foo> fooProvider();",
+ "}");
+
+ Compilation compilation =
+ compilerWithOptions(compilerMode.javacopts()).compile(foo, bar, module, component);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(6);
+
+ String fooError =
+ "Dagger does not support injecting @AssistedInject type, test.Foo. "
+ + "Did you mean to inject its assisted factory type instead?";
+ assertThat(compilation).hadErrorContaining(fooError).inFile(bar).onLine(8);
+ assertThat(compilation).hadErrorContaining(fooError).inFile(module).onLine(10);
+ assertThat(compilation).hadErrorContaining(fooError).inFile(component).onLine(8);
+
+ String fooProviderError =
+ "Dagger does not support injecting @AssistedInject type, javax.inject.Provider<test.Foo>. "
+ + "Did you mean to inject its assisted factory type instead?";
+ assertThat(compilation).hadErrorContaining(fooProviderError).inFile(bar).onLine(8);
+ assertThat(compilation).hadErrorContaining(fooProviderError).inFile(module).onLine(10);
+ assertThat(compilation).hadErrorContaining(fooProviderError).inFile(component).onLine(10);
+ }
+
+ @Test
+ public void testProvidesAssistedBindings() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "import dagger.assisted.AssistedFactory;",
+ "",
+ "class Foo {",
+ " @AssistedInject Foo(@Assisted int i) {}",
+ "",
+ " @AssistedFactory",
+ " interface Factory {",
+ " Foo create(int i);",
+ " }",
+ "}");
+
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.FooModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Provider;",
+ "",
+ "@Module",
+ "class FooModule {",
+ " @Provides",
+ " static Foo provideFoo() {",
+ " return null;",
+ " }",
+ "",
+ " @Provides",
+ " static Foo.Factory provideFooFactory() {",
+ " return null;",
+ " }",
+ "}");
+
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(foo, module);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(2);
+ assertThat(compilation)
+ .hadErrorContaining("[test.Foo] Dagger does not support providing @AssistedInject types.")
+ .inFile(module)
+ .onLine(10);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[test.Foo.Factory] Dagger does not support providing @AssistedFactory types.")
+ .inFile(module)
+ .onLine(15);
+ }
+
+ @Test
+ public void testProvidesAssistedBindingsAsFactoryBindsInstance() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "import dagger.assisted.AssistedFactory;",
+ "",
+ "class Foo {",
+ " @AssistedInject Foo(@Assisted int i) {}",
+ "",
+ " @AssistedFactory",
+ " interface Factory {",
+ " Foo create(int i);",
+ " }",
+ "}");
+
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.FooComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.BindsInstance;",
+ "",
+ "@Component",
+ "interface FooComponent {",
+ " @Component.Factory",
+ " interface Factory {",
+ " FooComponent create(",
+ " @BindsInstance Foo foo,",
+ " @BindsInstance Foo.Factory fooFactory);",
+ " }",
+ "}");
+
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(foo, component);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(2);
+ assertThat(compilation)
+ .hadErrorContaining("[test.Foo] Dagger does not support providing @AssistedInject types.")
+ .inFile(component)
+ .onLine(11);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[test.Foo.Factory] Dagger does not support providing @AssistedFactory types.")
+ .inFile(component)
+ .onLine(12);
+ }
+
+ @Test
+ public void testProvidesAssistedBindingsAsBuilderBindsInstance() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "import dagger.assisted.AssistedFactory;",
+ "",
+ "class Foo {",
+ " @AssistedInject Foo(@Assisted int i) {}",
+ "",
+ " @AssistedFactory",
+ " interface Factory {",
+ " Foo create(int i);",
+ " }",
+ "}");
+
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.FooComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.BindsInstance;",
+ "",
+ "@Component",
+ "interface FooComponent {",
+ " @Component.Builder",
+ " interface Builder {",
+ " @BindsInstance Builder foo(Foo foo);",
+ " @BindsInstance Builder fooFactory(Foo.Factory fooFactory);",
+ " FooComponent build();",
+ " }",
+ "}");
+
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(foo, component);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(2);
+ assertThat(compilation)
+ .hadErrorContaining("[test.Foo] Dagger does not support providing @AssistedInject types.")
+ .inFile(component)
+ .onLine(10);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[test.Foo.Factory] Dagger does not support providing @AssistedFactory types.")
+ .inFile(component)
+ .onLine(11);
+ }
+
+ @Test
+ public void testInjectsProviderOfAssistedFactory() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "import dagger.assisted.AssistedFactory;",
+ "",
+ "class Foo {",
+ " @AssistedInject Foo(@Assisted int i) {}",
+ "",
+ " @AssistedFactory",
+ " interface Factory {",
+ " Foo create(int i);",
+ " }",
+ "}");
+
+ JavaFileObject bar =
+ JavaFileObjects.forSourceLines(
+ "test.Bar",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "",
+ "class Bar {",
+ " @Inject",
+ " Bar(Foo.Factory fooFactory, Provider<Foo.Factory> fooFactoryProvider) {}",
+ "}");
+
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(foo, bar);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, or Produced<T> "
+ + "when T is an @AssistedFactory-annotated type such as test.Foo.Factory")
+ .inFile(bar)
+ .onLine(8);
+ }
+
+ @Test
+ public void testScopedAssistedInjection() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "import dagger.assisted.AssistedFactory;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "class Foo {",
+ " @AssistedInject",
+ " Foo(@Assisted int i) {}",
+ "",
+ " @AssistedFactory",
+ " interface Factory {",
+ " Foo create(int i);",
+ " }",
+ "}");
+
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(foo);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining("A type with an @AssistedInject-annotated constructor cannot be scoped")
+ .inFile(foo)
+ .onLine(8);
+ }
+
+ @Test
+ public void testMultipleInjectAnnotations() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "import javax.inject.Inject;",
+ "",
+ "class Foo {",
+ " @Inject",
+ " @AssistedInject",
+ " Foo(@Assisted int i) {}",
+ "}");
+
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(foo);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "Constructors cannot be annotated with both @Inject and @AssistedInject");
+ }
+
+ @Test
+ public void testAssistedInjectNotOnConstructor() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.AssistedInject;",
+ "",
+ "class Foo {",
+ " @AssistedInject",
+ " void someMethod() {}",
+ "}");
+
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(foo);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+
+ // Note: this isn't actually a Dagger error, it's a javac error since @AssistedInject only
+ // targets constructors. However, it's good to have this test in case that ever changes.
+ assertThat(compilation)
+ .hadErrorContaining("annotation type not applicable to this kind of declaration")
+ .inFile(foo)
+ .onLine(6);
+ }
+
+ @Test
+ public void testAssistedInjectWithNoAssistedParametersIsNotInjectable() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Foo {",
+ " @Inject",
+ " Foo(Bar bar) {}",
+ "}");
+
+ JavaFileObject bar =
+ JavaFileObjects.forSourceLines(
+ "test.Bar",
+ "package test;",
+ "",
+ "import dagger.assisted.AssistedInject;",
+ "import javax.inject.Inject;",
+ "",
+ "class Bar {",
+ " @AssistedInject",
+ " Bar() {}",
+ "}");
+
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.FooComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface FooComponent {",
+ " Foo foo();",
+ "}");
+
+ Compilation compilation =
+ compilerWithOptions(compilerMode.javacopts()).compile(foo, bar, component);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(2);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "Dagger does not support injecting @AssistedInject type, test.Bar. "
+ + "Did you mean to inject its assisted factory type instead?")
+ .inFile(foo)
+ .onLine(7);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "\033[1;31m[Dagger/MissingBinding]\033[0m "
+ + "Foo cannot be provided without an @Inject constructor or an @Provides-annotated "
+ + "method.");
+ }
+
+ @Test
+ public void testInaccessibleFoo() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.subpackage.InaccessibleFoo",
+ "package test.subpackage;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "",
+ "class InaccessibleFoo {",
+ " @AssistedInject InaccessibleFoo(@Assisted int i) {}",
+ "}");
+
+ JavaFileObject fooFactory =
+ JavaFileObjects.forSourceLines(
+ "test.subpackage.InaccessibleFooFactory",
+ "package test.subpackage;",
+ "",
+ "import dagger.assisted.AssistedFactory;",
+ "",
+ "@AssistedFactory",
+ "public interface InaccessibleFooFactory {",
+ " InaccessibleFoo create(int i);",
+ "}");
+
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.FooFactoryComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import test.subpackage.InaccessibleFooFactory;",
+ "",
+ "@Component",
+ "interface FooFactoryComponent {",
+ " InaccessibleFooFactory inaccessibleFooFactory();",
+ "}");
+
+ Compilation compilation =
+ compilerWithOptions(compilerMode.javacopts()).compile(foo, fooFactory, component);
+
+ if (compilerMode == CompilerMode.FAST_INIT_MODE) {
+ // TODO(bcorso): Remove once we fix inaccessible assisted factory imlementation for fastInit.
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "test.subpackage.InaccessibleFoo is not public in test.subpackage; cannot be "
+ + "accessed from outside package");
+ } else {
+ assertThat(compilation).succeeded();
+ }
+ }
+
+ @Test
+ public void testAssistedFactoryMethodWithTypeParametersFails() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.AssistedInject;",
+ "import dagger.assisted.AssistedFactory;",
+ "",
+ "class Foo<T> {",
+ " @AssistedInject",
+ " Foo() {}",
+ "",
+ " @AssistedFactory",
+ " interface FooFactory {",
+ " <T> Foo<T> create();",
+ " }",
+ "}");
+
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(foo);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@AssistedFactory does not currently support type parameters in the creator method.")
+ .inFile(foo)
+ .onLine(12);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/AssistedFactoryTest.java b/javatests/dagger/internal/codegen/AssistedFactoryTest.java
new file mode 100644
index 000000000..d75232197
--- /dev/null
+++ b/javatests/dagger/internal/codegen/AssistedFactoryTest.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
+import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
+
+import com.google.common.collect.ImmutableCollection;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class AssistedFactoryTest {
+ @Parameters(name = "{0}")
+ public static ImmutableCollection<Object[]> parameters() {
+ return CompilerMode.TEST_PARAMETERS;
+ }
+
+ private final CompilerMode compilerMode;
+
+ public AssistedFactoryTest(CompilerMode compilerMode) {
+ this.compilerMode = compilerMode;
+ }
+
+ @Test
+ public void testAssistedFactory() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "",
+ "class Foo {",
+ " @AssistedInject",
+ " Foo(@Assisted String str, Bar bar) {}",
+ "}");
+ JavaFileObject fooFactory =
+ JavaFileObjects.forSourceLines(
+ "test.FooFactory",
+ "package test;",
+ "",
+ "import dagger.assisted.AssistedFactory;",
+ "",
+ "@AssistedFactory",
+ "interface FooFactory {",
+ " Foo create(String factoryStr);",
+ "}");
+ JavaFileObject bar =
+ JavaFileObjects.forSourceLines(
+ "test.Bar",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Bar {",
+ " @Inject Bar() {}",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " FooFactory fooFactory();",
+ "}");
+ Compilation compilation =
+ compilerWithOptions(compilerMode.javacopts()).compile(foo, bar, fooFactory, component);
+ assertThat(compilation).succeeded();
+ JavaFileObject generatedComponent =
+ compilerMode
+ .javaFileBuilder("test.DaggerTestComponent")
+ .addLines("package test;", "", GENERATED_CODE_ANNOTATIONS)
+ .addLinesIn(
+ FAST_INIT_MODE,
+ "final class DaggerTestComponent implements TestComponent {",
+ "",
+ " private Foo foo(String str) {",
+ " return new Foo(str, new Bar());",
+ " }",
+ "",
+ " @Override",
+ " public FooFactory fooFactory() {",
+ " return new FooFactory() {",
+ " @Override",
+ " public Foo create(String str) {",
+ " return DaggerTestComponent.this.foo(str);",
+ " }",
+ " };",
+ " }",
+ "}")
+ .addLinesIn(
+ DEFAULT_MODE,
+ "final class DaggerTestComponent implements TestComponent {",
+ "",
+ " private Foo_Factory fooProvider;",
+ "",
+ " private Provider<FooFactory> fooFactoryProvider;",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.fooProvider = Foo_Factory.create(Bar_Factory.create());",
+ " this.fooFactoryProvider = FooFactory_Impl.create(fooProvider);",
+ " }",
+ "",
+ " @Override",
+ " public FooFactory fooFactory() {",
+ " return fooFactoryProvider.get();",
+ " }",
+ "}")
+ .build();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ @Test
+ public void testAssistedFactoryCycle() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "",
+ "class Foo {",
+ " @AssistedInject",
+ " Foo(@Assisted String str, Bar bar) {}",
+ "}");
+ JavaFileObject fooFactory =
+ JavaFileObjects.forSourceLines(
+ "test.FooFactory",
+ "package test;",
+ "",
+ "import dagger.assisted.AssistedFactory;",
+ "",
+ "@AssistedFactory",
+ "interface FooFactory {",
+ " Foo create(String factoryStr);",
+ "}");
+ JavaFileObject bar =
+ JavaFileObjects.forSourceLines(
+ "test.Bar",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Bar {",
+ " @Inject Bar(FooFactory fooFactory) {}",
+ "}");
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface TestComponent {",
+ " FooFactory fooFactory();",
+ "}");
+ Compilation compilation =
+ compilerWithOptions(compilerMode.javacopts()).compile(foo, bar, fooFactory, component);
+ assertThat(compilation).succeeded();
+ JavaFileObject generatedComponent =
+ compilerMode
+ .javaFileBuilder("test.DaggerTestComponent")
+ .addLines("package test;", "", GENERATED_CODE_ANNOTATIONS)
+ .addLinesIn(
+ FAST_INIT_MODE,
+ "final class DaggerTestComponent implements TestComponent {",
+ "",
+ " private Bar bar() {",
+ " return new Bar(fooFactory());",
+ " }",
+ "",
+ " private Foo foo(String str) {",
+ " return new Foo(str, bar());",
+ " }",
+ "",
+ " @Override",
+ " public FooFactory fooFactory() {",
+ " return new FooFactory() {",
+ " @Override",
+ " public Foo create(String str) {",
+ " return DaggerTestComponent.this.foo(str);",
+ " }",
+ " };",
+ " }",
+ "}")
+ .addLinesIn(
+ DEFAULT_MODE,
+ "final class DaggerTestComponent implements TestComponent {",
+ "",
+ " private Provider<FooFactory> fooFactoryProvider;",
+ "",
+ " private Provider<Bar> barProvider;",
+ "",
+ " private Foo_Factory fooProvider;",
+ "",
+ " @SuppressWarnings(\"unchecked\")",
+ " private void initialize() {",
+ " this.fooFactoryProvider = new DelegateFactory<>();",
+ " this.barProvider = Bar_Factory.create(fooFactoryProvider);",
+ " this.fooProvider = Foo_Factory.create(barProvider);",
+ " DelegateFactory.setDelegate(",
+ " fooFactoryProvider, FooFactory_Impl.create(fooProvider));",
+ " }",
+ "",
+ " @Override",
+ " public FooFactory fooFactory() {",
+ " return fooFactoryProvider.get();",
+ " }",
+ "}")
+ .build();
+ assertThat(compilation)
+ .generatedSourceFile("test.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+}
diff --git a/javatests/dagger/internal/codegen/AssistedInjectErrorsTest.java b/javatests/dagger/internal/codegen/AssistedInjectErrorsTest.java
new file mode 100644
index 000000000..47e2171c1
--- /dev/null
+++ b/javatests/dagger/internal/codegen/AssistedInjectErrorsTest.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
+
+import com.google.common.collect.ImmutableCollection;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class AssistedInjectErrorsTest {
+ @Parameters(name = "{0}")
+ public static ImmutableCollection<Object[]> parameters() {
+ return CompilerMode.TEST_PARAMETERS;
+ }
+
+ private final CompilerMode compilerMode;
+
+ public AssistedInjectErrorsTest(CompilerMode compilerMode) {
+ this.compilerMode = compilerMode;
+ }
+
+ @Test
+ public void testAssistedInjectWithDuplicateTypesFails() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "",
+ "class Foo {",
+ " @AssistedInject",
+ " Foo(@Assisted String str1, @Assisted String str2) {}",
+ "}");
+
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(foo);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@AssistedInject constructor has duplicate @Assisted type: @Assisted java.lang.String")
+ .inFile(foo)
+ .onLine(8);
+ }
+
+ @Test
+ public void testAssistedInjectWithDuplicateTypesEmptyQualifierFails() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "",
+ "class Foo {",
+ " @AssistedInject",
+ " Foo(@Assisted(\"\") String str1, @Assisted String str2) {}",
+ "}");
+
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(foo);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@AssistedInject constructor has duplicate @Assisted type: @Assisted java.lang.String")
+ .inFile(foo)
+ .onLine(8);
+ }
+
+ @Test
+ public void testAssistedInjectWithDuplicateQualifiedTypesFails() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "",
+ "class Foo<T> {",
+ " @AssistedInject",
+ " Foo(@Assisted(\"MyQualfier\") String s1, @Assisted(\"MyQualfier\") String s2) {}",
+ "}");
+
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(foo);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@AssistedInject constructor has duplicate @Assisted type: "
+ + "@Assisted(\"MyQualfier\") java.lang.String")
+ .inFile(foo)
+ .onLine(8);
+ }
+
+ @Test
+ public void testAssistedInjectWithDuplicateGenericTypesFails() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "import java.util.List;",
+ "",
+ "class Foo {",
+ " @AssistedInject",
+ " Foo(@Assisted List<String> list1, @Assisted List<String> list2) {}",
+ "}");
+
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(foo);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@AssistedInject constructor has duplicate @Assisted type: "
+ + "@Assisted java.util.List<java.lang.String>")
+ .inFile(foo)
+ .onLine(9);
+ }
+
+ @Test
+ public void testAssistedInjectWithDuplicateParameterizedTypesFails() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "",
+ "class Foo<T> {",
+ " @AssistedInject",
+ " Foo(@Assisted T t1, @Assisted T t2) {}",
+ "}");
+
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(foo);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining("@AssistedInject constructor has duplicate @Assisted type: @Assisted T")
+ .inFile(foo)
+ .onLine(8);
+ }
+
+ @Test
+ public void testAssistedInjectWithUniqueParameterizedTypesPasses() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "import java.util.List;",
+ "",
+ "class Foo<T1, T2> {",
+ " @AssistedInject",
+ " Foo(@Assisted T1 t1, @Assisted T2 t2) {}",
+ "}");
+
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(foo);
+ assertThat(compilation).succeeded();
+ }
+
+ @Test
+ public void testAssistedInjectWithUniqueGenericTypesPasses() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "import java.util.List;",
+ "",
+ "class Foo {",
+ " @AssistedInject",
+ " Foo(@Assisted List<String> list1, @Assisted List<Integer> list2) {}",
+ "}");
+
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(foo);
+ assertThat(compilation).succeeded();
+ }
+
+ @Test
+ public void testAssistedInjectWithUniqueQualifiedTypesPasses() {
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "test.Foo",
+ "package test;",
+ "",
+ "import dagger.assisted.Assisted;",
+ "import dagger.assisted.AssistedInject;",
+ "import java.util.List;",
+ "",
+ "class Foo {",
+ " @AssistedInject",
+ " Foo(",
+ " @Assisted(\"1\") Integer i1,",
+ " @Assisted(\"1\") String s1,",
+ " @Assisted(\"2\") String s2,",
+ " @Assisted String s3) {}",
+ "}");
+
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(foo);
+ assertThat(compilation).succeeded();
+ }
+}
diff --git a/javatests/dagger/internal/codegen/BUILD b/javatests/dagger/internal/codegen/BUILD
index ad214ff57..979ae71ef 100644
--- a/javatests/dagger/internal/codegen/BUILD
+++ b/javatests/dagger/internal/codegen/BUILD
@@ -15,42 +15,84 @@
# Description:
# Tests for the Dagger compiler/codegen
-package(default_visibility = ["//:src"])
-
+load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_library")
load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
load("//:test_defs.bzl", "GenJavaTests")
+package(default_visibility = ["//:src"])
+
+kt_jvm_library(
+ name = "kotlin_sources",
+ srcs = [
+ "KotlinInjectedQualifier.kt",
+ "KotlinObjectWithMemberInjection.kt",
+ ],
+ deps = [
+ "//java/dagger:core",
+ ],
+)
+
+# TODO(bcorso): Move this into a subpackage.
+java_library(
+ name = "compilers",
+ srcs = [
+ "CompilerMode.java",
+ "Compilers.java",
+ "JavaFileBuilder.java",
+ ],
+ deps = [
+ "//java/dagger/internal/codegen:package_info",
+ "//java/dagger/internal/codegen:processor",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "@com_google_auto_value_auto_value//jar",
+ "@google_bazel_common//third_party/java/compile_testing",
+ ],
+)
+
GenJavaTests(
name = "compiler_tests",
- srcs = glob(["*.java"]),
+ srcs = glob(
+ ["*.java"],
+ exclude = [
+ "CompilerMode.java",
+ "Compilers.java",
+ "JavaFileBuilder.java",
+ ],
+ ),
functional = False,
javacopts = DOCLINT_HTML_AND_SYNTAX,
+ plugins = ["//java/dagger/internal/codegen/bootstrap"],
deps = [
+ ":compilers",
+ ":kotlin_sources",
"//java/dagger:core",
- "//java/dagger/internal/codegen:base",
- "//java/dagger/internal/codegen:binding",
- "//java/dagger/internal/codegen:binding_graph_validation",
+ "//java/dagger/internal/codegen:package_info",
"//java/dagger/internal/codegen:processor",
- "//java/dagger/internal/codegen:validation",
- "//java/dagger/internal/codegen:writing",
+ "//java/dagger/internal/codegen/base",
+ "//java/dagger/internal/codegen/binding",
+ "//java/dagger/internal/codegen/bindinggraphvalidation",
+ "//java/dagger/internal/codegen/compileroption",
"//java/dagger/internal/codegen/javapoet",
+ "//java/dagger/internal/codegen/kotlin",
"//java/dagger/internal/codegen/langmodel",
- "//java/dagger/internal/codegen/serialization",
- "//java/dagger/model",
+ "//java/dagger/internal/codegen/validation",
+ "//java/dagger/internal/codegen/writing",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:concurrent",
"//java/dagger/model/testing",
"//java/dagger/producers",
"//java/dagger/spi",
- "@com_google_auto_value_auto_value//jar", # For AutoAnnotationProcessor
- "@google_bazel_common//third_party/java/auto:common",
+ "@com_google_auto_value_auto_value//jar",
"@google_bazel_common//third_party/java/auto:value",
"@google_bazel_common//third_party/java/compile_testing",
- "@google_bazel_common//third_party/java/guava",
"@google_bazel_common//third_party/java/javapoet",
"@google_bazel_common//third_party/java/jsr250_annotations",
"@google_bazel_common//third_party/java/jsr330_inject",
"@google_bazel_common//third_party/java/junit",
"@google_bazel_common//third_party/java/mockito",
"@google_bazel_common//third_party/java/truth",
- "@google_bazel_common//third_party/java/truth:truth8",
+ "@maven//:com_google_auto_auto_common",
],
)
diff --git a/javatests/dagger/internal/codegen/BindsDependsOnSubcomponentValidationTest.java b/javatests/dagger/internal/codegen/BindsDependsOnSubcomponentValidationTest.java
new file mode 100644
index 000000000..aa01b3a32
--- /dev/null
+++ b/javatests/dagger/internal/codegen/BindsDependsOnSubcomponentValidationTest.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests to make sure that delegate bindings where the impl depends on a binding in a subcomponent
+ * properly fail. These are regression tests for b/147020838.
+ */
+@RunWith(JUnit4.class)
+public class BindsDependsOnSubcomponentValidationTest {
+ @Test
+ public void testBinds() {
+ JavaFileObject parentComponent =
+ JavaFileObjects.forSourceLines(
+ "test.ParentComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = ParentModule.class)",
+ "interface ParentComponent {",
+ " ChildComponent getChild();",
+ "}");
+ JavaFileObject parentModule =
+ JavaFileObjects.forSourceLines(
+ "test.ParentModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "interface ParentModule {",
+ " @Binds Foo bindFoo(FooImpl impl);",
+ "}");
+ JavaFileObject childComponent =
+ JavaFileObjects.forSourceLines(
+ "test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = ChildModule.class)",
+ "interface ChildComponent {",
+ " Foo getFoo();",
+ "}");
+ JavaFileObject childModule =
+ JavaFileObjects.forSourceLines(
+ "test.ChildModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "interface ChildModule {",
+ " @Provides static Long providLong() {",
+ " return 0L;",
+ " }",
+ "}");
+ JavaFileObject iface =
+ JavaFileObjects.forSourceLines("test.Foo", "package test;", "", "interface Foo {", "}");
+ JavaFileObject impl =
+ JavaFileObjects.forSourceLines(
+ "test.FooImpl",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class FooImpl implements Foo {",
+ " @Inject FooImpl(Long l) {}",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .compile(parentComponent, parentModule, childComponent, childModule, iface, impl);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining("Long cannot be provided without an @Inject constructor")
+ .inFile(parentComponent)
+ .onLineContaining("interface ParentComponent");
+ }
+
+ @Test
+ public void testSetBindings() {
+ JavaFileObject parentComponent =
+ JavaFileObjects.forSourceLines(
+ "test.ParentComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = ParentModule.class)",
+ "interface ParentComponent {",
+ " ChildComponent getChild();",
+ "}");
+ JavaFileObject parentModule =
+ JavaFileObjects.forSourceLines(
+ "test.ParentModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "import dagger.multibindings.IntoSet;",
+ "",
+ "@Module",
+ "interface ParentModule {",
+ " @Binds @IntoSet Foo bindFoo(FooImpl impl);",
+ "}");
+ JavaFileObject childComponent =
+ JavaFileObjects.forSourceLines(
+ "test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Set;",
+ "",
+ "@Subcomponent(modules = ChildModule.class)",
+ "interface ChildComponent {",
+ " Set<Foo> getFooSet();",
+ "}");
+ JavaFileObject childModule =
+ JavaFileObjects.forSourceLines(
+ "test.ChildModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "interface ChildModule {",
+ " @Provides static Long providLong() {",
+ " return 0L;",
+ " }",
+ "}");
+ JavaFileObject iface =
+ JavaFileObjects.forSourceLines("test.Foo", "package test;", "", "interface Foo {", "}");
+ JavaFileObject impl =
+ JavaFileObjects.forSourceLines(
+ "test.FooImpl",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class FooImpl implements Foo {",
+ " @Inject FooImpl(Long l) {}",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .compile(parentComponent, parentModule, childComponent, childModule, iface, impl);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining("Long cannot be provided without an @Inject constructor")
+ .inFile(parentComponent)
+ .onLineContaining("interface ParentComponent");
+ }
+
+ @Test
+ public void testSetValueBindings() {
+ JavaFileObject parentComponent =
+ JavaFileObjects.forSourceLines(
+ "test.ParentComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = ParentModule.class)",
+ "interface ParentComponent {",
+ " ChildComponent getChild();",
+ "}");
+ JavaFileObject parentModule =
+ JavaFileObjects.forSourceLines(
+ "test.ParentModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.ElementsIntoSet;",
+ "import java.util.Collections;",
+ "import java.util.Set;",
+ "",
+ "@Module",
+ "interface ParentModule {",
+ " @Provides @ElementsIntoSet",
+ " static Set<Foo> provideFoo(FooImpl impl) {",
+ " return Collections.singleton(impl);",
+ " }",
+ "}");
+ JavaFileObject childComponent =
+ JavaFileObjects.forSourceLines(
+ "test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Set;",
+ "",
+ "@Subcomponent(modules = ChildModule.class)",
+ "interface ChildComponent {",
+ " Set<Foo> getFooSet();",
+ "}");
+ JavaFileObject childModule =
+ JavaFileObjects.forSourceLines(
+ "test.ChildModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "interface ChildModule {",
+ " @Provides static Long providLong() {",
+ " return 0L;",
+ " }",
+ "}");
+ JavaFileObject iface =
+ JavaFileObjects.forSourceLines("test.Foo", "package test;", "", "interface Foo {", "}");
+ JavaFileObject impl =
+ JavaFileObjects.forSourceLines(
+ "test.FooImpl",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class FooImpl implements Foo {",
+ " @Inject FooImpl(Long l) {}",
+ "}");
+ Compilation compilation =
+ daggerCompiler()
+ .compile(parentComponent, parentModule, childComponent, childModule, iface, impl);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining("Long cannot be provided without an @Inject constructor")
+ .inFile(parentComponent)
+ .onLineContaining("interface ParentComponent");
+ }
+
+ @Test
+ public void testMapBindings() {
+ JavaFileObject parentComponent =
+ JavaFileObjects.forSourceLines(
+ "test.ParentComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(modules = ParentModule.class)",
+ "interface ParentComponent {",
+ " ChildComponent getChild();",
+ "}");
+ JavaFileObject parentModule =
+ JavaFileObjects.forSourceLines(
+ "test.ParentModule",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.multibindings.StringKey;",
+ "",
+ "@Module",
+ "interface ParentModule {",
+ " @Binds @IntoMap @StringKey(\"foo\") Foo bindFoo(FooImpl impl);",
+ "}");
+ JavaFileObject childComponent =
+ JavaFileObjects.forSourceLines(
+ "test.ChildComponent",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "import java.util.Map;",
+ "",
+ "@Subcomponent(modules = ChildModule.class)",
+ "interface ChildComponent {",
+ " Map<String, Foo> getFooSet();",
+ "}");
+ JavaFileObject childModule =
+ JavaFileObjects.forSourceLines(
+ "test.ChildModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "interface ChildModule {",
+ " @Provides static Long providLong() {",
+ " return 0L;",
+ " }",
+ "}");
+ JavaFileObject iface =
+ JavaFileObjects.forSourceLines("test.Foo", "package test;", "", "interface Foo {", "}");
+ JavaFileObject impl =
+ JavaFileObjects.forSourceLines(
+ "test.FooImpl",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class FooImpl implements Foo {",
+ " @Inject FooImpl(Long l) {}",
+ "}");
+ Compilation compilation =
+ // TODO(erichang): make this flag the default and remove this
+ compilerWithOptions("-Adagger.strictMultibindingValidation=enabled")
+ .compile(parentComponent, parentModule, childComponent, childModule, iface, impl);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining("Long cannot be provided without an @Inject constructor")
+ .inFile(parentComponent)
+ .onLineContaining("interface ParentComponent");
+ }
+}
diff --git a/javatests/dagger/internal/codegen/BindsMethodValidationTest.java b/javatests/dagger/internal/codegen/BindsMethodValidationTest.java
index 6ba9e3ebd..ab2601091 100644
--- a/javatests/dagger/internal/codegen/BindsMethodValidationTest.java
+++ b/javatests/dagger/internal/codegen/BindsMethodValidationTest.java
@@ -25,7 +25,6 @@ import dagger.Module;
import dagger.multibindings.IntKey;
import dagger.multibindings.LongKey;
import dagger.producers.ProducerModule;
-import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.util.Collection;
@@ -79,9 +78,8 @@ public class BindsMethodValidationTest {
@Test
public void throwsException() {
- assertThatMethod("@Binds abstract Object throwsException(String s1) throws IOException;")
- .importing(IOException.class)
- .hasError("only throw unchecked");
+ assertThatMethod("@Binds abstract Object throwsException(String s1) throws RuntimeException;")
+ .hasError("may not throw");
}
@Test
diff --git a/javatests/dagger/internal/codegen/BindsMissingDelegateValidationTest.java b/javatests/dagger/internal/codegen/BindsMissingDelegateValidationTest.java
index 28b75beaf..bfc361781 100644
--- a/javatests/dagger/internal/codegen/BindsMissingDelegateValidationTest.java
+++ b/javatests/dagger/internal/codegen/BindsMissingDelegateValidationTest.java
@@ -97,7 +97,7 @@ public class BindsMissingDelegateValidationTest {
.hadErrorContainingMatch(
"\\Qtest.C.NotBound cannot be provided\\E|"
+ message(
- "\\Qjava.lang.Object is bound multiple times:",
+ "\\QObject is bound multiple times:",
" @Binds Object test.C.TestModule.bindObject(test.C.NotBound)",
" @Provides Object test.C.TestModule.provideObject()\\E"))
.inFile(component)
diff --git a/javatests/dagger/internal/codegen/BindsOptionalOfMethodValidationTest.java b/javatests/dagger/internal/codegen/BindsOptionalOfMethodValidationTest.java
index fa44a6e3d..ca3e5e412 100644
--- a/javatests/dagger/internal/codegen/BindsOptionalOfMethodValidationTest.java
+++ b/javatests/dagger/internal/codegen/BindsOptionalOfMethodValidationTest.java
@@ -37,7 +37,7 @@ import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
-/** Tests {@link BindsOptionalOfMethodValidator}. */
+/** Tests {@link dagger.internal.codegen.validation.BindsOptionalOfMethodValidator}. */
@RunWith(Parameterized.class)
public class BindsOptionalOfMethodValidationTest {
@Parameters(name = "{0}")
diff --git a/javatests/dagger/internal/codegen/CompilerMode.java b/javatests/dagger/internal/codegen/CompilerMode.java
index 23aa3127a..86ce53d13 100644
--- a/javatests/dagger/internal/codegen/CompilerMode.java
+++ b/javatests/dagger/internal/codegen/CompilerMode.java
@@ -23,9 +23,6 @@ import com.google.common.collect.ImmutableList;
enum CompilerMode {
DEFAULT_MODE,
FAST_INIT_MODE("-Adagger.fastInit=enabled"),
- AHEAD_OF_TIME_SUBCOMPONENTS_MODE(
- "-Adagger.experimentalAheadOfTimeSubcomponents=enabled",
- "-Adagger.emitModifiableMetadataAnnotations=disabled"),
JAVA7("-source", "7", "-target", "7"),
;
diff --git a/javatests/dagger/internal/codegen/Compilers.java b/javatests/dagger/internal/codegen/Compilers.java
index 3e08f6336..26796bfd2 100644
--- a/javatests/dagger/internal/codegen/Compilers.java
+++ b/javatests/dagger/internal/codegen/Compilers.java
@@ -19,42 +19,60 @@ package dagger.internal.codegen;
import static com.google.common.base.StandardSystemProperty.JAVA_CLASS_PATH;
import static com.google.common.base.StandardSystemProperty.PATH_SEPARATOR;
import static com.google.testing.compile.Compiler.javac;
-import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.collectingAndThen;
+import static java.util.stream.Collectors.toList;
import com.google.auto.value.processor.AutoAnnotationProcessor;
import com.google.common.base.Splitter;
-import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.testing.compile.Compiler;
+import java.io.File;
+import java.util.Arrays;
import javax.annotation.processing.Processor;
/** {@link Compiler} instances for testing Dagger. */
-final class Compilers {
+public final class Compilers {
private static final String GUAVA = "guava";
- static final ImmutableList<String> CLASS_PATH_WITHOUT_GUAVA_OPTION =
- ImmutableList.of(
- "-classpath",
- Splitter.on(PATH_SEPARATOR.value()).splitToList(JAVA_CLASS_PATH.value()).stream()
- .filter(jar -> !jar.contains(GUAVA))
- .collect(joining(PATH_SEPARATOR.value())));
+ static final ImmutableList<File> CLASS_PATH_WITHOUT_GUAVA_OPTION =
+ Splitter.on(PATH_SEPARATOR.value()).splitToList(JAVA_CLASS_PATH.value()).stream()
+ .filter(jar -> !jar.contains(GUAVA))
+ // Remove Bazel's runner deploy jar which leaks Guava classes into the classpath and
+ // the compile testing tests.
+ .filter(jar -> !jar.contains("Runner_deploy.jar"))
+ .map(File::new)
+ .collect(collectingAndThen(toList(), ImmutableList::copyOf));
+
+ static final ImmutableList<String> DEFAULT_JAVACOPTS =
+ ImmutableList.of("-Adagger.experimentalDaggerErrorMessages=enabled");
/**
* Returns a compiler that runs the Dagger and {@code @AutoAnnotation} processors, along with
* extras.
*/
- static Compiler daggerCompiler(Processor... extraProcessors) {
+ public static Compiler daggerCompiler(Processor... extraProcessors) {
ImmutableList.Builder<Processor> processors = ImmutableList.builder();
processors.add(new ComponentProcessor(), new AutoAnnotationProcessor());
processors.add(extraProcessors);
- return javac().withProcessors(processors.build());
+ return javac().withProcessors(processors.build()).withOptions(DEFAULT_JAVACOPTS);
}
- static Compiler compilerWithOptions(CompilerMode... compilerModes) {
- FluentIterable<String> options = FluentIterable.of();
+ public static Compiler compilerWithOptions(CompilerMode... compilerModes) {
+ ImmutableList.Builder<String> options = ImmutableList.builder();
for (CompilerMode compilerMode : compilerModes) {
- options = options.append(compilerMode.javacopts());
+ options = options.addAll(compilerMode.javacopts());
}
- return daggerCompiler().withOptions(options);
+ return compilerWithOptions(options.build());
+ }
+
+ public static Compiler compilerWithOptions(String... options) {
+ return compilerWithOptions(Arrays.asList(options));
}
+
+ public static Compiler compilerWithOptions(Iterable<String> options) {
+ return daggerCompiler()
+ .withOptions(ImmutableList.builder().addAll(DEFAULT_JAVACOPTS).addAll(options).build());
+ }
+
+ private Compilers() {}
}
diff --git a/javatests/dagger/internal/codegen/ComponentBuilderTest.java b/javatests/dagger/internal/codegen/ComponentBuilderTest.java
index f280bddfa..acf543721 100644
--- a/javatests/dagger/internal/codegen/ComponentBuilderTest.java
+++ b/javatests/dagger/internal/codegen/ComponentBuilderTest.java
@@ -17,13 +17,14 @@
package dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
-import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.ComponentCreatorAnnotation.COMPONENT_BUILDER;
-import static dagger.internal.codegen.ErrorMessages.creatorMessagesFor;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
+import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.COMPONENT_BUILDER;
+import static dagger.internal.codegen.binding.ErrorMessages.creatorMessagesFor;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
+import dagger.internal.codegen.binding.ErrorMessages;
import java.util.Collection;
import javax.tools.JavaFileObject;
import org.junit.Test;
@@ -87,7 +88,7 @@ public class ComponentBuilderTest {
"",
"import dagger.internal.Preconditions;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private static final class Builder implements TestComponent.Builder {",
" private TestModule testModule;",
@@ -108,7 +109,7 @@ public class ComponentBuilderTest {
" }",
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(moduleFile, componentFile);
+ compilerWithOptions(compilerMode.javacopts()).compile(moduleFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerTestComponent")
@@ -135,7 +136,7 @@ public class ComponentBuilderTest {
" }",
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ compilerWithOptions(compilerMode.javacopts()).compile(componentFile);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(MSGS.setterMethodsMustTakeOneArg())
@@ -168,7 +169,7 @@ public class ComponentBuilderTest {
" interface Builder extends Parent {}",
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ compilerWithOptions(compilerMode.javacopts()).compile(componentFile);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
@@ -198,7 +199,7 @@ public class ComponentBuilderTest {
" }",
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ compilerWithOptions(compilerMode.javacopts()).compile(componentFile);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(MSGS.setterMethodsMustReturnVoidOrBuilder())
@@ -227,7 +228,7 @@ public class ComponentBuilderTest {
" interface Builder extends Parent {}",
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ compilerWithOptions(compilerMode.javacopts()).compile(componentFile);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
@@ -256,7 +257,7 @@ public class ComponentBuilderTest {
" }",
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ compilerWithOptions(compilerMode.javacopts()).compile(componentFile);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(MSGS.methodsMayNotHaveTypeParameters())
@@ -285,7 +286,7 @@ public class ComponentBuilderTest {
" interface Builder extends Parent {}",
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ compilerWithOptions(compilerMode.javacopts()).compile(componentFile);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
@@ -318,7 +319,7 @@ public class ComponentBuilderTest {
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ compilerWithOptions(compilerMode.javacopts()).compile(componentFile);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(MSGS.bindsInstanceNotAllowedOnBothSetterMethodAndParameter())
@@ -352,7 +353,7 @@ public class ComponentBuilderTest {
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ compilerWithOptions(compilerMode.javacopts()).compile(componentFile);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
diff --git a/javatests/dagger/internal/codegen/ComponentCreatorTest.java b/javatests/dagger/internal/codegen/ComponentCreatorTest.java
index 6f4ea8d70..0a7e40eb1 100644
--- a/javatests/dagger/internal/codegen/ComponentCreatorTest.java
+++ b/javatests/dagger/internal/codegen/ComponentCreatorTest.java
@@ -16,28 +16,27 @@
package dagger.internal.codegen;
-import static com.google.common.collect.Sets.immutableEnumSet;
+import static com.google.common.truth.TruthJUnit.assume;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.ComponentCreatorAnnotation.COMPONENT_BUILDER;
-import static dagger.internal.codegen.ComponentCreatorAnnotation.COMPONENT_FACTORY;
-import static dagger.internal.codegen.ComponentCreatorKind.BUILDER;
-import static dagger.internal.codegen.ComponentCreatorKind.FACTORY;
-import static dagger.internal.codegen.ComponentKind.COMPONENT;
-import static dagger.internal.codegen.ErrorMessages.componentMessagesFor;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.ComponentCreatorTest.CompilerType.JAVAC;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
+import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.COMPONENT_BUILDER;
+import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.COMPONENT_FACTORY;
+import static dagger.internal.codegen.binding.ComponentCreatorKind.BUILDER;
+import static dagger.internal.codegen.binding.ComponentCreatorKind.FACTORY;
+import static dagger.internal.codegen.binding.ComponentKind.COMPONENT;
+import static dagger.internal.codegen.binding.ErrorMessages.componentMessagesFor;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
+import dagger.internal.codegen.binding.ComponentCreatorAnnotation;
import java.util.Collection;
-import java.util.List;
-import java.util.Set;
import javax.tools.JavaFileObject;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -47,22 +46,34 @@ import org.junit.runners.Parameterized.Parameters;
/** Tests for properties of component creators shared by both builders and factories. */
@RunWith(Parameterized.class)
public class ComponentCreatorTest extends ComponentCreatorTestHelper {
+ enum CompilerType {
+ JAVAC
+ }
+
+ private final CompilerType compilerType;
+ private final CompilerMode compilerMode;
+
@Parameters(name = "compilerMode={0}, creatorKind={1}")
public static Collection<Object[]> parameters() {
- Set<List<Object>> params =
- Sets.<Object>cartesianProduct(
- immutableEnumSet(DEFAULT_MODE, FAST_INIT_MODE),
- immutableEnumSet(COMPONENT_BUILDER, COMPONENT_FACTORY));
- return ImmutableList.copyOf(Iterables.transform(params, Collection::toArray));
+ return ImmutableList.of(
+ new Object[]{DEFAULT_MODE, COMPONENT_BUILDER, JAVAC},
+ new Object[]{DEFAULT_MODE, COMPONENT_FACTORY, JAVAC},
+ new Object[]{FAST_INIT_MODE, COMPONENT_BUILDER, JAVAC},
+ new Object[]{FAST_INIT_MODE, COMPONENT_FACTORY, JAVAC});
}
public ComponentCreatorTest(
- CompilerMode compilerMode, ComponentCreatorAnnotation componentCreatorAnnotation) {
+ CompilerMode compilerMode,
+ ComponentCreatorAnnotation componentCreatorAnnotation,
+ CompilerType compilerType) {
super(compilerMode, componentCreatorAnnotation);
+ this.compilerMode = compilerMode;
+ this.compilerType = compilerType;
}
@Test
public void testEmptyCreator() {
+ assume().that(compilerType).isEqualTo(JAVAC);
JavaFileObject injectableTypeFile =
JavaFileObjects.forSourceLines(
"test.SomeInjectableType",
@@ -95,7 +106,7 @@ public class ComponentCreatorTest extends ComponentCreatorTestHelper {
"test.DaggerSimpleComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerSimpleComponent implements SimpleComponent {",
" private static final class Builder implements SimpleComponent.Builder {",
" @Override",
@@ -113,6 +124,7 @@ public class ComponentCreatorTest extends ComponentCreatorTestHelper {
@Test
public void testCanInstantiateModulesUserCannotSet() {
+ assume().that(compilerType).isEqualTo(JAVAC);
JavaFileObject module =
JavaFileObjects.forSourceLines(
"test.TestModule",
@@ -149,7 +161,7 @@ public class ComponentCreatorTest extends ComponentCreatorTestHelper {
"",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private final TestModule testModule;",
"",
@@ -310,6 +322,7 @@ public class ComponentCreatorTest extends ComponentCreatorTestHelper {
@Test
public void testCreatorWithBindsInstanceNoStaticCreateGenerated() {
+ assume().that(compilerType).isEqualTo(JAVAC);
JavaFileObject componentFile =
javaFileBuilder("test.SimpleComponent")
.addLines(
@@ -347,7 +360,7 @@ public class ComponentCreatorTest extends ComponentCreatorTestHelper {
"import dagger.internal.Preconditions;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerSimpleComponent implements SimpleComponent {",
" private final Object object;",
"",
@@ -410,6 +423,7 @@ public class ComponentCreatorTest extends ComponentCreatorTestHelper {
@Test
public void testCreatorWithPrimitiveBindsInstance() {
+ assume().that(compilerType).isEqualTo(JAVAC);
JavaFileObject componentFile =
javaFileBuilder("test.SimpleComponent")
.addLines(
@@ -448,7 +462,7 @@ public class ComponentCreatorTest extends ComponentCreatorTestHelper {
"import dagger.internal.Preconditions;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerSimpleComponent implements SimpleComponent {",
" private final Integer i;",
"",
@@ -779,6 +793,7 @@ public class ComponentCreatorTest extends ComponentCreatorTestHelper {
@Test
public void testMultipleSettersPerTypeFails() {
+ assume().that(compilerType).isEqualTo(JAVAC);
JavaFileObject moduleFile =
JavaFileObjects.forSourceLines(
"test.TestModule",
@@ -837,6 +852,7 @@ public class ComponentCreatorTest extends ComponentCreatorTestHelper {
@Test
public void testMultipleSettersPerTypeIncludingResolvedGenericsFails() {
+ assume().that(compilerType).isEqualTo(JAVAC);
JavaFileObject moduleFile =
JavaFileObjects.forSourceLines(
"test.TestModule",
@@ -900,6 +916,7 @@ public class ComponentCreatorTest extends ComponentCreatorTestHelper {
@Test
public void testExtraSettersFails() {
+ assume().that(compilerType).isEqualTo(JAVAC);
JavaFileObject componentFile =
javaFileBuilder("test.SimpleComponent")
.addLines(
@@ -1032,6 +1049,7 @@ public class ComponentCreatorTest extends ComponentCreatorTestHelper {
@Test
public void covariantFactoryMethodReturnType() {
+ assume().that(compilerType).isEqualTo(JAVAC);
JavaFileObject foo =
JavaFileObjects.forSourceLines(
"test.Foo",
@@ -1072,6 +1090,7 @@ public class ComponentCreatorTest extends ComponentCreatorTestHelper {
@Test
public void covariantFactoryMethodReturnType_hasNewMethod() {
+ assume().that(compilerType).isEqualTo(JAVAC);
JavaFileObject foo =
JavaFileObjects.forSourceLines(
"test.Foo",
@@ -1133,6 +1152,7 @@ public class ComponentCreatorTest extends ComponentCreatorTestHelper {
@Test
public void covariantFactoryMethodReturnType_hasNewMethod_factoryMethodInherited() {
+ assume().that(compilerType).isEqualTo(JAVAC);
JavaFileObject foo =
JavaFileObjects.forSourceLines(
"test.Foo",
@@ -1250,4 +1270,13 @@ public class ComponentCreatorTest extends ComponentCreatorTestHelper {
.inFile(componentFile)
.onLineContaining(process("interface Builder"));
}
+
+ /** Compiles the given files with the set compiler mode's javacopts. */
+ @Override
+ Compilation compile(JavaFileObject... files) {
+ ImmutableList.Builder<String> options =
+ ImmutableList.<String>builder().addAll(compilerMode.javacopts());
+
+ return compilerWithOptions(options.build()).compile(files);
+ }
}
diff --git a/javatests/dagger/internal/codegen/ComponentCreatorTestHelper.java b/javatests/dagger/internal/codegen/ComponentCreatorTestHelper.java
index 2ee120ea2..8ad43227f 100644
--- a/javatests/dagger/internal/codegen/ComponentCreatorTestHelper.java
+++ b/javatests/dagger/internal/codegen/ComponentCreatorTestHelper.java
@@ -16,13 +16,16 @@
package dagger.internal.codegen;
-import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.ComponentCreatorKind.FACTORY;
-import static dagger.internal.codegen.ErrorMessages.creatorMessagesFor;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
+import static dagger.internal.codegen.binding.ComponentCreatorKind.FACTORY;
+import static dagger.internal.codegen.binding.ErrorMessages.creatorMessagesFor;
import static java.util.stream.Collectors.joining;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
+import dagger.internal.codegen.binding.ComponentCreatorAnnotation;
+import dagger.internal.codegen.binding.ComponentCreatorKind;
+import dagger.internal.codegen.binding.ErrorMessages;
import java.util.Arrays;
import java.util.stream.Stream;
import javax.tools.JavaFileObject;
@@ -84,6 +87,6 @@ abstract class ComponentCreatorTestHelper {
/** Compiles the given files with the set compiler mode's javacopts. */
Compilation compile(JavaFileObject... files) {
- return daggerCompiler().withOptions(compilerMode.javacopts()).compile(files);
+ return compilerWithOptions(compilerMode.javacopts()).compile(files);
}
}
diff --git a/javatests/dagger/internal/codegen/ComponentDependenciesTest.java b/javatests/dagger/internal/codegen/ComponentDependenciesTest.java
new file mode 100644
index 000000000..bbfa51c9b
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ComponentDependenciesTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class ComponentDependenciesTest {
+ @Test
+ public void dependenciesWithTwoOfSameMethodOnDifferentInterfaces_fail() {
+ JavaFileObject interfaceOne = JavaFileObjects.forSourceLines("test.One",
+ "package test;",
+ "",
+ "interface One {",
+ " String getOne();",
+ "}");
+ JavaFileObject interfaceTwo = JavaFileObjects.forSourceLines("test.Two",
+ "package test;",
+ "",
+ "interface Two {",
+ " String getTwo();",
+ "}");
+ JavaFileObject mergedInterface = JavaFileObjects.forSourceLines("test.Merged",
+ "package test;",
+ "",
+ "interface Merged extends One, Two {}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(dependencies = Merged.class)",
+ "interface TestComponent {",
+ " String getString();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(
+ interfaceOne, interfaceTwo, mergedInterface, componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("DuplicateBindings");
+ }
+
+ @Test
+ public void dependenciesWithTwoOfSameMethodOnDifferentInterfaces_producers_fail() {
+ JavaFileObject interfaceOne = JavaFileObjects.forSourceLines("test.One",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "",
+ "interface One {",
+ " ListenableFuture<String> getOne();",
+ "}");
+ JavaFileObject interfaceTwo = JavaFileObjects.forSourceLines("test.Two",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "",
+ "interface Two {",
+ " ListenableFuture<String> getTwo();",
+ "}");
+ JavaFileObject mergedInterface = JavaFileObjects.forSourceLines("test.Merged",
+ "package test;",
+ "",
+ "interface Merged extends One, Two {}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import com.google.common.util.concurrent.ListenableFuture;",
+ "import dagger.producers.ProductionComponent;",
+ "",
+ "@ProductionComponent(dependencies = Merged.class)",
+ "interface TestComponent {",
+ " ListenableFuture<String> getString();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(
+ interfaceOne, interfaceTwo, mergedInterface, componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("DuplicateBindings");
+ }
+
+ @Test
+ public void dependenciesWithTwoOfSameMethodButDifferentNullability_fail() {
+ JavaFileObject interfaceOne = JavaFileObjects.forSourceLines("test.One",
+ "package test;",
+ "",
+ "interface One {",
+ " String getString();",
+ "}");
+ JavaFileObject interfaceTwo = JavaFileObjects.forSourceLines("test.Two",
+ "package test;",
+ "import javax.annotation.Nullable;",
+ "",
+ "interface Two {",
+ " @Nullable String getString();",
+ "}");
+ JavaFileObject mergedInterface = JavaFileObjects.forSourceLines("test.Merged",
+ "package test;",
+ "",
+ "interface Merged extends One, Two {}");
+ JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component(dependencies = Merged.class)",
+ "interface TestComponent {",
+ " String getString();",
+ "}");
+ Compilation compilation = daggerCompiler().compile(
+ interfaceOne, interfaceTwo, mergedInterface, componentFile);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining("DuplicateBindings");
+ }
+
+}
diff --git a/javatests/dagger/internal/codegen/ComponentFactoryTest.java b/javatests/dagger/internal/codegen/ComponentFactoryTest.java
index 403498bfc..0f155b982 100644
--- a/javatests/dagger/internal/codegen/ComponentFactoryTest.java
+++ b/javatests/dagger/internal/codegen/ComponentFactoryTest.java
@@ -17,13 +17,14 @@
package dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
-import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.ComponentCreatorAnnotation.COMPONENT_FACTORY;
-import static dagger.internal.codegen.ErrorMessages.creatorMessagesFor;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
+import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.COMPONENT_FACTORY;
+import static dagger.internal.codegen.binding.ErrorMessages.creatorMessagesFor;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
+import dagger.internal.codegen.binding.ErrorMessages;
import java.util.Collection;
import javax.tools.JavaFileObject;
import org.junit.Test;
@@ -86,7 +87,7 @@ public class ComponentFactoryTest {
"",
"import dagger.internal.Preconditions;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private static final class Factory implements TestComponent.Factory {",
" @Override",
@@ -97,7 +98,7 @@ public class ComponentFactoryTest {
" }",
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(moduleFile, componentFile);
+ compilerWithOptions(compilerMode.javacopts()).compile(moduleFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerTestComponent")
@@ -123,7 +124,7 @@ public class ComponentFactoryTest {
" }",
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ compilerWithOptions(compilerMode.javacopts()).compile(componentFile);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(String.format(MSGS.twoFactoryMethods(), "create()"))
@@ -152,7 +153,7 @@ public class ComponentFactoryTest {
" interface Factory extends Parent {}",
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ compilerWithOptions(compilerMode.javacopts()).compile(componentFile);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(String.format(MSGS.twoFactoryMethods(), "create()"))
diff --git a/javatests/dagger/internal/codegen/ComponentHierarchyValidationTest.java b/javatests/dagger/internal/codegen/ComponentHierarchyValidationTest.java
index 19eabac95..6fb182015 100644
--- a/javatests/dagger/internal/codegen/ComponentHierarchyValidationTest.java
+++ b/javatests/dagger/internal/codegen/ComponentHierarchyValidationTest.java
@@ -17,6 +17,7 @@
package dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
import static dagger.internal.codegen.Compilers.daggerCompiler;
import static dagger.internal.codegen.TestUtils.message;
@@ -63,8 +64,7 @@ public class ComponentHierarchyValidationTest {
assertThat(compilation).hadErrorContaining("test.Parent also has @Singleton");
Compilation withoutScopeValidation =
- daggerCompiler()
- .withOptions("-Adagger.disableInterComponentScopeValidation=none")
+ compilerWithOptions("-Adagger.disableInterComponentScopeValidation=none")
.compile(component, subcomponent);
assertThat(withoutScopeValidation).succeeded();
}
diff --git a/javatests/dagger/internal/codegen/ComponentProcessorTest.java b/javatests/dagger/internal/codegen/ComponentProcessorTest.java
index 2b79b6f21..eee6a0c3f 100644
--- a/javatests/dagger/internal/codegen/ComponentProcessorTest.java
+++ b/javatests/dagger/internal/codegen/ComponentProcessorTest.java
@@ -20,11 +20,10 @@ import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
-import static dagger.internal.codegen.GeneratedLines.NPE_FROM_COMPONENT_METHOD;
-import static dagger.internal.codegen.GeneratedLines.NPE_FROM_PROVIDES_METHOD;
import com.google.auto.common.MoreElements;
import com.google.common.base.Predicate;
@@ -107,16 +106,15 @@ public class ComponentProcessorTest {
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(parent, child, another, componentFile);
assertThat(compilation).failed();
assertThat(compilation)
- .hadErrorContaining("java.util.List<java.lang.Integer> is bound multiple times");
+ .hadErrorContaining("List<Integer> is bound multiple times");
assertThat(compilation)
- .hadErrorContaining("@Provides List<Integer> test.ChildNumberModule.provideListB(Integer)");
+ .hadErrorContaining("@Provides List<Integer> ChildNumberModule.provideListB(Integer)");
assertThat(compilation)
- .hadErrorContaining("@Provides List<Integer> test.AnotherModule.provideListOfInteger()");
+ .hadErrorContaining("@Provides List<Integer> AnotherModule.provideListOfInteger()");
}
@Test public void privateNestedClassWithWarningThatIsAnErrorInComponent() {
@@ -142,8 +140,7 @@ public class ComponentProcessorTest {
" OuterClass outerClass();",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(
+ compilerWithOptions(
compilerMode.javacopts().append("-Adagger.privateMemberValidation=WARNING"))
.compile(outerClass, componentFile);
assertThat(compilation).failed();
@@ -185,7 +182,7 @@ public class ComponentProcessorTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerSimpleComponent implements SimpleComponent {")
.addLinesIn(
FAST_INIT_MODE,
@@ -261,8 +258,7 @@ public class ComponentProcessorTest {
.build();
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(injectableTypeFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -302,7 +298,7 @@ public class ComponentProcessorTest {
.addLines(
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerSimpleComponent implements SimpleComponent {")
.addLinesIn(
FAST_INIT_MODE,
@@ -387,8 +383,7 @@ public class ComponentProcessorTest {
" }")
.build();
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(injectableTypeFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -422,7 +417,7 @@ public class ComponentProcessorTest {
.addLines(
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerOuterType_SimpleComponent",
" implements OuterType.SimpleComponent {",
" private DaggerOuterType_SimpleComponent() {}",
@@ -446,7 +441,7 @@ public class ComponentProcessorTest {
.build();
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(nestedTypesFile);
+ compilerWithOptions(compilerMode.javacopts()).compile(nestedTypesFile);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerOuterType_SimpleComponent")
@@ -506,7 +501,7 @@ public class ComponentProcessorTest {
"import dagger.internal.Preconditions;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private final TestModule testModule;",
"",
@@ -514,13 +509,13 @@ public class ComponentProcessorTest {
" this.testModule = testModuleParam;",
" }",
"",
- " private B getB() {",
+ " private B b() {",
" return TestModule_BFactory.b(testModule, new C());",
" }",
"",
" @Override",
" public A a() {",
- " return new A(getB());",
+ " return new A(b());",
" }",
"",
" static final class Builder {",
@@ -542,8 +537,7 @@ public class ComponentProcessorTest {
.build();
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(aFile, bFile, cFile, moduleFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -610,22 +604,21 @@ public class ComponentProcessorTest {
.addLines(
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
- " private B getB() {",
+ " private B b() {",
" return TestModule_BFactory.b(new C());",
" }",
"",
" @Override",
" public A a() {",
- " return new A(getB());",
+ " return new A(b());",
" }",
"}")
.build();
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(aFile, bFile, cFile, moduleFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -708,7 +701,7 @@ public class ComponentProcessorTest {
"import dagger.internal.Preconditions;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" static final class Builder {",
"",
@@ -754,8 +747,7 @@ public class ComponentProcessorTest {
" }",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(
always,
testModule,
@@ -789,7 +781,7 @@ public class ComponentProcessorTest {
"@Component(modules = RootModule.class)",
"interface TestComponent {}");
assertThat(
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(rootModule, component))
+ compilerWithOptions(compilerMode.javacopts()).compile(rootModule, component))
.failed();
assertThat(
daggerCompiler(
@@ -828,7 +820,7 @@ public class ComponentProcessorTest {
" ChildComponent childComponent();",
"}");
assertThat(
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(subcomponent, component))
+ compilerWithOptions(compilerMode.javacopts()).compile(subcomponent, component))
.failed();
assertThat(
daggerCompiler(
@@ -893,7 +885,7 @@ public class ComponentProcessorTest {
"import dagger.internal.Preconditions;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerParent implements Parent {",
"",
" private DaggerParent() {}",
@@ -927,8 +919,7 @@ public class ComponentProcessorTest {
" }",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(component, module, subcomponent);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -962,8 +953,7 @@ public class ComponentProcessorTest {
" BClass bClass();",
"}");
assertThat(
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(aModule, aClass, bClass, component))
.succeeded();
}
@@ -1007,7 +997,7 @@ public class ComponentProcessorTest {
"",
"import com.google.errorprone.annotations.CanIgnoreReturnValue;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerSimpleComponent implements SimpleComponent {",
" @Override",
" public void inject(SomeInjectedType instance) {",
@@ -1029,8 +1019,7 @@ public class ComponentProcessorTest {
.build();
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(injectableTypeFile, injectedTypeFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -1064,7 +1053,7 @@ public class ComponentProcessorTest {
"test.DaggerSimpleComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerSimpleComponent implements SimpleComponent {",
" private Provider<SimpleComponent> simpleComponentProvider;",
"",
@@ -1084,8 +1073,7 @@ public class ComponentProcessorTest {
" }",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(injectableTypeFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -1129,7 +1117,7 @@ public class ComponentProcessorTest {
"",
"import com.google.errorprone.annotations.CanIgnoreReturnValue;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerSimpleComponent implements SimpleComponent {",
" @Override",
" public SomeInjectedType createAndInject() {",
@@ -1147,8 +1135,7 @@ public class ComponentProcessorTest {
.build();
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(injectableTypeFile, injectedTypeFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -1198,11 +1185,9 @@ public class ComponentProcessorTest {
.addLines(
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerBComponent implements BComponent {")
- .addLinesIn(
- DEFAULT_MODE,
- " private Provider<A> aProvider;")
+ .addLinesIn(DEFAULT_MODE, " private Provider<A> aProvider;")
.addLinesIn(
FAST_INIT_MODE,
" private final AComponent aComponent;",
@@ -1212,7 +1197,7 @@ public class ComponentProcessorTest {
" this.aComponent = aComponentParam;",
" }",
"",
- " private Provider<A> getAProvider() {",
+ " private Provider<A> aProvider() {",
" Object local = aProvider;",
" if (local == null) {",
" local = new SwitchingProvider<>(0);",
@@ -1226,16 +1211,9 @@ public class ComponentProcessorTest {
" private void initialize(final AComponent aComponentParam) {",
" this.aProvider = new test_AComponent_a(aComponentParam);",
" }")
- .addLines(
- "",
- " @Override",
- " public B b() {")
- .addLinesIn(
- DEFAULT_MODE,
- " return new B(aProvider);")
- .addLinesIn(
- FAST_INIT_MODE,
- " return new B(getAProvider());")
+ .addLines("", " @Override", " public B b() {")
+ .addLinesIn(DEFAULT_MODE, " return new B(aProvider);")
+ .addLinesIn(FAST_INIT_MODE, " return new B(aProvider());")
.addLines(
" }",
"",
@@ -1263,8 +1241,7 @@ public class ComponentProcessorTest {
" ",
" @Override()",
" public A get() {",
- " return Preconditions.checkNotNull(",
- " aComponent.a(), " + NPE_FROM_COMPONENT_METHOD + ");",
+ " return Preconditions.checkNotNullFromComponent(aComponent.a());",
" }",
" }",
"}")
@@ -1277,9 +1254,8 @@ public class ComponentProcessorTest {
" switch (id) {",
" case 0:",
" return (T)",
- " Preconditions.checkNotNull(",
- " DaggerBComponent.this.aComponent.a(),",
- " " + NPE_FROM_COMPONENT_METHOD + ");",
+ " Preconditions.checkNotNullFromComponent(",
+ " DaggerBComponent.this.aComponent.a());",
" default:",
" throw new AssertionError(id);",
" }",
@@ -1287,8 +1263,7 @@ public class ComponentProcessorTest {
" }")
.build();
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(aFile, bFile, aComponentFile, bComponentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -1343,7 +1318,7 @@ public class ComponentProcessorTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private final TestModule testModule;",
" private final other.test.TestModule testModule2;",
@@ -1391,8 +1366,7 @@ public class ComponentProcessorTest {
" }",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(aFile, otherAFile, moduleFile, otherModuleFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -1469,7 +1443,7 @@ public class ComponentProcessorTest {
"import dagger.internal.Preconditions;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerBComponent implements BComponent {",
" private final AComponent aComponent;",
"",
@@ -1480,20 +1454,17 @@ public class ComponentProcessorTest {
" @Override",
" public InjectedType injectedType() {",
" return new InjectedType(",
- " Preconditions.checkNotNull(",
- " aComponent.someStringInjection(),",
- " \"Cannot return null from a non-@Nullable component method\"),",
+ " Preconditions.checkNotNullFromComponent(",
+ " aComponent.someStringInjection()),",
" aComponent.someIntInjection(),",
" aComponent,",
- " Preconditions.checkNotNull(",
- " aComponent.someClassInjection(),",
- " \"Cannot return null from a non-@Nullable component method\"));",
+ " Preconditions.checkNotNullFromComponent(",
+ " aComponent.someClassInjection()));",
" }",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(injectedTypeFile, aComponentFile, bComponentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -1556,15 +1527,15 @@ public class ComponentProcessorTest {
"",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
- " private B getB() {",
+ " private B b() {",
" return new B(new C());",
" }",
"",
" @Override",
" public A a() {",
- " return new A(getB());",
+ " return new A(b());",
" }",
"",
" @Override",
@@ -1580,8 +1551,7 @@ public class ComponentProcessorTest {
.build();
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(aFile, bFile, cFile, xFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -1637,7 +1607,7 @@ public class ComponentProcessorTest {
"",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerSimpleComponent implements SimpleComponent {",
" private DaggerSimpleComponent() {}",
"",
@@ -1663,8 +1633,7 @@ public class ComponentProcessorTest {
" }",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(
injectableTypeFile,
componentSupertypeAFile,
@@ -1707,7 +1676,7 @@ public class ComponentProcessorTest {
"test.DaggerSimpleComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerSimpleComponent implements SimpleComponent {",
" @Override",
" public SomeInjectableType someInjectableType() {",
@@ -1715,8 +1684,7 @@ public class ComponentProcessorTest {
" }",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(injectableTypeFile, componentSupertype, depComponentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -1763,8 +1731,7 @@ public class ComponentProcessorTest {
" C c();",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(aFile, bFile, cFile, componentFile);
assertThat(compilation).failed();
assertThat(compilation)
@@ -1787,7 +1754,7 @@ public class ComponentProcessorTest {
" String[] array();",
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(component);
+ compilerWithOptions(compilerMode.javacopts()).compile(component);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining("String[] cannot be provided without an @Provides-annotated method");
@@ -2013,7 +1980,7 @@ public class ComponentProcessorTest {
" @Inject @AScope AClass() {}",
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(aScope, aClass);
+ compilerWithOptions(compilerMode.javacopts()).compile(aScope, aClass);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining("@Scope annotations are not allowed on @Inject constructors")
@@ -2082,7 +2049,7 @@ public class ComponentProcessorTest {
"import dagger.internal.Preconditions;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerParent implements Parent {",
" private DaggerParent() {",
" }",
@@ -2111,8 +2078,7 @@ public class ComponentProcessorTest {
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(foo, module, component, prunedSubcomponent);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -2175,7 +2141,7 @@ public class ComponentProcessorTest {
assertThat(compilation).failed();
assertThat(compilation).hadErrorCount(1);
assertThat(compilation)
- .hadErrorContaining("java.lang.String is bound multiple times")
+ .hadErrorContaining("String is bound multiple times")
.inFile(component)
.onLineContaining("interface TestComponent");
}
@@ -2183,8 +2149,7 @@ public class ComponentProcessorTest {
@Test
public void nullIncorrectlyReturnedFromNonNullableInlinedProvider() {
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(
JavaFileObjects.forSourceLines(
"test.TestModule",
@@ -2225,7 +2190,7 @@ public class ComponentProcessorTest {
"test.TestModule_NonNullableStringFactory",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class TestModule_NonNullableStringFactory",
" implements Factory<String> {",
" @Override",
@@ -2234,8 +2199,8 @@ public class ComponentProcessorTest {
" }",
"",
" public static String nonNullableString() {",
- " return Preconditions.checkNotNull(",
- " TestModule.nonNullableString(), " + NPE_FROM_PROVIDES_METHOD + ");",
+ " return Preconditions.checkNotNullFromProvides(",
+ " TestModule.nonNullableString());",
" }",
"}"));
@@ -2245,7 +2210,7 @@ public class ComponentProcessorTest {
.addLines(
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" @Override",
" public String nonNullableString() {",
@@ -2274,8 +2239,7 @@ public class ComponentProcessorTest {
@Test
public void nullCheckingIgnoredWhenProviderReturnsPrimitive() {
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(
JavaFileObjects.forSourceLines(
"test.TestModule",
@@ -2316,7 +2280,7 @@ public class ComponentProcessorTest {
"test.TestModule_PrimitiveIntegerFactory",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class TestModule_PrimitiveIntegerFactory",
" implements Factory<Integer> {",
"",
@@ -2336,7 +2300,7 @@ public class ComponentProcessorTest {
.addLines(
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" @Override",
" public Integer nonNullableInteger() {",
@@ -2412,9 +2376,9 @@ public class ComponentProcessorTest {
JavaFileObjects.forSourceLines(
"test.DaggerParent",
"package test;",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerParent implements Parent {",
- " private String getString() {",
+ " private String string() {",
" return TestModule_StringFactory.string(numberProvider.get());",
" }",
"}");
@@ -2482,7 +2446,7 @@ public class ComponentProcessorTest {
JavaFileObjects.forSourceLines(
"test.DaggerParent",
"package test;",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerParent implements Parent {",
" private final class ChildImpl implements Child {",
" @Override",
@@ -2549,7 +2513,7 @@ public class ComponentProcessorTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" @Override",
" public Injected injected() {",
@@ -2622,7 +2586,7 @@ public class ComponentProcessorTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" @Override",
" public String unqualified() {",
@@ -2676,7 +2640,7 @@ public class ComponentProcessorTest {
"",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class DaggerPublicComponent implements PublicComponent {",
" private DaggerPublicComponent() {}",
"",
diff --git a/javatests/dagger/internal/codegen/ComponentRequirementFieldTest.java b/javatests/dagger/internal/codegen/ComponentRequirementFieldTest.java
index 85e2d7b9a..049ff85d7 100644
--- a/javatests/dagger/internal/codegen/ComponentRequirementFieldTest.java
+++ b/javatests/dagger/internal/codegen/ComponentRequirementFieldTest.java
@@ -17,9 +17,8 @@
package dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
-import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
-import static dagger.internal.codegen.GeneratedLines.NPE_FROM_COMPONENT_METHOD;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
@@ -67,7 +66,7 @@ public class ComponentRequirementFieldTest {
" }",
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(component);
+ compilerWithOptions(compilerMode.javacopts()).compile(component);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerTestComponent")
@@ -76,7 +75,7 @@ public class ComponentRequirementFieldTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private final Integer i;",
" private final List<String> list;",
@@ -162,8 +161,7 @@ public class ComponentRequirementFieldTest {
" long l();",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(module, otherPackageModule, component);
assertThat(compilation).succeeded();
JavaFileObject generatedComponent =
@@ -174,7 +172,7 @@ public class ComponentRequirementFieldTest {
"import other.OtherPackageModule;",
"import other.OtherPackageModule_LFactory;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private final ParentModule parentModule;",
" private final OtherPackageModule otherPackageModule;",
@@ -236,8 +234,7 @@ public class ComponentRequirementFieldTest {
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(dependency, component, subcomponent);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -247,7 +244,7 @@ public class ComponentRequirementFieldTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private final Dep dep;",
"",
@@ -267,14 +264,13 @@ public class ComponentRequirementFieldTest {
"",
" @Override",
" public String methodOnDep() {",
- " return Preconditions.checkNotNull(",
- " dep.string(), " + NPE_FROM_COMPONENT_METHOD + " );",
+ " return Preconditions.checkNotNullFromComponent(",
+ " dep.string());",
" }",
"",
" @Override",
" public Object otherMethodOnDep() {",
- " return Preconditions.checkNotNull(",
- " dep.object(), " + NPE_FROM_COMPONENT_METHOD + " );",
+ " return Preconditions.checkNotNullFromComponent(dep.object());",
" }",
"",
" private final class TestSubcomponentImpl implements TestSubcomponent {",
@@ -361,7 +357,7 @@ public class ComponentRequirementFieldTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private final ParentModule parentModule;",
"",
@@ -370,15 +366,15 @@ public class ComponentRequirementFieldTest {
" }",
"",
" private final class TestSubcomponentImpl implements TestSubcomponent {",
- " private Set<Object> getSetOfObject() {",
+ " private Set<Object> setOfObject() {",
" return ImmutableSet.<Object>of(",
" ParentModule_ContributionFactory.contribution(),",
" ChildModule_ContributionFactory.contribution());",
" }",
"",
- " private Object getObject() {",
+ " private Object object() {",
" return ParentModule_ReliesOnMultibindingFactory.reliesOnMultibinding(",
- " DaggerTestComponent.this.parentModule, getSetOfObject());",
+ " DaggerTestComponent.this.parentModule, setOfObject());",
" }",
" }",
"}");
@@ -389,7 +385,7 @@ public class ComponentRequirementFieldTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private final ParentModule parentModule;",
"",
@@ -425,8 +421,7 @@ public class ComponentRequirementFieldTest {
"}");
}
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(parentModule, childModule, component, subcomponent);
assertThat(compilation).succeeded();
assertThat(compilation)
diff --git a/javatests/dagger/internal/codegen/ComponentShardTest.java b/javatests/dagger/internal/codegen/ComponentShardTest.java
new file mode 100644
index 000000000..fc59c926b
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ComponentShardTest.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.Compiler.javac;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
+import static java.util.stream.Collectors.joining;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.Compiler;
+import com.google.testing.compile.JavaFileObjects;
+import java.util.Arrays;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ComponentShardTest {
+ private static final int BINDINGS_PER_SHARD = 10;
+
+ @Test
+ public void testNewShardCreated() {
+ // Create 2N + 1 bindings: N in DaggerTestComponent, N in Shard1, and 1 in Shard2
+ int numBindings = 2 * BINDINGS_PER_SHARD + 1;
+ ImmutableList.Builder<JavaFileObject> javaFileObjects = ImmutableList.builder();
+ ImmutableList.Builder<String> entryPoints = ImmutableList.builder();
+ for (int i = 0; i < numBindings; i++) {
+ String bindingName = "Binding" + i;
+ entryPoints.add(String.format("%1$s get%1$s();", bindingName));
+ entryPoints.add(String.format("Provider<%1$s> get%1$sProvider();", bindingName));
+
+ // Add dependencies between main component and shard1: 9 -> 10 -> Provider<9>
+ // Add dependencies between shard1 and shard2: 19 -> 20 -> Provider<19>
+ switch (i) {
+ case 9:
+ javaFileObjects.add(createBinding(bindingName, "Binding10 dep"));
+ break;
+ case 10:
+ javaFileObjects.add(createBinding(bindingName, "Provider<Binding9> dep"));
+ break;
+ case 19:
+ javaFileObjects.add(createBinding(bindingName, "Binding20 dep"));
+ break;
+ case 20:
+ javaFileObjects.add(createBinding(bindingName, "Provider<Binding19> dep"));
+ break;
+ default:
+ javaFileObjects.add(createBinding(bindingName));
+ break;
+ }
+ }
+
+ javaFileObjects.add(createComponent(entryPoints.build()));
+
+ // This generated component shows a couple things:
+ // 1. Binding locations:
+ // * Binding #9 belongs to DaggerTestComponent
+ // * Binding #10 belongs to Shard1
+ // * Binding #20 belongs to Shard2
+ // 2. DaggerTestComponent entry point methods:
+ // * Binding #9 implementation is inlined DaggerTestComponent.
+ // * Binding #10 implementation is delegated to Shard1.
+ // * Binding #20 implementation is delegated to Shard2.
+ // 3. Dependencies between component and shard:
+ // * Binding #9 in DaggerTestComponent depends on #10 in Shard1.
+ // * Binding #10 in Shard1 depends on Provider<#9> in DaggerTestComponent.
+ // 4. Dependencies between shard and shard:
+ // * Binding #19 in Shard1 depends on #20 in Shard2.
+ // * Binding #20 in Shard2 depends on Provider<#19> in Shard1.
+ JavaFileObject generatedComponent =
+ JavaFileObjects.forSourceLines(
+ "dagger.internal.codegen.DaggerTestComponent",
+ "package dagger.internal.codegen;",
+ GENERATED_CODE_ANNOTATIONS,
+ "final class DaggerTestComponent implements TestComponent {",
+ " private final Shard1 shard1 = new Shard1();",
+ "",
+ " private volatile Provider<Binding9> binding9Provider;",
+ "",
+ " private volatile Object binding9 = new MemoizedSentinel();",
+ "",
+ " @Override",
+ " public Binding9 getBinding9() {",
+ " Object local = binding9;",
+ " if (local instanceof MemoizedSentinel) {",
+ " synchronized (local) {",
+ " local = binding9;",
+ " if (local instanceof MemoizedSentinel) {",
+ " local = new Binding9(DaggerTestComponent.this.shard1.binding10());",
+ " binding9 = DoubleCheck.reentrantCheck(binding9, local);",
+ " }",
+ " }",
+ " }",
+ " return (Binding9) local;",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Binding9> getBinding9Provider() {",
+ " Object local = binding9Provider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(9);",
+ " binding9Provider = (Provider<Binding9>) local;",
+ " }",
+ " return (Provider<Binding9>) local;",
+ " }",
+ "",
+ " @Override",
+ " public Binding10 getBinding10() {",
+ " return DaggerTestComponent.this.shard1.binding10();",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Binding10> getBinding10Provider() {",
+ " return DaggerTestComponent.this.shard1.binding10Provider();",
+ " }",
+ "",
+ " @Override",
+ " public Binding20 getBinding20() {",
+ " return DaggerTestComponent.this.shard2.binding20();",
+ " }",
+ "",
+ " @Override",
+ " public Provider<Binding20> getBinding20Provider() {",
+ " return DaggerTestComponent.this.shard2.binding20Provider();",
+ " }",
+ "",
+ " private final class Shard1 {",
+ " private volatile Object binding10 = new MemoizedSentinel();",
+ "",
+ " private volatile Provider<Binding10> binding10Provider;",
+ "",
+ " private volatile Provider<Binding19> binding19Provider;",
+ "",
+ " private volatile Object binding19 = new MemoizedSentinel();",
+ "",
+ " private Binding10 binding10() {",
+ " Object local = binding10;",
+ " if (local instanceof MemoizedSentinel) {",
+ " synchronized (local) {",
+ " local = binding10;",
+ " if (local instanceof MemoizedSentinel) {",
+ " local = new Binding10(",
+ " DaggerTestComponent.this.getBinding9Provider());",
+ " binding10 = DoubleCheck.reentrantCheck(binding10, local);",
+ " }",
+ " }",
+ " }",
+ " return (Binding10) local;",
+ " }",
+ "",
+ " private Provider<Binding10> binding10Provider() {",
+ " Object local = binding10Provider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(10);",
+ " binding10Provider = (Provider<Binding10>) local;",
+ " }",
+ " return (Provider<Binding10>) local;",
+ " }",
+ "",
+ " private Provider<Binding19> binding19Provider() {",
+ " Object local = binding19Provider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(19);",
+ " binding19Provider = (Provider<Binding19>) local;",
+ " }",
+ " return (Provider<Binding19>) local;",
+ " }",
+ "",
+ " private Binding19 binding19() {",
+ " Object local = binding19;",
+ " if (local instanceof MemoizedSentinel) {",
+ " synchronized (local) {",
+ " local = binding19;",
+ " if (local instanceof MemoizedSentinel) {",
+ " local = new Binding19(DaggerTestComponent.this.shard2.binding20());",
+ " binding19 = DoubleCheck.reentrantCheck(binding19, local);",
+ " }",
+ " }",
+ " }",
+ " return (Binding19) local;",
+ " }",
+ " }",
+ "",
+ " private final class Shard2 {",
+ " private volatile Object binding20 = new MemoizedSentinel();",
+ "",
+ " private volatile Provider<Binding20> binding20Provider;",
+ "",
+ " private Binding20 binding20() {",
+ " Object local = binding20;",
+ " if (local instanceof MemoizedSentinel) {",
+ " synchronized (local) {",
+ " local = binding20;",
+ " if (local instanceof MemoizedSentinel) {",
+ " local = new Binding20(",
+ " DaggerTestComponent.this.shard1.binding19Provider());",
+ " binding20 = DoubleCheck.reentrantCheck(binding20, local);",
+ " }",
+ " }",
+ " }",
+ " return (Binding20) local;",
+ " }",
+ "",
+ " private Provider<Binding20> binding20Provider() {",
+ " Object local = binding20Provider;",
+ " if (local == null) {",
+ " local = new SwitchingProvider<>(20);",
+ " binding20Provider = (Provider<Binding20>) local;",
+ " }",
+ " return (Provider<Binding20>) local;",
+ " }",
+ " }",
+ "}");
+
+ Compilation compilation = compilerWithAndroidMode().compile(javaFileObjects.build());
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("dagger.internal.codegen.DaggerTestComponent")
+ .containsElementsIn(generatedComponent);
+ }
+
+ private static JavaFileObject createBinding(String bindingName, String... deps) {
+ return JavaFileObjects.forSourceLines(
+ "dagger.internal.codegen." + bindingName,
+ "package dagger.internal.codegen;",
+ "",
+ "import javax.inject.Inject;",
+ "import javax.inject.Provider;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "final class " + bindingName + " {",
+ " @Inject",
+ " " + bindingName + "(" + Arrays.stream(deps).collect(joining(", ")) + ") {}",
+ "}");
+ }
+
+ private static JavaFileObject createComponent(ImmutableList<String> entryPoints) {
+ return JavaFileObjects.forSourceLines(
+ "dagger.internal.codegen.TestComponent",
+ "package dagger.internal.codegen;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Provider;",
+ "import javax.inject.Singleton;",
+ "",
+ "@Singleton",
+ "@Component",
+ "interface TestComponent {",
+ " " + entryPoints.stream().collect(joining("\n ")),
+ "}");
+ }
+
+ private static Compiler compilerWithAndroidMode() {
+ return javac()
+ .withProcessors(new ComponentProcessor())
+ .withOptions(
+ ImmutableSet.builder()
+ .add("-Adagger.keysPerComponentShard=" + BINDINGS_PER_SHARD)
+ .addAll(CompilerMode.FAST_INIT_MODE.javacopts())
+ .build());
+ }
+}
diff --git a/javatests/dagger/internal/codegen/ComponentValidationTest.java b/javatests/dagger/internal/codegen/ComponentValidationTest.java
index 169a318a7..6c642a409 100644
--- a/javatests/dagger/internal/codegen/ComponentValidationTest.java
+++ b/javatests/dagger/internal/codegen/ComponentValidationTest.java
@@ -17,6 +17,7 @@
package dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
import static dagger.internal.codegen.Compilers.daggerCompiler;
import static dagger.internal.codegen.TestUtils.message;
@@ -219,6 +220,17 @@ public final class ComponentValidationTest {
message(
"test.ComponentShort contains a cycle in its component dependencies:",
" test.ComponentShort"));
+
+ // Test that this also fails when transitive validation is disabled.
+ compilation =
+ compilerWithOptions("-Adagger.validateTransitiveComponentDependencies=DISABLED")
+ .compile(shortLifetime);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "test.ComponentShort contains a cycle in its component dependencies:",
+ " test.ComponentShort"));
}
@Test
@@ -281,6 +293,13 @@ public final class ComponentValidationTest {
" test.ComponentMedium",
" test.ComponentShort"))
.inFile(shortLifetime);
+
+ // Test that compilation succeeds when transitive validation is disabled because the cycle
+ // cannot be detected.
+ compilation =
+ compilerWithOptions("-Adagger.validateTransitiveComponentDependencies=DISABLED")
+ .compile(longLifetime, mediumLifetime, shortLifetime);
+ assertThat(compilation).succeeded();
}
@Test
diff --git a/javatests/dagger/internal/codegen/DaggerModuleMethodSubject.java b/javatests/dagger/internal/codegen/DaggerModuleMethodSubject.java
index 1fcf7bc3a..470b1edaf 100644
--- a/javatests/dagger/internal/codegen/DaggerModuleMethodSubject.java
+++ b/javatests/dagger/internal/codegen/DaggerModuleMethodSubject.java
@@ -36,7 +36,7 @@ import java.util.List;
import javax.tools.JavaFileObject;
/** A {@link Truth} subject for testing Dagger module methods. */
-final class DaggerModuleMethodSubject extends Subject<DaggerModuleMethodSubject, String> {
+final class DaggerModuleMethodSubject extends Subject {
/** A {@link Truth} subject factory for testing Dagger module methods. */
static final class Factory implements Subject.Factory<DaggerModuleMethodSubject, String> {
diff --git a/javatests/dagger/internal/codegen/DelegateBindingExpressionTest.java b/javatests/dagger/internal/codegen/DelegateBindingExpressionTest.java
index 2f4aecf85..bc729fc07 100644
--- a/javatests/dagger/internal/codegen/DelegateBindingExpressionTest.java
+++ b/javatests/dagger/internal/codegen/DelegateBindingExpressionTest.java
@@ -19,8 +19,8 @@ package dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
-import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.CompilationSubject;
@@ -144,14 +144,14 @@ public class DelegateBindingExpressionTest {
.addLines(
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {")
.addLinesIn(
FAST_INIT_MODE,
" private volatile Object regularScoped = new MemoizedSentinel();",
" private volatile ReusableScoped reusableScoped;",
"",
- " private RegularScoped getRegularScoped() {",
+ " private RegularScoped regularScoped() {",
" Object local = regularScoped;",
" if (local instanceof MemoizedSentinel) {",
" synchronized (local) {",
@@ -165,7 +165,7 @@ public class DelegateBindingExpressionTest {
" return (RegularScoped) local;",
" }",
"",
- " private ReusableScoped getReusableScoped() {",
+ " private ReusableScoped reusableScoped() {",
" Object local = reusableScoped;",
" if (local == null) {",
" local = new ReusableScoped();",
@@ -223,14 +223,14 @@ public class DelegateBindingExpressionTest {
.addLines(
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {")
.addLinesIn(
FAST_INIT_MODE,
" private volatile Object regularScoped = new MemoizedSentinel();",
" private volatile ReusableScoped reusableScoped;",
"",
- " private RegularScoped getRegularScoped() {",
+ " private RegularScoped regularScoped() {",
" Object local = regularScoped;",
" if (local instanceof MemoizedSentinel) {",
" synchronized (local) {",
@@ -244,7 +244,7 @@ public class DelegateBindingExpressionTest {
" return (RegularScoped) local;",
" }",
"",
- " private ReusableScoped getReusableScoped() {",
+ " private ReusableScoped reusableScoped() {",
" Object local = reusableScoped;",
" if (local == null) {",
" local = new ReusableScoped();",
@@ -299,14 +299,14 @@ public class DelegateBindingExpressionTest {
.addLines(
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {")
.addLinesIn(
FAST_INIT_MODE,
" private volatile Object regularScoped = new MemoizedSentinel();",
" private volatile ReusableScoped reusableScoped;",
"",
- " private RegularScoped getRegularScoped() {",
+ " private RegularScoped regularScoped() {",
" Object local = regularScoped;",
" if (local instanceof MemoizedSentinel) {",
" synchronized (local) {",
@@ -320,7 +320,7 @@ public class DelegateBindingExpressionTest {
" return (RegularScoped) local;",
" }",
"",
- " private ReusableScoped getReusableScoped() {",
+ " private ReusableScoped reusableScoped() {",
" Object local = reusableScoped;",
" if (local == null) {",
" local = new ReusableScoped();",
@@ -390,8 +390,7 @@ public class DelegateBindingExpressionTest {
" other.Supertype supertype();",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(accessibleSupertype, inaccessibleSubtype, module, component);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -402,7 +401,7 @@ public class DelegateBindingExpressionTest {
.addLines(
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {")
.addLinesIn(
DEFAULT_MODE,
@@ -422,7 +421,7 @@ public class DelegateBindingExpressionTest {
FAST_INIT_MODE,
" private volatile Object subtype = new MemoizedSentinel();",
"",
- " private Object getSubtype() {",
+ " private Object subtype() {",
" Object local = subtype;",
" if (local instanceof MemoizedSentinel) {",
" synchronized (local) {",
@@ -438,7 +437,7 @@ public class DelegateBindingExpressionTest {
"",
" @Override",
" public Supertype supertype() {",
- " return (Supertype) getSubtype();",
+ " return (Supertype) subtype();",
" }")
.build());
}
@@ -499,8 +498,7 @@ public class DelegateBindingExpressionTest {
" other.UsesSupertype usesSupertype();",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(supertype, subtype, usesSupertype, module, component);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -511,7 +509,7 @@ public class DelegateBindingExpressionTest {
.addLines(
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {")
.addLinesIn(
DEFAULT_MODE,
@@ -528,7 +526,7 @@ public class DelegateBindingExpressionTest {
FAST_INIT_MODE,
" private volatile Object subtype = new MemoizedSentinel();",
"",
- " private Object getSubtype() {",
+ " private Object subtype() {",
" Object local = subtype;",
" if (local instanceof MemoizedSentinel) {",
" synchronized (local) {",
@@ -544,7 +542,7 @@ public class DelegateBindingExpressionTest {
"",
" @Override",
" public UsesSupertype usesSupertype() {",
- " return UsesSupertype_Factory.newInstance(getSubtype());",
+ " return UsesSupertype_Factory.newInstance(subtype());",
" }")
.build());
}
@@ -590,8 +588,7 @@ public class DelegateBindingExpressionTest {
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(module, component);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -602,7 +599,7 @@ public class DelegateBindingExpressionTest {
.addLines(
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {")
.addLinesIn(
DEFAULT_MODE,
@@ -620,7 +617,7 @@ public class DelegateBindingExpressionTest {
FAST_INIT_MODE,
" private volatile Provider<String> provideStringProvider;",
"",
- " private Provider<String> getStringProvider() {",
+ " private Provider<String> stringProvider() {",
" Object local = provideStringProvider;",
" if (local == null) {",
" local = new SwitchingProvider<>(0);",
@@ -631,12 +628,12 @@ public class DelegateBindingExpressionTest {
"",
" @Override",
" public Provider<CharSequence> charSequence() {",
- " return (Provider) getStringProvider();",
+ " return (Provider) stringProvider();",
" }",
"",
" @Override",
" public Provider<String> namedString() {",
- " return getStringProvider();",
+ " return stringProvider();",
" }",
"",
" private final class SwitchingProvider<T> implements Provider<T> {",
@@ -692,8 +689,7 @@ public class DelegateBindingExpressionTest {
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(module, component);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -704,7 +700,7 @@ public class DelegateBindingExpressionTest {
.addLines(
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {")
.addLinesIn(
DEFAULT_MODE,
@@ -721,7 +717,7 @@ public class DelegateBindingExpressionTest {
FAST_INIT_MODE,
" private volatile Provider<String> provideStringProvider;",
"",
- " private Provider<String> getStringProvider() {",
+ " private Provider<String> stringProvider() {",
" Object local = provideStringProvider;",
" if (local == null) {",
" local = new SwitchingProvider<>(0);",
@@ -732,12 +728,12 @@ public class DelegateBindingExpressionTest {
"",
" @Override",
" public Provider<CharSequence> charSequence() {",
- " return (Provider) getStringProvider();",
+ " return (Provider) stringProvider();",
" }",
"",
" @Override",
" public Provider<Object> object() {",
- " return (Provider) getStringProvider();",
+ " return (Provider) stringProvider();",
" }",
"",
" private final class SwitchingProvider<T> implements Provider<T> {",
@@ -798,8 +794,7 @@ public class DelegateBindingExpressionTest {
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(supertype, injectableSubtype, module, component);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -810,7 +805,7 @@ public class DelegateBindingExpressionTest {
.addLines(
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerRequestsSubtypeAsProvider",
" implements RequestsSubtypeAsProvider {")
.addLinesIn(
@@ -824,7 +819,7 @@ public class DelegateBindingExpressionTest {
FAST_INIT_MODE,
" private volatile Provider subtypeProvider;",
"",
- " private Provider getSubtypeProvider() {",
+ " private Provider subtypeProvider() {",
" Object local = subtypeProvider;",
" if (local == null) {",
" local = new SwitchingProvider<>(0);",
@@ -835,7 +830,7 @@ public class DelegateBindingExpressionTest {
"",
" @Override",
" public Provider<Supertype> supertypeProvider() {",
- " return getSubtypeProvider();",
+ " return subtypeProvider();",
" }",
"",
" private final class SwitchingProvider<T> implements Provider<T> {",
@@ -888,11 +883,10 @@ public class DelegateBindingExpressionTest {
"@Singleton",
"@Component(modules = TestModule.class)",
"interface TestComponent {",
- " Provider<Object> getObject();",
+ " Provider<Object> object();",
"}");
- Compilation compilation = daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts())
.compile(module, component);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -903,7 +897,7 @@ public class DelegateBindingExpressionTest {
.addLines(
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {")
.addLinesIn(
DEFAULT_MODE,
@@ -919,7 +913,7 @@ public class DelegateBindingExpressionTest {
" }",
"",
" @Override",
- " public Provider<Object> getObject() {",
+ " public Provider<Object> object() {",
" return bindStringProvider;",
" }",
"}")
@@ -929,7 +923,7 @@ public class DelegateBindingExpressionTest {
" private volatile Object object = new MemoizedSentinel();",
" private volatile Provider<Object> bindStringProvider;",
"",
- " private String getString() {",
+ " private String string() {",
" Object local = string;",
" if (local == null) {",
" local = TestModule_ProvideStringFactory.provideString();",
@@ -938,13 +932,13 @@ public class DelegateBindingExpressionTest {
" return (String) local;",
" }",
"",
- " private Object getObject2() {",
+ " private Object object2() {",
" Object local = object;",
" if (local instanceof MemoizedSentinel) {",
" synchronized (local) {",
" local = object;",
" if (local instanceof MemoizedSentinel) {",
- " local = getString();",
+ " local = string();",
" object = DoubleCheck.reentrantCheck(object, local);",
" }",
" }",
@@ -953,7 +947,7 @@ public class DelegateBindingExpressionTest {
" }",
"",
" @Override",
- " public Provider<Object> getObject() {",
+ " public Provider<Object> object() {",
" Object local = bindStringProvider;",
" if (local == null) {",
" local = new SwitchingProvider<>(0);",
@@ -967,7 +961,7 @@ public class DelegateBindingExpressionTest {
" @Override",
" public T get() {",
" switch (id) {",
- " case 0: return (T) DaggerTestComponent.this.getObject2();",
+ " case 0: return (T) DaggerTestComponent.this.object2();",
" default: throw new AssertionError(id);",
" }",
" }",
@@ -977,8 +971,7 @@ public class DelegateBindingExpressionTest {
private CompilationSubject assertThatCompilationWithModule(JavaFileObject module) {
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(
module,
COMPONENT,
diff --git a/javatests/dagger/internal/codegen/DependencyCycleValidationTest.java b/javatests/dagger/internal/codegen/DependencyCycleValidationTest.java
index 4a4e0a51d..22547b5cb 100644
--- a/javatests/dagger/internal/codegen/DependencyCycleValidationTest.java
+++ b/javatests/dagger/internal/codegen/DependencyCycleValidationTest.java
@@ -17,6 +17,7 @@
package dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
import static dagger.internal.codegen.Compilers.daggerCompiler;
import static dagger.internal.codegen.TestUtils.endsWithMessage;
import static dagger.internal.codegen.TestUtils.message;
@@ -75,14 +76,14 @@ public class DependencyCycleValidationTest {
.hadErrorContaining(
message(
"Found a dependency cycle:",
- " test.Outer.C is injected at",
- " test.Outer.A(cParam)",
- " test.Outer.A is injected at",
- " test.Outer.B(aParam)",
- " test.Outer.B is injected at",
- " test.Outer.C(bParam)",
- " test.Outer.C is provided at",
- " test.Outer.CComponent.getC()"))
+ " Outer.C is injected at",
+ " Outer.A(cParam)",
+ " Outer.A is injected at",
+ " Outer.B(aParam)",
+ " Outer.B is injected at",
+ " Outer.C(bParam)",
+ " Outer.C is requested at",
+ " Outer.CComponent.getC()"))
.inFile(SIMPLE_CYCLIC_DEPENDENCY)
.onLineContaining("interface CComponent");
@@ -92,21 +93,28 @@ public class DependencyCycleValidationTest {
@Test
public void cyclicDependencyWithModuleBindingValidation() {
// Cycle errors should not show a dependency trace to an entry point when doing full binding
- // graph validation. So ensure that the message doesn't end with "test.Outer.C is provided at
+ // graph validation. So ensure that the message doesn't end with "test.Outer.C is requested at
// test.Outer.CComponent.getC()", as the previous test's message does.
Pattern moduleBindingValidationError =
endsWithMessage(
"Found a dependency cycle:",
- " test.Outer.C is injected at",
- " test.Outer.A(cParam)",
- " test.Outer.A is injected at",
- " test.Outer.B(aParam)",
- " test.Outer.B is injected at",
- " test.Outer.C(bParam)");
+ " Outer.C is injected at",
+ " Outer.A(cParam)",
+ " Outer.A is injected at",
+ " Outer.B(aParam)",
+ " Outer.B is injected at",
+ " Outer.C(bParam)",
+ "",
+ "======================",
+ "Full classname legend:",
+ "======================",
+ "Outer: test.Outer",
+ "========================",
+ "End of classname legend:",
+ "========================");
Compilation compilation =
- daggerCompiler()
- .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ compilerWithOptions("-Adagger.fullBindingGraphValidation=ERROR")
.compile(SIMPLE_CYCLIC_DEPENDENCY);
assertThat(compilation).failed();
@@ -163,16 +171,16 @@ public class DependencyCycleValidationTest {
.hadErrorContaining(
message(
"Found a dependency cycle:",
- " test.Outer.C is injected at",
- " test.Outer.A(cParam)",
- " test.Outer.A is injected at",
- " test.Outer.B(aParam)",
- " test.Outer.B is injected at",
- " test.Outer.C(bParam)",
- " test.Outer.C is injected at",
- " test.Outer.D(cParam)",
- " test.Outer.D is provided at",
- " test.Outer.DComponent.getD()"))
+ " Outer.C is injected at",
+ " Outer.A(cParam)",
+ " Outer.A is injected at",
+ " Outer.B(aParam)",
+ " Outer.B is injected at",
+ " Outer.C(bParam)",
+ " Outer.C is injected at",
+ " Outer.D(cParam)",
+ " Outer.D is requested at",
+ " Outer.DComponent.getD()"))
.inFile(component)
.onLineContaining("interface DComponent");
}
@@ -226,16 +234,16 @@ public class DependencyCycleValidationTest {
.hadErrorContaining(
message(
"Found a dependency cycle:",
- " test.Outer.C is injected at",
- " test.Outer.CModule.c(c)",
- " java.util.Map<java.lang.String,test.Outer.C> is injected at",
- " test.Outer.A(cMap)",
- " test.Outer.A is injected at",
- " test.Outer.B(aParam)",
- " test.Outer.B is injected at",
- " test.Outer.C(bParam)",
- " test.Outer.C is provided at",
- " test.Outer.CComponent.getC()"))
+ " Outer.C is injected at",
+ " Outer.CModule.c(c)",
+ " Map<String,Outer.C> is injected at",
+ " Outer.A(cMap)",
+ " Outer.A is injected at",
+ " Outer.B(aParam)",
+ " Outer.B is injected at",
+ " Outer.C(bParam)",
+ " Outer.C is requested at",
+ " Outer.CComponent.getC()"))
.inFile(component)
.onLineContaining("interface CComponent");
}
@@ -287,16 +295,16 @@ public class DependencyCycleValidationTest {
.hadErrorContaining(
message(
"Found a dependency cycle:",
- " test.Outer.C is injected at",
- " test.Outer.CModule.c(c)",
- " java.util.Set<test.Outer.C> is injected at",
- " test.Outer.A(cSet)",
- " test.Outer.A is injected at",
- " test.Outer.B(aParam)",
- " test.Outer.B is injected at",
- " test.Outer.C(bParam)",
- " test.Outer.C is provided at",
- " test.Outer.CComponent.getC()"))
+ " Outer.C is injected at",
+ " Outer.CModule.c(c)",
+ " Set<Outer.C> is injected at",
+ " Outer.A(cSet)",
+ " Outer.A is injected at",
+ " Outer.B(aParam)",
+ " Outer.B is injected at",
+ " Outer.C(bParam)",
+ " Outer.C is requested at",
+ " Outer.CComponent.getC()"))
.inFile(component)
.onLineContaining("interface CComponent");
}
@@ -343,16 +351,16 @@ public class DependencyCycleValidationTest {
.hadErrorContaining(
message(
"Found a dependency cycle:",
- " test.Outer.C is injected at",
- " test.Outer.A(cParam)",
- " test.Outer.A is injected at",
- " test.Outer.B(aParam)",
- " test.Outer.B is injected at",
- " test.Outer.C(bParam)",
- " javax.inject.Provider<test.Outer.C> is injected at",
- " test.Outer.D(cParam)",
- " test.Outer.D is provided at",
- " test.Outer.DComponent.getD()"))
+ " Outer.C is injected at",
+ " Outer.A(cParam)",
+ " Outer.A is injected at",
+ " Outer.B(aParam)",
+ " Outer.B is injected at",
+ " Outer.C(bParam)",
+ " Provider<Outer.C> is injected at",
+ " Outer.D(cParam)",
+ " Outer.D is requested at",
+ " Outer.DComponent.getD()"))
.inFile(component)
.onLineContaining("interface DComponent");
}
@@ -427,12 +435,12 @@ public class DependencyCycleValidationTest {
.hadErrorContaining(
message(
"Found a dependency cycle:",
- " java.lang.String is injected at",
- " test.CycleModule.object(string)",
- " java.lang.Object is injected at",
- " test.CycleModule.string(object)",
- " java.lang.String is provided at",
- " test.Grandchild.entry()"))
+ " String is injected at",
+ " CycleModule.object(string)",
+ " Object is injected at",
+ " CycleModule.string(object)",
+ " String is requested at",
+ " Grandchild.entry()"))
.inFile(parent)
.onLineContaining("interface Parent");
}
@@ -509,12 +517,12 @@ public class DependencyCycleValidationTest {
.hadErrorContaining(
message(
"Found a dependency cycle:",
- " java.lang.String is injected at",
- " test.CycleModule.object(string)",
- " java.lang.Object is injected at",
- " test.CycleModule.string(object)",
- " java.lang.String is provided at",
- " test.Child.entry() [test.Parent → test.Child]"))
+ " String is injected at",
+ " CycleModule.object(string)",
+ " Object is injected at",
+ " CycleModule.string(object)",
+ " String is requested at",
+ " Child.entry() [Parent → Child]"))
.inFile(parent)
.onLineContaining("interface Parent");
}
@@ -560,12 +568,12 @@ public class DependencyCycleValidationTest {
.hadErrorContaining(
message(
"Found a dependency cycle:",
- " java.lang.Object is injected at",
- " test.TestModule.bindQualified(unqualified)",
- " @test.SomeQualifier java.lang.Object is injected at",
- " test.TestModule.bindUnqualified(qualified)",
- " java.lang.Object is provided at",
- " test.TestComponent.unqualified()"))
+ " Object is injected at",
+ " TestModule.bindQualified(unqualified)",
+ " @SomeQualifier Object is injected at",
+ " TestModule.bindUnqualified(qualified)",
+ " Object is requested at",
+ " TestComponent.unqualified()"))
.inFile(component)
.onLineContaining("interface TestComponent");
}
@@ -602,10 +610,10 @@ public class DependencyCycleValidationTest {
.hadErrorContaining(
message(
"Found a dependency cycle:",
- " java.lang.Object is injected at",
- " test.TestModule.bindToSelf(sameKey)",
- " java.lang.Object is provided at",
- " test.TestComponent.selfReferential()"))
+ " Object is injected at",
+ " TestModule.bindToSelf(sameKey)",
+ " Object is requested at",
+ " TestComponent.selfReferential()"))
.inFile(component)
.onLineContaining("interface TestComponent");
}
@@ -659,7 +667,7 @@ public class DependencyCycleValidationTest {
" test.B is injected at",
" test.A.b",
" test.A is injected at",
- " test.CycleComponent.inject(test.A)"))
+ " CycleComponent.inject(test.A)"))
.inFile(component)
.onLineContaining("interface CycleComponent");
}
diff --git a/javatests/dagger/internal/codegen/DiagnosticFormattingTest.java b/javatests/dagger/internal/codegen/DiagnosticFormattingTest.java
index a2da92f57..9b0dde00e 100644
--- a/javatests/dagger/internal/codegen/DiagnosticFormattingTest.java
+++ b/javatests/dagger/internal/codegen/DiagnosticFormattingTest.java
@@ -18,6 +18,7 @@ package dagger.internal.codegen;
import static com.google.common.truth.Truth.assertThat;
+import dagger.internal.codegen.base.DiagnosticFormatting;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
diff --git a/javatests/dagger/internal/codegen/DuplicateBindingsValidationTest.java b/javatests/dagger/internal/codegen/DuplicateBindingsValidationTest.java
index 14a6fb967..fbda59d78 100644
--- a/javatests/dagger/internal/codegen/DuplicateBindingsValidationTest.java
+++ b/javatests/dagger/internal/codegen/DuplicateBindingsValidationTest.java
@@ -17,6 +17,7 @@
package dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
import static dagger.internal.codegen.Compilers.daggerCompiler;
import static dagger.internal.codegen.TestUtils.message;
import static org.junit.Assume.assumeFalse;
@@ -82,14 +83,16 @@ public class DuplicateBindingsValidationTest {
"}");
Compilation compilation =
- daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
+ compilerWithOptions(
+ fullBindingGraphValidationOption())
+ .compile(component);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
- "test.Outer.A is bound multiple times:",
- " @Provides test.Outer.A test.Outer.AModule.provideA(String)",
- " test.Outer.A test.Outer.Parent.getA()"))
+ "Outer.A is bound multiple times:",
+ " @Provides Outer.A Outer.AModule.provideA(String)",
+ " Outer.A Outer.Parent.getA()"))
.inFile(component)
.onLineContaining("interface Child");
}
@@ -134,14 +137,16 @@ public class DuplicateBindingsValidationTest {
"}");
Compilation compilation =
- daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
+ compilerWithOptions(
+ fullBindingGraphValidationOption())
+ .compile(component);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
- "test.Outer.A is bound multiple times:",
- " @Provides test.Outer.A test.Outer.Module1.provideA1()",
- " @Provides test.Outer.A test.Outer.Module2.provideA2(String)"))
+ "Outer.A is bound multiple times:",
+ " @Provides Outer.A Outer.Module1.provideA1()",
+ " @Provides Outer.A Outer.Module2.provideA2(String)"))
.inFile(component)
.onLineContaining("interface TestComponent");
@@ -149,9 +154,9 @@ public class DuplicateBindingsValidationTest {
assertThat(compilation)
.hadErrorContaining(
message(
- "test.Outer.A is bound multiple times:",
- " @Provides test.Outer.A test.Outer.Module1.provideA1()",
- " @Provides test.Outer.A test.Outer.Module2.provideA2(String)"))
+ "Outer.A is bound multiple times:",
+ " @Provides Outer.A Outer.Module1.provideA1()",
+ " @Provides Outer.A Outer.Module2.provideA2(String)"))
.inFile(component)
.onLineContaining("class Module3");
}
@@ -200,14 +205,16 @@ public class DuplicateBindingsValidationTest {
"}");
Compilation compilation =
- daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
+ compilerWithOptions(
+ fullBindingGraphValidationOption())
+ .compile(component);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
- "test.Outer.A is bound multiple times:",
- " @Provides test.Outer.A test.Outer.Module1.provideA1()",
- " @Binds test.Outer.A test.Outer.Module2.bindA2(test.Outer.B)"))
+ "Outer.A is bound multiple times:",
+ " @Provides Outer.A Outer.Module1.provideA1()",
+ " @Binds Outer.A Outer.Module2.bindA2(Outer.B)"))
.inFile(component)
.onLineContaining("interface TestComponent");
@@ -215,9 +222,9 @@ public class DuplicateBindingsValidationTest {
assertThat(compilation)
.hadErrorContaining(
message(
- "test.Outer.A is bound multiple times:",
- " @Provides test.Outer.A test.Outer.Module1.provideA1()",
- " @Binds test.Outer.A test.Outer.Module2.bindA2(test.Outer.B)"))
+ "Outer.A is bound multiple times:",
+ " @Provides Outer.A Outer.Module1.provideA1()",
+ " @Binds Outer.A Outer.Module2.bindA2(Outer.B)"))
.inFile(component)
.onLineContaining("class Module3");
}
@@ -268,20 +275,22 @@ public class DuplicateBindingsValidationTest {
"}");
Compilation compilation =
- daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
+ compilerWithOptions(
+ fullBindingGraphValidationOption())
+ .compile(component);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
- "java.util.Set<java.lang.String> has incompatible bindings or declarations:",
+ "Set<String> has incompatible bindings or declarations:",
" Set bindings and declarations:",
- " @Binds @dagger.multibindings.IntoSet String "
- + "test.Outer.TestModule1.bindStringSetElement(@test.Outer.SomeQualifier "
+ " @Binds @IntoSet String "
+ + "Outer.TestModule1.bindStringSetElement(@Outer.SomeQualifier "
+ "String)",
- " @Provides @dagger.multibindings.IntoSet String "
- + "test.Outer.TestModule1.stringSetElement()",
+ " @Provides @IntoSet String "
+ + "Outer.TestModule1.stringSetElement()",
" Unique bindings and declarations:",
- " @Provides Set<String> test.Outer.TestModule2.stringSet()"))
+ " @Provides Set<String> Outer.TestModule2.stringSet()"))
.inFile(component)
.onLineContaining(
fullBindingGraphValidation ? "class TestModule3" : "interface TestComponent");
@@ -337,23 +346,23 @@ public class DuplicateBindingsValidationTest {
"}");
Compilation compilation =
- daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
+ compilerWithOptions(
+ fullBindingGraphValidationOption())
+ .compile(component);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
- "java.util.Map<java.lang.String,java.lang.String> has incompatible bindings "
+ "Map<String,String> has incompatible bindings "
+ "or declarations:",
" Map bindings and declarations:",
- " @Binds @dagger.multibindings.IntoMap "
- + "@dagger.multibindings.StringKey(\"bar\") String"
- + " test.Outer.TestModule1.bindStringMapEntry(@test.Outer.SomeQualifier "
+ " @Binds @IntoMap @StringKey(\"bar\") String"
+ + " Outer.TestModule1.bindStringMapEntry(@Outer.SomeQualifier "
+ "String)",
- " @Provides @dagger.multibindings.IntoMap "
- + "@dagger.multibindings.StringKey(\"foo\") String"
- + " test.Outer.TestModule1.stringMapEntry()",
+ " @Provides @IntoMap @StringKey(\"foo\") String"
+ + " Outer.TestModule1.stringMapEntry()",
" Unique bindings and declarations:",
- " @Provides Map<String,String> test.Outer.TestModule2.stringMap()"))
+ " @Provides Map<String,String> Outer.TestModule2.stringMap()"))
.inFile(component)
.onLineContaining(
fullBindingGraphValidation ? "class TestModule3" : "interface TestComponent");
@@ -394,17 +403,19 @@ public class DuplicateBindingsValidationTest {
"}");
Compilation compilation =
- daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
+ compilerWithOptions(
+ fullBindingGraphValidationOption())
+ .compile(component);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
- "java.util.Set<java.lang.String> has incompatible bindings or declarations:",
+ "Set<String> has incompatible bindings or declarations:",
" Set bindings and declarations:",
- " @dagger.multibindings.Multibinds Set<String> "
- + "test.Outer.TestModule1.stringSet()",
+ " @Multibinds Set<String> "
+ + "Outer.TestModule1.stringSet()",
" Unique bindings and declarations:",
- " @Provides Set<String> test.Outer.TestModule2.stringSet()"))
+ " @Provides Set<String> Outer.TestModule2.stringSet()"))
.inFile(component)
.onLineContaining(
fullBindingGraphValidation ? "class TestModule3" : "interface TestComponent");
@@ -447,18 +458,20 @@ public class DuplicateBindingsValidationTest {
"}");
Compilation compilation =
- daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
+ compilerWithOptions(
+ fullBindingGraphValidationOption())
+ .compile(component);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
- "java.util.Map<java.lang.String,java.lang.String> has incompatible bindings "
+ "Map<String,String> has incompatible bindings "
+ "or declarations:",
" Map bindings and declarations:",
- " @dagger.multibindings.Multibinds Map<String,String> "
- + "test.Outer.TestModule1.stringMap()",
+ " @Multibinds Map<String,String> "
+ + "Outer.TestModule1.stringMap()",
" Unique bindings and declarations:",
- " @Provides Map<String,String> test.Outer.TestModule2.stringMap()"))
+ " @Provides Map<String,String> Outer.TestModule2.stringMap()"))
.inFile(component)
.onLineContaining(
fullBindingGraphValidation ? "class TestModule3" : "interface TestComponent");
@@ -574,22 +587,24 @@ public class DuplicateBindingsValidationTest {
"}");
Compilation compilation =
- daggerCompiler().withOptions(fullBindingGraphValidationOption()).compile(component);
+ compilerWithOptions(
+ fullBindingGraphValidationOption())
+ .compile(component);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
- "test.Outer.A is bound multiple times:",
- " @Provides test.Outer.A test.Outer.Module01.provideA()",
- " @Provides test.Outer.A test.Outer.Module02.provideA()",
- " @Provides test.Outer.A test.Outer.Module03.provideA()",
- " @Provides test.Outer.A test.Outer.Module04.provideA()",
- " @Provides test.Outer.A test.Outer.Module05.provideA()",
- " @Provides test.Outer.A test.Outer.Module06.provideA()",
- " @Provides test.Outer.A test.Outer.Module07.provideA()",
- " @Provides test.Outer.A test.Outer.Module08.provideA()",
- " @Provides test.Outer.A test.Outer.Module09.provideA()",
- " @Provides test.Outer.A test.Outer.Module10.provideA()",
+ "Outer.A is bound multiple times:",
+ " @Provides Outer.A Outer.Module01.provideA()",
+ " @Provides Outer.A Outer.Module02.provideA()",
+ " @Provides Outer.A Outer.Module03.provideA()",
+ " @Provides Outer.A Outer.Module04.provideA()",
+ " @Provides Outer.A Outer.Module05.provideA()",
+ " @Provides Outer.A Outer.Module06.provideA()",
+ " @Provides Outer.A Outer.Module07.provideA()",
+ " @Provides Outer.A Outer.Module08.provideA()",
+ " @Provides Outer.A Outer.Module09.provideA()",
+ " @Provides Outer.A Outer.Module10.provideA()",
" and 2 others"))
.inFile(component)
.onLineContaining(fullBindingGraphValidation ? "class Modules" : "interface TestComponent");
@@ -646,14 +661,14 @@ public class DuplicateBindingsValidationTest {
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(fullBindingGraphValidationOption())
+ compilerWithOptions(
+ fullBindingGraphValidationOption())
.compile(aComponent, bComponent);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
- "java.lang.Object is bound multiple times:",
+ "Object is bound multiple times:",
" @Provides Object test.A.AModule.abConflict()",
" @Provides Object test.B.BModule.abConflict()"))
.inFile(aComponent)
@@ -727,14 +742,14 @@ public class DuplicateBindingsValidationTest {
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(fullBindingGraphValidationOption())
+ compilerWithOptions(
+ fullBindingGraphValidationOption())
.compile(aComponent, bComponent, cComponent);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
- "java.lang.Object is bound multiple times:",
+ "Object is bound multiple times:",
" @Provides Object test.A.AModule.acConflict()",
" @Provides Object test.C.CModule.acConflict()"))
.inFile(aComponent)
@@ -803,14 +818,14 @@ public class DuplicateBindingsValidationTest {
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(fullBindingGraphValidationOption())
+ compilerWithOptions(
+ fullBindingGraphValidationOption())
.compile(aComponent, bComponent, cComponent);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
- "java.lang.Object is bound multiple times:",
+ "Object is bound multiple times:",
" @Provides Object test.B.BModule.bcConflict()",
" @Provides Object test.C.CModule.bcConflict()"))
.inFile(fullBindingGraphValidation ? bComponent : aComponent)
@@ -933,10 +948,10 @@ public class DuplicateBindingsValidationTest {
assertThat(compilation)
.hadWarningContaining(
message(
- "test.Foo is bound multiple times:",
- " @Inject test.Foo(Set<String>) [test.Injected1]",
- " @Provides test.Foo test.Provided1.Provided1Module.provideFoo(Set<String>) "
- + "[test.Injected1 → test.Injected2 → test.Provided1]"))
+ "Foo is bound multiple times:",
+ " @Inject Foo(Set<String>) [Injected1]",
+ " @Provides Foo Provided1.Provided1Module.provideFoo(Set<String>) "
+ + "[Injected1 → Injected2 → Provided1]"))
.inFile(injected1)
.onLineContaining("interface Injected1 {");
}
@@ -991,17 +1006,18 @@ public class DuplicateBindingsValidationTest {
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions("-Adagger.nullableValidation=WARNING", fullBindingGraphValidationOption())
+ compilerWithOptions(
+ "-Adagger.nullableValidation=WARNING",
+ fullBindingGraphValidationOption())
.compile(parentConflictsWithChild, child);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
message(
- "java.lang.Object is bound multiple times:",
- " @Provides Object test.Child.ChildModule.nonNullableParentChildConflict()",
- " @Provides @javax.annotation.Nullable Object"
- + " test.ParentConflictsWithChild.ParentModule.nullableParentChildConflict()"))
+ "Object is bound multiple times:",
+ " @Provides Object Child.ChildModule.nonNullableParentChildConflict()",
+ " @Provides @Nullable Object"
+ + " ParentConflictsWithChild.ParentModule.nullableParentChildConflict()"))
.inFile(parentConflictsWithChild)
.onLineContaining(
fullBindingGraphValidation
@@ -1075,7 +1091,7 @@ public class DuplicateBindingsValidationTest {
Compilation compilation = daggerCompiler().compile(parent, parentModule, child, childModule);
assertThat(compilation).failed();
assertThat(compilation)
- .hadErrorContaining("java.lang.String is bound multiple times")
+ .hadErrorContaining("String is bound multiple times")
.inFile(parent)
.onLineContaining("interface Parent");
assertThat(compilation).hadErrorCount(1);
diff --git a/javatests/dagger/internal/codegen/ElementDescriptorsTest.java b/javatests/dagger/internal/codegen/ElementDescriptorsTest.java
new file mode 100644
index 000000000..e97e36012
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ElementDescriptorsTest.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.testing.compile.CompilationRule;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.ElementFilter;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ElementDescriptorsTest {
+
+ @Rule public CompilationRule compilation = new CompilationRule();
+
+ static class TestClassA<T> {
+ int field1;
+ String field2;
+ T field3;
+ List<String> field4;
+ }
+
+ @Test
+ public void fieldDescriptor() {
+ assertThat(getFieldDescriptors(TestClassA.class.getCanonicalName()))
+ .containsExactly(
+ "field1:I",
+ "field2:Ljava/lang/String;",
+ "field3:Ljava/lang/Object;",
+ "field4:Ljava/util/List;");
+ }
+
+ static class TestClassB<T> {
+ void method1(boolean yesOrNo, int number) {}
+
+ byte method2(char letter) {
+ return 0;
+ }
+
+ void method3(double realNumber1, float realNummber2) {}
+
+ void method4(long bigNumber, short littlerNumber) {}
+ }
+
+ @Test
+ public void methodDescriptor_primitives() {
+ assertThat(getMethodDescriptors(TestClassB.class.getCanonicalName()))
+ .containsExactly("method1(ZI)V", "method2(C)B", "method3(DF)V", "method4(JS)V");
+ }
+
+ static class TestClassC<T> {
+ void method1(Object something) {}
+
+ Object method2() {
+ return null;
+ }
+
+ List<String> method3(ArrayList<Integer> list) {
+ return null;
+ }
+
+ Map<String, Object> method4() {
+ return null;
+ }
+ }
+
+ @Test
+ public void methodDescriptor_javaTypes() {
+ assertThat(getMethodDescriptors(TestClassC.class.getCanonicalName()))
+ .containsExactly(
+ "method1(Ljava/lang/Object;)V",
+ "method2()Ljava/lang/Object;",
+ "method3(Ljava/util/ArrayList;)Ljava/util/List;",
+ "method4()Ljava/util/Map;");
+ }
+
+ static class TestClassD<T> {
+ void method1(TestDataClass data) {}
+
+ TestDataClass method2() {
+ return null;
+ }
+ }
+
+ @Test
+ public void methodDescriptor_testTypes() {
+ assertThat(getMethodDescriptors(TestClassD.class.getCanonicalName()))
+ .containsExactly(
+ "method1(Ldagger/internal/codegen/TestDataClass;)V",
+ "method2()Ldagger/internal/codegen/TestDataClass;");
+ }
+
+ static class TestClassE<T> {
+ void method1(TestDataClass[] data) {}
+
+ TestDataClass[] method2() {
+ return null;
+ }
+
+ void method3(int[] array) {}
+
+ void method4(int... array) {}
+ }
+
+ @Test
+ public void methodDescriptor_arrays() {
+ assertThat(getMethodDescriptors(TestClassE.class.getCanonicalName()))
+ .containsExactly(
+ "method1([Ldagger/internal/codegen/TestDataClass;)V",
+ "method2()[Ldagger/internal/codegen/TestDataClass;",
+ "method3([I)V",
+ "method4([I)V");
+ }
+
+ static class TestClassF<T> {
+ void method1(TestDataClass.MemberInnerData data) {}
+
+ void method2(TestDataClass.StaticInnerData data) {}
+
+ void method3(TestDataClass.EnumData enumData) {}
+
+ TestDataClass.StaticInnerData method4() {
+ return null;
+ }
+ }
+
+ @Test
+ public void methodDescriptor_innerTestType() {
+ assertThat(getMethodDescriptors(TestClassF.class.getCanonicalName()))
+ .containsExactly(
+ "method1(Ldagger/internal/codegen/TestDataClass$MemberInnerData;)V",
+ "method2(Ldagger/internal/codegen/TestDataClass$StaticInnerData;)V",
+ "method3(Ldagger/internal/codegen/TestDataClass$EnumData;)V",
+ "method4()Ldagger/internal/codegen/TestDataClass$StaticInnerData;");
+ }
+
+ @SuppressWarnings("TypeParameterUnusedInFormals")
+ static class TestClassG<T> {
+ void method1(T something) {}
+
+ T method2() {
+ return null;
+ }
+
+ List<? extends String> method3() {
+ return null;
+ }
+
+ Map<T, String> method4() {
+ return null;
+ }
+
+ ArrayList<Map<T, String>> method5() {
+ return null;
+ }
+
+ static <I, O extends I> O method6(I input) {
+ return null;
+ }
+
+ static <I, O extends String> O method7(I input) {
+ return null;
+ }
+
+ static <P extends Collection<String> & Comparable<String>> P method8() {
+ return null;
+ }
+
+ static <P extends String & List<Character>> P method9() {
+ return null;
+ }
+ }
+
+ @Test
+ public void methodDescriptor_erasure() {
+ assertThat(getMethodDescriptors(TestClassG.class.getCanonicalName()))
+ .containsExactly(
+ "method1(Ljava/lang/Object;)V",
+ "method2()Ljava/lang/Object;",
+ "method3()Ljava/util/List;",
+ "method4()Ljava/util/Map;",
+ "method5()Ljava/util/ArrayList;",
+ "method6(Ljava/lang/Object;)Ljava/lang/Object;",
+ "method7(Ljava/lang/Object;)Ljava/lang/String;",
+ "method8()Ljava/util/Collection;",
+ "method9()Ljava/lang/String;");
+ }
+
+ private Set<String> getFieldDescriptors(String className) {
+ TypeElement testElement = compilation.getElements().getTypeElement(className);
+ return ElementFilter.fieldsIn(testElement.getEnclosedElements()).stream()
+ .map(DaggerElements::getFieldDescriptor)
+ .collect(Collectors.toSet());
+ }
+
+ private Set<String> getMethodDescriptors(String className) {
+ TypeElement testElement = compilation.getElements().getTypeElement(className);
+ return ElementFilter.methodsIn(testElement.getEnclosedElements()).stream()
+ .map(DaggerElements::getMethodDescriptor)
+ .collect(Collectors.toSet());
+ }
+}
+
+@SuppressWarnings("ClassCanBeStatic")
+class TestDataClass {
+ class MemberInnerData {}
+
+ static class StaticInnerData {}
+
+ enum EnumData {
+ VALUE1,
+ VALUE2
+ }
+}
diff --git a/javatests/dagger/internal/codegen/ElidedFactoriesTest.java b/javatests/dagger/internal/codegen/ElidedFactoriesTest.java
index 58ddb826a..6857039af 100644
--- a/javatests/dagger/internal/codegen/ElidedFactoriesTest.java
+++ b/javatests/dagger/internal/codegen/ElidedFactoriesTest.java
@@ -17,8 +17,8 @@
package dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
-import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
import com.google.testing.compile.Compilation;
@@ -84,7 +84,7 @@ public class ElidedFactoriesTest {
"",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerSimpleComponent implements SimpleComponent {",
" private DaggerSimpleComponent() {}",
"",
@@ -112,8 +112,7 @@ public class ElidedFactoriesTest {
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(injectedType, dependsOnInjected, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -185,7 +184,7 @@ public class ElidedFactoriesTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerSimpleComponent implements SimpleComponent {",
" private volatile Object scopedType = new MemoizedSentinel();",
" private volatile Provider<DependsOnScoped> dependsOnScopedProvider;",
@@ -200,7 +199,7 @@ public class ElidedFactoriesTest {
" return new Builder().build();",
" }",
"",
- " private ScopedType getScopedType() {",
+ " private ScopedType scopedType() {",
" Object local = scopedType;",
" if (local instanceof MemoizedSentinel) {",
" synchronized (local) {",
@@ -214,11 +213,11 @@ public class ElidedFactoriesTest {
" return (ScopedType) local;",
" }",
"",
- " private DependsOnScoped getDependsOnScoped() {",
- " return new DependsOnScoped(getScopedType());",
+ " private DependsOnScoped dependsOnScoped() {",
+ " return new DependsOnScoped(scopedType());",
" }",
"",
- " private Provider<DependsOnScoped> getDependsOnScopedProvider() {",
+ " private Provider<DependsOnScoped> dependsOnScopedProvider() {",
" Object local = dependsOnScopedProvider;",
" if (local == null) {",
" local = new SwitchingProvider<>(0);",
@@ -229,7 +228,7 @@ public class ElidedFactoriesTest {
"",
" @Override",
" public NeedsProvider needsProvider() {",
- " return new NeedsProvider(getDependsOnScopedProvider());",
+ " return new NeedsProvider(dependsOnScopedProvider());",
" }",
"",
" static final class Builder {",
@@ -250,7 +249,7 @@ public class ElidedFactoriesTest {
" @Override",
" public T get() {",
" switch (id) {",
- " case 0: return (T) DaggerSimpleComponent.this.getDependsOnScoped();",
+ " case 0: return (T) DaggerSimpleComponent.this.dependsOnScoped();",
" default: throw new AssertionError(id);",
" }",
" }",
@@ -267,7 +266,7 @@ public class ElidedFactoriesTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerSimpleComponent implements SimpleComponent {",
" private Provider<ScopedType> scopedTypeProvider;",
" private Provider<DependsOnScoped> dependsOnScopedProvider;",
@@ -307,8 +306,7 @@ public class ElidedFactoriesTest {
"}");
}
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(scopedType, dependsOnScoped, componentFile, needsProvider);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -379,7 +377,7 @@ public class ElidedFactoriesTest {
"import dagger.internal.MemoizedSentinel;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerSimpleComponent implements SimpleComponent {",
" private volatile Object scopedType = new MemoizedSentinel();",
"",
@@ -393,7 +391,7 @@ public class ElidedFactoriesTest {
" return new Builder().build();",
" }",
"",
- " private ScopedType getScopedType() {",
+ " private ScopedType scopedType() {",
" Object local = scopedType;",
" if (local instanceof MemoizedSentinel) {",
" synchronized (local) {",
@@ -425,7 +423,7 @@ public class ElidedFactoriesTest {
"",
" @Override",
" public DependsOnScoped dependsOnScoped() {",
- " return new DependsOnScoped(DaggerSimpleComponent.this.getScopedType());",
+ " return new DependsOnScoped(DaggerSimpleComponent.this.scopedType());",
" }",
" }",
"}");
@@ -440,7 +438,7 @@ public class ElidedFactoriesTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerSimpleComponent implements SimpleComponent {",
" private Provider<ScopedType> scopedTypeProvider;",
"",
@@ -486,8 +484,7 @@ public class ElidedFactoriesTest {
"}");
}
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(scopedType, dependsOnScoped, componentFile, subcomponentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
diff --git a/javatests/dagger/internal/codegen/FrameworkFieldTest.java b/javatests/dagger/internal/codegen/FrameworkFieldTest.java
index cbc8c03b3..be495d79b 100644
--- a/javatests/dagger/internal/codegen/FrameworkFieldTest.java
+++ b/javatests/dagger/internal/codegen/FrameworkFieldTest.java
@@ -24,6 +24,7 @@ import static dagger.internal.codegen.javapoet.TypeNames.providerOf;
import com.google.testing.compile.CompilationRule;
import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.binding.FrameworkField;
import javax.inject.Inject;
import org.junit.Before;
import org.junit.Rule;
diff --git a/javatests/dagger/internal/codegen/FrameworkTypeMapperTest.java b/javatests/dagger/internal/codegen/FrameworkTypeMapperTest.java
index 3e6e3ad69..e27534d7b 100644
--- a/javatests/dagger/internal/codegen/FrameworkTypeMapperTest.java
+++ b/javatests/dagger/internal/codegen/FrameworkTypeMapperTest.java
@@ -23,6 +23,8 @@ import static dagger.model.RequestKind.PRODUCED;
import static dagger.model.RequestKind.PRODUCER;
import static dagger.model.RequestKind.PROVIDER;
+import dagger.internal.codegen.binding.FrameworkType;
+import dagger.internal.codegen.binding.FrameworkTypeMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
diff --git a/javatests/dagger/internal/codegen/FullBindingGraphValidationTest.java b/javatests/dagger/internal/codegen/FullBindingGraphValidationTest.java
index 944e30794..b56bc04f3 100644
--- a/javatests/dagger/internal/codegen/FullBindingGraphValidationTest.java
+++ b/javatests/dagger/internal/codegen/FullBindingGraphValidationTest.java
@@ -17,6 +17,7 @@
package dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
import static dagger.internal.codegen.Compilers.daggerCompiler;
import static dagger.internal.codegen.TestUtils.endsWithMessage;
@@ -48,9 +49,35 @@ public final class FullBindingGraphValidationTest {
// Make sure the error doesn't show other bindings or a dependency trace afterwards.
private static final Pattern MODULE_WITH_ERRORS_MESSAGE =
endsWithMessage(
- "[Dagger/DuplicateBindings] java.lang.Object is bound multiple times:",
- " @Binds Object test.ModuleWithErrors.object1(String)",
- " @Binds Object test.ModuleWithErrors.object2(Long)");
+ "\033[1;31m[Dagger/DuplicateBindings]\033[0m Object is bound multiple times:",
+ " @Binds Object ModuleWithErrors.object1(String)",
+ " @Binds Object ModuleWithErrors.object2(Long)",
+ " in component: [ModuleWithErrors]",
+ "",
+ "======================",
+ "Full classname legend:",
+ "======================",
+ "ModuleWithErrors: test.ModuleWithErrors",
+ "========================",
+ "End of classname legend:",
+ "========================");
+
+ private static final Pattern INCLUDES_MODULE_WITH_ERRORS_MESSAGE =
+ endsWithMessage(
+ "\033[1;31m[Dagger/DuplicateBindings]\033[0m Object is bound multiple times:",
+ " @Binds Object ModuleWithErrors.object1(String)",
+ " @Binds Object ModuleWithErrors.object2(Long)",
+ " in component: [IncludesModuleWithErrors]",
+ "",
+ "======================",
+ "Full classname legend:",
+ "======================",
+ "IncludesModuleWithErrors: test.IncludesModuleWithErrors",
+ "ModuleWithErrors: test.ModuleWithErrors",
+ "========================",
+ "End of classname legend:",
+ "========================");
+
@Test
public void moduleWithErrors_validationTypeNone() {
@@ -61,8 +88,7 @@ public final class FullBindingGraphValidationTest {
@Test
public void moduleWithErrors_validationTypeError() {
Compilation compilation =
- daggerCompiler()
- .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ compilerWithOptions("-Adagger.fullBindingGraphValidation=ERROR")
.compile(MODULE_WITH_ERRORS);
assertThat(compilation).failed();
@@ -78,8 +104,7 @@ public final class FullBindingGraphValidationTest {
@Test
public void moduleWithErrors_validationTypeWarning() {
Compilation compilation =
- daggerCompiler()
- .withOptions("-Adagger.fullBindingGraphValidation=WARNING")
+ compilerWithOptions("-Adagger.fullBindingGraphValidation=WARNING")
.compile(MODULE_WITH_ERRORS);
assertThat(compilation).succeeded();
@@ -113,8 +138,7 @@ public final class FullBindingGraphValidationTest {
@Test
public void includesModuleWithErrors_validationTypeError() {
Compilation compilation =
- daggerCompiler()
- .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ compilerWithOptions("-Adagger.fullBindingGraphValidation=ERROR")
.compile(MODULE_WITH_ERRORS, INCLUDES_MODULE_WITH_ERRORS);
assertThat(compilation).failed();
@@ -125,7 +149,7 @@ public final class FullBindingGraphValidationTest {
.onLineContaining("interface ModuleWithErrors");
assertThat(compilation)
- .hadErrorContainingMatch("test.ModuleWithErrors has errors")
+ .hadErrorContainingMatch("ModuleWithErrors has errors")
.inFile(INCLUDES_MODULE_WITH_ERRORS)
.onLineContaining("ModuleWithErrors.class");
@@ -135,8 +159,7 @@ public final class FullBindingGraphValidationTest {
@Test
public void includesModuleWithErrors_validationTypeWarning() {
Compilation compilation =
- daggerCompiler()
- .withOptions("-Adagger.fullBindingGraphValidation=WARNING")
+ compilerWithOptions("-Adagger.fullBindingGraphValidation=WARNING")
.compile(MODULE_WITH_ERRORS, INCLUDES_MODULE_WITH_ERRORS);
assertThat(compilation).succeeded();
@@ -148,7 +171,7 @@ public final class FullBindingGraphValidationTest {
// TODO(b/130284666)
assertThat(compilation)
- .hadWarningContainingMatch(MODULE_WITH_ERRORS_MESSAGE)
+ .hadWarningContainingMatch(INCLUDES_MODULE_WITH_ERRORS_MESSAGE)
.inFile(INCLUDES_MODULE_WITH_ERRORS)
.onLineContaining("interface IncludesModuleWithErrors");
@@ -184,9 +207,19 @@ public final class FullBindingGraphValidationTest {
// Make sure the error doesn't show other bindings or a dependency trace afterwards.
private static final Pattern COMBINED_WITH_A_MODULE_HAS_ERRORS_MESSAGE =
endsWithMessage(
- "[Dagger/DuplicateBindings] java.lang.Object is bound multiple times:",
- " @Binds Object test.AModule.object(String)",
- " @Binds Object test.CombinedWithAModuleHasErrors.object(Long)");
+ "\033[1;31m[Dagger/DuplicateBindings]\033[0m Object is bound multiple times:",
+ " @Binds Object AModule.object(String)",
+ " @Binds Object CombinedWithAModuleHasErrors.object(Long)",
+ " in component: [CombinedWithAModuleHasErrors]",
+ "",
+ "======================",
+ "Full classname legend:",
+ "======================",
+ "AModule: test.AModule",
+ "CombinedWithAModuleHasErrors: test.CombinedWithAModuleHasErrors",
+ "========================",
+ "End of classname legend:",
+ "========================");
@Test
public void moduleIncludingModuleWithCombinedErrors_validationTypeNone() {
@@ -198,8 +231,7 @@ public final class FullBindingGraphValidationTest {
@Test
public void moduleIncludingModuleWithCombinedErrors_validationTypeError() {
Compilation compilation =
- daggerCompiler()
- .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ compilerWithOptions("-Adagger.fullBindingGraphValidation=ERROR")
.compile(A_MODULE, COMBINED_WITH_A_MODULE_HAS_ERRORS);
assertThat(compilation).failed();
@@ -215,8 +247,7 @@ public final class FullBindingGraphValidationTest {
@Test
public void moduleIncludingModuleWithCombinedErrors_validationTypeWarning() {
Compilation compilation =
- daggerCompiler()
- .withOptions("-Adagger.fullBindingGraphValidation=WARNING")
+ compilerWithOptions("-Adagger.fullBindingGraphValidation=WARNING")
.compile(A_MODULE, COMBINED_WITH_A_MODULE_HAS_ERRORS);
assertThat(compilation).succeeded();
@@ -249,10 +280,38 @@ public final class FullBindingGraphValidationTest {
// Make sure the error doesn't show other bindings or a dependency trace afterwards.
private static final Pattern SUBCOMPONENT_WITH_ERRORS_MESSAGE =
endsWithMessage(
- "[Dagger/DuplicateBindings] java.lang.Object is bound multiple times:",
- " @Binds Object test.AModule.object(String)",
- " @BindsInstance test.SubcomponentWithErrors.Builder"
- + " test.SubcomponentWithErrors.Builder.object(Object)");
+ "\033[1;31m[Dagger/DuplicateBindings]\033[0m Object is bound multiple times:",
+ " @Binds Object AModule.object(String)",
+ " @BindsInstance SubcomponentWithErrors.Builder"
+ + " SubcomponentWithErrors.Builder.object(Object)",
+ " in component: [SubcomponentWithErrors]",
+ "",
+ "======================",
+ "Full classname legend:",
+ "======================",
+ "AModule: test.AModule",
+ "SubcomponentWithErrors: test.SubcomponentWithErrors",
+ "========================",
+ "End of classname legend:",
+ "========================");
+
+ private static final Pattern MODULE_WITH_SUBCOMPONENT_WITH_ERRORS_MESSAGE =
+ endsWithMessage(
+ "\033[1;31m[Dagger/DuplicateBindings]\033[0m Object is bound multiple times:",
+ " @Binds Object AModule.object(String)",
+ " @BindsInstance SubcomponentWithErrors.Builder"
+ + " SubcomponentWithErrors.Builder.object(Object)",
+ " in component: [ModuleWithSubcomponentWithErrors → SubcomponentWithErrors]",
+ "",
+ "======================",
+ "Full classname legend:",
+ "======================",
+ "AModule: test.AModule",
+ "ModuleWithSubcomponentWithErrors: test.ModuleWithSubcomponentWithErrors",
+ "SubcomponentWithErrors: test.SubcomponentWithErrors",
+ "========================",
+ "End of classname legend:",
+ "========================");
@Test
public void subcomponentWithErrors_validationTypeNone() {
@@ -264,8 +323,7 @@ public final class FullBindingGraphValidationTest {
@Test
public void subcomponentWithErrors_validationTypeError() {
Compilation compilation =
- daggerCompiler()
- .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ compilerWithOptions("-Adagger.fullBindingGraphValidation=ERROR")
.compile(SUBCOMPONENT_WITH_ERRORS, A_MODULE);
assertThat(compilation).failed();
@@ -281,8 +339,7 @@ public final class FullBindingGraphValidationTest {
@Test
public void subcomponentWithErrors_validationTypeWarning() {
Compilation compilation =
- daggerCompiler()
- .withOptions("-Adagger.fullBindingGraphValidation=WARNING")
+ compilerWithOptions("-Adagger.fullBindingGraphValidation=WARNING")
.compile(SUBCOMPONENT_WITH_ERRORS, A_MODULE);
assertThat(compilation).succeeded();
@@ -318,14 +375,13 @@ public final class FullBindingGraphValidationTest {
@Test
public void moduleWithSubcomponentWithErrors_validationTypeError() {
Compilation compilation =
- daggerCompiler()
- .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ compilerWithOptions("-Adagger.fullBindingGraphValidation=ERROR")
.compile(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS, SUBCOMPONENT_WITH_ERRORS, A_MODULE);
assertThat(compilation).failed();
assertThat(compilation)
- .hadErrorContainingMatch(SUBCOMPONENT_WITH_ERRORS_MESSAGE)
+ .hadErrorContainingMatch(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS_MESSAGE)
.inFile(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS)
.onLineContaining("interface ModuleWithSubcomponentWithErrors");
@@ -341,14 +397,13 @@ public final class FullBindingGraphValidationTest {
@Test
public void moduleWithSubcomponentWithErrors_validationTypeWarning() {
Compilation compilation =
- daggerCompiler()
- .withOptions("-Adagger.fullBindingGraphValidation=WARNING")
+ compilerWithOptions("-Adagger.fullBindingGraphValidation=WARNING")
.compile(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS, SUBCOMPONENT_WITH_ERRORS, A_MODULE);
assertThat(compilation).succeeded();
assertThat(compilation)
- .hadWarningContainingMatch(SUBCOMPONENT_WITH_ERRORS_MESSAGE)
+ .hadWarningContainingMatch(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS_MESSAGE)
.inFile(MODULE_WITH_SUBCOMPONENT_WITH_ERRORS)
.onLineContaining("interface ModuleWithSubcomponentWithErrors");
@@ -393,9 +448,20 @@ public final class FullBindingGraphValidationTest {
// Make sure the error doesn't show other bindings or a dependency trace afterwards.
private static final Pattern COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS_MESSAGE =
endsWithMessage(
- "[Dagger/DuplicateBindings] java.lang.Object is bound multiple times:",
- " @Binds Object test.AModule.object(String)",
- " @Binds Object test.CombinedWithASubcomponentHasErrors.object(Number)");
+ "\033[1;31m[Dagger/DuplicateBindings]\033[0m Object is bound multiple times:",
+ " @Binds Object AModule.object(String)",
+ " @Binds Object CombinedWithASubcomponentHasErrors.object(Number)",
+ " in component: [CombinedWithASubcomponentHasErrors → ASubcomponent]",
+ "",
+ "======================",
+ "Full classname legend:",
+ "======================",
+ "AModule: test.AModule",
+ "ASubcomponent: test.ASubcomponent",
+ "CombinedWithASubcomponentHasErrors: test.CombinedWithASubcomponentHasErrors",
+ "========================",
+ "End of classname legend:",
+ "========================");
@Test
public void moduleWithSubcomponentWithCombinedErrors_validationTypeNone() {
@@ -408,8 +474,7 @@ public final class FullBindingGraphValidationTest {
@Test
public void moduleWithSubcomponentWithCombinedErrors_validationTypeError() {
Compilation compilation =
- daggerCompiler()
- .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ compilerWithOptions("-Adagger.fullBindingGraphValidation=ERROR")
.compile(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS, A_SUBCOMPONENT, A_MODULE);
assertThat(compilation).failed();
@@ -425,8 +490,7 @@ public final class FullBindingGraphValidationTest {
@Test
public void moduleWithSubcomponentWithCombinedErrors_validationTypeWarning() {
Compilation compilation =
- daggerCompiler()
- .withOptions("-Adagger.fullBindingGraphValidation=WARNING")
+ compilerWithOptions("-Adagger.fullBindingGraphValidation=WARNING")
.compile(COMBINED_WITH_A_SUBCOMPONENT_HAS_ERRORS, A_SUBCOMPONENT, A_MODULE);
assertThat(compilation).succeeded();
@@ -442,8 +506,7 @@ public final class FullBindingGraphValidationTest {
@Test
public void bothAliasesDifferentValues() {
Compilation compilation =
- daggerCompiler()
- .withOptions(
+ compilerWithOptions(
"-Adagger.moduleBindingValidation=NONE",
"-Adagger.fullBindingGraphValidation=ERROR")
.compile(MODULE_WITH_ERRORS);
@@ -462,8 +525,7 @@ public final class FullBindingGraphValidationTest {
@Test
public void bothAliasesSameValue() {
Compilation compilation =
- daggerCompiler()
- .withOptions(
+ compilerWithOptions(
"-Adagger.moduleBindingValidation=NONE", "-Adagger.fullBindingGraphValidation=NONE")
.compile(MODULE_WITH_ERRORS);
diff --git a/javatests/dagger/internal/codegen/GeneratedLines.java b/javatests/dagger/internal/codegen/GeneratedLines.java
index 2aa17acd1..f9a1b7004 100644
--- a/javatests/dagger/internal/codegen/GeneratedLines.java
+++ b/javatests/dagger/internal/codegen/GeneratedLines.java
@@ -16,9 +16,7 @@
package dagger.internal.codegen;
-import static dagger.internal.codegen.javapoet.CodeBlocks.stringLiteral;
-
-import com.squareup.javapoet.CodeBlock;
+import com.google.common.base.Joiner;
/**
* Common lines outputted during code generation.
@@ -29,6 +27,12 @@ public final class GeneratedLines {
+ "value = \"dagger.internal.codegen.ComponentProcessor\", "
+ "comments = \"https://dagger.dev\")";
+ private static final String SUPPRESS_WARNINGS_ANNOTATION =
+ "@SuppressWarnings({\"unchecked\", \"rawtypes\"})";
+
+ public static final String GENERATED_CODE_ANNOTATIONS =
+ Joiner.on('\n').join(GENERATED_ANNOTATION, SUPPRESS_WARNINGS_ANNOTATION);
+
public static final String IMPORT_GENERATED_ANNOTATION =
isBeforeJava9()
? "import javax.annotation.Generated;"
@@ -44,10 +48,4 @@ public final class GeneratedLines {
return true;
}
}
-
- public static final CodeBlock NPE_FROM_PROVIDES_METHOD =
- stringLiteral("Cannot return null from a non-@Nullable @Provides method");
-
- public static final CodeBlock NPE_FROM_COMPONENT_METHOD =
- stringLiteral("Cannot return null from a non-@Nullable component method");
}
diff --git a/javatests/dagger/internal/codegen/InjectConstructorFactoryGeneratorTest.java b/javatests/dagger/internal/codegen/InjectConstructorFactoryGeneratorTest.java
index e6e970648..579e87a4a 100644
--- a/javatests/dagger/internal/codegen/InjectConstructorFactoryGeneratorTest.java
+++ b/javatests/dagger/internal/codegen/InjectConstructorFactoryGeneratorTest.java
@@ -20,8 +20,9 @@ import static com.google.common.truth.Truth.assertAbout;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
import com.google.common.collect.ImmutableList;
@@ -137,7 +138,7 @@ public final class InjectConstructorFactoryGeneratorTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class GenericClass_Factory<T> implements Factory<GenericClass<T>> {",
" private final Provider<T> tProvider;",
"",
@@ -147,7 +148,7 @@ public final class InjectConstructorFactoryGeneratorTest {
"",
" @Override",
" public GenericClass<T> get() {",
- " return new GenericClass<T>(tProvider.get());",
+ " return newInstance(tProvider.get());",
" }",
"",
" public static <T> GenericClass_Factory<T> create(Provider<T> tProvider) {",
@@ -186,8 +187,9 @@ public final class InjectConstructorFactoryGeneratorTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
- "public final class GenericClass_Factory<A, B> implements Factory<GenericClass<A, B>> {",
+ GENERATED_CODE_ANNOTATIONS,
+ "public final class GenericClass_Factory<A, B> implements",
+ " Factory<GenericClass<A, B>> {",
" private final Provider<A> aProvider;",
" private final Provider<B> bProvider;",
"",
@@ -199,7 +201,7 @@ public final class InjectConstructorFactoryGeneratorTest {
"",
" @Override",
" public GenericClass<A, B> get() {",
- " GenericClass<A, B> instance = new GenericClass<A, B>();",
+ " GenericClass<A, B> instance = newInstance();",
" GenericClass_MembersInjector.injectA(instance, aProvider.get());",
" GenericClass_MembersInjector.injectRegister(instance, bProvider.get());",
" return instance;",
@@ -237,24 +239,26 @@ public final class InjectConstructorFactoryGeneratorTest {
"import dagger.internal.Factory;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class GenericClass_Factory<T> implements Factory<GenericClass<T>> {",
- " @SuppressWarnings(\"rawtypes\")",
- " private static final GenericClass_Factory INSTANCE = new GenericClass_Factory();",
- "",
" @Override",
" public GenericClass<T> get() {",
- " return new GenericClass<T>();",
+ " return newInstance();",
" }",
"",
" @SuppressWarnings(\"unchecked\")",
" public static <T> GenericClass_Factory<T> create() {",
- " return INSTANCE;",
+ " return InstanceHolder.INSTANCE;",
" }",
"",
" public static <T> GenericClass<T> newInstance() {",
" return new GenericClass<T>();",
" }",
+ "",
+ " private static final class InstanceHolder {",
+ " @SuppressWarnings(\"rawtypes\")",
+ " private static final GenericClass_Factory INSTANCE = new GenericClass_Factory();",
+ " }",
"}");
assertAbout(javaSource()).that(file)
.processedWith(new ComponentProcessor())
@@ -280,8 +284,9 @@ public final class InjectConstructorFactoryGeneratorTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
- "public final class GenericClass_Factory<A, B> implements Factory<GenericClass<A, B>> {",
+ GENERATED_CODE_ANNOTATIONS,
+ "public final class GenericClass_Factory<A, B>",
+ " implements Factory<GenericClass<A, B>> {",
" private final Provider<A> aProvider;",
" private final Provider<B> bProvider;",
"",
@@ -292,7 +297,7 @@ public final class InjectConstructorFactoryGeneratorTest {
"",
" @Override",
" public GenericClass<A, B> get() {",
- " return new GenericClass<A, B>(aProvider.get(), bProvider.get());",
+ " return newInstance(aProvider.get(), bProvider.get());",
" }",
"",
" public static <A, B> GenericClass_Factory<A, B> create(",
@@ -332,7 +337,7 @@ public final class InjectConstructorFactoryGeneratorTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class GenericClass_Factory<A extends Number & Comparable<A>,",
" B extends List<? extends String>,",
" C extends List<? super String>>",
@@ -351,8 +356,7 @@ public final class InjectConstructorFactoryGeneratorTest {
"",
" @Override",
" public GenericClass<A, B, C> get() {",
- " return new GenericClass<A, B, C>(",
- " aProvider.get(), bProvider.get(), cProvider.get());",
+ " return newInstance(aProvider.get(), bProvider.get(), cProvider.get());",
" }",
"",
" public static <A extends Number & Comparable<A>,",
@@ -401,64 +405,110 @@ public final class InjectConstructorFactoryGeneratorTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class GenericClass_Factory<A, B>",
" implements Factory<GenericClass<A, B>> {",
- " private final Provider<A> aAndA2AndPaAndLaProvider;",
+ " private final Provider<A> aProvider;",
+ " private final Provider<A> a2Provider;",
+ " private final Provider<A> paProvider;",
" private final Provider<A> qaProvider;",
- " private final Provider<String> sAndS2AndPsAndLsProvider;",
+ " private final Provider<A> laProvider;",
+ " private final Provider<String> sProvider;",
+ " private final Provider<String> s2Provider;",
+ " private final Provider<String> psProvider;",
" private final Provider<String> qsProvider;",
- " private final Provider<B> bAndB2AndPbAndLbProvider;",
+ " private final Provider<String> lsProvider;",
+ " private final Provider<B> bProvider;",
+ " private final Provider<B> b2Provider;",
+ " private final Provider<B> pbProvider;",
" private final Provider<B> qbProvider;",
+ " private final Provider<B> lbProvider;",
"",
- " public GenericClass_Factory(Provider<A> aAndA2AndPaAndLaProvider,",
+ " public GenericClass_Factory(",
+ " Provider<A> aProvider,",
+ " Provider<A> a2Provider,",
+ " Provider<A> paProvider,",
" Provider<A> qaProvider,",
- " Provider<String> sAndS2AndPsAndLsProvider,",
+ " Provider<A> laProvider,",
+ " Provider<String> sProvider,",
+ " Provider<String> s2Provider,",
+ " Provider<String> psProvider,",
" Provider<String> qsProvider,",
- " Provider<B> bAndB2AndPbAndLbProvider,",
- " Provider<B> qbProvider) {",
- " this.aAndA2AndPaAndLaProvider = aAndA2AndPaAndLaProvider;",
+ " Provider<String> lsProvider,",
+ " Provider<B> bProvider,",
+ " Provider<B> b2Provider,",
+ " Provider<B> pbProvider,",
+ " Provider<B> qbProvider,",
+ " Provider<B> lbProvider) {",
+ " this.aProvider = aProvider;",
+ " this.a2Provider = a2Provider;",
+ " this.paProvider = paProvider;",
" this.qaProvider = qaProvider;",
- " this.sAndS2AndPsAndLsProvider = sAndS2AndPsAndLsProvider;",
+ " this.laProvider = laProvider;",
+ " this.sProvider = sProvider;",
+ " this.s2Provider = s2Provider;",
+ " this.psProvider = psProvider;",
" this.qsProvider = qsProvider;",
- " this.bAndB2AndPbAndLbProvider = bAndB2AndPbAndLbProvider;",
+ " this.lsProvider = lsProvider;",
+ " this.bProvider = bProvider;",
+ " this.b2Provider = b2Provider;",
+ " this.pbProvider = pbProvider;",
" this.qbProvider = qbProvider;",
+ " this.lbProvider = lbProvider;",
" }",
"",
" @Override",
" public GenericClass<A, B> get() {",
- " return new GenericClass<A, B>(",
- " aAndA2AndPaAndLaProvider.get(),",
- " aAndA2AndPaAndLaProvider.get(),",
- " aAndA2AndPaAndLaProvider,",
- " qaProvider.get(),",
- " DoubleCheck.lazy(aAndA2AndPaAndLaProvider),",
- " sAndS2AndPsAndLsProvider.get(),",
- " sAndS2AndPsAndLsProvider.get(),",
- " sAndS2AndPsAndLsProvider,",
- " qsProvider.get(),",
- " DoubleCheck.lazy(sAndS2AndPsAndLsProvider),",
- " bAndB2AndPbAndLbProvider.get(),",
- " bAndB2AndPbAndLbProvider.get(),",
- " bAndB2AndPbAndLbProvider,",
- " qbProvider.get(),",
- " DoubleCheck.lazy(bAndB2AndPbAndLbProvider));",
+ " return newInstance(",
+ " aProvider.get(),",
+ " a2Provider.get(),",
+ " paProvider,",
+ " qaProvider.get(),",
+ " DoubleCheck.lazy(laProvider),",
+ " sProvider.get(),",
+ " s2Provider.get(),",
+ " psProvider,",
+ " qsProvider.get(),",
+ " DoubleCheck.lazy(lsProvider),",
+ " bProvider.get(),",
+ " b2Provider.get(),",
+ " pbProvider,",
+ " qbProvider.get(),",
+ " DoubleCheck.lazy(lbProvider));",
" }",
"",
" public static <A, B> GenericClass_Factory<A, B> create(",
- " Provider<A> aAndA2AndPaAndLaProvider,",
+ " Provider<A> aProvider,",
+ " Provider<A> a2Provider,",
+ " Provider<A> paProvider,",
" Provider<A> qaProvider,",
- " Provider<String> sAndS2AndPsAndLsProvider,",
+ " Provider<A> laProvider,",
+ " Provider<String> sProvider,",
+ " Provider<String> s2Provider,",
+ " Provider<String> psProvider,",
" Provider<String> qsProvider,",
- " Provider<B> bAndB2AndPbAndLbProvider,",
- " Provider<B> qbProvider) {",
+ " Provider<String> lsProvider,",
+ " Provider<B> bProvider,",
+ " Provider<B> b2Provider,",
+ " Provider<B> pbProvider,",
+ " Provider<B> qbProvider,",
+ " Provider<B> lbProvider) {",
" return new GenericClass_Factory<A, B>(",
- " aAndA2AndPaAndLaProvider,",
+ " aProvider,",
+ " a2Provider,",
+ " paProvider,",
" qaProvider,",
- " sAndS2AndPsAndLsProvider,",
+ " laProvider,",
+ " sProvider,",
+ " s2Provider,",
+ " psProvider,",
" qsProvider,",
- " bAndB2AndPbAndLbProvider,",
- " qbProvider);",
+ " lsProvider,",
+ " bProvider,",
+ " b2Provider,",
+ " pbProvider,",
+ " qbProvider,",
+ " lbProvider);",
" }",
"",
" public static <A, B> GenericClass<A, B> newInstance(",
@@ -501,11 +551,11 @@ public final class InjectConstructorFactoryGeneratorTest {
Compilation compilation = daggerCompiler().compile(file);
assertThat(compilation).failed();
assertThat(compilation)
- .hadErrorContaining("Types may only contain one @Inject constructor")
+ .hadErrorContaining("Types may only contain one injected constructor")
.inFile(file)
.onLine(6);
assertThat(compilation)
- .hadErrorContaining("Types may only contain one @Inject constructor")
+ .hadErrorContaining("Types may only contain one injected constructor")
.inFile(file)
.onLine(8);
}
@@ -602,7 +652,7 @@ public final class InjectConstructorFactoryGeneratorTest {
" @Inject CheckedExceptionClass() throws Exception {}",
"}");
Compilation compilation =
- daggerCompiler().withOptions("-Adagger.privateMemberValidation=WARNING").compile(file);
+ compilerWithOptions("-Adagger.privateMemberValidation=WARNING").compile(file);
assertThat(compilation).succeeded();
assertThat(compilation)
.hadWarningContaining("Dagger does not support checked exceptions on @Inject constructors")
@@ -641,7 +691,7 @@ public final class InjectConstructorFactoryGeneratorTest {
" }",
"}");
Compilation compilation =
- daggerCompiler().withOptions("-Adagger.privateMemberValidation=WARNING").compile(file);
+ compilerWithOptions("-Adagger.privateMemberValidation=WARNING").compile(file);
assertThat(compilation).succeeded();
assertThat(compilation)
.hadWarningContaining("Dagger does not support injection into private classes")
@@ -684,7 +734,7 @@ public final class InjectConstructorFactoryGeneratorTest {
" }",
"}");
Compilation compilation =
- daggerCompiler().withOptions("-Adagger.privateMemberValidation=WARNING").compile(file);
+ compilerWithOptions("-Adagger.privateMemberValidation=WARNING").compile(file);
assertThat(compilation).succeeded();
assertThat(compilation)
.hadWarningContaining("Dagger does not support injection into private classes")
@@ -736,7 +786,7 @@ public final class InjectConstructorFactoryGeneratorTest {
" @Inject private String s;",
"}");
Compilation compilation =
- daggerCompiler().withOptions("-Adagger.privateMemberValidation=WARNING").compile(file);
+ compilerWithOptions("-Adagger.privateMemberValidation=WARNING").compile(file);
assertThat(compilation).succeeded(); // TODO: Verify warning message when supported
}
@@ -767,7 +817,7 @@ public final class InjectConstructorFactoryGeneratorTest {
" @Inject static String s;",
"}");
Compilation compilation =
- daggerCompiler().withOptions("-Adagger.staticMemberValidation=WARNING").compile(file);
+ compilerWithOptions("-Adagger.staticMemberValidation=WARNING").compile(file);
assertThat(compilation).succeeded(); // TODO: Verify warning message when supported
}
@@ -838,7 +888,7 @@ public final class InjectConstructorFactoryGeneratorTest {
" @Inject private void method(){}",
"}");
Compilation compilation =
- daggerCompiler().withOptions("-Adagger.privateMemberValidation=WARNING").compile(file);
+ compilerWithOptions("-Adagger.privateMemberValidation=WARNING").compile(file);
assertThat(compilation).succeeded(); // TODO: Verify warning message when supported
}
@@ -869,7 +919,7 @@ public final class InjectConstructorFactoryGeneratorTest {
" @Inject static void method(){}",
"}");
Compilation compilation =
- daggerCompiler().withOptions("-Adagger.staticMemberValidation=WARNING").compile(file);
+ compilerWithOptions("-Adagger.staticMemberValidation=WARNING").compile(file);
assertThat(compilation).succeeded(); // TODO: Verify warning message when supported
}
@@ -1003,6 +1053,7 @@ public final class InjectConstructorFactoryGeneratorTest {
.hadErrorContaining("Producer may only be injected in @Produces methods");
}
+
@Test public void injectConstructor() {
JavaFileObject file = JavaFileObjects.forSourceLines("test.InjectConstructor",
"package test;",
@@ -1021,7 +1072,7 @@ public final class InjectConstructorFactoryGeneratorTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class InjectConstructor_Factory ",
" implements Factory<InjectConstructor> {",
"",
@@ -1032,7 +1083,7 @@ public final class InjectConstructorFactoryGeneratorTest {
" }",
"",
" @Override public InjectConstructor get() {",
- " return new InjectConstructor(sProvider.get());",
+ " return newInstance(sProvider.get());",
" }",
"",
" public static InjectConstructor_Factory create(Provider<String> sProvider) {",
@@ -1068,28 +1119,39 @@ public final class InjectConstructorFactoryGeneratorTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class AllInjections_Factory implements Factory<AllInjections> {",
" private final Provider<String> sProvider;",
+ " private final Provider<String> sProvider2;",
+ " private final Provider<String> sProvider3;",
"",
- " public AllInjections_Factory(Provider<String> sProvider) {",
+ " public AllInjections_Factory(",
+ " Provider<String> sProvider,",
+ " Provider<String> sProvider2,",
+ " Provider<String> sProvider3) {",
" this.sProvider = sProvider;",
+ " this.sProvider2 = sProvider2;",
+ " this.sProvider3 = sProvider3;",
" }",
"",
- " @Override public AllInjections get() {",
- " AllInjections instance = new AllInjections(sProvider.get());",
- " AllInjections_MembersInjector.injectS(instance, sProvider.get());",
- " AllInjections_MembersInjector.injectS2(instance, sProvider.get());",
+ " @Override",
+ " public AllInjections get() {",
+ " AllInjections instance = newInstance(sProvider.get());",
+ " AllInjections_MembersInjector.injectS(instance, sProvider2.get());",
+ " AllInjections_MembersInjector.injectS2(instance, sProvider3.get());",
" return instance;",
" }",
"",
- " public static AllInjections_Factory create(Provider<String> sProvider) {",
- " return new AllInjections_Factory(sProvider);",
+ " public static AllInjections_Factory create(",
+ " Provider<String> sProvider,",
+ " Provider<String> sProvider2,",
+ " Provider<String> sProvider3) {",
+ " return new AllInjections_Factory(sProvider, sProvider2, sProvider3);",
" }",
"",
" public static AllInjections newInstance(String s) {",
- " return new AllInjections(s);",
- " }",
+ " return new AllInjections(s);",
+ " }",
"}");
assertAbout(javaSource()).that(file).processedWith(new ComponentProcessor())
.compilesWithoutError()
@@ -1118,7 +1180,7 @@ public final class InjectConstructorFactoryGeneratorTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class InjectConstructor_Factory ",
" implements Factory<InjectConstructor> {",
"",
@@ -1129,7 +1191,7 @@ public final class InjectConstructorFactoryGeneratorTest {
" }",
"",
" @Override public InjectConstructor get() {",
- " return new InjectConstructor(objectsProvider.get());",
+ " return newInstance(objectsProvider.get());",
" }",
"",
" public static InjectConstructor_Factory create(",
@@ -1170,7 +1232,7 @@ public final class InjectConstructorFactoryGeneratorTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class InjectConstructor_Factory ",
" implements Factory<InjectConstructor> {",
"",
@@ -1181,7 +1243,7 @@ public final class InjectConstructorFactoryGeneratorTest {
" }",
"",
" @Override public InjectConstructor get() {",
- " return new InjectConstructor(factoryProvider.get());",
+ " return newInstance(factoryProvider.get());",
" }",
"",
" public static InjectConstructor_Factory create(",
@@ -1227,7 +1289,7 @@ public final class InjectConstructorFactoryGeneratorTest {
"import javax.inject.Provider;",
"import other.pkg.Outer;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class InjectConstructor_Factory ",
" implements Factory<InjectConstructor> {",
"",
@@ -1238,7 +1300,7 @@ public final class InjectConstructorFactoryGeneratorTest {
" }",
"",
" @Override public InjectConstructor get() {",
- " return new InjectConstructor(factoryProvider.get());",
+ " return newInstance(factoryProvider.get());",
" }",
"",
" public static InjectConstructor_Factory create(",
@@ -1285,7 +1347,7 @@ public final class InjectConstructorFactoryGeneratorTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class InjectConstructor_Factory ",
" implements Factory<InjectConstructor> {",
"",
@@ -1300,8 +1362,7 @@ public final class InjectConstructorFactoryGeneratorTest {
" }",
"",
" @Override public InjectConstructor get() {",
- " return new InjectConstructor(",
- " otherPackageProvider.get(), samePackageProvider.get());",
+ " return newInstance(otherPackageProvider.get(), samePackageProvider.get());",
" }",
"",
" public static InjectConstructor_Factory create(",
@@ -1340,21 +1401,23 @@ public final class InjectConstructorFactoryGeneratorTest {
"import dagger.internal.Factory;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class SimpleType_Factory implements Factory<SimpleType> {",
- " private static final SimpleType_Factory INSTANCE = new SimpleType_Factory();",
- "",
" @Override public SimpleType get() {",
- " return new SimpleType();",
+ " return newInstance();",
" }",
"",
" public static SimpleType_Factory create() {",
- " return INSTANCE;",
+ " return InstanceHolder.INSTANCE;",
" }",
"",
" public static SimpleType newInstance() {",
" return new SimpleType();",
" }",
+ "",
+ " private static final class InstanceHolder {",
+ " private static final SimpleType_Factory INSTANCE = new SimpleType_Factory();",
+ " }",
"}");
assertAbout(javaSource())
.that(simpleType)
@@ -1386,21 +1449,23 @@ public final class InjectConstructorFactoryGeneratorTest {
"import dagger.internal.Factory;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class OuterType_A_Factory implements Factory<OuterType.A> {",
- " private static final OuterType_A_Factory INSTANCE = new OuterType_A_Factory();",
- "",
" @Override public OuterType.A get() {",
- " return new OuterType.A();",
+ " return newInstance();",
" }",
"",
" public static OuterType_A_Factory create() {",
- " return INSTANCE;",
+ " return InstanceHolder.INSTANCE;",
" }",
"",
" public static OuterType.A newInstance() {",
" return new OuterType.A();",
" }",
+ "",
+ " private static final class InstanceHolder {",
+ " private static final OuterType_A_Factory INSTANCE = new OuterType_A_Factory();",
+ " }",
"}");
assertAbout(javaSources()).that(ImmutableList.of(nestedTypesFile))
.processedWith(new ComponentProcessor())
diff --git a/javatests/dagger/internal/codegen/KeyFactoryTest.java b/javatests/dagger/internal/codegen/KeyFactoryTest.java
index d526ec944..fdf62cbbf 100644
--- a/javatests/dagger/internal/codegen/KeyFactoryTest.java
+++ b/javatests/dagger/internal/codegen/KeyFactoryTest.java
@@ -24,8 +24,11 @@ import com.google.auto.common.MoreTypes;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.testing.compile.CompilationRule;
+import dagger.BindsInstance;
+import dagger.Component;
import dagger.Module;
import dagger.Provides;
+import dagger.internal.codegen.binding.KeyFactory;
import dagger.internal.codegen.langmodel.DaggerElements;
import dagger.internal.codegen.langmodel.DaggerTypes;
import dagger.model.Key;
@@ -38,6 +41,7 @@ import java.lang.annotation.Retention;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Qualifier;
+import javax.inject.Singleton;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
@@ -57,16 +61,12 @@ import org.junit.runners.JUnit4;
public class KeyFactoryTest {
@Rule public CompilationRule compilationRule = new CompilationRule();
- private DaggerElements elements;
- private DaggerTypes types;
- private KeyFactory keyFactory;
+ @Inject DaggerElements elements;
+ @Inject DaggerTypes types;
+ @Inject KeyFactory keyFactory;
@Before public void setUp() {
- this.elements = new DaggerElements(compilationRule.getElements(), compilationRule.getTypes());
- this.types = new DaggerTypes(compilationRule.getTypes(), elements);
- TypeProtoConverter typeProtoConverter = new TypeProtoConverter(types, elements);
- this.keyFactory = new KeyFactory(
- types, elements, typeProtoConverter, new AnnotationProtoConverter(typeProtoConverter));
+ DaggerKeyFactoryTest_TestComponent.factory().create(compilationRule).inject(this);
}
@Test public void forInjectConstructorWithResolvedType() {
@@ -250,7 +250,7 @@ public class KeyFactoryTest {
ExecutableElement integerMethod = Iterables.getOnlyElement(
ElementFilter.methodsIn(boxedPrimitiveHolder.getEnclosedElements()));
- // TODO(cgruber): Truth subject for TypeMirror and TypeElement
+ // TODO(user): Truth subject for TypeMirror and TypeElement
TypeMirror intType = intMethod.getReturnType();
assertThat(intType.getKind().isPrimitive()).isTrue();
TypeMirror integerType = integerMethod.getReturnType();
@@ -329,4 +329,28 @@ public class KeyFactoryTest {
throw new UnsupportedOperationException();
}
}
+
+ @Singleton
+ @Component(modules = {TestModule.class})
+ interface TestComponent {
+ void inject(KeyFactoryTest test);
+
+ @Component.Factory
+ interface Factory {
+ TestComponent create(@BindsInstance CompilationRule compilationRule);
+ }
+ }
+
+ @Module
+ static class TestModule {
+ @Provides
+ static DaggerElements elements(CompilationRule compilationRule) {
+ return new DaggerElements(compilationRule.getElements(), compilationRule.getTypes());
+ }
+
+ @Provides
+ static DaggerTypes types(CompilationRule compilationRule, DaggerElements elements) {
+ return new DaggerTypes(compilationRule.getTypes(), elements);
+ }
+ }
}
diff --git a/javatests/dagger/internal/codegen/KotlinInjectedQualifier.kt b/javatests/dagger/internal/codegen/KotlinInjectedQualifier.kt
new file mode 100644
index 000000000..05e275812
--- /dev/null
+++ b/javatests/dagger/internal/codegen/KotlinInjectedQualifier.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen
+
+import javax.inject.Inject
+import javax.inject.Named
+
+class KotlinInjectedQualifier {
+ @Inject
+ @Named("TheString")
+ lateinit var qualifiedString: String
+}
diff --git a/javatests/dagger/internal/codegen/KotlinObjectWithMemberInjection.kt b/javatests/dagger/internal/codegen/KotlinObjectWithMemberInjection.kt
new file mode 100644
index 000000000..619c1dd86
--- /dev/null
+++ b/javatests/dagger/internal/codegen/KotlinObjectWithMemberInjection.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen
+
+import javax.inject.Inject
+
+object KotlinObjectWithMemberInjection {
+ @Inject
+ lateinit var property: String
+}
+
+object KotlinObjectWithSetterMemberInjection {
+ @set:Inject
+ lateinit var setterProperty: String
+}
+
+class KotlinClassWithMemberInjectedCompanion {
+ companion object {
+ @Inject
+ lateinit var property: String
+ }
+}
+
+class KotlinClassWithSetterMemberInjectedCompanion {
+ companion object {
+ @set:Inject
+ lateinit var setterProperty: String
+ }
+}
+
+class KotlinClassWithMemberInjectedNamedCompanion {
+ companion object TheCompanion {
+ @Inject
+ lateinit var property: String
+ }
+}
+
+class KotlinClassWithSetterMemberInjectedNamedCompanion {
+ companion object TheCompanion {
+ @set:Inject
+ lateinit var setterProperty: String
+ }
+}
diff --git a/javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java b/javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java
index ad48712ec..ed9dd658a 100644
--- a/javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java
+++ b/javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java
@@ -17,8 +17,9 @@
package dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
@@ -128,7 +129,7 @@ public class MapBindingComponentProcessorTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private final MapModuleOne mapModuleOne;",
" private final MapModuleTwo mapModuleTwo;",
@@ -137,7 +138,7 @@ public class MapBindingComponentProcessorTest {
" private volatile Provider<Map<PathEnum, Provider<Handler>>>",
" mapOfPathEnumAndProviderOfHandlerProvider;",
"",
- " private Provider<Handler> getProvideAdminHandlerProvider() {",
+ " private Provider<Handler> provideAdminHandlerProvider() {",
" Object local = provideAdminHandlerProvider;",
" if (local == null) {",
" local = new SwitchingProvider<>(1);",
@@ -146,7 +147,7 @@ public class MapBindingComponentProcessorTest {
" return (Provider<Handler>) local;",
" }",
"",
- " private Provider<Handler> getProvideLoginHandlerProvider() {",
+ " private Provider<Handler> provideLoginHandlerProvider() {",
" Object local = provideLoginHandlerProvider;",
" if (local == null) {",
" local = new SwitchingProvider<>(2);",
@@ -156,10 +157,10 @@ public class MapBindingComponentProcessorTest {
" }",
"",
" private Map<PathEnum, Provider<Handler>>",
- " getMapOfPathEnumAndProviderOfHandler() {",
+ " mapOfPathEnumAndProviderOfHandler() {",
" return ImmutableMap.<PathEnum, Provider<Handler>>of(",
- " PathEnum.ADMIN, getProvideAdminHandlerProvider(),",
- " PathEnum.LOGIN, getProvideLoginHandlerProvider());",
+ " PathEnum.ADMIN, provideAdminHandlerProvider(),",
+ " PathEnum.LOGIN, provideLoginHandlerProvider());",
" }",
"",
" @Override",
@@ -186,7 +187,7 @@ public class MapBindingComponentProcessorTest {
" switch (id) {",
" case 0:",
" return (T) DaggerTestComponent.this",
- " .getMapOfPathEnumAndProviderOfHandler();",
+ " .mapOfPathEnumAndProviderOfHandler();",
" case 1:",
" return (T) MapModuleOne_ProvideAdminHandlerFactory",
" .provideAdminHandler(DaggerTestComponent.this.mapModuleOne);",
@@ -205,7 +206,7 @@ public class MapBindingComponentProcessorTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private Provider<Handler> provideAdminHandlerProvider;",
" private Provider<Handler> provideLoginHandlerProvider;",
@@ -234,8 +235,7 @@ public class MapBindingComponentProcessorTest {
"}");
}
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(
mapModuleOneFile,
mapModuleTwoFile,
@@ -357,7 +357,7 @@ public class MapBindingComponentProcessorTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private Provider<Map<Class<?>, Integer>> mapOfClassOfAndIntegerProvider;",
"",
@@ -367,7 +367,7 @@ public class MapBindingComponentProcessorTest {
" private Provider<Map<MapKeys.ComplexKey, Integer>>",
" mapOfComplexKeyAndIntegerProvider;",
"",
- " private Map getMapOfPackagePrivateEnumAndInteger() {",
+ " private Map mapOfPackagePrivateEnumAndInteger() {",
" return ImmutableMap.of(",
" MapModule_EnumKeyMapKey.create(), MapModule.enumKey());",
" }",
@@ -411,7 +411,7 @@ public class MapBindingComponentProcessorTest {
"",
" @Override",
" public Object inaccessibleEnum() {",
- " return getMapOfPackagePrivateEnumAndInteger();",
+ " return mapOfPackagePrivateEnumAndInteger();",
" }",
"",
" @Override",
@@ -443,7 +443,7 @@ public class MapBindingComponentProcessorTest {
"mapkeys.MapModule_ComplexKeyWithInaccessibleAnnotationValueMapKey",
"package mapkeys;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class MapModule_ComplexKeyWithInaccessibleAnnotationValueMapKey {",
" public static MapKeys.ComplexKey create() {",
" return MapKeys_ComplexKeyCreator.createComplexKey(",
@@ -459,7 +459,7 @@ public class MapBindingComponentProcessorTest {
"mapkeys.MapModule_ClassKeyMapKey",
"package mapkeys;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class MapModule_ClassKeyMapKey {",
" public static Class<?> create() {",
" return MapKeys.Inaccessible.class;",
@@ -536,7 +536,7 @@ public class MapBindingComponentProcessorTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private final MapModuleOne mapModuleOne;",
" private final MapModuleTwo mapModuleTwo;",
@@ -545,7 +545,7 @@ public class MapBindingComponentProcessorTest {
" private volatile Provider<Map<String, Provider<Handler>>>",
" mapOfStringAndProviderOfHandlerProvider;",
"",
- " private Provider<Handler> getProvideAdminHandlerProvider() {",
+ " private Provider<Handler> provideAdminHandlerProvider() {",
" Object local = provideAdminHandlerProvider;",
" if (local == null) {",
" local = new SwitchingProvider<>(1);",
@@ -554,7 +554,7 @@ public class MapBindingComponentProcessorTest {
" return (Provider<Handler>) local;",
" }",
"",
- " private Provider<Handler> getProvideLoginHandlerProvider() {",
+ " private Provider<Handler> provideLoginHandlerProvider() {",
" Object local = provideLoginHandlerProvider;",
" if (local == null) {",
" local = new SwitchingProvider<>(2);",
@@ -564,10 +564,10 @@ public class MapBindingComponentProcessorTest {
" }",
"",
" private Map<String, Provider<Handler>>",
- " getMapOfStringAndProviderOfHandler() {",
+ " mapOfStringAndProviderOfHandler() {",
" return ImmutableMap.<String, Provider<Handler>>of(",
- " \"Admin\", getProvideAdminHandlerProvider(),",
- " \"Login\", getProvideLoginHandlerProvider());",
+ " \"Admin\", provideAdminHandlerProvider(),",
+ " \"Login\", provideLoginHandlerProvider());",
" }",
"",
" @Override",
@@ -594,7 +594,7 @@ public class MapBindingComponentProcessorTest {
" switch (id) {",
" case 0:",
" return (T) DaggerTestComponent.this",
- " .getMapOfStringAndProviderOfHandler();",
+ " .mapOfStringAndProviderOfHandler();",
" case 1:",
" return (T) MapModuleOne_ProvideAdminHandlerFactory",
" .provideAdminHandler(DaggerTestComponent.this.mapModuleOne);",
@@ -613,7 +613,7 @@ public class MapBindingComponentProcessorTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private Provider<Handler> provideAdminHandlerProvider;",
" private Provider<Handler> provideLoginHandlerProvider;",
@@ -642,8 +642,7 @@ public class MapBindingComponentProcessorTest {
"}");
}
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(
mapModuleOneFile,
mapModuleTwoFile,
@@ -737,7 +736,7 @@ public class MapBindingComponentProcessorTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private final MapModuleOne mapModuleOne;",
" private final MapModuleTwo mapModuleTwo;",
@@ -753,7 +752,7 @@ public class MapBindingComponentProcessorTest {
" this.mapModuleTwo = mapModuleTwoParam;",
" }",
"",
- " private Provider<Handler> getProvideAdminHandlerProvider() {",
+ " private Provider<Handler> provideAdminHandlerProvider() {",
" Object local = provideAdminHandlerProvider;",
" if (local == null) {",
" local = new SwitchingProvider<>(1);",
@@ -762,7 +761,7 @@ public class MapBindingComponentProcessorTest {
" return (Provider<Handler>) local;",
" }",
"",
- " private Provider<Handler> getProvideLoginHandlerProvider() {",
+ " private Provider<Handler> provideLoginHandlerProvider() {",
" Object local = provideLoginHandlerProvider;",
" if (local == null) {",
" local = new SwitchingProvider<>(2);",
@@ -772,12 +771,12 @@ public class MapBindingComponentProcessorTest {
" }",
"",
" private Map<WrappedClassKey, Provider<Handler>>",
- " getMapOfWrappedClassKeyAndProviderOfHandler() {",
+ " mapOfWrappedClassKeyAndProviderOfHandler() {",
" return ImmutableMap.<WrappedClassKey, Provider<Handler>>of(",
" WrappedClassKeyCreator.createWrappedClassKey(Integer.class),",
- " getProvideAdminHandlerProvider(),",
+ " provideAdminHandlerProvider(),",
" WrappedClassKeyCreator.createWrappedClassKey(Long.class),",
- " getProvideLoginHandlerProvider());",
+ " provideLoginHandlerProvider());",
" }",
"",
" @Override",
@@ -804,7 +803,7 @@ public class MapBindingComponentProcessorTest {
" switch (id) {",
" case 0:",
" return (T) DaggerTestComponent.this",
- " .getMapOfWrappedClassKeyAndProviderOfHandler();",
+ " .mapOfWrappedClassKeyAndProviderOfHandler();",
" case 1:",
" return (T) MapModuleOne_ProvideAdminHandlerFactory",
" .provideAdminHandler(DaggerTestComponent.this.mapModuleOne);",
@@ -823,7 +822,7 @@ public class MapBindingComponentProcessorTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private Provider<Handler> provideAdminHandlerProvider;",
" private Provider<Handler> provideLoginHandlerProvider;",
@@ -854,8 +853,7 @@ public class MapBindingComponentProcessorTest {
"}");
}
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(
mapModuleOneFile,
mapModuleTwoFile,
@@ -951,14 +949,14 @@ public class MapBindingComponentProcessorTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private final MapModuleOne mapModuleOne;",
" private final MapModuleTwo mapModuleTwo;",
" private volatile Provider<Map<PathEnum, Handler>>",
" mapOfPathEnumAndHandlerProvider;",
"",
- " private Map<PathEnum, Handler> getMapOfPathEnumAndHandler() {",
+ " private Map<PathEnum, Handler> mapOfPathEnumAndHandler() {",
" return ImmutableMap.<PathEnum, Handler>of(",
" PathEnum.ADMIN,",
" MapModuleOne_ProvideAdminHandlerFactory.provideAdminHandler(",
@@ -989,7 +987,7 @@ public class MapBindingComponentProcessorTest {
" @Override",
" public T get() {",
" switch (id) {",
- " case 0: return (T) DaggerTestComponent.this.getMapOfPathEnumAndHandler();",
+ " case 0: return (T) DaggerTestComponent.this.mapOfPathEnumAndHandler();",
" default: throw new AssertionError(id);",
" }",
" }",
@@ -1002,7 +1000,7 @@ public class MapBindingComponentProcessorTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private Provider<Handler> provideAdminHandlerProvider;",
" private Provider<Handler> provideLoginHandlerProvider;",
@@ -1030,8 +1028,7 @@ public class MapBindingComponentProcessorTest {
"}");
}
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(
mapModuleOneFile,
mapModuleTwoFile,
@@ -1080,7 +1077,7 @@ public class MapBindingComponentProcessorTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private final MapModule mapModule;",
"",
@@ -1090,8 +1087,7 @@ public class MapBindingComponentProcessorTest {
" }",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(mapModuleFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
diff --git a/javatests/dagger/internal/codegen/MapBindingExpressionTest.java b/javatests/dagger/internal/codegen/MapBindingExpressionTest.java
index 11f6bc13c..3f33bd569 100644
--- a/javatests/dagger/internal/codegen/MapBindingExpressionTest.java
+++ b/javatests/dagger/internal/codegen/MapBindingExpressionTest.java
@@ -20,8 +20,8 @@ import static com.google.testing.compile.CompilationSubject.assertThat;
import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
import static dagger.internal.codegen.Compilers.CLASS_PATH_WITHOUT_GUAVA_OPTION;
-import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.Compiler;
@@ -92,7 +92,7 @@ public class MapBindingExpressionTest {
"",
"import dagger.internal.MapBuilder;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {")
.addLinesIn(
FAST_INIT_MODE,
@@ -101,7 +101,7 @@ public class MapBindingExpressionTest {
" private volatile Provider<Long> provideLong1Provider;",
" private volatile Provider<Long> provideLong2Provider;",
"",
- " private Provider<Integer> getProvideIntProvider() {",
+ " private Provider<Integer> provideIntProvider() {",
" Object local = provideIntProvider;",
" if (local == null) {",
" local = new SwitchingProvider<>(0);",
@@ -110,7 +110,7 @@ public class MapBindingExpressionTest {
" return (Provider<Integer>) local;",
" }",
"",
- " private Provider<Long> getProvideLong0Provider() {",
+ " private Provider<Long> provideLong0Provider() {",
" Object local = provideLong0Provider;",
" if (local == null) {",
" local = new SwitchingProvider<>(1);",
@@ -119,7 +119,7 @@ public class MapBindingExpressionTest {
" return (Provider<Long>) local;",
" }",
"",
- " private Provider<Long> getProvideLong1Provider() {",
+ " private Provider<Long> provideLong1Provider() {",
" Object local = provideLong1Provider;",
" if (local == null) {",
" local = new SwitchingProvider<>(2);",
@@ -128,7 +128,7 @@ public class MapBindingExpressionTest {
" return (Provider<Long>) local;",
" }",
"",
- " private Provider<Long> getProvideLong2Provider() {",
+ " private Provider<Long> provideLong2Provider() {",
" Object local = provideLong2Provider;",
" if (local == null) {",
" local = new SwitchingProvider<>(3);",
@@ -160,7 +160,7 @@ public class MapBindingExpressionTest {
" 0, MapModule_ProvideIntFactory.create());")
.addLinesIn(
FAST_INIT_MODE,
- " 0, getProvideIntProvider());")
+ " 0, provideIntProvider());")
.addLines(
" }",
"",
@@ -183,9 +183,9 @@ public class MapBindingExpressionTest {
" .put(2L, MapModule_ProvideLong2Factory.create())")
.addLinesIn(
FAST_INIT_MODE,
- " .put(0L, getProvideLong0Provider())",
- " .put(1L, getProvideLong1Provider())",
- " .put(2L, getProvideLong2Provider())")
+ " .put(0L, provideLong0Provider())",
+ " .put(1L, provideLong1Provider())",
+ " .put(2L, provideLong2Provider())")
.addLines( //
" .build();", " }")
.addLinesIn(
@@ -274,7 +274,7 @@ public class MapBindingExpressionTest {
"import other.UsesInaccessible;",
"import other.UsesInaccessible_Factory;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" @Override",
" public UsesInaccessible usesInaccessible() {",
@@ -337,7 +337,7 @@ public class MapBindingExpressionTest {
"test.DaggerParent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerParent implements Parent {",
" private final ParentModule parentModule;",
"",
@@ -360,7 +360,7 @@ public class MapBindingExpressionTest {
}
private Compiler daggerCompilerWithoutGuava() {
- return daggerCompiler()
- .withOptions(compilerMode.javacopts().append(CLASS_PATH_WITHOUT_GUAVA_OPTION));
+ return compilerWithOptions(compilerMode.javacopts())
+ .withClasspath(CLASS_PATH_WITHOUT_GUAVA_OPTION);
}
}
diff --git a/javatests/dagger/internal/codegen/MapBindingExpressionWithGuavaTest.java b/javatests/dagger/internal/codegen/MapBindingExpressionWithGuavaTest.java
index aa1a715aa..69ab5a799 100644
--- a/javatests/dagger/internal/codegen/MapBindingExpressionWithGuavaTest.java
+++ b/javatests/dagger/internal/codegen/MapBindingExpressionWithGuavaTest.java
@@ -20,8 +20,7 @@ import static com.google.testing.compile.CompilationSubject.assertThat;
import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
import static dagger.internal.codegen.Compilers.compilerWithOptions;
-import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
@@ -128,7 +127,7 @@ public class MapBindingExpressionWithGuavaTest {
.addLines(
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {")
.addLinesIn(
FAST_INIT_MODE,
@@ -137,7 +136,7 @@ public class MapBindingExpressionWithGuavaTest {
" private volatile Provider<Long> provideLong1Provider;",
" private volatile Provider<Long> provideLong2Provider;",
"",
- " private Provider<Integer> getProvideIntProvider() {",
+ " private Provider<Integer> provideIntProvider() {",
" Object local = provideIntProvider;",
" if (local == null) {",
" local = new SwitchingProvider<>(0);",
@@ -146,7 +145,7 @@ public class MapBindingExpressionWithGuavaTest {
" return (Provider<Integer>) local;",
" }",
"",
- " private Provider<Long> getProvideLong0Provider() {",
+ " private Provider<Long> provideLong0Provider() {",
" Object local = provideLong0Provider;",
" if (local == null) {",
" local = new SwitchingProvider<>(1);",
@@ -155,7 +154,7 @@ public class MapBindingExpressionWithGuavaTest {
" return (Provider<Long>) local;",
" }",
"",
- " private Provider<Long> getProvideLong1Provider() {",
+ " private Provider<Long> provideLong1Provider() {",
" Object local = provideLong1Provider;",
" if (local == null) {",
" local = new SwitchingProvider<>(2);",
@@ -164,7 +163,7 @@ public class MapBindingExpressionWithGuavaTest {
" return (Provider<Long>) local;",
" }",
"",
- " private Provider<Long> getProvideLong2Provider() {",
+ " private Provider<Long> provideLong2Provider() {",
" Object local = provideLong2Provider;",
" if (local == null) {",
" local = new SwitchingProvider<>(3);",
@@ -196,7 +195,7 @@ public class MapBindingExpressionWithGuavaTest {
" 0, MapModule_ProvideIntFactory.create());")
.addLinesIn(
FAST_INIT_MODE, //
- " 0, getProvideIntProvider());")
+ " 0, provideIntProvider());")
.addLines(
" }",
"",
@@ -218,9 +217,9 @@ public class MapBindingExpressionWithGuavaTest {
" 2L, MapModule_ProvideLong2Factory.create());")
.addLinesIn(
FAST_INIT_MODE,
- " 0L, getProvideLong0Provider(),",
- " 1L, getProvideLong1Provider(),",
- " 2L, getProvideLong2Provider());")
+ " 0L, provideLong0Provider(),",
+ " 1L, provideLong1Provider(),",
+ " 2L, provideLong2Provider());")
.addLines(
" }",
"",
@@ -237,7 +236,7 @@ public class MapBindingExpressionWithGuavaTest {
" private volatile Provider<Long> provideLong5Provider;",
" private SubImpl() {}",
"",
- " private Provider<Long> getProvideLong3Provider() {",
+ " private Provider<Long> provideLong3Provider() {",
" Object local = provideLong3Provider;",
" if (local == null) {",
" local = new SwitchingProvider<>(0);",
@@ -246,7 +245,7 @@ public class MapBindingExpressionWithGuavaTest {
" return (Provider<Long>) local;",
" }",
"",
- " private Provider<Long> getProvideLong4Provider() {",
+ " private Provider<Long> provideLong4Provider() {",
" Object local = provideLong4Provider;",
" if (local == null) {",
" local = new SwitchingProvider<>(1);",
@@ -255,7 +254,7 @@ public class MapBindingExpressionWithGuavaTest {
" return (Provider<Long>) local;",
" }",
"",
- " private Provider<Long> getProvideLong5Provider() {",
+ " private Provider<Long> provideLong5Provider() {",
" Object local = provideLong5Provider;",
" if (local == null) {",
" local = new SwitchingProvider<>(2);",
@@ -289,12 +288,12 @@ public class MapBindingExpressionWithGuavaTest {
" .put(5L, SubcomponentMapModule_ProvideLong5Factory.create())")
.addLinesIn(
FAST_INIT_MODE,
- " .put(0L, DaggerTestComponent.this.getProvideLong0Provider())",
- " .put(1L, DaggerTestComponent.this.getProvideLong1Provider())",
- " .put(2L, DaggerTestComponent.this.getProvideLong2Provider())",
- " .put(3L, getProvideLong3Provider())",
- " .put(4L, getProvideLong4Provider())",
- " .put(5L, getProvideLong5Provider())")
+ " .put(0L, DaggerTestComponent.this.provideLong0Provider())",
+ " .put(1L, DaggerTestComponent.this.provideLong1Provider())",
+ " .put(2L, DaggerTestComponent.this.provideLong2Provider())",
+ " .put(3L, provideLong3Provider())",
+ " .put(4L, provideLong4Provider())",
+ " .put(5L, provideLong5Provider())")
.addLines( //
" .build();", " }")
.addLinesIn(
@@ -341,8 +340,7 @@ public class MapBindingExpressionWithGuavaTest {
"}")
.build();
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(mapModuleFile, componentFile, subcomponentModuleFile, subcomponent);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -403,7 +401,7 @@ public class MapBindingExpressionWithGuavaTest {
"import other.UsesInaccessible;",
"import other.UsesInaccessible_Factory;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" @Override",
" public UsesInaccessible usesInaccessible() {",
@@ -411,8 +409,7 @@ public class MapBindingExpressionWithGuavaTest {
" }",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(module, inaccessible, usesInaccessible, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -466,7 +463,7 @@ public class MapBindingExpressionWithGuavaTest {
"test.DaggerParent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerParent implements Parent {",
" private final ParentModule parentModule;",
"",
@@ -482,7 +479,7 @@ public class MapBindingExpressionWithGuavaTest {
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(parent, parentModule, child);
+ compilerWithOptions(compilerMode.javacopts()).compile(parent, parentModule, child);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerParent")
@@ -522,7 +519,7 @@ public class MapBindingExpressionWithGuavaTest {
"",
"import dagger.producers.internal.CancellationListener;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent, "
+ "CancellationListener {",
" @Override",
diff --git a/javatests/dagger/internal/codegen/MapKeyProcessorTest.java b/javatests/dagger/internal/codegen/MapKeyProcessorTest.java
index 746c60d17..ffdb2bc18 100644
--- a/javatests/dagger/internal/codegen/MapKeyProcessorTest.java
+++ b/javatests/dagger/internal/codegen/MapKeyProcessorTest.java
@@ -18,7 +18,7 @@ package dagger.internal.codegen;
import static com.google.common.truth.Truth.assertAbout;
import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
import com.google.auto.value.processor.AutoAnnotationProcessor;
@@ -73,7 +73,7 @@ public class MapKeyProcessorTest {
"import com.google.auto.value.AutoAnnotation;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class PathKeyCreator {",
" private PathKeyCreator() {}",
"",
@@ -122,7 +122,7 @@ public class MapKeyProcessorTest {
"import com.google.auto.value.AutoAnnotation;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class Container_PathKeyCreator {",
" private Container_PathKeyCreator() {}",
"",
diff --git a/javatests/dagger/internal/codegen/MapMultibindingValidationTest.java b/javatests/dagger/internal/codegen/MapMultibindingValidationTest.java
index 04b0986e8..753fb5361 100644
--- a/javatests/dagger/internal/codegen/MapMultibindingValidationTest.java
+++ b/javatests/dagger/internal/codegen/MapMultibindingValidationTest.java
@@ -17,7 +17,9 @@
package dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.TestUtils.message;
import com.google.common.collect.ImmutableList;
import com.google.testing.compile.Compilation;
@@ -30,7 +32,7 @@ import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class MapMultibindingValidationTest {
@Test
- public void duplicateMapKeys() {
+ public void duplicateMapKeys_UnwrappedMapKey() {
JavaFileObject module =
JavaFileObjects.forSourceLines(
"test.MapModule",
@@ -65,18 +67,19 @@ public class MapMultibindingValidationTest {
assertThat(compilation)
.hadErrorContaining(
"The same map key is bound more than once for "
- + "java.util.Map<java.lang.String,java.lang.Object>");
+ + "Map<String,Object>");
assertThat(compilation).hadErrorContaining("provideObjectForAKey()");
assertThat(compilation).hadErrorContaining("provideObjectForAKeyAgain()");
assertThat(compilation).hadErrorCount(1);
compilation =
- daggerCompiler().withOptions("-Adagger.fullBindingGraphValidation=ERROR").compile(module);
+ compilerWithOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ .compile(module);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
"The same map key is bound more than once for "
- + "java.util.Map<java.lang.String,javax.inject.Provider<java.lang.Object>>")
+ + "Map<String,Provider<Object>>")
.inFile(module)
.onLineContaining("class MapModule");
assertThat(compilation).hadErrorContaining("provideObjectForAKey()");
@@ -95,7 +98,7 @@ public class MapMultibindingValidationTest {
assertThat(compilation)
.hadErrorContaining(
"The same map key is bound more than once for "
- + "java.util.Map<java.lang.String,java.lang.Object>");
+ + "Map<String,Object>");
assertThat(compilation).hadErrorCount(1);
// If there's Map<K, V> and Map<K, Producer<V>>, report only Map<K, V>.
@@ -110,7 +113,7 @@ public class MapMultibindingValidationTest {
assertThat(compilation)
.hadErrorContaining(
"The same map key is bound more than once for "
- + "java.util.Map<java.lang.String,java.lang.Object>");
+ + "Map<String,Object>");
assertThat(compilation).hadErrorCount(1);
// If there's Map<K, Provider<V>> and Map<K, Producer<V>>, report only Map<K, Provider<V>>.
@@ -125,7 +128,7 @@ public class MapMultibindingValidationTest {
assertThat(compilation)
.hadErrorContaining(
"The same map key is bound more than once for "
- + "java.util.Map<java.lang.String,javax.inject.Provider<java.lang.Object>>");
+ + "Map<String,Provider<Object>>");
assertThat(compilation).hadErrorCount(1);
compilation = daggerCompiler().compile(module, component("Map<String, Object> objects();"));
@@ -133,7 +136,7 @@ public class MapMultibindingValidationTest {
assertThat(compilation)
.hadErrorContaining(
"The same map key is bound more than once for "
- + "java.util.Map<java.lang.String,java.lang.Object>");
+ + "Map<String,Object>");
assertThat(compilation).hadErrorCount(1);
compilation =
@@ -143,7 +146,7 @@ public class MapMultibindingValidationTest {
assertThat(compilation)
.hadErrorContaining(
"The same map key is bound more than once for "
- + "java.util.Map<java.lang.String,javax.inject.Provider<java.lang.Object>>");
+ + "Map<String,Provider<Object>>");
assertThat(compilation).hadErrorCount(1);
compilation =
@@ -154,11 +157,59 @@ public class MapMultibindingValidationTest {
assertThat(compilation)
.hadErrorContaining(
"The same map key is bound more than once for "
- + "java.util.Map<java.lang.String,dagger.producers.Producer<java.lang.Object>>");
+ + "Map<String,Producer<Object>>");
assertThat(compilation).hadErrorCount(1);
}
@Test
+ public void duplicateMapKeys_WrappedMapKey() {
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.MapModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import dagger.multibindings.IntoMap;",
+ "import dagger.MapKey;",
+ "",
+ "@Module",
+ "abstract class MapModule {",
+ "",
+ " @MapKey(unwrapValue = false)",
+ " @interface WrappedMapKey {",
+ " String value();",
+ " }",
+ "",
+ " @Provides",
+ " @IntoMap",
+ " @WrappedMapKey(\"foo\")",
+ " static String stringMapEntry1() { return \"\"; }",
+ "",
+ " @Provides",
+ " @IntoMap",
+ " @WrappedMapKey(\"foo\")",
+ " static String stringMapEntry2() { return \"\"; }",
+ "}");
+
+ JavaFileObject component = component("Map<test.MapModule.WrappedMapKey, String> objects();");
+
+ Compilation compilation = daggerCompiler().compile(component, module);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "\033[1;31m[Dagger/MapKeys]\033[0m The same map key is bound more than once for "
+ + "Map<MapModule.WrappedMapKey,String>",
+ " @Provides @IntoMap @MapModule.WrappedMapKey(\"foo\") String "
+ + "MapModule.stringMapEntry1()",
+ " @Provides @IntoMap @MapModule.WrappedMapKey(\"foo\") String "
+ + "MapModule.stringMapEntry2()"))
+ .inFile(component)
+ .onLineContaining("interface TestComponent");
+ }
+
+ @Test
public void inconsistentMapKeyAnnotations() {
JavaFileObject module =
JavaFileObjects.forSourceLines(
@@ -205,20 +256,19 @@ public class MapMultibindingValidationTest {
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
- "java.util.Map<java.lang.String,java.lang.Object>"
+ "Map<String,Object>"
+ " uses more than one @MapKey annotation type");
assertThat(compilation).hadErrorContaining("provideObjectForAKey()");
assertThat(compilation).hadErrorContaining("provideObjectForBKey()");
assertThat(compilation).hadErrorCount(1);
compilation =
- daggerCompiler()
- .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ compilerWithOptions("-Adagger.fullBindingGraphValidation=ERROR")
.compile(module, stringKeyTwoFile);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
- "java.util.Map<java.lang.String,javax.inject.Provider<java.lang.Object>>"
+ "Map<String,Provider<Object>>"
+ " uses more than one @MapKey annotation type")
.inFile(module)
.onLineContaining("class MapModule");
@@ -238,7 +288,7 @@ public class MapMultibindingValidationTest {
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
- "java.util.Map<java.lang.String,java.lang.Object>"
+ "Map<String,Object>"
+ " uses more than one @MapKey annotation type");
assertThat(compilation).hadErrorCount(1);
@@ -254,7 +304,7 @@ public class MapMultibindingValidationTest {
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
- "java.util.Map<java.lang.String,java.lang.Object>"
+ "Map<String,Object>"
+ " uses more than one @MapKey annotation type");
assertThat(compilation).hadErrorCount(1);
@@ -270,7 +320,7 @@ public class MapMultibindingValidationTest {
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
- "java.util.Map<java.lang.String,javax.inject.Provider<java.lang.Object>>"
+ "Map<String,Provider<Object>>"
+ " uses more than one @MapKey annotation type");
assertThat(compilation).hadErrorCount(1);
@@ -280,7 +330,7 @@ public class MapMultibindingValidationTest {
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
- "java.util.Map<java.lang.String,java.lang.Object>"
+ "Map<String,Object>"
+ " uses more than one @MapKey annotation type");
assertThat(compilation).hadErrorCount(1);
@@ -293,7 +343,7 @@ public class MapMultibindingValidationTest {
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
- "java.util.Map<java.lang.String,javax.inject.Provider<java.lang.Object>>"
+ "Map<String,Provider<Object>>"
+ " uses more than one @MapKey annotation type");
assertThat(compilation).hadErrorCount(1);
@@ -306,7 +356,7 @@ public class MapMultibindingValidationTest {
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
- "java.util.Map<java.lang.String,dagger.producers.Producer<java.lang.Object>>"
+ "Map<String,Producer<Object>>"
+ " uses more than one @MapKey annotation type");
assertThat(compilation).hadErrorCount(1);
}
diff --git a/javatests/dagger/internal/codegen/MembersInjectionTest.java b/javatests/dagger/internal/codegen/MembersInjectionTest.java
index edaedaf19..ef69c712d 100644
--- a/javatests/dagger/internal/codegen/MembersInjectionTest.java
+++ b/javatests/dagger/internal/codegen/MembersInjectionTest.java
@@ -22,8 +22,8 @@ import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
-import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
import static javax.tools.StandardLocation.CLASS_OUTPUT;
@@ -87,7 +87,7 @@ public class MembersInjectionTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" @Override",
" public Child child() {",
@@ -95,8 +95,7 @@ public class MembersInjectionTest {
" }",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(childFile, parentFile, componentFile);
assertThat(compilation).succeeded();
@@ -147,7 +146,7 @@ public class MembersInjectionTest {
"",
"import com.google.errorprone.annotations.CanIgnoreReturnValue;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" @Override",
" public Child child() {",
@@ -161,8 +160,7 @@ public class MembersInjectionTest {
" }",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(childFile, parentFile, depFile, componentFile);
assertThat(compilation).succeeded();
@@ -184,44 +182,47 @@ public class MembersInjectionTest {
"",
" @Inject void register(B b) {}",
"}");
- JavaFileObject expected = JavaFileObjects.forSourceLines(
- "test.GenericClass_MembersInjector",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATED_ANNOTATION,
- "public final class GenericClass_MembersInjector<A, B>",
- " implements MembersInjector<GenericClass<A, B>> {",
- " private final Provider<A> aProvider;",
- " private final Provider<B> bProvider;",
- "",
- " public GenericClass_MembersInjector(Provider<A> aProvider, Provider<B> bProvider) {",
- " this.aProvider = aProvider;",
- " this.bProvider = bProvider;",
- " }",
- "",
- " public static <A, B> MembersInjector<GenericClass<A, B>> create(",
- " Provider<A> aProvider, Provider<B> bProvider) {",
- " return new GenericClass_MembersInjector<A, B>(aProvider, bProvider);",
- " }",
- "",
- " @Override",
- " public void injectMembers(GenericClass<A, B> instance) {",
- " injectA(instance, aProvider.get());",
- " injectRegister(instance, bProvider.get());",
- " }",
- "",
- " public static <A, B> void injectA(Object instance, A a) {",
- " ((GenericClass<A, B>) instance).a = a;",
- " }",
- "",
- " public static <A, B> void injectRegister(Object instance, B b) {",
- " ((GenericClass<A, B>) instance).register(b);",
- " }",
- "}");
+ JavaFileObject expected =
+ JavaFileObjects.forSourceLines(
+ "test.GenericClass_MembersInjector",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ "import dagger.internal.InjectedFieldSignature;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_CODE_ANNOTATIONS,
+ "public final class GenericClass_MembersInjector<A, B>",
+ " implements MembersInjector<GenericClass<A, B>> {",
+ " private final Provider<A> aProvider;",
+ " private final Provider<B> bProvider;",
+ "",
+ " public GenericClass_MembersInjector(Provider<A> aProvider, Provider<B> bProvider) {",
+ " this.aProvider = aProvider;",
+ " this.bProvider = bProvider;",
+ " }",
+ "",
+ " public static <A, B> MembersInjector<GenericClass<A, B>> create(",
+ " Provider<A> aProvider, Provider<B> bProvider) {",
+ " return new GenericClass_MembersInjector<A, B>(aProvider, bProvider);",
+ " }",
+ "",
+ " @Override",
+ " public void injectMembers(GenericClass<A, B> instance) {",
+ " injectA(instance, aProvider.get());",
+ " injectRegister(instance, bProvider.get());",
+ " }",
+ "",
+ " @InjectedFieldSignature(\"test.GenericClass.a\")",
+ " public static <A, B> void injectA(Object instance, A a) {",
+ " ((GenericClass<A, B>) instance).a = a;",
+ " }",
+ "",
+ " public static <A, B> void injectRegister(Object instance, B b) {",
+ " ((GenericClass<A, B>) instance).register(b);",
+ " }",
+ "}");
assertAbout(javaSource())
.that(file)
.withCompilerOptions(compilerMode.javacopts())
@@ -271,50 +272,67 @@ public class MembersInjectionTest {
"",
" @Inject Child() {}",
"}");
- JavaFileObject expected = JavaFileObjects.forSourceLines(
- "test.Child_MembersInjector",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATED_ANNOTATION,
- "public final class Child_MembersInjector<T>",
- " implements MembersInjector<Child<T>> {",
- " private final Provider<T> tAndXProvider;",
- " private final Provider<A> aAndYProvider;",
- " private final Provider<A2> a2Provider;",
- "",
- " public Child_MembersInjector(",
- " Provider<T> tAndXProvider, Provider<A> aAndYProvider, Provider<A2> a2Provider) {",
- " this.tAndXProvider = tAndXProvider;",
- " this.aAndYProvider = aAndYProvider;",
- " this.a2Provider = a2Provider;",
- " }",
- "",
- " public static <T> MembersInjector<Child<T>> create(",
- " Provider<T> tAndXProvider, Provider<A> aAndYProvider, Provider<A2> a2Provider) {",
- " return new Child_MembersInjector<T>(tAndXProvider, aAndYProvider, a2Provider);",
- " }",
- "",
- " @Override",
- " public void injectMembers(Child<T> instance) {",
- " Parent_MembersInjector.injectX(instance, tAndXProvider.get());",
- " Parent_MembersInjector.injectY(instance, aAndYProvider.get());",
- " Parent_MembersInjector.injectA2(instance, a2Provider.get());",
- " injectA(instance, aAndYProvider.get());",
- " injectT(instance, tAndXProvider.get());",
- " }",
- "",
- " public static <T> void injectA(Object instance, Object a) {",
- " ((Child<T>) instance).a = (A) a;",
- " }",
- "",
- " public static <T> void injectT(Object instance, T t) {",
- " ((Child<T>) instance).t = t;",
- " }",
- "}");
+ JavaFileObject expected =
+ JavaFileObjects.forSourceLines(
+ "test.Child_MembersInjector",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ "import dagger.internal.InjectedFieldSignature;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_CODE_ANNOTATIONS,
+ "public final class Child_MembersInjector<T>",
+ " implements MembersInjector<Child<T>> {",
+ " private final Provider<T> xProvider;",
+ " private final Provider<A> yProvider;",
+ " private final Provider<A2> a2Provider;",
+ " private final Provider<A> aProvider;",
+ " private final Provider<T> tProvider;",
+ "",
+ " public Child_MembersInjector(",
+ " Provider<T> xProvider,",
+ " Provider<A> yProvider,",
+ " Provider<A2> a2Provider,",
+ " Provider<A> aProvider,",
+ " Provider<T> tProvider) {",
+ " this.xProvider = xProvider;",
+ " this.yProvider = yProvider;",
+ " this.a2Provider = a2Provider;",
+ " this.aProvider = aProvider;",
+ " this.tProvider = tProvider;",
+ " }",
+ "",
+ " public static <T> MembersInjector<Child<T>> create(",
+ " Provider<T> xProvider,",
+ " Provider<A> yProvider,",
+ " Provider<A2> a2Provider,",
+ " Provider<A> aProvider,",
+ " Provider<T> tProvider) {",
+ " return new Child_MembersInjector<T>(xProvider, yProvider, a2Provider, aProvider,"
+ + " tProvider);",
+ "}",
+ "",
+ " @Override",
+ " public void injectMembers(Child<T> instance) {",
+ " Parent_MembersInjector.injectX(instance, xProvider.get());",
+ " Parent_MembersInjector.injectY(instance, yProvider.get());",
+ " Parent_MembersInjector.injectA2(instance, a2Provider.get());",
+ " injectA(instance, aProvider.get());",
+ " injectT(instance, tProvider.get());",
+ " }",
+ "",
+ " @InjectedFieldSignature(\"test.Child.a\")",
+ " public static <T> void injectA(Object instance, Object a) {",
+ " ((Child<T>) instance).a = (A) a;",
+ " }",
+ "",
+ " @InjectedFieldSignature(\"test.Child.t\")",
+ " public static <T> void injectT(Object instance, T t) {",
+ " ((Child<T>) instance).t = t;",
+ " }",
+ "}");
assertAbout(javaSources())
.that(ImmutableList.of(a, a2, parent, child))
.withCompilerOptions(compilerMode.javacopts())
@@ -345,38 +363,50 @@ public class MembersInjectionTest {
"import dagger.Lazy;",
"import dagger.MembersInjector;",
"import dagger.internal.DoubleCheck;",
+ "import dagger.internal.InjectedFieldSignature;",
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class FieldInjection_MembersInjector",
" implements MembersInjector<FieldInjection> {",
" private final Provider<String> stringProvider;",
+ " private final Provider<String> stringProvider2;",
+ " private final Provider<String> stringProvider3;",
"",
- " public FieldInjection_MembersInjector(Provider<String> stringProvider) {",
+ " public FieldInjection_MembersInjector(Provider<String> stringProvider,",
+ " Provider<String> stringProvider2, Provider<String> stringProvider3) {",
" this.stringProvider = stringProvider;",
+ " this.stringProvider2 = stringProvider2;",
+ " this.stringProvider3 = stringProvider3;",
" }",
"",
" public static MembersInjector<FieldInjection> create(",
- " Provider<String> stringProvider) {",
- " return new FieldInjection_MembersInjector(stringProvider);",
+ " Provider<String> stringProvider,",
+ " Provider<String> stringProvider2,",
+ " Provider<String> stringProvider3) {",
+ " return new FieldInjection_MembersInjector(",
+ " stringProvider, stringProvider2, stringProvider3);",
" }",
"",
" @Override",
" public void injectMembers(FieldInjection instance) {",
" injectString(instance, stringProvider.get());",
- " injectLazyString(instance, DoubleCheck.lazy(stringProvider));",
- " injectStringProvider(instance, stringProvider);",
+ " injectLazyString(instance, DoubleCheck.lazy(stringProvider2));",
+ " injectStringProvider(instance, stringProvider3);",
" }",
"",
+ " @InjectedFieldSignature(\"test.FieldInjection.string\")",
" public static void injectString(Object instance, String string) {",
" ((FieldInjection) instance).string = string;",
" }",
"",
+ " @InjectedFieldSignature(\"test.FieldInjection.lazyString\")",
" public static void injectLazyString(Object instance, Lazy<String> lazyString) {",
" ((FieldInjection) instance).lazyString = lazyString;",
" }",
"",
+ " @InjectedFieldSignature(\"test.FieldInjection.stringProvider\")",
" public static void injectStringProvider(",
" Object instance, Provider<String> stringProvider) {",
" ((FieldInjection) instance).stringProvider = stringProvider;",
@@ -391,6 +421,77 @@ public class MembersInjectionTest {
.generatesSources(expected);
}
+ @Test
+ public void fieldInjectionWithQualifier() {
+ JavaFileObject file =
+ JavaFileObjects.forSourceLines(
+ "test.FieldInjectionWithQualifier",
+ "package test;",
+ "",
+ "import dagger.Lazy;",
+ "import javax.inject.Inject;",
+ "import javax.inject.Named;",
+ "import javax.inject.Provider;",
+ "",
+ "class FieldInjectionWithQualifier {",
+ " @Inject @Named(\"A\") String a;",
+ " @Inject @Named(\"B\") String b;",
+ "}");
+ JavaFileObject expected =
+ JavaFileObjects.forSourceLines(
+ "test.FieldInjectionWithQualifier_MembersInjector",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ "import dagger.internal.InjectedFieldSignature;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Named;",
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_CODE_ANNOTATIONS,
+ "public final class FieldInjectionWithQualifier_MembersInjector",
+ " implements MembersInjector<FieldInjectionWithQualifier> {",
+ " private final Provider<String> aProvider;",
+ " private final Provider<String> bProvider;",
+ "",
+ " public FieldInjectionWithQualifier_MembersInjector(Provider<String> aProvider,",
+ " Provider<String> bProvider) {",
+ " this.aProvider = aProvider;",
+ " this.bProvider = bProvider;",
+ " }",
+ "",
+ " public static MembersInjector<FieldInjectionWithQualifier> create(",
+ " Provider<String> aProvider, Provider<String> bProvider) {",
+ " return new FieldInjectionWithQualifier_MembersInjector(aProvider, bProvider);",
+ " }",
+ "",
+ "@Override",
+ " public void injectMembers(FieldInjectionWithQualifier instance) {",
+ " injectA(instance, aProvider.get());",
+ " injectB(instance, bProvider.get());",
+ "}",
+ "",
+ " @InjectedFieldSignature(\"test.FieldInjectionWithQualifier.a\")",
+ " @Named(\"A\")",
+ " public static void injectA(Object instance, String a) {",
+ " ((FieldInjectionWithQualifier) instance).a = a;",
+ " }",
+ "",
+ " @InjectedFieldSignature(\"test.FieldInjectionWithQualifier.b\")",
+ " @Named(\"B\")",
+ " public static void injectB(Object instance, String b) {",
+ " ((FieldInjectionWithQualifier) instance).b = b;",
+ " }",
+ "}");
+ assertAbout(javaSource())
+ .that(file)
+ .withCompilerOptions(compilerMode.javacopts())
+ .processedWith(new ComponentProcessor())
+ .compilesWithoutError()
+ .and()
+ .generatesSources(expected);
+ }
+
@Test public void methodInjection() {
JavaFileObject file = JavaFileObjects.forSourceLines("test.MethodInjection",
"package test;",
@@ -416,20 +517,32 @@ public class MembersInjectionTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class MethodInjection_MembersInjector",
" implements MembersInjector<MethodInjection> {",
- "",
" private final Provider<String> stringProvider;",
- "",
- " public MethodInjection_MembersInjector(Provider<String> stringProvider) {",
+ " private final Provider<String> stringProvider2;",
+ " private final Provider<String> stringProvider3;",
+ " private final Provider<String> stringProvider4;",
+ "",
+ " public MethodInjection_MembersInjector(",
+ " Provider<String> stringProvider,",
+ " Provider<String> stringProvider2,",
+ " Provider<String> stringProvider3,",
+ " Provider<String> stringProvider4) {",
" this.stringProvider = stringProvider;",
+ " this.stringProvider2 = stringProvider2;",
+ " this.stringProvider3 = stringProvider3;",
+ " this.stringProvider4 = stringProvider4;",
" }",
"",
" public static MembersInjector<MethodInjection> create(",
- " Provider<String> stringProvider) {",
- " return new MethodInjection_MembersInjector(stringProvider);",
- " }",
+ " Provider<String> stringProvider,",
+ " Provider<String> stringProvider2,",
+ " Provider<String> stringProvider3,",
+ " Provider<String> stringProvider4) {",
+ " return new MethodInjection_MembersInjector(",
+ " stringProvider, stringProvider2, stringProvider3, stringProvider4);}",
"",
" @Override",
" public void injectMembers(MethodInjection instance) {",
@@ -437,9 +550,9 @@ public class MembersInjectionTest {
" injectOneArg(instance, stringProvider.get());",
" injectManyArgs(",
" instance,",
- " stringProvider.get(),",
- " DoubleCheck.lazy(stringProvider),",
- " stringProvider);",
+ " stringProvider2.get(),",
+ " DoubleCheck.lazy(stringProvider3),",
+ " stringProvider4);",
" }",
"",
" public static void injectNoArgs(Object instance) {",
@@ -483,59 +596,69 @@ public class MembersInjectionTest {
" @Inject Object object;",
" @Inject void setObject(Object o) {}",
"}");
- JavaFileObject expected = JavaFileObjects.forSourceLines(
- "test.MixedMemberInjection_MembersInjector",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATED_ANNOTATION,
- "public final class MixedMemberInjection_MembersInjector",
- " implements MembersInjector<MixedMemberInjection> {",
- "",
- " private final Provider<String> stringAndSProvider;",
- " private final Provider<Object> objectAndOProvider;",
- "",
- " public MixedMemberInjection_MembersInjector(",
- " Provider<String> stringAndSProvider,",
- " Provider<Object> objectAndOProvider) {",
- " this.stringAndSProvider = stringAndSProvider;",
- " this.objectAndOProvider = objectAndOProvider;",
- " }",
- "",
- " public static MembersInjector<MixedMemberInjection> create(",
- " Provider<String> stringAndSProvider,",
- " Provider<Object> objectAndOProvider) {",
- " return new MixedMemberInjection_MembersInjector(",
- " stringAndSProvider, objectAndOProvider);",
- " }",
- "",
- " @Override",
- " public void injectMembers(MixedMemberInjection instance) {",
- " injectString(instance, stringAndSProvider.get());",
- " injectObject(instance, objectAndOProvider.get());",
- " injectSetString(instance, stringAndSProvider.get());",
- " injectSetObject(instance, objectAndOProvider.get());",
- " }",
- "",
- " public static void injectString(Object instance, String string) {",
- " ((MixedMemberInjection) instance).string = string;",
- " }",
- "",
- " public static void injectObject(Object instance, Object object) {",
- " ((MixedMemberInjection) instance).object = object;",
- " }",
- "",
- " public static void injectSetString(Object instance, String s) {",
- " ((MixedMemberInjection) instance).setString(s);",
- " }",
- "",
- " public static void injectSetObject(Object instance, Object o) {",
- " ((MixedMemberInjection) instance).setObject(o);",
- " }",
- "}");
+ JavaFileObject expected =
+ JavaFileObjects.forSourceLines(
+ "test.MixedMemberInjection_MembersInjector",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ "import dagger.internal.InjectedFieldSignature;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_CODE_ANNOTATIONS,
+ "public final class MixedMemberInjection_MembersInjector",
+ " implements MembersInjector<MixedMemberInjection> {",
+ " private final Provider<String> stringProvider;",
+ " private final Provider<Object> objectProvider;",
+ " private final Provider<String> sProvider;",
+ " private final Provider<Object> oProvider;",
+ "",
+ " public MixedMemberInjection_MembersInjector(",
+ " Provider<String> stringProvider,",
+ " Provider<Object> objectProvider,",
+ " Provider<String> sProvider,",
+ " Provider<Object> oProvider) {",
+ " this.stringProvider = stringProvider;",
+ " this.objectProvider = objectProvider;",
+ " this.sProvider = sProvider;",
+ " this.oProvider = oProvider;",
+ " }",
+ "",
+ " public static MembersInjector<MixedMemberInjection> create(",
+ " Provider<String> stringProvider,",
+ " Provider<Object> objectProvider,",
+ " Provider<String> sProvider,",
+ " Provider<Object> oProvider) {",
+ " return new MixedMemberInjection_MembersInjector(",
+ " stringProvider, objectProvider, sProvider, oProvider);}",
+ "",
+ " @Override",
+ " public void injectMembers(MixedMemberInjection instance) {",
+ " injectString(instance, stringProvider.get());",
+ " injectObject(instance, objectProvider.get());",
+ " injectSetString(instance, sProvider.get());",
+ " injectSetObject(instance, oProvider.get());",
+ " }",
+ "",
+ " @InjectedFieldSignature(\"test.MixedMemberInjection.string\")",
+ " public static void injectString(Object instance, String string) {",
+ " ((MixedMemberInjection) instance).string = string;",
+ " }",
+ "",
+ " @InjectedFieldSignature(\"test.MixedMemberInjection.object\")",
+ " public static void injectObject(Object instance, Object object) {",
+ " ((MixedMemberInjection) instance).object = object;",
+ " }",
+ "",
+ " public static void injectSetString(Object instance, String s) {",
+ " ((MixedMemberInjection) instance).setString(s);",
+ " }",
+ "",
+ " public static void injectSetObject(Object instance, Object o) {",
+ " ((MixedMemberInjection) instance).setObject(o);",
+ " }",
+ "}");
assertAbout(javaSource())
.that(file)
.withCompilerOptions(compilerMode.javacopts())
@@ -556,45 +679,51 @@ public class MembersInjectionTest {
" @Inject AllInjections(String s) {}",
" @Inject void s(String s) {}",
"}");
- JavaFileObject expectedMembersInjector = JavaFileObjects.forSourceLines(
- "test.AllInjections_MembersInjector",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATED_ANNOTATION,
- "public final class AllInjections_MembersInjector ",
- " implements MembersInjector<AllInjections> {",
- "",
- " private final Provider<String> sProvider;",
- "",
- " public AllInjections_MembersInjector(Provider<String> sProvider) {",
- " this.sProvider = sProvider;",
- " }",
- "",
- " public static MembersInjector<AllInjections> create(Provider<String> sProvider) {",
- " return new AllInjections_MembersInjector(sProvider);",
- " }",
- "",
- " @Override",
- " public void injectMembers(AllInjections instance) {",
- " injectS(instance, sProvider.get());",
- " injectS2(instance, sProvider.get());",
- " }",
- "",
- // TODO(b/64477506): now that these all take "object", it would be nice to rename "instance"
- // to the type name
- " public static void injectS(Object instance, String s) {",
- " ((AllInjections) instance).s = s;",
- " }",
- "",
- " public static void injectS2(Object instance, String s) {",
- " ((AllInjections) instance).s(s);",
- " }",
- "",
- "}");
+ JavaFileObject expectedMembersInjector =
+ JavaFileObjects.forSourceLines(
+ "test.AllInjections_MembersInjector",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ "import dagger.internal.InjectedFieldSignature;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_CODE_ANNOTATIONS,
+ "public final class AllInjections_MembersInjector ",
+ " implements MembersInjector<AllInjections> {",
+ " private final Provider<String> sProvider;",
+ " private final Provider<String> sProvider2;",
+ "",
+ " public AllInjections_MembersInjector(",
+ " Provider<String> sProvider, Provider<String> sProvider2) {",
+ " this.sProvider = sProvider;",
+ " this.sProvider2 = sProvider2;",
+ " }",
+ "",
+ " public static MembersInjector<AllInjections> create(",
+ " Provider<String> sProvider, Provider<String> sProvider2) {",
+ " return new AllInjections_MembersInjector(sProvider, sProvider2);}",
+ "",
+ " @Override",
+ " public void injectMembers(AllInjections instance) {",
+ " injectS(instance, sProvider.get());",
+ " injectS2(instance, sProvider2.get());",
+ " }",
+ "",
+ // TODO(b/64477506): now that these all take "object", it would be nice to rename
+ // "instance"
+ // to the type name
+ " @InjectedFieldSignature(\"test.AllInjections.s\")",
+ " public static void injectS(Object instance, String s) {",
+ " ((AllInjections) instance).s = s;",
+ " }",
+ "",
+ " public static void injectS2(Object instance, String s) {",
+ " ((AllInjections) instance).s(s);",
+ " }",
+ "",
+ "}");
assertAbout(javaSource())
.that(file)
.withCompilerOptions(compilerMode.javacopts())
@@ -617,35 +746,38 @@ public class MembersInjectionTest {
"class B extends A {",
" @Inject String s;",
"}");
- JavaFileObject expectedMembersInjector = JavaFileObjects.forSourceLines(
- "test.AllInjections_MembersInjector",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATED_ANNOTATION,
- "public final class B_MembersInjector implements MembersInjector<B> {",
- " private final Provider<String> sProvider;",
- "",
- " public B_MembersInjector(Provider<String> sProvider) {",
- " this.sProvider = sProvider;",
- " }",
- "",
- " public static MembersInjector<B> create(Provider<String> sProvider) {",
- " return new B_MembersInjector(sProvider);",
- " }",
- "",
- " @Override",
- " public void injectMembers(B instance) {",
- " injectS(instance, sProvider.get());",
- " }",
- "",
- " public static void injectS(Object instance, String s) {",
- " ((B) instance).s = s;",
- " }",
- "}");
+ JavaFileObject expectedMembersInjector =
+ JavaFileObjects.forSourceLines(
+ "test.AllInjections_MembersInjector",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ "import dagger.internal.InjectedFieldSignature;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_CODE_ANNOTATIONS,
+ "public final class B_MembersInjector implements MembersInjector<B> {",
+ " private final Provider<String> sProvider;",
+ "",
+ " public B_MembersInjector(Provider<String> sProvider) {",
+ " this.sProvider = sProvider;",
+ " }",
+ "",
+ " public static MembersInjector<B> create(Provider<String> sProvider) {",
+ " return new B_MembersInjector(sProvider);",
+ " }",
+ "",
+ " @Override",
+ " public void injectMembers(B instance) {",
+ " injectS(instance, sProvider.get());",
+ " }",
+ "",
+ " @InjectedFieldSignature(\"test.B.s\")",
+ " public static void injectS(Object instance, String s) {",
+ " ((B) instance).s = s;",
+ " }",
+ "}");
assertAbout(javaSources())
.that(ImmutableList.of(aFile, bFile))
.withCompilerOptions(compilerMode.javacopts())
@@ -676,36 +808,40 @@ public class MembersInjectionTest {
" void inject(B b);",
" }",
"}");
- JavaFileObject bMembersInjector = JavaFileObjects.forSourceLines(
- "test.OuterType_B_MembersInjector",
- "package test;",
- "",
- "import dagger.MembersInjector;",
- IMPORT_GENERATED_ANNOTATION,
- "import javax.inject.Provider;",
- "",
- GENERATED_ANNOTATION,
- "public final class OuterType_B_MembersInjector",
- " implements MembersInjector<OuterType.B> {",
- " private final Provider<OuterType.A> aProvider;",
- "",
- " public OuterType_B_MembersInjector(Provider<OuterType.A> aProvider) {",
- " this.aProvider = aProvider;",
- " }",
- "",
- " public static MembersInjector<OuterType.B> create(Provider<OuterType.A> aProvider) {",
- " return new OuterType_B_MembersInjector(aProvider);",
- " }",
- "",
- " @Override",
- " public void injectMembers(OuterType.B instance) {",
- " injectA(instance, aProvider.get());",
- " }",
- "",
- " public static void injectA(Object instance, Object a) {",
- " ((OuterType.B) instance).a = (OuterType.A) a;",
- " }",
- "}");
+ JavaFileObject bMembersInjector =
+ JavaFileObjects.forSourceLines(
+ "test.OuterType_B_MembersInjector",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ "import dagger.internal.InjectedFieldSignature;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_CODE_ANNOTATIONS,
+ "public final class OuterType_B_MembersInjector",
+ " implements MembersInjector<OuterType.B> {",
+ " private final Provider<OuterType.A> aProvider;",
+ "",
+ " public OuterType_B_MembersInjector(Provider<OuterType.A> aProvider) {",
+ " this.aProvider = aProvider;",
+ " }",
+ "",
+ " public static MembersInjector<OuterType.B> create(",
+ " Provider<OuterType.A> aProvider) {",
+ " return new OuterType_B_MembersInjector(aProvider);",
+ " }",
+ "",
+ " @Override",
+ " public void injectMembers(OuterType.B instance) {",
+ " injectA(instance, aProvider.get());",
+ " }",
+ "",
+ " @InjectedFieldSignature(\"test.OuterType.B.a\")",
+ " public static void injectA(Object instance, Object a) {",
+ " ((OuterType.B) instance).a = (OuterType.A) a;",
+ " }",
+ "}");
assertAbout(javaSources())
.that(ImmutableList.of(nestedTypesFile))
.withCompilerOptions(compilerMode.javacopts())
@@ -744,10 +880,11 @@ public class MembersInjectionTest {
"package test;",
"",
"import dagger.MembersInjector;",
+ "import dagger.internal.InjectedFieldSignature;",
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class OuterType_B_MembersInjector",
" implements MembersInjector<OuterType.B> {",
" private final Provider<OuterType.A> aProvider;",
@@ -766,6 +903,7 @@ public class MembersInjectionTest {
" injectA(instance, aProvider.get());",
" }",
"",
+ " @InjectedFieldSignature(\"test.OuterType.B.a\")",
" public static void injectA(Object instance, Object a) {",
" ((OuterType.B) instance).a = (OuterType.A) a;",
" }",
@@ -852,8 +990,7 @@ public class MembersInjectionTest {
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(foo, fooModule, fooComponent);
assertThat(compilation).succeeded();
assertThat(compilation).generatedFile(CLASS_OUTPUT, "test", "foo_MembersInjector.class");
@@ -919,10 +1056,11 @@ public class MembersInjectionTest {
"package test;",
"",
"import dagger.MembersInjector;",
+ "import dagger.internal.InjectedFieldSignature;",
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class Child_MembersInjector implements MembersInjector<Child> {",
" private final Provider<Foo> objectProvider;",
" private final Provider<Bar> objectProvider2;",
@@ -944,6 +1082,7 @@ public class MembersInjectionTest {
" injectObject(instance, objectProvider2.get());",
" }",
"",
+ " @InjectedFieldSignature(\"test.Child.object\")",
" public static void injectObject(Object instance, Object object) {",
" ((Child) instance).object = (Bar) object;",
" }",
@@ -969,7 +1108,7 @@ public class MembersInjectionTest {
" @Inject int field;",
" }",
"}");
- Compilation compilation = daggerCompiler().withOptions(compilerMode.javacopts()).compile(file);
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(file);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining("Dagger does not support injection into private classes")
@@ -989,8 +1128,7 @@ public class MembersInjectionTest {
" }",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(
+ compilerWithOptions(
compilerMode.javacopts().append("-Adagger.privateMemberValidation=WARNING"))
.compile(file);
assertThat(compilation).succeeded();
@@ -1013,7 +1151,7 @@ public class MembersInjectionTest {
" @Inject int field;",
" }",
"}");
- Compilation compilation = daggerCompiler().withOptions(compilerMode.javacopts()).compile(file);
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(file);
assertThat(compilation).succeeded();
}
@@ -1037,15 +1175,37 @@ public class MembersInjectionTest {
" void inject(RawProviderField rawProviderField);",
"}");
- Compilation compilation = daggerCompiler().withOptions(compilerMode.javacopts()).compile(file);
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(file);
assertThat(compilation).failed();
assertThat(compilation)
- .hadErrorContaining("javax.inject.Provider cannot be provided")
+ .hadErrorContaining("Provider cannot be provided")
.inFile(file)
.onLineContaining("interface C");
}
@Test
+ public void throwExceptionInjectedMethod() {
+ JavaFileObject file =
+ JavaFileObjects.forSourceLines(
+ "test.",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import javax.inject.Inject;",
+ "class SomeClass {",
+ "@Inject void inject() throws Exception {}",
+ "}");
+
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Methods with @Inject may not throw checked exceptions. "
+ + "Please wrap your exceptions in a RuntimeException instead.")
+ .inFile(file)
+ .onLineContaining("throws Exception");
+ }
+
+ @Test
public void rawFrameworkTypeParameter() {
JavaFileObject file =
JavaFileObjects.forSourceLines(
@@ -1065,10 +1225,10 @@ public class MembersInjectionTest {
" void inject(RawProviderParameter rawProviderParameter);",
"}");
- Compilation compilation = daggerCompiler().withOptions(compilerMode.javacopts()).compile(file);
+ Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(file);
assertThat(compilation).failed();
assertThat(compilation)
- .hadErrorContaining("javax.inject.Provider cannot be provided")
+ .hadErrorContaining("Provider cannot be provided")
.inFile(file)
.onLineContaining("interface C");
}
@@ -1094,34 +1254,38 @@ public class MembersInjectionTest {
"package test;",
"",
"import dagger.MembersInjector;",
+ "import dagger.internal.InjectedFieldSignature;",
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class InjectedType_MembersInjector ",
" implements MembersInjector<InjectedType> {",
- " private final Provider<Integer> boxedIntAndPrimitiveIntProvider;",
+ " private final Provider<Integer> primitiveIntProvider;",
+ " private final Provider<Integer> boxedIntProvider;",
"",
" public InjectedType_MembersInjector(",
- " Provider<Integer> boxedIntAndPrimitiveIntProvider) {",
- " this.boxedIntAndPrimitiveIntProvider = boxedIntAndPrimitiveIntProvider;",
+ " Provider<Integer> primitiveIntProvider, Provider<Integer> boxedIntProvider) {",
+ " this.primitiveIntProvider = primitiveIntProvider;",
+ " this.boxedIntProvider = boxedIntProvider;",
" }",
"",
" public static MembersInjector<InjectedType> create(",
- " Provider<Integer> boxedIntAndPrimitiveIntProvider) {",
- " return new InjectedType_MembersInjector(boxedIntAndPrimitiveIntProvider);",
- " }",
+ " Provider<Integer> primitiveIntProvider, Provider<Integer> boxedIntProvider) {",
+ " return new InjectedType_MembersInjector(primitiveIntProvider, boxedIntProvider);}",
"",
" @Override",
" public void injectMembers(InjectedType instance) {",
- " injectPrimitiveInt(instance, boxedIntAndPrimitiveIntProvider.get());",
- " injectBoxedInt(instance, boxedIntAndPrimitiveIntProvider.get());",
+ " injectPrimitiveInt(instance, primitiveIntProvider.get());",
+ " injectBoxedInt(instance, boxedIntProvider.get());",
" }",
"",
+ " @InjectedFieldSignature(\"test.InjectedType.primitiveInt\")",
" public static void injectPrimitiveInt(Object instance, int primitiveInt) {",
" ((InjectedType) instance).primitiveInt = primitiveInt;",
" }",
"",
+ " @InjectedFieldSignature(\"test.InjectedType.boxedInt\")",
" public static void injectBoxedInt(Object instance, Integer boxedInt) {",
" ((InjectedType) instance).boxedInt = boxedInt;",
" }",
@@ -1135,27 +1299,30 @@ public class MembersInjectionTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class InjectedType_Factory implements Factory<InjectedType> {",
- " private final Provider<Integer> boxedIntAndPrimitiveIntProvider;",
+ " private final Provider<Integer> primitiveIntProvider;",
"",
- " public InjectedType_Factory(Provider<Integer> boxedIntAndPrimitiveIntProvider) {",
- " this.boxedIntAndPrimitiveIntProvider = boxedIntAndPrimitiveIntProvider;",
+ " private final Provider<Integer> boxedIntProvider;",
+ "",
+ " public InjectedType_Factory(",
+ " Provider<Integer> primitiveIntProvider, Provider<Integer> boxedIntProvider) {",
+ " this.primitiveIntProvider = primitiveIntProvider;",
+ " this.boxedIntProvider = boxedIntProvider;",
" }",
"",
" @Override",
" public InjectedType get() {",
- " InjectedType instance = new InjectedType();",
+ " InjectedType instance = newInstance();",
" InjectedType_MembersInjector.injectPrimitiveInt(",
- " instance, boxedIntAndPrimitiveIntProvider.get());",
- " InjectedType_MembersInjector.injectBoxedInt(",
- " instance, boxedIntAndPrimitiveIntProvider.get());",
+ " instance, primitiveIntProvider.get());",
+ " InjectedType_MembersInjector.injectBoxedInt(instance, boxedIntProvider.get());",
" return instance;",
" }",
"",
" public static InjectedType_Factory create(",
- " Provider<Integer> boxedIntAndPrimitiveIntProvider) {",
- " return new InjectedType_Factory(boxedIntAndPrimitiveIntProvider);",
+ " Provider<Integer> primitiveIntProvider, Provider<Integer> boxedIntProvider) {",
+ " return new InjectedType_Factory(primitiveIntProvider, boxedIntProvider);",
" }",
"",
" public static InjectedType newInstance() {",
@@ -1163,7 +1330,7 @@ public class MembersInjectionTest {
" }",
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(injectedType);
+ compilerWithOptions(compilerMode.javacopts()).compile(injectedType);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.InjectedType_MembersInjector")
@@ -1221,8 +1388,7 @@ public class MembersInjectionTest {
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(foo, inaccessible, usesInaccessible, component);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -1233,28 +1399,33 @@ public class MembersInjectionTest {
"package other;",
"",
"import dagger.MembersInjector;",
+ "import dagger.internal.InjectedFieldSignature;",
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class Inaccessible_MembersInjector",
" implements MembersInjector<Inaccessible> {",
" private final Provider<Foo> fooProvider;",
+ " private final Provider<Foo> fooProvider2;",
"",
- " public Inaccessible_MembersInjector(Provider<Foo> fooProvider) {",
+ " public Inaccessible_MembersInjector(",
+ " Provider<Foo> fooProvider, Provider<Foo> fooProvider2) {",
" this.fooProvider = fooProvider;",
+ " this.fooProvider2 = fooProvider2;",
" }",
"",
- " public static MembersInjector<Inaccessible> create(Provider<Foo> fooProvider) {",
- " return new Inaccessible_MembersInjector(fooProvider);",
- " }",
+ " public static MembersInjector<Inaccessible> create(",
+ " Provider<Foo> fooProvider, Provider<Foo> fooProvider2) {",
+ " return new Inaccessible_MembersInjector(fooProvider, fooProvider2);}",
"",
" @Override",
" public void injectMembers(Inaccessible instance) {",
" injectFoo(instance, fooProvider.get());",
- " injectMethod(instance, fooProvider.get());",
+ " injectMethod(instance, fooProvider2.get());",
" }",
"",
+ " @InjectedFieldSignature(\"other.Inaccessible.foo\")",
" public static void injectFoo(Object instance, Object foo) {",
" ((Inaccessible) instance).foo = (Foo) foo;",
" }",
@@ -1276,16 +1447,16 @@ public class MembersInjectionTest {
"import other.UsesInaccessible;",
"import other.UsesInaccessible_Factory;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
- " private Object getInaccessible() {",
+ " private Object inaccessible() {",
" return injectInaccessible(Inaccessible_Factory.newInstance());",
" }",
"",
" @Override",
" public UsesInaccessible usesInaccessible() {",
" return UsesInaccessible_Factory.newInstance(",
- " getInaccessible());",
+ " inaccessible());",
" }",
"",
// TODO(ronshapiro): if possible, it would be great to rename "instance", but we
@@ -1358,8 +1529,7 @@ public class MembersInjectionTest {
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(inaccessible, inaccessiblesModule, usesInaccessibles, component);
assertThat(compilation).succeeded();
JavaFileObject generatedComponent =
@@ -1375,13 +1545,13 @@ public class MembersInjectionTest {
"import other.UsesInaccessibles_Factory;",
"import other.UsesInaccessibles_MembersInjector;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {")
.addLinesIn(
FAST_INIT_MODE,
" private volatile Object listOfInaccessible = new MemoizedSentinel();",
"",
- " private List getListOfInaccessible() {",
+ " private List listOfInaccessible() {",
" Object local = listOfInaccessible;",
" if (local instanceof MemoizedSentinel) {",
" synchronized (local) {",
@@ -1419,7 +1589,7 @@ public class MembersInjectionTest {
" UsesInaccessibles_MembersInjector.injectInaccessibles(")
.addLinesIn(
FAST_INIT_MODE,
- " instance, (List) getListOfInaccessible());")
+ " instance, (List) listOfInaccessible());")
.addLinesIn(
DEFAULT_MODE,
" instance, (List) inaccessiblesProvider.get());")
@@ -1488,8 +1658,7 @@ public class MembersInjectionTest {
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(foo, supertype, subtype, injectsSubtype, component);
assertThat(compilation).succeeded();
JavaFileObject generatedComponent =
@@ -1505,15 +1674,15 @@ public class MembersInjectionTest {
"import other.Supertype;",
"import other.Supertype_MembersInjector;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
- " private Object getSubtype() {",
+ " private Object subtype() {",
" return injectSubtype(Subtype_Factory.newInstance());",
" }",
"",
" @Override",
" public InjectsSubtype injectsSubtype() {",
- " return InjectsSubtype_Factory.newInstance(getSubtype());",
+ " return InjectsSubtype_Factory.newInstance(subtype());",
" }",
"",
" @CanIgnoreReturnValue",
@@ -1528,4 +1697,227 @@ public class MembersInjectionTest {
.generatedSourceFile("test.DaggerTestComponent")
.containsElementsIn(generatedComponent);
}
+
+ // Shows that we shouldn't create a members injector for a type that doesn't have
+ // @Inject fields or @Inject constructor even if it extends and is extended by types that do.
+ @Test
+ public void middleClassNoFieldInjection() {
+ JavaFileObject classA =
+ JavaFileObjects.forSourceLines(
+ "test.A",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class A extends B {",
+ " @Inject String valueA;",
+ "}");
+ JavaFileObject classB =
+ JavaFileObjects.forSourceLines(
+ "test.B",
+ "package test;",
+ "",
+ "class B extends C {",
+ "}");
+ JavaFileObject classC =
+ JavaFileObjects.forSourceLines(
+ "test.C",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class C { ",
+ " @Inject String valueC;",
+ "}");
+ JavaFileObject expectedAMembersInjector =
+ JavaFileObjects.forSourceLines(
+ "test.A_MembersInjector",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ "import dagger.internal.InjectedFieldSignature;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_CODE_ANNOTATIONS,
+ "public final class A_MembersInjector implements MembersInjector<A> {",
+ " private final Provider<String> valueCProvider;",
+ " private final Provider<String> valueAProvider;",
+ "",
+ " public A_MembersInjector(",
+ " Provider<String> valueCProvider, Provider<String> valueAProvider) {",
+ " this.valueCProvider = valueCProvider;",
+ " this.valueAProvider = valueAProvider;",
+ " }",
+ "",
+ " public static MembersInjector<A> create(",
+ " Provider<String> valueCProvider, Provider<String> valueAProvider) {",
+ " return new A_MembersInjector(valueCProvider, valueAProvider);",
+ " }",
+ "",
+ " @Override",
+ " public void injectMembers(A instance) {",
+ " C_MembersInjector.injectValueC(instance, valueCProvider.get());",
+ " injectValueA(instance, valueAProvider.get());",
+ " }",
+ "",
+ " @InjectedFieldSignature(\"test.A.valueA\")",
+ " public static void injectValueA(Object instance, String valueA) {",
+ " ((A) instance).valueA = valueA;",
+ " }",
+ "}");
+
+ JavaFileObject expectedCMembersInjector =
+ JavaFileObjects.forSourceLines(
+ "test.C_MembersInjector",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ "import dagger.internal.InjectedFieldSignature;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_CODE_ANNOTATIONS,
+ "public final class C_MembersInjector implements MembersInjector<C> {",
+ " private final Provider<String> valueCProvider;",
+ "",
+ " public C_MembersInjector(Provider<String> valueCProvider) {",
+ " this.valueCProvider = valueCProvider;",
+ " }",
+ "",
+ " public static MembersInjector<C> create(",
+ " Provider<String> valueCProvider) {",
+ " return new C_MembersInjector(valueCProvider);",
+ " }",
+ "",
+ " @Override",
+ " public void injectMembers(C instance) {",
+ " injectValueC(instance, valueCProvider.get());",
+ " }",
+ "",
+ " @InjectedFieldSignature(\"test.C.valueC\")",
+ " public static void injectValueC(Object instance, String valueC) {",
+ " ((C) instance).valueC = valueC;",
+ " }",
+ "}");
+
+
+ Compilation compilation =
+ compilerWithOptions(compilerMode.javacopts())
+ .compile(classA, classB, classC);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.A_MembersInjector")
+ .hasSourceEquivalentTo(expectedAMembersInjector);
+ assertThat(compilation)
+ .generatedSourceFile("test.C_MembersInjector")
+ .hasSourceEquivalentTo(expectedCMembersInjector);
+
+ try {
+ assertThat(compilation).generatedSourceFile("test.B_MembersInjector");
+ // Can't throw an assertion error since it would be caught.
+ throw new IllegalStateException("Test generated a B_MembersInjector");
+ } catch (AssertionError expected) {
+ }
+ }
+
+ // Shows that we do generate a MembersInjector for a type that has an @Inject
+ // constructor and that extends a type with @Inject fields, even if it has no local field
+ // injection sites
+ // TODO(erichang): Are these even used anymore?
+ @Test
+ public void testConstructorInjectedFieldInjection() {
+ JavaFileObject classA =
+ JavaFileObjects.forSourceLines(
+ "test.A",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class A extends B {",
+ " @Inject A() {}",
+ "}");
+ JavaFileObject classB =
+ JavaFileObjects.forSourceLines(
+ "test.B",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class B { ",
+ " @Inject String valueB;",
+ "}");
+ JavaFileObject expectedAMembersInjector =
+ JavaFileObjects.forSourceLines(
+ "test.A_MembersInjector",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_CODE_ANNOTATIONS,
+ "public final class A_MembersInjector implements MembersInjector<A> {",
+ " private final Provider<String> valueBProvider;",
+ "",
+ " public A_MembersInjector(Provider<String> valueBProvider) {",
+ " this.valueBProvider = valueBProvider;",
+ " }",
+ "",
+ " public static MembersInjector<A> create(Provider<String> valueBProvider) {",
+ " return new A_MembersInjector(valueBProvider);",
+ " }",
+ "",
+ " @Override",
+ " public void injectMembers(A instance) {",
+ " B_MembersInjector.injectValueB(instance, valueBProvider.get());",
+ " }",
+ "}");
+
+ JavaFileObject expectedBMembersInjector =
+ JavaFileObjects.forSourceLines(
+ "test.B_MembersInjector",
+ "package test;",
+ "",
+ "import dagger.MembersInjector;",
+ "import dagger.internal.InjectedFieldSignature;",
+ IMPORT_GENERATED_ANNOTATION,
+ "import javax.inject.Provider;",
+ "",
+ GENERATED_CODE_ANNOTATIONS,
+ "public final class B_MembersInjector implements MembersInjector<B> {",
+ " private final Provider<String> valueBProvider;",
+ "",
+ " public B_MembersInjector(Provider<String> valueBProvider) {",
+ " this.valueBProvider = valueBProvider;",
+ " }",
+ "",
+ " public static MembersInjector<B> create(",
+ " Provider<String> valueBProvider) {",
+ " return new B_MembersInjector(valueBProvider);",
+ " }",
+ "",
+ " @Override",
+ " public void injectMembers(B instance) {",
+ " injectValueB(instance, valueBProvider.get());",
+ " }",
+ "",
+ " @InjectedFieldSignature(\"test.B.valueB\")",
+ " public static void injectValueB(Object instance, String valueB) {",
+ " ((B) instance).valueB = valueB;",
+ " }",
+ "}");
+
+
+ Compilation compilation =
+ compilerWithOptions(compilerMode.javacopts())
+ .compile(classA, classB);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("test.A_MembersInjector")
+ .hasSourceEquivalentTo(expectedAMembersInjector);
+ assertThat(compilation)
+ .generatedSourceFile("test.B_MembersInjector")
+ .hasSourceEquivalentTo(expectedBMembersInjector);
+ }
}
diff --git a/javatests/dagger/internal/codegen/MembersInjectionValidationTest.java b/javatests/dagger/internal/codegen/MembersInjectionValidationTest.java
index 68d5daaa7..d8cd6d98a 100644
--- a/javatests/dagger/internal/codegen/MembersInjectionValidationTest.java
+++ b/javatests/dagger/internal/codegen/MembersInjectionValidationTest.java
@@ -236,4 +236,177 @@ public class MembersInjectionValidationTest {
assertThat(compilation).failed();
assertThat(compilation).hadErrorContaining("static fields").inFile(injected).onLine(6);
}
+
+ @Test
+ public void missingMembersInjectorForKotlinProperty() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.internal.codegen.KotlinInjectedQualifier;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " void inject(KotlinInjectedQualifier injected);",
+ "}");
+ JavaFileObject module =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Named;",
+ "",
+ "@Module",
+ "class TestModule {",
+ " @Provides",
+ " @Named(\"TheString\")",
+ " String theString() { return \"\"; }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(component, module);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Unable to read annotations on an injected Kotlin property.");
+ }
+
+ @Test
+ public void memberInjectionForKotlinObjectFails() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.internal.codegen.KotlinObjectWithMemberInjection;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " void inject(KotlinObjectWithMemberInjection injected);",
+ "}");
+ Compilation compilation = daggerCompiler().compile(component, testModule);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Dagger does not support injection into Kotlin objects");
+ }
+
+ @Test
+ public void setterMemberInjectionForKotlinObjectFails() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.internal.codegen.KotlinObjectWithSetterMemberInjection;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " void inject(KotlinObjectWithSetterMemberInjection injected);",
+ "}");
+ Compilation compilation = daggerCompiler().compile(component, testModule);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Dagger does not support injection into Kotlin objects");
+ }
+
+ @Test
+ public void memberInjectionForKotlinClassWithCompanionObjectFails() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.internal.codegen.KotlinClassWithMemberInjectedCompanion;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " void inject(KotlinClassWithMemberInjectedCompanion injected);",
+ " void injectCompanion(KotlinClassWithMemberInjectedCompanion.Companion injected);",
+ "}");
+ Compilation compilation = daggerCompiler().compile(component, testModule);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Dagger does not support injection into static fields");
+ }
+
+ @Test
+ public void setterMemberInjectionForKotlinClassWithCompanionObjectFails() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.internal.codegen.KotlinClassWithSetterMemberInjectedCompanion;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " void inject(KotlinClassWithSetterMemberInjectedCompanion.Companion injected);",
+ "}");
+ Compilation compilation = daggerCompiler().compile(component, testModule);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Dagger does not support injection into Kotlin objects");
+ }
+
+ @Test
+ public void memberInjectionForKotlinClassWithNamedCompanionObjectFails() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.internal.codegen.KotlinClassWithMemberInjectedNamedCompanion;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " void inject(KotlinClassWithMemberInjectedNamedCompanion injected);",
+ " void injectCompanion(KotlinClassWithMemberInjectedNamedCompanion.TheCompanion"
+ + " injected);",
+ "}");
+ Compilation compilation = daggerCompiler().compile(component, testModule);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Dagger does not support injection into static fields");
+ }
+
+ @Test
+ public void setterMemberInjectionForKotlinClassWithNamedCompanionObjectFails() {
+ JavaFileObject component =
+ JavaFileObjects.forSourceLines(
+ "test.TestComponent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "import dagger.internal.codegen.KotlinClassWithSetterMemberInjectedNamedCompanion;",
+ "",
+ "@Component(modules = TestModule.class)",
+ "interface TestComponent {",
+ " void inject(",
+ " KotlinClassWithSetterMemberInjectedNamedCompanion.TheCompanion injected);",
+ "}");
+ Compilation compilation = daggerCompiler().compile(component, testModule);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("Dagger does not support injection into Kotlin objects");
+ }
+
+ private final JavaFileObject testModule =
+ JavaFileObjects.forSourceLines(
+ "test.TestModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "class TestModule {",
+ " @Provides",
+ " String theString() { return \"\"; }",
+ "}");
}
diff --git a/javatests/dagger/internal/codegen/MethodSignatureFormatterTest.java b/javatests/dagger/internal/codegen/MethodSignatureFormatterTest.java
index 687c29aaf..6d2f98988 100644
--- a/javatests/dagger/internal/codegen/MethodSignatureFormatterTest.java
+++ b/javatests/dagger/internal/codegen/MethodSignatureFormatterTest.java
@@ -22,12 +22,20 @@ import static javax.lang.model.util.ElementFilter.methodsIn;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.testing.compile.CompilationRule;
+import dagger.BindsInstance;
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
import dagger.internal.codegen.MethodSignatureFormatterTest.OuterClass.InnerClass;
+import dagger.internal.codegen.binding.InjectionAnnotations;
+import dagger.internal.codegen.binding.MethodSignatureFormatter;
import dagger.internal.codegen.langmodel.DaggerElements;
import dagger.internal.codegen.langmodel.DaggerTypes;
+import javax.inject.Inject;
import javax.inject.Singleton;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -37,6 +45,10 @@ import org.junit.runners.JUnit4;
public class MethodSignatureFormatterTest {
@Rule public CompilationRule compilationRule = new CompilationRule();
+ @Inject DaggerElements elements;
+ @Inject DaggerTypes types;
+ @Inject InjectionAnnotations injectionAnnotations;
+
static class OuterClass {
@interface Foo {
Class<?> bar();
@@ -53,13 +65,15 @@ public class MethodSignatureFormatterTest {
}
}
+ @Before
+ public void setUp() {
+ DaggerMethodSignatureFormatterTest_TestComponent.factory().create(compilationRule).inject(this);
+ }
+
@Test public void methodSignatureTest() {
- DaggerElements elements =
- new DaggerElements(compilationRule.getElements(), compilationRule.getTypes());
- DaggerTypes types = new DaggerTypes(compilationRule.getTypes(), elements);
TypeElement inner = elements.getTypeElement(InnerClass.class);
ExecutableElement method = Iterables.getOnlyElement(methodsIn(inner.getEnclosedElements()));
- String formatted = new MethodSignatureFormatter(types).format(method);
+ String formatted = new MethodSignatureFormatter(types, injectionAnnotations).format(method);
// This is gross, but it turns out that annotation order is not guaranteed when getting
// all the AnnotationMirrors from an Element, so I have to test this chopped-up to make it
// less brittle.
@@ -71,4 +85,28 @@ public class MethodSignatureFormatterTest {
assertThat(formatted).contains(" String "); // return type compressed
assertThat(formatted).contains("int, ImmutableList<Boolean>)"); // parameters compressed.
}
+
+ @Singleton
+ @Component(modules = TestModule.class)
+ interface TestComponent {
+ void inject(MethodSignatureFormatterTest test);
+
+ @Component.Factory
+ interface Factory {
+ TestComponent create(@BindsInstance CompilationRule compilationRule);
+ }
+ }
+
+ @Module
+ static class TestModule {
+ @Provides
+ static DaggerElements elements(CompilationRule compilationRule) {
+ return new DaggerElements(compilationRule.getElements(), compilationRule.getTypes());
+ }
+
+ @Provides
+ static DaggerTypes types(CompilationRule compilationRule, DaggerElements elements) {
+ return new DaggerTypes(compilationRule.getTypes(), elements);
+ }
+ }
}
diff --git a/javatests/dagger/internal/codegen/MissingBindingSuggestionsTest.java b/javatests/dagger/internal/codegen/MissingBindingSuggestionsTest.java
index e2f963494..3c066bab2 100644
--- a/javatests/dagger/internal/codegen/MissingBindingSuggestionsTest.java
+++ b/javatests/dagger/internal/codegen/MissingBindingSuggestionsTest.java
@@ -18,6 +18,7 @@ package dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static dagger.internal.codegen.TestUtils.message;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
@@ -94,8 +95,9 @@ public class MissingBindingSuggestionsTest {
Compilation compilation =
daggerCompiler().compile(fooComponent, barComponent, topComponent, foo, bar, barModule);
assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
assertThat(compilation)
- .hadErrorContaining("A binding with matching key exists in component: test.BarComponent");
+ .hadErrorContaining("A binding with matching key exists in component: BarComponent");
}
@Test public void suggestsBindingInNestedSubcomponent() {
@@ -154,7 +156,164 @@ public class MissingBindingSuggestionsTest {
daggerCompiler()
.compile(fooComponent, barComponent, bazComponent, topComponent, foo, baz, bazModule);
assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
assertThat(compilation)
- .hadErrorContaining("A binding with matching key exists in component: test.BazComponent");
+ .hadErrorContaining("A binding with matching key exists in component: BazComponent");
+ }
+
+ @Test
+ public void missingBindingInParentComponent() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "Parent",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface Parent {",
+ " Foo foo();",
+ " Bar bar();",
+ " Child child();",
+ "}");
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "Child",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules=BazModule.class)",
+ "interface Child {",
+ " Foo foo();",
+ " Baz baz();",
+ "}");
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "Foo",
+ "import javax.inject.Inject;",
+ "",
+ "class Foo {",
+ " @Inject Foo(Bar bar) {}",
+ "}");
+ JavaFileObject bar =
+ JavaFileObjects.forSourceLines(
+ "Bar",
+ "import javax.inject.Inject;",
+ "",
+ "class Bar {",
+ " @Inject Bar(Baz baz) {}",
+ "}");
+ JavaFileObject baz = JavaFileObjects.forSourceLines("Baz", "class Baz {}");
+ JavaFileObject bazModule = JavaFileObjects.forSourceLines(
+ "BazModule",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Inject;",
+ "",
+ "@Module",
+ "final class BazModule {",
+ " @Provides Baz provideBaz() {return new Baz();}",
+ "}");
+
+ Compilation compilation = daggerCompiler().compile(parent, child, foo, bar, baz, bazModule);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "\033[1;31m[Dagger/MissingBinding]\033[0m Baz cannot be provided without an "
+ + "@Inject constructor or an @Provides-annotated method.",
+ "A binding with matching key exists in component: Child",
+ " Baz is injected at",
+ " Bar(baz)",
+ " Bar is requested at",
+ " Parent.bar()",
+ "The following other entry points also depend on it:",
+ " Parent.foo()",
+ " Child.foo() [Parent → Child]"))
+ .inFile(parent)
+ .onLineContaining("interface Parent");
+ }
+
+ @Test
+ public void missingBindingInSiblingComponent() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "Parent",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface Parent {",
+ " Foo foo();",
+ " Bar bar();",
+ " Child1 child1();",
+ " Child2 child2();",
+ "}");
+ JavaFileObject child1 =
+ JavaFileObjects.forSourceLines(
+ "Child1",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Child1 {",
+ " Foo foo();",
+ " Baz baz();",
+ "}");
+ JavaFileObject child2 =
+ JavaFileObjects.forSourceLines(
+ "Child2",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = BazModule.class)",
+ "interface Child2 {",
+ " Foo foo();",
+ " Baz baz();",
+ "}");
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "Foo",
+ "import javax.inject.Inject;",
+ "",
+ "class Foo {",
+ " @Inject Foo(Bar bar) {}",
+ "}");
+ JavaFileObject bar =
+ JavaFileObjects.forSourceLines(
+ "Bar",
+ "import javax.inject.Inject;",
+ "",
+ "class Bar {",
+ " @Inject Bar(Baz baz) {}",
+ "}");
+ JavaFileObject baz = JavaFileObjects.forSourceLines("Baz", "class Baz {}");
+ JavaFileObject bazModule = JavaFileObjects.forSourceLines(
+ "BazModule",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "import javax.inject.Inject;",
+ "",
+ "@Module",
+ "final class BazModule {",
+ " @Provides Baz provideBaz() {return new Baz();}",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler().compile(parent, child1, child2, foo, bar, baz, bazModule);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "\033[1;31m[Dagger/MissingBinding]\033[0m Baz cannot be provided without an "
+ + "@Inject constructor or an @Provides-annotated method.",
+ "A binding with matching key exists in component: Child2",
+ " Baz is injected at",
+ " Bar(baz)",
+ " Bar is requested at",
+ " Parent.bar()",
+ "The following other entry points also depend on it:",
+ " Parent.foo()",
+ " Child1.foo() [Parent → Child1]",
+ " Child2.foo() [Parent → Child2]",
+ " Child1.baz() [Parent → Child1]"))
+ .inFile(parent)
+ .onLineContaining("interface Parent");
}
}
diff --git a/javatests/dagger/internal/codegen/MissingBindingValidationTest.java b/javatests/dagger/internal/codegen/MissingBindingValidationTest.java
index 8eaa8d5a2..1aabd4899 100644
--- a/javatests/dagger/internal/codegen/MissingBindingValidationTest.java
+++ b/javatests/dagger/internal/codegen/MissingBindingValidationTest.java
@@ -56,8 +56,9 @@ public class MissingBindingValidationTest {
"interface Bar {}");
Compilation compilation = daggerCompiler().compile(component, injectable, nonInjectable);
assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
assertThat(compilation)
- .hadErrorContaining("test.Bar cannot be provided without an @Provides-annotated method.")
+ .hadErrorContaining("Bar cannot be provided without an @Provides-annotated method.")
.inFile(component)
.onLineContaining("interface MyComponent");
}
@@ -81,9 +82,10 @@ public class MissingBindingValidationTest {
"}");
Compilation compilation = daggerCompiler().compile(component);
assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
assertThat(compilation)
.hadErrorContaining(
- "[Dagger/MissingBinding] test.TestClass.A cannot be provided "
+ "\033[1;31m[Dagger/MissingBinding]\033[0m TestClass.A cannot be provided "
+ "without an @Provides-annotated method.")
.inFile(component)
.onLineContaining("interface AComponent");
@@ -110,9 +112,10 @@ public class MissingBindingValidationTest {
"}");
Compilation compilation = daggerCompiler().compile(component);
assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
assertThat(compilation)
.hadErrorContaining(
- "[Dagger/MissingBinding] @test.TestClass.Q test.TestClass.A cannot be provided "
+ "\033[1;31m[Dagger/MissingBinding]\033[0m @TestClass.Q TestClass.A cannot be provided "
+ "without an @Provides-annotated method.")
.inFile(component)
.onLineContaining("interface AComponent");
@@ -140,9 +143,10 @@ public class MissingBindingValidationTest {
Compilation compilation = daggerCompiler().compile(component);
assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
assertThat(compilation)
.hadErrorContaining(
- "test.TestClass.A cannot be provided without an @Inject constructor or an "
+ "TestClass.A cannot be provided without an @Inject constructor or an "
+ "@Provides-annotated method.")
.inFile(component)
.onLineContaining("interface AComponent");
@@ -174,9 +178,10 @@ public class MissingBindingValidationTest {
Compilation compilation = daggerCompiler().compile(component);
assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
assertThat(compilation)
.hadErrorContaining(
- "test.TestClass.B cannot be provided without an @Inject constructor or an "
+ "TestClass.B cannot be provided without an @Inject constructor or an "
+ "@Provides-annotated method. This type supports members injection but cannot be "
+ "implicitly provided.")
.inFile(component)
@@ -210,8 +215,9 @@ public class MissingBindingValidationTest {
Compilation compilation = daggerCompiler().compile(self, component);
assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
assertThat(compilation)
- .hadErrorContaining("test.Self cannot be provided without an @Inject constructor")
+ .hadErrorContaining("Self cannot be provided without an @Inject constructor")
.inFile(component)
.onLineContaining("interface SelfComponent");
}
@@ -241,9 +247,10 @@ public class MissingBindingValidationTest {
"}");
Compilation compilation = daggerCompiler().compile(component, foo);
assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
assertThat(compilation)
.hadErrorContaining(
- "test.Foo<? extends java.lang.Number> cannot be provided "
+ "Foo<? extends Number> cannot be provided "
+ "without an @Provides-annotated method");
}
@@ -300,21 +307,22 @@ public class MissingBindingValidationTest {
Compilation compilation = daggerCompiler().compile(component);
assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
assertThat(compilation)
.hadErrorContaining(
message(
- "test.TestClass.A cannot be provided without an @Provides-annotated method.",
- " test.TestClass.A is injected at",
- " test.TestClass.B(a)",
- " test.TestClass.B is injected at",
- " test.TestClass.C.b",
- " test.TestClass.C is injected at",
- " test.TestClass.AComponent.injectC(test.TestClass.C)",
+ "TestClass.A cannot be provided without an @Provides-annotated method.",
+ " TestClass.A is injected at",
+ " TestClass.B(a)",
+ " TestClass.B is injected at",
+ " TestClass.C.b",
+ " TestClass.C is injected at",
+ " TestClass.AComponent.injectC(TestClass.C)",
"The following other entry points also depend on it:",
- " test.TestClass.AComponent.getFoo()",
- " test.TestClass.AComponent.cProvider()",
- " test.TestClass.AComponent.lazyC()",
- " test.TestClass.AComponent.lazyCProvider()"))
+ " TestClass.AComponent.getFoo()",
+ " TestClass.AComponent.cProvider()",
+ " TestClass.AComponent.lazyC()",
+ " TestClass.AComponent.lazyCProvider()"))
.inFile(component)
.onLineContaining("interface AComponent");
}
@@ -354,16 +362,17 @@ public class MissingBindingValidationTest {
Compilation compilation =
daggerCompiler().compile(component, module, interfaceFile, implementationFile);
assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
assertThat(compilation)
.hadErrorContaining(
message(
- "java.lang.String cannot be provided without an @Inject constructor or an "
+ "String cannot be provided without an @Inject constructor or an "
+ "@Provides-annotated method.",
- " java.lang.String is injected at",
+ " String is injected at",
" TestImplementation(missingBinding)",
" TestImplementation is injected at",
" TestModule.bindTestInterface(implementation)",
- " TestInterface is provided at",
+ " TestInterface is requested at",
" TestComponent.testInterface()"))
.inFile(component)
.onLineContaining("interface TestComponent");
@@ -408,18 +417,19 @@ public class MissingBindingValidationTest {
Compilation compilation = daggerCompiler().compile(generic, testClass, usesTest, component);
assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
assertThat(compilation)
.hadErrorContaining(
message(
- "java.util.List cannot be provided without an @Provides-annotated method.",
- " java.util.List is injected at",
- " test.TestClass(list)",
- " test.TestClass is injected at",
- " test.Generic(t)",
- " test.Generic<test.TestClass> is injected at",
- " test.UsesTest(genericTestClass)",
- " test.UsesTest is provided at",
- " test.TestComponent.usesTest()"));
+ "List cannot be provided without an @Provides-annotated method.",
+ " List is injected at",
+ " TestClass(list)",
+ " TestClass is injected at",
+ " Generic(t)",
+ " Generic<TestClass> is injected at",
+ " UsesTest(genericTestClass)",
+ " UsesTest is requested at",
+ " TestComponent.usesTest()"));
}
@Test public void resolvedVariablesInDependencyTrace() {
@@ -462,18 +472,19 @@ public class MissingBindingValidationTest {
Compilation compilation = daggerCompiler().compile(generic, testClass, usesTest, component);
assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
assertThat(compilation)
.hadErrorContaining(
message(
- "java.util.List cannot be provided without an @Provides-annotated method.",
- " java.util.List is injected at",
- " test.TestClass(list)",
- " test.TestClass is injected at",
- " test.Generic.t",
- " test.Generic<test.TestClass> is injected at",
- " test.UsesTest(genericTestClass)",
- " test.UsesTest is provided at",
- " test.TestComponent.usesTest()"));
+ "List cannot be provided without an @Provides-annotated method.",
+ " List is injected at",
+ " TestClass(list)",
+ " TestClass is injected at",
+ " Generic.t",
+ " Generic<TestClass> is injected at",
+ " UsesTest(genericTestClass)",
+ " UsesTest is requested at",
+ " TestComponent.usesTest()"));
}
@Test
@@ -524,9 +535,10 @@ public class MissingBindingValidationTest {
Compilation compilation = daggerCompiler().compile(parent, parentModule, child, childModule);
assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
assertThat(compilation)
.hadErrorContainingMatch(
- "(?s)\\Qjava.lang.String cannot be provided\\E.*\\QChild.needsString()\\E")
+ "(?s)\\QString cannot be provided\\E.*\\QChild.needsString()\\E")
.inFile(parent)
.onLineContaining("interface Parent");
}
@@ -599,9 +611,10 @@ public class MissingBindingValidationTest {
Compilation compilation =
daggerCompiler().compile(parent, parentModule, child, childModule, grandchild);
assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
assertThat(compilation)
.hadErrorContainingMatch(
- "(?s)\\Qjava.lang.Double cannot be provided\\E.*"
+ "(?s)\\QDouble cannot be provided\\E.*"
+ "\\QGrandchild.object() [Parent → Child → Grandchild]\\E$")
.inFile(parent)
.onLineContaining("interface Parent");
@@ -646,19 +659,20 @@ public class MissingBindingValidationTest {
"interface NotBound {}");
Compilation compilation = daggerCompiler().compile(component, module, notBound);
assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
assertThat(compilation)
.hadErrorContaining(
message(
- "[Dagger/MissingBinding] "
- + "test.NotBound cannot be provided without an @Provides-annotated method.",
- " test.NotBound is injected at",
- " test.TestModule.object(notBound)",
- " java.lang.Object is provided at",
- " test.TestComponent.object()",
+ "\033[1;31m[Dagger/MissingBinding]\033[0m "
+ + "NotBound cannot be provided without an @Provides-annotated method.",
+ " NotBound is injected at",
+ " TestModule.object(notBound)",
+ " Object is requested at",
+ " TestComponent.object()",
"It is also requested at:",
- " test.TestModule.string(notBound, …)",
+ " TestModule.string(notBound, …)",
"The following other entry points also depend on it:",
- " test.TestComponent.string()"))
+ " TestComponent.string()"))
.inFile(component)
.onLineContaining("interface TestComponent");
assertThat(compilation).hadErrorCount(1);
@@ -705,24 +719,25 @@ public class MissingBindingValidationTest {
Compilation compilation = daggerCompiler().compile(foo, component);
assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
assertThat(compilation)
.hadErrorContaining(
message(
- "[Dagger/MissingBinding] java.lang.String cannot be provided without an @Inject "
- + "constructor or an @Provides-annotated method.",
- " java.lang.String is provided at",
- " test.TestComponent.string()",
+ "\033[1;31m[Dagger/MissingBinding]\033[0m String cannot be provided without an "
+ + "@Inject constructor or an @Provides-annotated method.",
+ " String is requested at",
+ " TestComponent.string()",
"It is also requested at:",
- " test.Foo(one, …)",
- " test.Foo(…, two, …)",
- " test.Foo(…, three, …)",
- " test.Foo(…, four, …)",
- " test.Foo(…, five, …)",
- " test.Foo(…, six, …)",
- " test.Foo(…, seven, …)",
- " test.Foo(…, eight, …)",
- " test.Foo(…, nine, …)",
- " test.Foo(…, ten, …)",
+ " Foo(one, …)",
+ " Foo(…, two, …)",
+ " Foo(…, three, …)",
+ " Foo(…, four, …)",
+ " Foo(…, five, …)",
+ " Foo(…, six, …)",
+ " Foo(…, seven, …)",
+ " Foo(…, eight, …)",
+ " Foo(…, nine, …)",
+ " Foo(…, ten, …)",
" and 3 others"))
.inFile(component)
.onLineContaining("interface TestComponent");
@@ -755,26 +770,162 @@ public class MissingBindingValidationTest {
Compilation compilation = daggerCompiler().compile(component);
assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
assertThat(compilation)
.hadErrorContaining(
message(
- "[Dagger/MissingBinding] java.lang.String cannot be provided without an @Inject "
- + "constructor or an @Provides-annotated method.",
- " java.lang.String is provided at",
- " test.TestComponent.string1()",
+ "\033[1;31m[Dagger/MissingBinding]\033[0m String cannot be provided without an "
+ + "@Inject constructor or an @Provides-annotated method.",
+ " String is requested at",
+ " TestComponent.string1()",
"The following other entry points also depend on it:",
- " test.TestComponent.string2()",
- " test.TestComponent.string3()",
- " test.TestComponent.string4()",
- " test.TestComponent.string5()",
- " test.TestComponent.string6()",
- " test.TestComponent.string7()",
- " test.TestComponent.string8()",
- " test.TestComponent.string9()",
- " test.TestComponent.string10()",
- " test.TestComponent.string11()",
+ " TestComponent.string2()",
+ " TestComponent.string3()",
+ " TestComponent.string4()",
+ " TestComponent.string5()",
+ " TestComponent.string6()",
+ " TestComponent.string7()",
+ " TestComponent.string8()",
+ " TestComponent.string9()",
+ " TestComponent.string10()",
+ " TestComponent.string11()",
" and 1 other"))
.inFile(component)
.onLineContaining("interface TestComponent");
}
+
+ @Test
+ public void missingBindingInAllComponentsAndEntryPoints() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "Parent",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface Parent {",
+ " Foo foo();",
+ " Bar bar();",
+ " Child child();",
+ "}");
+ JavaFileObject child =
+ JavaFileObjects.forSourceLines(
+ "Child",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Child {",
+ " Foo foo();",
+ " Baz baz();",
+ "}");
+ JavaFileObject foo =
+ JavaFileObjects.forSourceLines(
+ "Foo",
+ "import javax.inject.Inject;",
+ "",
+ "class Foo {",
+ " @Inject Foo(Bar bar) {}",
+ "}");
+ JavaFileObject bar =
+ JavaFileObjects.forSourceLines(
+ "Bar",
+ "import javax.inject.Inject;",
+ "",
+ "class Bar {",
+ " @Inject Bar(Baz baz) {}",
+ "}");
+ JavaFileObject baz = JavaFileObjects.forSourceLines("Baz", "class Baz {}");
+
+ Compilation compilation = daggerCompiler().compile(parent, child, foo, bar, baz);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "\033[1;31m[Dagger/MissingBinding]\033[0m Baz cannot be provided without an "
+ + "@Inject constructor or an @Provides-annotated method.",
+ " Baz is injected at",
+ " Bar(baz)",
+ " Bar is requested at",
+ " Parent.bar()",
+ "The following other entry points also depend on it:",
+ " Parent.foo()",
+ " Child.foo() [Parent → Child]",
+ " Child.baz() [Parent → Child]"))
+ .inFile(parent)
+ .onLineContaining("interface Parent");
+ }
+
+ // Regression test for b/147423208 where if the same subcomponent was used
+ // in two different parts of the hierarchy and only one side had a missing binding
+ // incorrect caching during binding graph conversion might cause validation to pass
+ // incorrectly.
+ @Test
+ public void sameSubcomponentUsedInDifferentHierarchies() {
+ JavaFileObject parent = JavaFileObjects.forSourceLines("test.Parent",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@Component",
+ "interface Parent {",
+ " Child1 getChild1();",
+ " Child2 getChild2();",
+ "}");
+ JavaFileObject child1 = JavaFileObjects.forSourceLines("test.Child1",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent(modules = LongModule.class)",
+ "interface Child1 {",
+ " RepeatedSub getSub();",
+ "}");
+ JavaFileObject child2 = JavaFileObjects.forSourceLines("test.Child2",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface Child2 {",
+ " RepeatedSub getSub();",
+ "}");
+ JavaFileObject repeatedSub = JavaFileObjects.forSourceLines("test.RepeatedSub",
+ "package test;",
+ "",
+ "import dagger.Subcomponent;",
+ "",
+ "@Subcomponent",
+ "interface RepeatedSub {",
+ " Foo getFoo();",
+ "}");
+ JavaFileObject injectable = JavaFileObjects.forSourceLines("test.Foo",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class Foo {",
+ " @Inject Foo(Long value) {}",
+ "}");
+ JavaFileObject module = JavaFileObjects.forSourceLines("test.LongModule",
+ "package test;",
+ "",
+ "import dagger.Module;",
+ "import dagger.Provides;",
+ "",
+ "@Module",
+ "interface LongModule {",
+ " @Provides static Long provideLong() {",
+ " return 0L;",
+ " }",
+ "}");
+ Compilation compilation = daggerCompiler().compile(
+ parent, child1, child2, repeatedSub, injectable, module);
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining("Long cannot be provided without an @Inject constructor")
+ .inFile(parent)
+ .onLineContaining("interface Parent");
+ }
+
}
diff --git a/javatests/dagger/internal/codegen/ModuleFactoryGeneratorTest.java b/javatests/dagger/internal/codegen/ModuleFactoryGeneratorTest.java
index 50c41edb0..cbda1b8a5 100644
--- a/javatests/dagger/internal/codegen/ModuleFactoryGeneratorTest.java
+++ b/javatests/dagger/internal/codegen/ModuleFactoryGeneratorTest.java
@@ -24,9 +24,8 @@ import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
import static dagger.internal.codegen.Compilers.daggerCompiler;
import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatMethodInUnannotatedClass;
import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatModuleMethod;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
-import static dagger.internal.codegen.GeneratedLines.NPE_FROM_PROVIDES_METHOD;
import com.google.common.collect.ImmutableList;
import com.google.testing.compile.Compilation;
@@ -229,7 +228,7 @@ public class ModuleFactoryGeneratorTest {
"import dagger.internal.Preconditions;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class TestModule_ProvideStringFactory implements Factory<String> {",
" private final TestModule module;",
"",
@@ -246,8 +245,7 @@ public class ModuleFactoryGeneratorTest {
" }",
"",
" public static String provideString(TestModule instance) {",
- " return Preconditions.checkNotNull(",
- " instance.provideString(), " + NPE_FROM_PROVIDES_METHOD + ");",
+ " return Preconditions.checkNotNullFromProvides(instance.provideString());",
" }",
"}");
assertAbout(javaSource()).that(moduleFile)
@@ -277,7 +275,7 @@ public class ModuleFactoryGeneratorTest {
"import dagger.internal.Factory;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class TestModule_ProvideStringFactory implements Factory<String> {",
" private final TestModule module;",
"",
@@ -323,7 +321,7 @@ public class ModuleFactoryGeneratorTest {
"import dagger.internal.Factory;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class TestModule_ProvideStringFactory implements Factory<String> {",
" private final TestModule module;",
"",
@@ -397,7 +395,7 @@ public class ModuleFactoryGeneratorTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class TestModule_ProvideObjectsFactory",
" implements Factory<List<Object>> {",
" private final TestModule module;",
@@ -432,8 +430,8 @@ public class ModuleFactoryGeneratorTest {
"",
" public static List<Object> provideObjects(",
" TestModule instance, Object a, Object b, MembersInjector<X> xInjector) {",
- " return Preconditions.checkNotNull(",
- " instance.provideObjects(a, b, xInjector), " + NPE_FROM_PROVIDES_METHOD + ");",
+ " return Preconditions.checkNotNullFromProvides(",
+ " instance.provideObjects(a, b, xInjector));",
" }",
"}");
assertAbout(javaSources()).that(
@@ -467,7 +465,7 @@ public class ModuleFactoryGeneratorTest {
"import dagger.internal.Preconditions;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class TestModule_ProvideStringFactory implements Factory<String> {",
" private final TestModule module;",
"",
@@ -484,9 +482,7 @@ public class ModuleFactoryGeneratorTest {
" }",
"",
" public static String provideString(TestModule instance) {",
- " return Preconditions.checkNotNull(instance.provideString(), "
- + NPE_FROM_PROVIDES_METHOD
- + ");",
+ " return Preconditions.checkNotNullFromProvides(instance.provideString());",
" }",
"}");
assertAbout(javaSource()).that(moduleFile)
@@ -522,7 +518,7 @@ public class ModuleFactoryGeneratorTest {
"import java.util.List;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class TestModule_ProvideWildcardListFactory implements "
+ "Factory<List<List<?>>> {",
" private final TestModule module;",
@@ -540,8 +536,8 @@ public class ModuleFactoryGeneratorTest {
" }",
"",
" public static List<List<?>> provideWildcardList(TestModule instance) {",
- " return Preconditions.checkNotNull(",
- " instance.provideWildcardList(), " + NPE_FROM_PROVIDES_METHOD + ");",
+ " return Preconditions.checkNotNullFromProvides(",
+ " instance.provideWildcardList());",
" }",
"}");
assertAbout(javaSource()).that(moduleFile)
@@ -575,7 +571,7 @@ public class ModuleFactoryGeneratorTest {
"import java.util.Set;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class TestModule_ProvideStringsFactory implements Factory<Set<String>> {",
" private final TestModule module;",
"",
@@ -592,8 +588,8 @@ public class ModuleFactoryGeneratorTest {
" }",
"",
" public static Set<String> provideStrings(TestModule instance) {",
- " return Preconditions.checkNotNull(",
- " instance.provideStrings(), " + NPE_FROM_PROVIDES_METHOD + ");",
+ " return Preconditions.checkNotNullFromProvides(",
+ " instance.provideStrings());",
" }",
"}");
assertAbout(javaSource()).that(moduleFile)
@@ -724,6 +720,7 @@ public class ModuleFactoryGeneratorTest {
.onLine(6);
}
+
@Test
public void enclosedInPrivateModule() {
JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.Enclosing",
@@ -886,7 +883,7 @@ public class ModuleFactoryGeneratorTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class ParentModule_ProvideListBFactory<A extends CharSequence,",
" B, C extends Number & Comparable<C>> implements Factory<List<B>> {",
" private final ParentModule<A, B, C> module;",
@@ -911,8 +908,7 @@ public class ModuleFactoryGeneratorTest {
"",
" public static <A extends CharSequence, B, C extends Number & Comparable<C>> List<B>",
" provideListB(ParentModule<A, B, C> instance, B b) {",
- " return Preconditions.checkNotNull(",
- " instance.provideListB(b), " + NPE_FROM_PROVIDES_METHOD + ");",
+ " return Preconditions.checkNotNullFromProvides(instance.provideListB(b));",
" }",
"}");
JavaFileObject bElementFactory =
@@ -925,7 +921,7 @@ public class ModuleFactoryGeneratorTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class ParentModule_ProvideBElementFactory<A extends CharSequence,",
" B, C extends Number & Comparable<C>> implements Factory<B> {",
" private final ParentModule<A, B, C> module;",
@@ -951,8 +947,7 @@ public class ModuleFactoryGeneratorTest {
" public static <A extends CharSequence, B, C extends Number & Comparable<C>>",
" B provideBElement(",
" ParentModule<A, B, C> instance, B b) {",
- " return Preconditions.checkNotNull(",
- " instance.provideBElement(b), " + NPE_FROM_PROVIDES_METHOD + ");",
+ " return Preconditions.checkNotNullFromProvides(instance.provideBElement(b));",
" }",
"}");
JavaFileObject bEntryFactory =
@@ -965,7 +960,7 @@ public class ModuleFactoryGeneratorTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class ParentModule_ProvideBEntryFactory<A extends CharSequence,",
" B, C extends Number & Comparable<C>> implements Factory<B>> {",
" private final ParentModule<A, B, C> module;",
@@ -991,8 +986,7 @@ public class ModuleFactoryGeneratorTest {
" public static <A extends CharSequence, B, C extends Number & Comparable<C>>",
" B provideBEntry(",
" ParentModule<A, B, C> instance, B b) {",
- " return Preconditions.checkNotNull(",
- " instance.provideBEntry(b), " + NPE_FROM_PROVIDES_METHOD + ");",
+ " return Preconditions.checkNotNullFromProvides(instance.provideBEntry(b));",
" }",
"}");
JavaFileObject numberFactory =
@@ -1004,7 +998,7 @@ public class ModuleFactoryGeneratorTest {
"import dagger.internal.Preconditions;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class ChildNumberModule_ProvideNumberFactory",
" implements Factory<Number> {",
" private final ChildNumberModule module;",
@@ -1024,8 +1018,7 @@ public class ModuleFactoryGeneratorTest {
" }",
"",
" public static Number provideNumber(ChildNumberModule instance) {",
- " return Preconditions.checkNotNull(",
- " instance.provideNumber(), " + NPE_FROM_PROVIDES_METHOD + ");",
+ " return Preconditions.checkNotNullFromProvides(instance.provideNumber());",
" }",
"}");
JavaFileObject integerFactory =
@@ -1037,7 +1030,7 @@ public class ModuleFactoryGeneratorTest {
"import dagger.internal.Preconditions;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class ChildIntegerModule_ProvideIntegerFactory",
" implements Factory<Integer> {",
" private final ChildIntegerModule module;",
@@ -1057,8 +1050,8 @@ public class ModuleFactoryGeneratorTest {
" }",
"",
" public static Integer provideInteger(ChildIntegerModule instance) {",
- " return Preconditions.checkNotNull(",
- " instance.provideInteger(), " + NPE_FROM_PROVIDES_METHOD + ");",
+ " return Preconditions.checkNotNullFromProvides(",
+ " instance.provideInteger());",
" }",
"}");
assertAbout(javaSources())
@@ -1111,24 +1104,26 @@ public class ModuleFactoryGeneratorTest {
"import java.util.Map;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class ParameterizedModule_ProvideMapStringNumberFactory",
" implements Factory<Map<String, Number>> {",
- " private static final ParameterizedModule_ProvideMapStringNumberFactory INSTANCE =",
- " new ParameterizedModule_ProvideMapStringNumberFactory();",
- "",
" @Override",
" public Map<String, Number> get() {",
" return provideMapStringNumber();",
" }",
"",
" public static ParameterizedModule_ProvideMapStringNumberFactory create() {",
- " return INSTANCE;",
+ " return InstanceHolder.INSTANCE;",
" }",
"",
" public static Map<String, Number> provideMapStringNumber() {",
- " return Preconditions.checkNotNull(ParameterizedModule.provideMapStringNumber(),",
- " " + NPE_FROM_PROVIDES_METHOD + ");",
+ " return Preconditions.checkNotNullFromProvides(",
+ " ParameterizedModule.provideMapStringNumber());",
+ " }",
+ "",
+ " private static final class InstanceHolder {",
+ " private static final ParameterizedModule_ProvideMapStringNumberFactory INSTANCE =",
+ " new ParameterizedModule_ProvideMapStringNumberFactory();",
" }",
"}");
@@ -1141,24 +1136,26 @@ public class ModuleFactoryGeneratorTest {
"import dagger.internal.Preconditions;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class ParameterizedModule_ProvideNonGenericTypeFactory",
" implements Factory<Object> {",
- " private static final ParameterizedModule_ProvideNonGenericTypeFactory INSTANCE = ",
- " new ParameterizedModule_ProvideNonGenericTypeFactory();",
- "",
" @Override",
" public Object get() {",
" return provideNonGenericType();",
" }",
"",
" public static ParameterizedModule_ProvideNonGenericTypeFactory create() {",
- " return INSTANCE;",
+ " return InstanceHolder.INSTANCE;",
" }",
"",
" public static Object provideNonGenericType() {",
- " return Preconditions.checkNotNull(ParameterizedModule.provideNonGenericType(),",
- " " + NPE_FROM_PROVIDES_METHOD + ");",
+ " return Preconditions.checkNotNullFromProvides(",
+ " ParameterizedModule.provideNonGenericType());",
+ " }",
+ "",
+ " private static final class InstanceHolder {",
+ " private static final ParameterizedModule_ProvideNonGenericTypeFactory INSTANCE =",
+ " new ParameterizedModule_ProvideNonGenericTypeFactory();",
" }",
"}");
@@ -1172,7 +1169,7 @@ public class ModuleFactoryGeneratorTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class ParameterizedModule_ProvideNonGenericTypeWithDepsFactory",
" implements Factory<String> {",
" private final Provider<Object> oProvider;",
@@ -1193,9 +1190,8 @@ public class ModuleFactoryGeneratorTest {
" }",
"",
" public static String provideNonGenericTypeWithDeps(Object o) {",
- " return Preconditions.checkNotNull(",
- " ParameterizedModule.provideNonGenericTypeWithDeps(o),",
- " " + NPE_FROM_PROVIDES_METHOD + ");",
+ " return Preconditions.checkNotNullFromProvides(",
+ " ParameterizedModule.provideNonGenericTypeWithDeps(o));",
" }",
"}");
@@ -1410,7 +1406,7 @@ public class ModuleFactoryGeneratorTest {
"test.TestModule_GetFactory",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class TestModule_GetFactory implements Factory<Integer> {",
" @Override",
" public Integer get() {",
@@ -1418,7 +1414,7 @@ public class ModuleFactoryGeneratorTest {
" }",
"",
" public static TestModule_GetFactory create() {",
- " return INSTANCE;",
+ " return InstanceHolder.INSTANCE;",
" }",
"",
" public static int proxyGet() {",
@@ -1433,7 +1429,7 @@ public class ModuleFactoryGeneratorTest {
"test.TestModule_CreateFactory",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"public final class TestModule_CreateFactory implements Factory<Boolean> {",
" @Override",
" public Boolean get() {",
@@ -1441,7 +1437,7 @@ public class ModuleFactoryGeneratorTest {
" }",
"",
" public static TestModule_CreateFactory create() {",
- " return INSTANCE;",
+ " return InstanceHolder.INSTANCE;",
" }",
"",
" public static boolean proxyCreate() {",
diff --git a/javatests/dagger/internal/codegen/ModuleValidationTest.java b/javatests/dagger/internal/codegen/ModuleValidationTest.java
index 649649ad8..162c8c022 100644
--- a/javatests/dagger/internal/codegen/ModuleValidationTest.java
+++ b/javatests/dagger/internal/codegen/ModuleValidationTest.java
@@ -342,10 +342,18 @@ public final class ModuleValidationTest {
"",
"@Module(includes = BadModule.class)",
"abstract class IncludesBadModule {}");
- assertThat(daggerCompiler().compile(badModule, module))
+ Compilation compilation = daggerCompiler().compile(badModule, module);
+ assertThat(compilation).hadErrorCount(2);
+ assertThat(compilation)
.hadErrorContaining("test.BadModule has errors")
.inFile(module)
.onLine(5);
+ assertThat(compilation)
+ .hadErrorContaining(
+ "@Binds methods must have exactly one parameter, whose type is assignable to the "
+ + "return type")
+ .inFile(badModule)
+ .onLine(8);
}
@Test
diff --git a/javatests/dagger/internal/codegen/MultibindingTest.java b/javatests/dagger/internal/codegen/MultibindingTest.java
index 2bf949470..152adb001 100644
--- a/javatests/dagger/internal/codegen/MultibindingTest.java
+++ b/javatests/dagger/internal/codegen/MultibindingTest.java
@@ -131,7 +131,7 @@ public class MultibindingTest {
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
- "java.util.Map<java.lang.String,java.lang.String> "
+ "Map<String,String> "
+ "cannot be provided without an @Provides-annotated method")
.inFile(component)
.onLineContaining("interface TestComponent");
@@ -183,7 +183,7 @@ public class MultibindingTest {
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
- "java.util.Set<dagger.producers.Produced<java.lang.String>> "
+ "Set<Produced<String>> "
+ "cannot be provided without an @Provides- or @Produces-annotated method")
.inFile(component)
.onLineContaining("interface TestComponent");
diff --git a/javatests/dagger/internal/codegen/NullableBindingValidationTest.java b/javatests/dagger/internal/codegen/NullableBindingValidationTest.java
deleted file mode 100644
index 24d563643..000000000
--- a/javatests/dagger/internal/codegen/NullableBindingValidationTest.java
+++ /dev/null
@@ -1,419 +0,0 @@
-/*
- * Copyright (C) 2018 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.testing.compile.CompilationSubject.assertThat;
-import static com.google.testing.compile.Compiler.javac;
-import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.NullableBindingValidator.nullableToNonNullable;
-
-import com.google.testing.compile.Compilation;
-import com.google.testing.compile.JavaFileObjects;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class NullableBindingValidationTest {
- private static final JavaFileObject NULLABLE =
- JavaFileObjects.forSourceLines(
- "test.Nullable", // force one-string-per-line format
- "package test;",
- "",
- "public @interface Nullable {}");
-
- @Test public void nullCheckForConstructorParameters() {
- JavaFileObject a = JavaFileObjects.forSourceLines("test.A",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class A {",
- " @Inject A(String string) {}",
- "}");
- JavaFileObject module = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "",
- "@dagger.Module",
- "final class TestModule {",
- " @Nullable @Provides String provideString() { return null; }",
- "}");
- JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = TestModule.class)",
- "interface TestComponent {",
- " A a();",
- "}");
- Compilation compilation = daggerCompiler().compile(NULLABLE, a, module, component);
- assertThat(compilation).failed();
- assertThat(compilation)
- .hadErrorContaining(
- nullableToNonNullable(
- "java.lang.String",
- "@test.Nullable @Provides String test.TestModule.provideString()"));
-
- // but if we disable the validation, then it compiles fine.
- Compilation compilation2 =
- javac()
- .withOptions("-Adagger.nullableValidation=WARNING")
- .withProcessors(new ComponentProcessor())
- .compile(NULLABLE, a, module, component);
- assertThat(compilation2).succeeded();
- }
-
- @Test public void nullCheckForMembersInjectParam() {
- JavaFileObject a = JavaFileObjects.forSourceLines("test.A",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class A {",
- " @Inject A() {}",
- " @Inject void register(String string) {}",
- "}");
- JavaFileObject module = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "",
- "@dagger.Module",
- "final class TestModule {",
- " @Nullable @Provides String provideString() { return null; }",
- "}");
- JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = TestModule.class)",
- "interface TestComponent {",
- " A a();",
- "}");
- Compilation compilation = daggerCompiler().compile(NULLABLE, a, module, component);
- assertThat(compilation).failed();
- assertThat(compilation)
- .hadErrorContaining(
- nullableToNonNullable(
- "java.lang.String",
- "@test.Nullable @Provides String test.TestModule.provideString()"));
-
- // but if we disable the validation, then it compiles fine.
- Compilation compilation2 =
- javac()
- .withOptions("-Adagger.nullableValidation=WARNING")
- .withProcessors(new ComponentProcessor())
- .compile(NULLABLE, a, module, component);
- assertThat(compilation2).succeeded();
- }
-
- @Test public void nullCheckForVariable() {
- JavaFileObject a = JavaFileObjects.forSourceLines("test.A",
- "package test;",
- "",
- "import javax.inject.Inject;",
- "",
- "final class A {",
- " @Inject String string;",
- " @Inject A() {}",
- "}");
- JavaFileObject module = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "",
- "@dagger.Module",
- "final class TestModule {",
- " @Nullable @Provides String provideString() { return null; }",
- "}");
- JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = TestModule.class)",
- "interface TestComponent {",
- " A a();",
- "}");
- Compilation compilation = daggerCompiler().compile(NULLABLE, a, module, component);
- assertThat(compilation).failed();
- assertThat(compilation)
- .hadErrorContaining(
- nullableToNonNullable(
- "java.lang.String",
- "@test.Nullable @Provides String test.TestModule.provideString()"));
-
- // but if we disable the validation, then it compiles fine.
- Compilation compilation2 =
- javac()
- .withOptions("-Adagger.nullableValidation=WARNING")
- .withProcessors(new ComponentProcessor())
- .compile(NULLABLE, a, module, component);
- assertThat(compilation2).succeeded();
- }
-
- @Test public void nullCheckForComponentReturn() {
- JavaFileObject module = JavaFileObjects.forSourceLines("test.TestModule",
- "package test;",
- "",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "",
- "@dagger.Module",
- "final class TestModule {",
- " @Nullable @Provides String provideString() { return null; }",
- "}");
- JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = TestModule.class)",
- "interface TestComponent {",
- " String string();",
- "}");
- Compilation compilation = daggerCompiler().compile(NULLABLE, module, component);
- assertThat(compilation).failed();
- assertThat(compilation)
- .hadErrorContaining(
- nullableToNonNullable(
- "java.lang.String",
- "@test.Nullable @Provides String test.TestModule.provideString()"));
-
- // but if we disable the validation, then it compiles fine.
- Compilation compilation2 =
- javac()
- .withOptions("-Adagger.nullableValidation=WARNING")
- .withProcessors(new ComponentProcessor())
- .compile(NULLABLE, module, component);
- assertThat(compilation2).succeeded();
- }
-
- @Test
- public void nullCheckForOptionalInstance() {
- JavaFileObject a =
- JavaFileObjects.forSourceLines(
- "test.A",
- "package test;",
- "",
- "import com.google.common.base.Optional;",
- "import javax.inject.Inject;",
- "",
- "final class A {",
- " @Inject A(Optional<String> optional) {}",
- "}");
- JavaFileObject module =
- JavaFileObjects.forSourceLines(
- "test.TestModule",
- "package test;",
- "",
- "import dagger.BindsOptionalOf;",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "",
- "@dagger.Module",
- "abstract class TestModule {",
- " @Nullable @Provides static String provideString() { return null; }",
- " @BindsOptionalOf abstract String optionalString();",
- "}");
- JavaFileObject component =
- JavaFileObjects.forSourceLines(
- "test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = TestModule.class)",
- "interface TestComponent {",
- " A a();",
- "}");
- Compilation compilation = daggerCompiler().compile(NULLABLE, a, module, component);
- assertThat(compilation).failed();
- assertThat(compilation)
- .hadErrorContaining(
- nullableToNonNullable(
- "java.lang.String",
- "@test.Nullable @Provides String test.TestModule.provideString()"));
- }
-
- @Test
- public void nullCheckForOptionalProvider() {
- JavaFileObject a =
- JavaFileObjects.forSourceLines(
- "test.A",
- "package test;",
- "",
- "import com.google.common.base.Optional;",
- "import javax.inject.Inject;",
- "import javax.inject.Provider;",
- "",
- "final class A {",
- " @Inject A(Optional<Provider<String>> optional) {}",
- "}");
- JavaFileObject module =
- JavaFileObjects.forSourceLines(
- "test.TestModule",
- "package test;",
- "",
- "import dagger.BindsOptionalOf;",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "",
- "@dagger.Module",
- "abstract class TestModule {",
- " @Nullable @Provides static String provideString() { return null; }",
- " @BindsOptionalOf abstract String optionalString();",
- "}");
- JavaFileObject component =
- JavaFileObjects.forSourceLines(
- "test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = TestModule.class)",
- "interface TestComponent {",
- " A a();",
- "}");
- Compilation compilation = daggerCompiler().compile(NULLABLE, a, module, component);
- assertThat(compilation).succeeded();
- }
-
- @Test
- public void nullCheckForOptionalLazy() {
- JavaFileObject a =
- JavaFileObjects.forSourceLines(
- "test.A",
- "package test;",
- "",
- "import com.google.common.base.Optional;",
- "import dagger.Lazy;",
- "import javax.inject.Inject;",
- "",
- "final class A {",
- " @Inject A(Optional<Lazy<String>> optional) {}",
- "}");
- JavaFileObject module =
- JavaFileObjects.forSourceLines(
- "test.TestModule",
- "package test;",
- "",
- "import dagger.BindsOptionalOf;",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "",
- "@dagger.Module",
- "abstract class TestModule {",
- " @Nullable @Provides static String provideString() { return null; }",
- " @BindsOptionalOf abstract String optionalString();",
- "}");
- JavaFileObject component =
- JavaFileObjects.forSourceLines(
- "test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = TestModule.class)",
- "interface TestComponent {",
- " A a();",
- "}");
- Compilation compilation = daggerCompiler().compile(NULLABLE, a, module, component);
- assertThat(compilation).succeeded();
- }
-
- @Test
- public void nullCheckForOptionalProviderOfLazy() {
- JavaFileObject a =
- JavaFileObjects.forSourceLines(
- "test.A",
- "package test;",
- "",
- "import com.google.common.base.Optional;",
- "import dagger.Lazy;",
- "import javax.inject.Inject;",
- "import javax.inject.Provider;",
- "",
- "final class A {",
- " @Inject A(Optional<Provider<Lazy<String>>> optional) {}",
- "}");
- JavaFileObject module =
- JavaFileObjects.forSourceLines(
- "test.TestModule",
- "package test;",
- "",
- "import dagger.BindsOptionalOf;",
- "import dagger.Provides;",
- "import javax.inject.Inject;",
- "",
- "@dagger.Module",
- "abstract class TestModule {",
- " @Nullable @Provides static String provideString() { return null; }",
- " @BindsOptionalOf abstract String optionalString();",
- "}");
- JavaFileObject component =
- JavaFileObjects.forSourceLines(
- "test.TestComponent",
- "package test;",
- "",
- "import dagger.Component;",
- "",
- "@Component(modules = TestModule.class)",
- "interface TestComponent {",
- " A a();",
- "}");
- Compilation compilation = daggerCompiler().compile(NULLABLE, a, module, component);
- assertThat(compilation).succeeded();
- }
-
- @Test
- public void moduleValidation() {
- JavaFileObject module =
- JavaFileObjects.forSourceLines(
- "test.TestModule",
- "package test;",
- "",
- "import dagger.Binds;",
- "import dagger.Module;",
- "import dagger.Provides;",
- "",
- "@Module",
- "abstract class TestModule {",
- " @Provides @Nullable static String nullableString() { return null; }",
- " @Binds abstract Object object(String string);",
- "}");
-
- Compilation compilation =
- daggerCompiler()
- .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
- .compile(module, NULLABLE);
- assertThat(compilation).failed();
- assertThat(compilation)
- .hadErrorContaining(
- nullableToNonNullable(
- "java.lang.String",
- "@Provides @test.Nullable String test.TestModule.nullableString()"));
- }
-}
diff --git a/javatests/dagger/internal/codegen/OptionalBindingRequestFulfillmentTest.java b/javatests/dagger/internal/codegen/OptionalBindingRequestFulfillmentTest.java
index b76516616..60b6898c1 100644
--- a/javatests/dagger/internal/codegen/OptionalBindingRequestFulfillmentTest.java
+++ b/javatests/dagger/internal/codegen/OptionalBindingRequestFulfillmentTest.java
@@ -20,7 +20,7 @@ import static com.google.testing.compile.CompilationSubject.assertThat;
import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
import static dagger.internal.codegen.Compilers.compilerWithOptions;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
@@ -110,13 +110,13 @@ public class OptionalBindingRequestFulfillmentTest {
"",
"import com.google.common.base.Optional;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {")
.addLinesIn(
FAST_INIT_MODE,
" private volatile Provider<Maybe> provideMaybeProvider;",
"",
- " private Provider<Maybe> getMaybeProvider() {",
+ " private Provider<Maybe> maybeProvider() {",
" Object local = provideMaybeProvider;",
" if (local == null) {",
" local = new SwitchingProvider<>(0);",
@@ -139,7 +139,7 @@ public class OptionalBindingRequestFulfillmentTest {
" Maybe_MaybeModule_ProvideMaybeFactory.create()));")
.addLinesIn(
FAST_INIT_MODE, //
- " getMaybeProvider()));")
+ " maybeProvider()));")
.addLines(
" }",
"",
@@ -250,7 +250,7 @@ public class OptionalBindingRequestFulfillmentTest {
"import com.google.common.base.Optional;",
"import dagger.producers.internal.CancellationListener;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent, CancellationListener {",
" @Override",
" public ListenableFuture<Optional<Maybe>> maybe() {",
@@ -261,7 +261,6 @@ public class OptionalBindingRequestFulfillmentTest {
" @Override",
" public ListenableFuture<Optional<DefinitelyNot>> definitelyNot() {",
" return Futures.immediateFuture(Optional.<DefinitelyNot>absent());",
-
" }",
"",
" @Override",
diff --git a/javatests/dagger/internal/codegen/OptionalBindingTest.java b/javatests/dagger/internal/codegen/OptionalBindingTest.java
index 175510142..4e158efd7 100644
--- a/javatests/dagger/internal/codegen/OptionalBindingTest.java
+++ b/javatests/dagger/internal/codegen/OptionalBindingTest.java
@@ -89,7 +89,7 @@ public class OptionalBindingTest {
Compilation compilation = daggerCompiler().compile(parent, parentModule, child, childModule);
assertThat(compilation).failed();
assertThat(compilation)
- .hadErrorContaining("Optional<java.lang.String> is bound multiple times")
+ .hadErrorContaining("Optional<String> is bound multiple times")
.inFile(parent)
.onLineContaining("interface Parent");
}
diff --git a/javatests/dagger/internal/codegen/PluginsVisitFullBindingGraphTest.java b/javatests/dagger/internal/codegen/PluginsVisitFullBindingGraphTest.java
new file mode 100644
index 000000000..b7664639c
--- /dev/null
+++ b/javatests/dagger/internal/codegen/PluginsVisitFullBindingGraphTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dagger.internal.codegen;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.Compiler.javac;
+import static dagger.internal.codegen.TestUtils.endsWithMessage;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.Compiler;
+import com.google.testing.compile.JavaFileObjects;
+import dagger.model.BindingGraph;
+import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
+import java.util.regex.Pattern;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for -Adagger.pluginsVisitFullBindingGraph. */
+@RunWith(JUnit4.class)
+public final class PluginsVisitFullBindingGraphTest {
+ private static final JavaFileObject MODULE_WITHOUT_ERRORS =
+ JavaFileObjects.forSourceLines(
+ "test.ModuleWithoutErrors",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "interface ModuleWithoutErrors {",
+ " @Binds Object object(String string);",
+ "}");
+
+ private static final JavaFileObject MODULE_WITH_ERRORS =
+ JavaFileObjects.forSourceLines(
+ "test.ModuleWithErrors",
+ "package test;",
+ "",
+ "import dagger.Binds;",
+ "import dagger.Module;",
+ "",
+ "@Module",
+ "interface ModuleWithErrors {",
+ " @Binds Object object1(String string);",
+ " @Binds Object object2(Long l);",
+ "}");
+
+ private static final Pattern PLUGIN_ERROR_MESSAGE =
+ endsWithMessage(
+ "[dagger.internal.codegen.PluginsVisitFullBindingGraphTest.ErrorPlugin] Error!");
+
+ @Test
+ public void testNoFlags() {
+ Compilation compilation = daggerCompiler().compile(MODULE_WITH_ERRORS);
+ assertThat(compilation).succeeded();
+ }
+
+ @Test
+ public void testWithVisitPlugins() {
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.pluginsVisitFullBindingGraphs=Enabled")
+ .compile(MODULE_WITH_ERRORS);
+
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContainingMatch(PLUGIN_ERROR_MESSAGE)
+ .inFile(MODULE_WITH_ERRORS)
+ .onLineContaining("interface ModuleWithErrors");
+ }
+
+ @Test
+ public void testWithValidationNone() {
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.fullBindingGraphValidation=NONE")
+ .compile(MODULE_WITHOUT_ERRORS);
+ assertThat(compilation).succeeded();
+ }
+
+ @Test
+ public void testWithValidationError() {
+ // Test that pluginsVisitFullBindingGraph is enabled with fullBindingGraphValidation.
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ .compile(MODULE_WITHOUT_ERRORS);
+
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContainingMatch(PLUGIN_ERROR_MESSAGE)
+ .inFile(MODULE_WITHOUT_ERRORS)
+ .onLineContaining("interface ModuleWithoutErrors");
+ }
+
+ @Test
+ public void testWithValidationErrorAndVisitPlugins() {
+ Compilation compilation =
+ daggerCompiler()
+ .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ .withOptions("-Adagger.pluginsVisitFullBindingGraphs=Enabled")
+ .compile(MODULE_WITHOUT_ERRORS);
+
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContainingMatch(PLUGIN_ERROR_MESSAGE)
+ .inFile(MODULE_WITHOUT_ERRORS)
+ .onLineContaining("interface ModuleWithoutErrors");
+ }
+
+ /** A test plugin that just reports each component with the given {@link Diagnostic.Kind}. */
+ private static final class ErrorPlugin implements BindingGraphPlugin {
+ @Override
+ public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+ diagnosticReporter.reportComponent(ERROR, bindingGraph.rootComponentNode(), "Error!");
+ }
+ }
+
+ private static Compiler daggerCompiler() {
+ return javac().withProcessors(ComponentProcessor.forTesting(new ErrorPlugin()));
+ }
+}
diff --git a/javatests/dagger/internal/codegen/ProducerModuleFactoryGeneratorTest.java b/javatests/dagger/internal/codegen/ProducerModuleFactoryGeneratorTest.java
index 0b6ef8f58..d41bc2d17 100644
--- a/javatests/dagger/internal/codegen/ProducerModuleFactoryGeneratorTest.java
+++ b/javatests/dagger/internal/codegen/ProducerModuleFactoryGeneratorTest.java
@@ -214,6 +214,7 @@ public class ProducerModuleFactoryGeneratorTest {
.onLine(6);
}
+
@Test
public void enclosedInPrivateModule() {
JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.Enclosing",
@@ -369,8 +370,8 @@ public class ProducerModuleFactoryGeneratorTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- "@SuppressWarnings(\"FutureReturnValueIgnored\")",
GENERATED_ANNOTATION,
+ "@SuppressWarnings({\"FutureReturnValueIgnored\", \"unchecked\", \"rawtypes\"})",
"public final class TestModule_ProduceStringFactory",
" extends AbstractProducesMethodProducer<Void, String> {",
" private final TestModule module;",
@@ -442,8 +443,8 @@ public class ProducerModuleFactoryGeneratorTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- "@SuppressWarnings(\"FutureReturnValueIgnored\")",
GENERATED_ANNOTATION,
+ "@SuppressWarnings({\"FutureReturnValueIgnored\", \"unchecked\", \"rawtypes\"})",
"public final class TestModule_ProduceStringFactory",
" extends AbstractProducesMethodProducer<Void, String> {",
" private final TestModule module;",
diff --git a/javatests/dagger/internal/codegen/ProductionComponentProcessorTest.java b/javatests/dagger/internal/codegen/ProductionComponentProcessorTest.java
index 9a852c779..797fe2ecb 100644
--- a/javatests/dagger/internal/codegen/ProductionComponentProcessorTest.java
+++ b/javatests/dagger/internal/codegen/ProductionComponentProcessorTest.java
@@ -19,8 +19,9 @@ package dagger.internal.codegen;
import static com.google.common.truth.Truth.assertAbout;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
import com.google.testing.compile.Compilation;
@@ -69,7 +70,7 @@ public class ProductionComponentProcessorTest {
" INSTANCE",
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ compilerWithOptions(compilerMode.javacopts()).compile(componentFile);
assertThat(compilation).failed();
assertThat(compilation).hadErrorContaining("interface");
}
@@ -83,7 +84,7 @@ public class ProductionComponentProcessorTest {
"@ProductionComponent",
"@interface NotAComponent {}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ compilerWithOptions(compilerMode.javacopts()).compile(componentFile);
assertThat(compilation).failed();
assertThat(compilation).hadErrorContaining("interface");
}
@@ -97,7 +98,7 @@ public class ProductionComponentProcessorTest {
"@ProductionComponent(modules = Object.class)",
"interface NotAComponent {}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(componentFile);
+ compilerWithOptions(compilerMode.javacopts()).compile(componentFile);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining("is not annotated with one of @Module, @ProducerModule");
@@ -161,17 +162,16 @@ public class ProductionComponentProcessorTest {
.compile(moduleFile, producerModuleFile, componentFile);
assertThat(compilation).failed();
assertThat(compilation)
- .hadErrorContaining("java.lang.String may not depend on the production executor")
+ .hadErrorContaining("String may not depend on the production executor")
.inFile(componentFile)
.onLineContaining("interface SimpleComponent");
compilation =
- daggerCompiler()
- .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ compilerWithOptions("-Adagger.fullBindingGraphValidation=ERROR")
.compile(producerModuleFile);
assertThat(compilation).failed();
assertThat(compilation)
- .hadErrorContaining("java.lang.String may not depend on the production executor")
+ .hadErrorContaining("String may not depend on the production executor")
.inFile(producerModuleFile)
.onLineContaining("class SimpleModule");
// TODO(dpb): Report at the binding if enclosed in the module.
@@ -248,7 +248,7 @@ public class ProductionComponentProcessorTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestClass_SimpleComponent",
" implements TestClass.SimpleComponent, CancellationListener {",
" private final TestClass.BModule bModule;",
@@ -278,7 +278,7 @@ public class ProductionComponentProcessorTest {
" return new Builder().build();",
" }",
"",
- " private Executor getProductionImplementationExecutor() {",
+ " private Executor productionImplementationExecutor() {",
" Object local = productionImplementationExecutor;",
" if (local instanceof MemoizedSentinel) {",
" synchronized (local) {",
@@ -295,7 +295,7 @@ public class ProductionComponentProcessorTest {
" return (Executor) local;",
" }",
"",
- " private Provider<Executor> getProductionImplementationExecutorProvider() {",
+ " private Provider<Executor> productionImplementationExecutorProvider() {",
" Object local = productionImplementationExecutorProvider;",
" if (local == null) {",
" local = new SwitchingProvider<>(0);",
@@ -304,7 +304,7 @@ public class ProductionComponentProcessorTest {
" return (Provider<Executor>) local;",
" }",
"",
- " private ProductionComponentMonitor getProductionComponentMonitor() {",
+ " private ProductionComponentMonitor productionComponentMonitor() {",
" Object local = productionComponentMonitor;",
" if (local instanceof MemoizedSentinel) {",
" synchronized (local) {",
@@ -325,7 +325,7 @@ public class ProductionComponentProcessorTest {
" }",
"",
" private Provider<ProductionComponentMonitor>",
- " getProductionComponentMonitorProvider() {",
+ " productionComponentMonitorProvider() {",
" Object local = monitorProvider;",
" if (local == null) {",
" local = new SwitchingProvider<>(1);",
@@ -334,11 +334,11 @@ public class ProductionComponentProcessorTest {
" return (Provider<ProductionComponentMonitor>) local;",
" }",
"",
- " private TestClass.B getB() {",
+ " private TestClass.B b() {",
" return TestClass_BModule_BFactory.b(bModule, new TestClass.C());",
" }",
"",
- " private Provider<TestClass.B> getBProvider() {",
+ " private Provider<TestClass.B> bProvider() {",
" Object local = bProvider;",
" if (local == null) {",
" local = new SwitchingProvider<>(2);",
@@ -353,12 +353,12 @@ public class ProductionComponentProcessorTest {
" final TestClass.BModule bModuleParam) {",
" this.simpleComponentProvider =",
" InstanceFactory.create((TestClass.SimpleComponent) this);",
- " this.bProducer = Producers.producerFromProvider(getBProvider());",
+ " this.bProducer = Producers.producerFromProvider(bProvider());",
" this.aProducer =",
" TestClass_AModule_AFactory.create(",
" aModuleParam,",
- " getProductionImplementationExecutorProvider(),",
- " getProductionComponentMonitorProvider(),",
+ " productionImplementationExecutorProvider(),",
+ " productionComponentMonitorProvider(),",
" bProducer);",
" this.aEntryPoint = Producers.entryPointViewOf(aProducer, this);",
" }",
@@ -413,11 +413,11 @@ public class ProductionComponentProcessorTest {
" public T get() {",
" switch (id) {",
" case 0: return (T) DaggerTestClass_SimpleComponent.this",
- " .getProductionImplementationExecutor();",
+ " .productionImplementationExecutor();",
" case 1: return (T)",
- " DaggerTestClass_SimpleComponent.this.getProductionComponentMonitor();",
+ " DaggerTestClass_SimpleComponent.this.productionComponentMonitor();",
" case 2: return (T)",
- " DaggerTestClass_SimpleComponent.this.getB();",
+ " DaggerTestClass_SimpleComponent.this.b();",
" default: throw new AssertionError(id);",
" }",
" }",
@@ -443,7 +443,7 @@ public class ProductionComponentProcessorTest {
IMPORT_GENERATED_ANNOTATION,
"import javax.inject.Provider;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestClass_SimpleComponent",
" implements TestClass.SimpleComponent, CancellationListener {",
" private Producer<TestClass.A> aEntryPoint;",
@@ -592,7 +592,7 @@ public class ProductionComponentProcessorTest {
" }",
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(component);
+ compilerWithOptions(compilerMode.javacopts()).compile(component);
assertThat(compilation).succeeded();
assertThat(compilation)
.hadWarningContaining("@Nullable on @Produces methods does not do anything")
@@ -641,8 +641,7 @@ public class ProductionComponentProcessorTest {
" ProductionScoped productionScoped();",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(productionScoped, parent, child);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -651,7 +650,7 @@ public class ProductionComponentProcessorTest {
new JavaFileBuilder(compilerMode, "test.DaggerRoot")
.addLines(
"package test;",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerParent implements Parent, CancellationListener {",
" private final class ChildImpl implements Child, CancellationListener {",
" @Override",
@@ -661,7 +660,7 @@ public class ProductionComponentProcessorTest {
" return DaggerParent.this.productionScopedProvider.get();")
.addLinesIn(
CompilerMode.FAST_INIT_MODE, //
- " return DaggerParent.this.getProductionScoped();")
+ " return DaggerParent.this.productionScoped();")
.addLines(
" }", //
" }", //
diff --git a/javatests/dagger/internal/codegen/ProductionGraphValidationTest.java b/javatests/dagger/internal/codegen/ProductionGraphValidationTest.java
index 8453e03e7..3e416e993 100644
--- a/javatests/dagger/internal/codegen/ProductionGraphValidationTest.java
+++ b/javatests/dagger/internal/codegen/ProductionGraphValidationTest.java
@@ -17,6 +17,7 @@
package dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
import static dagger.internal.codegen.Compilers.daggerCompiler;
import com.google.testing.compile.Compilation;
@@ -77,7 +78,7 @@ public class ProductionGraphValidationTest {
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
- "test.Bar cannot be provided without an @Inject constructor or an @Provides- or "
+ "Bar cannot be provided without an @Inject constructor or an @Provides- or "
+ "@Produces-annotated method.")
.inFile(component)
.onLineContaining("interface MyComponent");
@@ -103,7 +104,7 @@ public class ProductionGraphValidationTest {
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
- "test.TestClass.A cannot be provided without an @Provides- or @Produces-annotated "
+ "TestClass.A cannot be provided without an @Provides- or @Produces-annotated "
+ "method.")
.inFile(component)
.onLineContaining("interface AComponent");
@@ -148,17 +149,16 @@ public class ProductionGraphValidationTest {
Compilation compilation = daggerCompiler().compile(EXECUTOR_MODULE, component);
assertThat(compilation).failed();
assertThat(compilation)
- .hadErrorContaining("test.TestClass.A is a provision, which cannot depend on a production.")
+ .hadErrorContaining("TestClass.A is a provision, which cannot depend on a production.")
.inFile(component)
.onLineContaining("interface AComponent");
compilation =
- daggerCompiler()
- .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ compilerWithOptions("-Adagger.fullBindingGraphValidation=ERROR")
.compile(EXECUTOR_MODULE, component);
assertThat(compilation).failed();
assertThat(compilation)
- .hadErrorContaining("test.TestClass.A is a provision, which cannot depend on a production.")
+ .hadErrorContaining("TestClass.A is a provision, which cannot depend on a production.")
.inFile(component)
.onLineContaining("class AModule");
}
@@ -194,7 +194,7 @@ public class ProductionGraphValidationTest {
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
- "test.TestClass.A is a provision entry-point, which cannot depend on a production.")
+ "TestClass.A is a provision entry-point, which cannot depend on a production.")
.inFile(component)
.onLineContaining("interface AComponent");
}
@@ -252,7 +252,7 @@ public class ProductionGraphValidationTest {
Compilation compilation = daggerCompiler().compile(EXECUTOR_MODULE, component);
assertThat(compilation).failed();
assertThat(compilation)
- .hadErrorContaining("test.TestClass.A is a provision, which cannot depend on a production")
+ .hadErrorContaining("TestClass.A is a provision, which cannot depend on a production")
.inFile(component)
.onLineContaining("interface AComponent");
}
@@ -303,7 +303,7 @@ public class ProductionGraphValidationTest {
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
- "test.TestClass.A cannot be provided without an @Provides-annotated method.")
+ "TestClass.A cannot be provided without an @Provides-annotated method.")
.inFile(component)
.onLineContaining("interface StringComponent");
}
@@ -357,8 +357,8 @@ public class ProductionGraphValidationTest {
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
- "java.util.Set<dagger.producers.monitoring.ProductionComponentMonitor.Factory>"
- + " test.TestClass.MonitoringModule#monitorFactory is a provision,"
+ "Set<ProductionComponentMonitor.Factory>"
+ + " TestClass.MonitoringModule#monitorFactory is a provision,"
+ " which cannot depend on a production.")
.inFile(component)
.onLineContaining("interface StringComponent");
@@ -488,7 +488,7 @@ public class ProductionGraphValidationTest {
Compilation compilation = daggerCompiler().compile(badModule, badComponent);
assertThat(compilation).failed();
assertThat(compilation)
- .hadErrorContaining("test.BadModule has errors")
+ .hadErrorContaining("BadModule has errors")
.inFile(badComponent)
.onLine(7);
}
diff --git a/javatests/dagger/internal/codegen/ScopingValidationTest.java b/javatests/dagger/internal/codegen/ScopingValidationTest.java
index 9efcc2a59..58ab2af44 100644
--- a/javatests/dagger/internal/codegen/ScopingValidationTest.java
+++ b/javatests/dagger/internal/codegen/ScopingValidationTest.java
@@ -17,6 +17,7 @@
package dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
import static dagger.internal.codegen.Compilers.daggerCompiler;
import static dagger.internal.codegen.TestUtils.message;
@@ -76,9 +77,9 @@ public class ScopingValidationTest {
assertThat(compilation)
.hadErrorContaining(
message(
- "test.MyComponent (unscoped) may not reference scoped bindings:",
- " @Singleton class test.ScopedType",
- " @Provides @Singleton String test.ScopedModule.string()"));
+ "MyComponent (unscoped) may not reference scoped bindings:",
+ " @Singleton class ScopedType",
+ " @Provides @Singleton String ScopedModule.string()"));
}
@Test // b/79859714
@@ -157,9 +158,9 @@ public class ScopingValidationTest {
assertThat(compilation)
.hadErrorContaining(
message(
- "test.Parent scoped with @Singleton may not reference bindings with different "
+ "Parent scoped with @Singleton may not reference bindings with different "
+ "scopes:",
- " @Binds @test.ChildScope test.Foo test.ParentModule.bind(test.FooImpl)"));
+ " @Binds @ChildScope Foo ParentModule.bind(FooImpl)"));
}
@Test
@@ -232,28 +233,27 @@ public class ScopingValidationTest {
assertThat(compilation)
.hadErrorContaining(
message(
- "test.MyComponent scoped with @Singleton "
+ "MyComponent scoped with @Singleton "
+ "may not reference bindings with different scopes:",
- " @test.PerTest class test.ScopedType",
- " @Provides @test.PerTest String test.ScopedModule.string()",
- " @Provides @test.Per(test.MyComponent.class) boolean "
- + "test.ScopedModule.bool()"))
+ " @PerTest class ScopedType",
+ " @Provides @PerTest String ScopedModule.string()",
+ " @Provides @Per(MyComponent.class) boolean "
+ + "ScopedModule.bool()"))
.inFile(componentFile)
.onLineContaining("interface MyComponent");
compilation =
- daggerCompiler()
- .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
+ compilerWithOptions("-Adagger.fullBindingGraphValidation=ERROR")
.compile(componentFile, scopeFile, scopeWithAttribute, typeFile, moduleFile);
// The @Inject binding for ScopedType should not appear here, but the @Singleton binding should.
assertThat(compilation)
.hadErrorContaining(
message(
- "test.ScopedModule contains bindings with different scopes:",
- " @Provides @test.PerTest String test.ScopedModule.string()",
- " @Provides @Singleton float test.ScopedModule.floatingPoint()",
- " @Provides @test.Per(test.MyComponent.class) boolean "
- + "test.ScopedModule.bool()"))
+ "ScopedModule contains bindings with different scopes:",
+ " @Provides @PerTest String ScopedModule.string()",
+ " @Provides @Singleton float ScopedModule.floatingPoint()",
+ " @Provides @Per(MyComponent.class) boolean "
+ + "ScopedModule.bool()"))
.inFile(moduleFile)
.onLineContaining("class ScopedModule");
}
@@ -261,8 +261,7 @@ public class ScopingValidationTest {
@Test
public void fullBindingGraphValidationDoesNotReportForOneScope() {
Compilation compilation =
- daggerCompiler()
- .withOptions(
+ compilerWithOptions(
"-Adagger.fullBindingGraphValidation=ERROR",
"-Adagger.moduleHasDifferentScopesValidation=ERROR")
.compile(
@@ -286,8 +285,7 @@ public class ScopingValidationTest {
@Test
public void fullBindingGraphValidationDoesNotReportInjectBindings() {
Compilation compilation =
- daggerCompiler()
- .withOptions(
+ compilerWithOptions(
"-Adagger.fullBindingGraphValidation=ERROR",
"-Adagger.moduleHasDifferentScopesValidation=ERROR")
.compile(
@@ -384,9 +382,8 @@ public class ScopingValidationTest {
}
@Test
- public void componentWithScopeMayDependOnOnlyOneScopedComponent() {
- // If a scoped component will have dependencies, they must only include, at most, a single
- // scoped component
+ public void componentWithScopeCanDependOnMultipleScopedComponents() {
+ // If a scoped component will have dependencies, they can include multiple scoped component
JavaFileObject type =
JavaFileObjects.forSourceLines(
"test.SimpleType",
@@ -461,14 +458,116 @@ public class ScopingValidationTest {
daggerCompiler()
.compile(
type, simpleScope, simpleScoped, singletonScopedA, singletonScopedB, scopeless);
- assertThat(compilation).failed();
- assertThat(compilation)
- .hadErrorContaining(
- message(
- "@test.SimpleScope test.SimpleScopedComponent depends on more than one scoped "
- + "component:",
- " @Singleton test.SingletonComponentA",
- " @Singleton test.SingletonComponentB"));
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+
+
+
+ // Tests the following component hierarchy:
+ //
+ // @ScopeA
+ // ComponentA
+ // [SimpleType getSimpleType()]
+ // / \
+ // / \
+ // @ScopeB @ScopeB
+ // ComponentB1 ComponentB2
+ // \ [SimpleType getSimpleType()]
+ // \ /
+ // \ /
+ // @ScopeC
+ // ComponentC
+ // [SimpleType getSimpleType()]
+ @Test
+ public void componentWithScopeCanDependOnMultipleScopedComponentsEvenDoingADiamond() {
+ JavaFileObject type =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class SimpleType {",
+ " @Inject SimpleType() {}",
+ "}");
+ JavaFileObject simpleScope =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleScope",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope @interface SimpleScope {}");
+ JavaFileObject scopeA =
+ JavaFileObjects.forSourceLines(
+ "test.ScopeA",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope @interface ScopeA {}");
+ JavaFileObject scopeB =
+ JavaFileObjects.forSourceLines(
+ "test.ScopeB",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope @interface ScopeB {}");
+ JavaFileObject componentA =
+ JavaFileObjects.forSourceLines(
+ "test.ComponentA",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@ScopeA",
+ "@Component",
+ "interface ComponentA {",
+ " SimpleType type();",
+ "}");
+ JavaFileObject componentB1 =
+ JavaFileObjects.forSourceLines(
+ "test.ComponentB1",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@ScopeB",
+ "@Component(dependencies = ComponentA.class)",
+ "interface ComponentB1 {",
+ " SimpleType type();",
+ "}");
+ JavaFileObject componentB2 =
+ JavaFileObjects.forSourceLines(
+ "test.ComponentB2",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@ScopeB",
+ "@Component(dependencies = ComponentA.class)",
+ "interface ComponentB2 {",
+ "}");
+ JavaFileObject componentC =
+ JavaFileObjects.forSourceLines(
+ "test.ComponentC",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@SimpleScope",
+ "@Component(dependencies = {ComponentB1.class, ComponentB2.class})",
+ "interface ComponentC {",
+ " SimpleType type();",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .compile(
+ type, simpleScope, scopeA, scopeB,
+ componentA, componentB1, componentB2, componentC);
+ assertThat(compilation).succeededWithoutWarnings();
}
@Test
@@ -576,6 +675,98 @@ public class ScopingValidationTest {
}
@Test
+ public void componentScopeWithMultipleScopedDependenciesMustNotCycle() {
+ JavaFileObject type =
+ JavaFileObjects.forSourceLines(
+ "test.SimpleType",
+ "package test;",
+ "",
+ "import javax.inject.Inject;",
+ "",
+ "class SimpleType {",
+ " @Inject SimpleType() {}",
+ "}");
+ JavaFileObject scopeA =
+ JavaFileObjects.forSourceLines(
+ "test.ScopeA",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope @interface ScopeA {}");
+ JavaFileObject scopeB =
+ JavaFileObjects.forSourceLines(
+ "test.ScopeB",
+ "package test;",
+ "",
+ "import javax.inject.Scope;",
+ "",
+ "@Scope @interface ScopeB {}");
+ JavaFileObject longLifetime =
+ JavaFileObjects.forSourceLines(
+ "test.ComponentLong",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@ScopeA",
+ "@Component",
+ "interface ComponentLong {",
+ " SimpleType type();",
+ "}");
+ JavaFileObject mediumLifetime1 =
+ JavaFileObjects.forSourceLines(
+ "test.ComponentMedium1",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@ScopeB",
+ "@Component(dependencies = ComponentLong.class)",
+ "interface ComponentMedium1 {",
+ " SimpleType type();",
+ "}");
+ JavaFileObject mediumLifetime2 =
+ JavaFileObjects.forSourceLines(
+ "test.ComponentMedium2",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@ScopeB",
+ "@Component",
+ "interface ComponentMedium2 {",
+ "}");
+ JavaFileObject shortLifetime =
+ JavaFileObjects.forSourceLines(
+ "test.ComponentShort",
+ "package test;",
+ "",
+ "import dagger.Component;",
+ "",
+ "@ScopeA",
+ "@Component(dependencies = {ComponentMedium1.class, ComponentMedium2.class})",
+ "interface ComponentShort {",
+ " SimpleType type();",
+ "}");
+
+ Compilation compilation =
+ daggerCompiler()
+ .compile(
+ type, scopeA, scopeB,
+ longLifetime, mediumLifetime1, mediumLifetime2, shortLifetime);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ message(
+ "test.ComponentShort depends on scoped components in a non-hierarchical scope "
+ + "ordering:",
+ " @test.ScopeA test.ComponentLong",
+ " @test.ScopeB test.ComponentMedium1",
+ " @test.ScopeA test.ComponentShort"));
+ }
+
+ @Test
public void componentScopeAncestryMustNotCycle() {
// The dependency relationship of components is necessarily from shorter lifetimes to
// longer lifetimes. The scoping annotations must reflect this, and so one cannot declare
@@ -654,6 +845,14 @@ public class ScopingValidationTest {
" @test.ScopeA test.ComponentLong",
" @test.ScopeB test.ComponentMedium",
" @test.ScopeA test.ComponentShort"));
+
+
+ // Test that compilation succeeds when transitive validation is disabled because the scope cycle
+ // cannot be detected.
+ compilation =
+ compilerWithOptions("-Adagger.validateTransitiveComponentDependencies=DISABLED")
+ .compile(type, scopeA, scopeB, longLifetime, mediumLifetime, shortLifetime);
+ assertThat(compilation).succeeded();
}
@Test
diff --git a/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentTest.java b/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentTest.java
index 3fb0e9c7a..e143370dc 100644
--- a/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentTest.java
+++ b/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentTest.java
@@ -18,8 +18,8 @@ package dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static dagger.internal.codegen.Compilers.CLASS_PATH_WITHOUT_GUAVA_OPTION;
-import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
import com.google.testing.compile.Compilation;
@@ -96,7 +96,7 @@ public class SetBindingRequestFulfillmentTest {
"",
"import dagger.internal.SetBuilder;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" @Override",
" public Set<String> strings() {",
@@ -191,9 +191,9 @@ public class SetBindingRequestFulfillmentTest {
"import other.UsesInaccessible;",
"import other.UsesInaccessible_Factory;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
- " private Set getSetOfInaccessible2() {",
+ " private Set setOfInaccessible2() {",
" return SetBuilder.newSetBuilder(1)",
" .addAll(TestModule_EmptySetFactory.emptySet())",
" .build();",
@@ -203,7 +203,7 @@ public class SetBindingRequestFulfillmentTest {
" public UsesInaccessible usesInaccessible() {",
" return UsesInaccessible_Factory.newInstance(",
" (Set) Collections.emptySet(),",
- " (Set) getSetOfInaccessible2());",
+ " (Set) setOfInaccessible2());",
" }",
"}");
Compilation compilation =
@@ -266,7 +266,7 @@ public class SetBindingRequestFulfillmentTest {
"import java.util.Set;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerParent implements Parent {",
" private DaggerParent() {}",
"",
@@ -316,7 +316,7 @@ public class SetBindingRequestFulfillmentTest {
}
private Compiler daggerCompilerWithoutGuava() {
- return daggerCompiler()
- .withOptions(compilerMode.javacopts().append(CLASS_PATH_WITHOUT_GUAVA_OPTION));
+ return compilerWithOptions(compilerMode.javacopts())
+ .withClasspath(CLASS_PATH_WITHOUT_GUAVA_OPTION);
}
}
diff --git a/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentWithGuavaTest.java b/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentWithGuavaTest.java
index 7a4739385..2e3686fd5 100644
--- a/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentWithGuavaTest.java
+++ b/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentWithGuavaTest.java
@@ -17,8 +17,8 @@
package dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
-import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
import com.google.testing.compile.Compilation;
@@ -99,7 +99,7 @@ public class SetBindingRequestFulfillmentWithGuavaTest {
"",
"import com.google.common.collect.ImmutableSet;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" @Override",
" public Set<String> strings() {",
@@ -122,8 +122,7 @@ public class SetBindingRequestFulfillmentWithGuavaTest {
" }",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(emptySetModuleFile, setModuleFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -203,9 +202,9 @@ public class SetBindingRequestFulfillmentWithGuavaTest {
"import other.UsesInaccessible;",
"import other.UsesInaccessible_Factory;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
- " private Set getSetOfInaccessible2() {",
+ " private Set setOfInaccessible2() {",
" return ImmutableSet.copyOf(TestModule_EmptySetFactory.emptySet());",
" }",
"",
@@ -213,12 +212,11 @@ public class SetBindingRequestFulfillmentWithGuavaTest {
" public UsesInaccessible usesInaccessible() {",
" return UsesInaccessible_Factory.newInstance(",
" (Set) ImmutableSet.of(),",
- " (Set) getSetOfInaccessible2());",
+ " (Set) setOfInaccessible2());",
" }",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(module, inaccessible, inaccessible2, usesInaccessible, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -274,7 +272,7 @@ public class SetBindingRequestFulfillmentWithGuavaTest {
"",
"import com.google.common.collect.ImmutableSet;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerParent implements Parent {",
" private final class ChildImpl implements Child {",
" @Override",
@@ -285,7 +283,7 @@ public class SetBindingRequestFulfillmentWithGuavaTest {
" }",
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(parent, parentModule, child);
+ compilerWithOptions(compilerMode.javacopts()).compile(parent, parentModule, child);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerParent")
@@ -333,7 +331,7 @@ public class SetBindingRequestFulfillmentWithGuavaTest {
"import java.util.Set;",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent, "
+ "CancellationListener {",
" private DaggerTestComponent() {}",
@@ -346,14 +344,14 @@ public class SetBindingRequestFulfillmentWithGuavaTest {
" return new Builder().build();",
" }",
"",
- " private Set<String> getSetOfString() {",
+ " private Set<String> setOfString() {",
" return ImmutableSet.<String>copyOf(",
" EmptySetModule_EmptySetFactory.emptySet());",
" }",
"",
" @Override",
" public ListenableFuture<Set<String>> strings() {",
- " return Futures.immediateFuture(getSetOfString());",
+ " return Futures.immediateFuture(setOfString());",
" }",
"",
" @Override",
@@ -369,8 +367,7 @@ public class SetBindingRequestFulfillmentWithGuavaTest {
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(emptySetModuleFile, componentFile);
assertThat(compilation).succeeded();
assertThat(compilation)
diff --git a/javatests/dagger/internal/codegen/SourceFilesTest.java b/javatests/dagger/internal/codegen/SourceFilesTest.java
index c7fe99876..248e2efe9 100644
--- a/javatests/dagger/internal/codegen/SourceFilesTest.java
+++ b/javatests/dagger/internal/codegen/SourceFilesTest.java
@@ -17,9 +17,10 @@
package dagger.internal.codegen;
import static com.google.common.truth.Truth.assertThat;
-import static dagger.internal.codegen.SourceFiles.simpleVariableName;
+import static dagger.internal.codegen.binding.SourceFiles.simpleVariableName;
import com.google.testing.compile.CompilationRule;
+import dagger.internal.codegen.binding.SourceFiles;
import java.util.List;
import javax.lang.model.element.TypeElement;
import org.junit.Rule;
diff --git a/javatests/dagger/internal/codegen/SubcomponentBuilderValidationTest.java b/javatests/dagger/internal/codegen/SubcomponentBuilderValidationTest.java
index 5dab4c488..0fd7e084d 100644
--- a/javatests/dagger/internal/codegen/SubcomponentBuilderValidationTest.java
+++ b/javatests/dagger/internal/codegen/SubcomponentBuilderValidationTest.java
@@ -18,11 +18,12 @@ package dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.ComponentCreatorAnnotation.SUBCOMPONENT_BUILDER;
-import static dagger.internal.codegen.ErrorMessages.creatorMessagesFor;
+import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.SUBCOMPONENT_BUILDER;
+import static dagger.internal.codegen.binding.ErrorMessages.creatorMessagesFor;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
+import dagger.internal.codegen.binding.ErrorMessages;
import javax.tools.JavaFileObject;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/javatests/dagger/internal/codegen/SubcomponentCreatorRequestFulfillmentTest.java b/javatests/dagger/internal/codegen/SubcomponentCreatorRequestFulfillmentTest.java
index de0067f4a..ea0b4cc19 100644
--- a/javatests/dagger/internal/codegen/SubcomponentCreatorRequestFulfillmentTest.java
+++ b/javatests/dagger/internal/codegen/SubcomponentCreatorRequestFulfillmentTest.java
@@ -21,14 +21,15 @@ import static com.google.common.collect.Sets.immutableEnumSet;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
-import static dagger.internal.codegen.ComponentCreatorAnnotation.SUBCOMPONENT_BUILDER;
-import static dagger.internal.codegen.ComponentCreatorAnnotation.SUBCOMPONENT_FACTORY;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
+import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.SUBCOMPONENT_BUILDER;
+import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.SUBCOMPONENT_FACTORY;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.testing.compile.Compilation;
+import dagger.internal.codegen.binding.ComponentCreatorAnnotation;
import java.util.Collection;
import java.util.List;
import java.util.Set;
@@ -100,7 +101,7 @@ public class SubcomponentCreatorRequestFulfillmentTest extends ComponentCreatorT
"",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerC implements C {",
" @Override",
" public Sub.Builder sBuilder() {",
diff --git a/javatests/dagger/internal/codegen/SubcomponentCreatorValidationTest.java b/javatests/dagger/internal/codegen/SubcomponentCreatorValidationTest.java
index b5753d470..371253a81 100644
--- a/javatests/dagger/internal/codegen/SubcomponentCreatorValidationTest.java
+++ b/javatests/dagger/internal/codegen/SubcomponentCreatorValidationTest.java
@@ -18,18 +18,19 @@ package dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
-import static dagger.internal.codegen.ComponentCreatorAnnotation.SUBCOMPONENT_BUILDER;
-import static dagger.internal.codegen.ComponentCreatorAnnotation.SUBCOMPONENT_FACTORY;
-import static dagger.internal.codegen.ComponentCreatorKind.BUILDER;
-import static dagger.internal.codegen.ComponentCreatorKind.FACTORY;
-import static dagger.internal.codegen.ComponentKind.SUBCOMPONENT;
-import static dagger.internal.codegen.ErrorMessages.ComponentCreatorMessages.moreThanOneRefToSubcomponent;
-import static dagger.internal.codegen.ErrorMessages.componentMessagesFor;
import static dagger.internal.codegen.TestUtils.message;
+import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.SUBCOMPONENT_BUILDER;
+import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.SUBCOMPONENT_FACTORY;
+import static dagger.internal.codegen.binding.ComponentCreatorKind.BUILDER;
+import static dagger.internal.codegen.binding.ComponentCreatorKind.FACTORY;
+import static dagger.internal.codegen.binding.ComponentKind.SUBCOMPONENT;
+import static dagger.internal.codegen.binding.ErrorMessages.ComponentCreatorMessages.moreThanOneRefToSubcomponent;
+import static dagger.internal.codegen.binding.ErrorMessages.componentMessagesFor;
import com.google.common.collect.ImmutableList;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
+import dagger.internal.codegen.binding.ComponentCreatorAnnotation;
import java.util.Collection;
import javax.tools.JavaFileObject;
import org.junit.Test;
@@ -673,19 +674,18 @@ public class SubcomponentCreatorValidationTest extends ComponentCreatorTestHelpe
Compilation compilation = compile(componentFile, childComponentFile);
assertThat(compilation).failed();
String firstBinding = creatorKind.equals(FACTORY)
- ? "test.ChildComponent.Factory.create(s1, …)"
- : "@BindsInstance void test.ChildComponent.Builder.set1(String)";
+ ? "ChildComponent.Factory.create(s1, …)"
+ : "@BindsInstance void ChildComponent.Builder.set1(String)";
String secondBinding = creatorKind.equals(FACTORY)
- ? "test.ChildComponent.Factory.create(…, s2)"
- : "@BindsInstance void test.ChildComponent.Builder.set2(String)";
+ ? "ChildComponent.Factory.create(…, s2)"
+ : "@BindsInstance void ChildComponent.Builder.set2(String)";
assertThat(compilation)
.hadErrorContaining(
message(
- "java.lang.String is bound multiple times:",
+ "String is bound multiple times:",
" " + firstBinding,
" " + secondBinding,
- " java.lang.String is provided at",
- " test.ChildComponent.s() [test.ParentComponent → test.ChildComponent]"))
+ " in component: [ParentComponent → ChildComponent]"))
.inFile(componentFile)
.onLineContaining("interface ParentComponent {");
}
diff --git a/javatests/dagger/internal/codegen/SubcomponentValidationTest.java b/javatests/dagger/internal/codegen/SubcomponentValidationTest.java
index ad08160c5..19a27ff85 100644
--- a/javatests/dagger/internal/codegen/SubcomponentValidationTest.java
+++ b/javatests/dagger/internal/codegen/SubcomponentValidationTest.java
@@ -19,8 +19,9 @@ package dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
import com.google.testing.compile.Compilation;
@@ -83,8 +84,7 @@ public class SubcomponentValidationTest {
" }",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(componentFile, childComponentFile, moduleFile);
assertThat(compilation).failed();
assertThat(compilation)
@@ -152,16 +152,15 @@ public class SubcomponentValidationTest {
" }",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(component, childComponent, grandchildComponent, grandchildModule);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
- "[test.ChildComponent.newGrandchildComponent()] "
- + "test.GrandchildComponent requires modules which have no visible default "
+ "[ChildComponent.newGrandchildComponent()] "
+ + "GrandchildComponent requires modules which have no visible default "
+ "constructors. Add the following modules as parameters to this method: "
- + "test.GrandchildModule")
+ + "GrandchildModule")
.inFile(component)
.onLineContaining("interface TestComponent");
}
@@ -184,8 +183,7 @@ public class SubcomponentValidationTest {
"@Subcomponent",
"interface ChildComponent {}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(componentFile, childComponentFile);
assertThat(compilation).failed();
assertThat(compilation)
@@ -221,8 +219,7 @@ public class SubcomponentValidationTest {
"@Subcomponent(modules = TestModule.class)",
"interface ChildComponent {}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(moduleFile, componentFile, childComponentFile);
assertThat(compilation).failed();
assertThat(compilation)
@@ -259,8 +256,7 @@ public class SubcomponentValidationTest {
"@Subcomponent",
"interface ChildComponent {}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(moduleFile, componentFile, childComponentFile);
assertThat(compilation).failed();
assertThat(compilation)
@@ -300,16 +296,15 @@ public class SubcomponentValidationTest {
"",
"@Subcomponent(modules = TestModule.class)",
"interface ChildComponent {",
- " String getString();",
+ " String string();",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(moduleFile, componentFile, childComponentFile);
assertThat(compilation).failed();
assertThat(compilation)
.hadErrorContaining(
- "java.lang.Integer cannot be provided without an @Inject constructor or an "
+ "Integer cannot be provided without an @Inject constructor or an "
+ "@Provides-annotated method")
.inFile(componentFile)
.onLineContaining("interface TestComponent");
@@ -324,7 +319,7 @@ public class SubcomponentValidationTest {
"@Subcomponent",
"final class NotASubcomponent {}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(subcomponentFile);
+ compilerWithOptions(compilerMode.javacopts()).compile(subcomponentFile);
assertThat(compilation).failed();
assertThat(compilation).hadErrorContaining("interface");
}
@@ -348,7 +343,7 @@ public class SubcomponentValidationTest {
"",
"@Subcomponent(modules = ChildModule.class)",
"interface ChildComponent {",
- " Object getObject();",
+ " Object object();",
"}");
JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.ChildModule",
"package test;",
@@ -362,8 +357,7 @@ public class SubcomponentValidationTest {
" @Provides @Singleton Object provideObject() { return null; }",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(componentFile, subcomponentFile, moduleFile);
assertThat(compilation).failed();
assertThat(compilation).hadErrorContaining("@Singleton");
@@ -383,8 +377,8 @@ public class SubcomponentValidationTest {
"@Component",
"interface ParentComponent {",
" ChildComponent childComponent();",
- " Dep1 getDep1();",
- " Dep2 getDep2();",
+ " Dep1 dep1();",
+ " Dep2 dep2();",
"}");
JavaFileObject childComponentFile =
JavaFileObjects.forSourceLines(
@@ -395,7 +389,7 @@ public class SubcomponentValidationTest {
"",
"@Subcomponent(modules = ChildModule.class)",
"interface ChildComponent {",
- " Object getObject();",
+ " Object object();",
"}");
JavaFileObject childModuleFile =
JavaFileObjects.forSourceLines(
@@ -463,7 +457,7 @@ public class SubcomponentValidationTest {
.addLines(
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerParentComponent implements ParentComponent {")
.addLinesIn(
DEFAULT_MODE,
@@ -475,7 +469,7 @@ public class SubcomponentValidationTest {
"")
.addLines(
" @Override", //
- " public Dep1 getDep1() {")
+ " public Dep1 dep1() {")
.addLinesIn(
FAST_INIT_MODE,
" Object local = dep1;",
@@ -496,7 +490,7 @@ public class SubcomponentValidationTest {
" }", //
"",
" @Override",
- " public Dep2 getDep2() {")
+ " public Dep2 dep2() {")
.addLinesIn(
FAST_INIT_MODE,
" Object local = dep2;",
@@ -545,34 +539,34 @@ public class SubcomponentValidationTest {
"")
.addLinesIn(
DEFAULT_MODE,
- " private NeedsDep1 getNeedsDep1() {",
+ " private NeedsDep1 needsDep1() {",
" return new NeedsDep1(DaggerParentComponent.this.dep1Provider.get());",
" }")
.addLinesIn(
FAST_INIT_MODE,
- " private NeedsDep1 getNeedsDep1() {",
- " return new NeedsDep1(DaggerParentComponent.this.getDep1());",
+ " private NeedsDep1 needsDep1() {",
+ " return new NeedsDep1(DaggerParentComponent.this.dep1());",
" }")
.addLines(
- " private A getA() {",
+ " private A a() {",
" return injectA(",
" A_Factory.newInstance(",
- " getNeedsDep1(),")
+ " needsDep1(),")
.addLinesIn(
DEFAULT_MODE,
" DaggerParentComponent.this.dep1Provider.get(),",
" DaggerParentComponent.this.dep2Provider.get()));")
.addLinesIn(
FAST_INIT_MODE,
- " DaggerParentComponent.this.getDep1(),",
- " DaggerParentComponent.this.getDep2()));")
+ " DaggerParentComponent.this.dep1(),",
+ " DaggerParentComponent.this.dep2()));")
.addLines(
" }",
"",
" @Override",
- " public Object getObject() {",
+ " public Object object() {",
" return ChildModule_ProvideObjectFactory.provideObject(",
- " childModule, getA());",
+ " childModule, a());",
" }",
"",
" @CanIgnoreReturnValue",
@@ -585,8 +579,7 @@ public class SubcomponentValidationTest {
.build();
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(
parentComponentFile,
childComponentFile,
@@ -663,7 +656,7 @@ public class SubcomponentValidationTest {
"",
"import test.subpackage.Sub;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerParentComponent implements ParentComponent {",
" @Override",
" public Foo.Sub newInstanceSubcomponent() {",
@@ -700,8 +693,7 @@ public class SubcomponentValidationTest {
" private final class NoConflictImpl implements NoConflict {}",
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(parent, foo, bar, baz, noConflict);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -746,7 +738,7 @@ public class SubcomponentValidationTest {
"test.DaggerParentComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerParentComponent implements ParentComponent {",
" @Override",
" public Sub newSubcomponent() {",
@@ -772,7 +764,7 @@ public class SubcomponentValidationTest {
" }",
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(parent, sub, deepSub);
+ compilerWithOptions(compilerMode.javacopts()).compile(parent, sub, deepSub);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerParentComponent")
@@ -811,7 +803,7 @@ public class SubcomponentValidationTest {
JavaFileObjects.forSourceLines(
"DaggerParentComponent",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerParentComponent implements ParentComponent {",
" @Override",
" public Sub newSubcomponent() {",
@@ -835,7 +827,7 @@ public class SubcomponentValidationTest {
"");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(parent, sub, deepSub);
+ compilerWithOptions(compilerMode.javacopts()).compile(parent, sub, deepSub);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("DaggerParentComponent")
@@ -888,7 +880,7 @@ public class SubcomponentValidationTest {
"",
"import top1.a.b.c.d.E;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerParentComponent implements ParentComponent {",
" @Override",
" public E.F.Sub top1() {",
@@ -909,7 +901,7 @@ public class SubcomponentValidationTest {
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(parent, top1, top2);
+ compilerWithOptions(compilerMode.javacopts()).compile(parent, top1, top2);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerParentComponent")
@@ -945,7 +937,7 @@ public class SubcomponentValidationTest {
"test.DaggerC",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerC implements C {",
" @Override",
" public Foo.C newInstanceC() {",
@@ -956,8 +948,7 @@ public class SubcomponentValidationTest {
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(parent, subcomponentWithSameSimpleNameAsParent);
assertThat(compilation).succeeded();
assertThat(compilation)
@@ -1007,7 +998,7 @@ public class SubcomponentValidationTest {
"",
IMPORT_GENERATED_ANNOTATION,
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerC implements C {",
" @Override",
" public C.Foo.Sub.Builder fooBuilder() {",
@@ -1019,7 +1010,7 @@ public class SubcomponentValidationTest {
" return new B_SubBuilder();",
" }",
"",
- // TODO(user): Reverse the order of subcomponent and builder so that subcomponent
+ // TODO(bcorso): Reverse the order of subcomponent and builder so that subcomponent
// comes first.
" private final class F_SubBuilder implements C.Foo.Sub.Builder {",
" @Override",
@@ -1044,7 +1035,7 @@ public class SubcomponentValidationTest {
" }",
"}");
Compilation compilation =
- daggerCompiler().withOptions(compilerMode.javacopts()).compile(parent);
+ compilerWithOptions(compilerMode.javacopts()).compile(parent);
assertThat(compilation).succeeded();
assertThat(compilation)
.generatedSourceFile("test.DaggerC")
@@ -1097,17 +1088,16 @@ public class SubcomponentValidationTest {
"}");
Compilation compilation =
- daggerCompiler()
- .withOptions(compilerMode.javacopts())
+ compilerWithOptions(compilerMode.javacopts())
.compile(module, component, subcomponent);
assertThat(compilation).failed();
- assertThat(compilation).hadErrorContaining("test.Sub.Builder is bound multiple times:");
+ assertThat(compilation).hadErrorContaining("Sub.Builder is bound multiple times:");
assertThat(compilation)
.hadErrorContaining(
- "@Provides test.Sub.Builder "
- + "test.TestModule.providesConflictsWithModuleSubcomponents()");
+ "@Provides Sub.Builder "
+ + "TestModule.providesConflictsWithModuleSubcomponents()");
assertThat(compilation)
- .hadErrorContaining("@Module(subcomponents = test.Sub.class) for test.TestModule");
+ .hadErrorContaining("@Module(subcomponents = Sub.class) for TestModule");
}
@Test
diff --git a/javatests/dagger/internal/codegen/SwitchingProviderTest.java b/javatests/dagger/internal/codegen/SwitchingProviderTest.java
index 2898f2e43..593ad497f 100644
--- a/javatests/dagger/internal/codegen/SwitchingProviderTest.java
+++ b/javatests/dagger/internal/codegen/SwitchingProviderTest.java
@@ -18,7 +18,7 @@ package dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
import com.google.common.collect.ImmutableList;
import com.google.testing.compile.Compilation;
@@ -68,7 +68,7 @@ public class SwitchingProviderTest {
JavaFileObjects.forSourceLines(
"test.DaggerTestComponent",
"package test;",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private final class SwitchingProvider<T> implements Provider<T> {",
" @SuppressWarnings(\"unchecked\")",
@@ -248,11 +248,11 @@ public class SwitchingProviderTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private volatile Provider<String> sProvider;",
"",
- " private Provider<String> getStringProvider() {",
+ " private Provider<String> stringProvider() {",
" Object local = sProvider;",
" if (local == null) {",
" local = new SwitchingProvider<>(0);",
@@ -263,12 +263,12 @@ public class SwitchingProviderTest {
"",
" @Override",
" public Provider<Object> objectProvider() {",
- " return (Provider) getStringProvider();",
+ " return (Provider) stringProvider();",
" }",
"",
" @Override",
" public Provider<CharSequence> charSequenceProvider() {",
- " return (Provider) getStringProvider();",
+ " return (Provider) stringProvider();",
" }",
"",
" private final class SwitchingProvider<T> implements Provider<T> {",
@@ -333,12 +333,12 @@ public class SwitchingProviderTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private volatile Object charSequence = new MemoizedSentinel();",
" private volatile Provider<CharSequence> cProvider;",
"",
- " private CharSequence getCharSequence() {",
+ " private CharSequence charSequence() {",
" Object local = charSequence;",
" if (local instanceof MemoizedSentinel) {",
" synchronized (local) {",
@@ -373,7 +373,7 @@ public class SwitchingProviderTest {
" public T get() {",
" switch (id) {",
" case 0:",
- " return (T) DaggerTestComponent.this.getCharSequence();",
+ " return (T) DaggerTestComponent.this.charSequence();",
" default:",
" throw new AssertionError(id);",
" }",
@@ -424,7 +424,7 @@ public class SwitchingProviderTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" @Override",
" public Provider<Set<String>> setProvider() {",
@@ -469,7 +469,7 @@ public class SwitchingProviderTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private Provider<MembersInjector<Foo>> fooMembersInjectorProvider;",
"",
@@ -540,7 +540,7 @@ public class SwitchingProviderTest {
"test.DaggerTestComponent",
"package test;",
"",
- GENERATED_ANNOTATION,
+ GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" @SuppressWarnings(\"rawtypes\")",
" private static final Provider ABSENT_JDK_OPTIONAL_PROVIDER =",
diff --git a/javatests/dagger/internal/codegen/TypeProtoConverterTest.java b/javatests/dagger/internal/codegen/TypeProtoConverterTest.java
deleted file mode 100644
index 8c8628cfd..000000000
--- a/javatests/dagger/internal/codegen/TypeProtoConverterTest.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2019 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.truth.Truth.assertWithMessage;
-import static javax.lang.model.util.ElementFilter.fieldsIn;
-
-import com.google.testing.compile.CompilationRule;
-import dagger.internal.Factory;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.internal.codegen.serialization.TypeProto;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import javax.lang.model.type.TypeMirror;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/** Tests {@link TypeProtoConverter}. */
-@RunWith(JUnit4.class)
-public class TypeProtoConverterTest {
- @Rule public CompilationRule compilationRule = new CompilationRule();
-
- private DaggerElements elements;
- private DaggerTypes types;
- private TypeProtoConverter typeProtoConverter;
-
- @Before
- public void setUp() {
- this.elements = new DaggerElements(compilationRule.getElements(), compilationRule.getTypes());
- this.types = new DaggerTypes(compilationRule.getTypes(), elements);
- this.typeProtoConverter = new TypeProtoConverter(types, elements);
- }
-
- static class Outer<O> {
- @SuppressWarnings("ClassCanBeStatic") // We want to specifically test inner classes
- class Inner<I> {}
- }
-
- @SuppressWarnings({"rawtypes", "unused"})
- static class TypeMirrorConversionSubjects {
- private Map rawMap;
- private List<String> listOfString;
- private List<HashMap<String, Integer>> listOfHashMapOfStringToInteger;
- private Map<HashMap<String, Integer>, Set<Factory>> mapOfHashMapOfStringToIntegerToSetOfFactory;
- private Map<HashMap<String, Integer>, Set<Factory>>[][]
- arrayOfArrayOfMapOfHashMapOfStringToIntegerToSetOfFactory;
- private Map<HashMap<?, Integer>, ?> mapOfHashMapOfWildcardToIntegerToWildcard;
- private List<? extends String> listOfWildcardExtendsString;
- private List<? extends Set<? super String>> listOfWildcardExtendsSetOfWildcardSuperString;
- private Outer<Object>.Inner<Integer> outerOfObjectDotInnerOfInteger;
- private List<int[]> listOfIntArray;
- private List<? extends CharSequence[]> listOfWildcardExtendsCharSequenceArray;
- }
-
- @Test
- public void typeMirrorProtoConversions() {
- assertProtoConversionEquality(fieldType("rawMap"));
- assertProtoConversionEquality(fieldType("listOfString"));
- assertProtoConversionEquality(fieldType("listOfHashMapOfStringToInteger"));
- assertProtoConversionEquality(fieldType("mapOfHashMapOfStringToIntegerToSetOfFactory"));
- assertProtoConversionEquality(
- fieldType("arrayOfArrayOfMapOfHashMapOfStringToIntegerToSetOfFactory"));
- assertProtoConversionEquality(fieldType("mapOfHashMapOfWildcardToIntegerToWildcard"));
- assertProtoConversionEquality(fieldType("listOfWildcardExtendsString"));
- assertProtoConversionEquality(fieldType("listOfWildcardExtendsSetOfWildcardSuperString"));
- assertProtoConversionEquality(fieldType("outerOfObjectDotInnerOfInteger"));
- assertProtoConversionEquality(fieldType("listOfIntArray"));
- assertProtoConversionEquality(fieldType("listOfWildcardExtendsCharSequenceArray"));
- }
-
- private TypeMirror fieldType(String fieldName) {
- return fieldsIn(
- elements.getTypeElement(TypeMirrorConversionSubjects.class).getEnclosedElements())
- .stream()
- .filter(field -> field.getSimpleName().contentEquals(fieldName))
- .findFirst()
- .get()
- .asType();
- }
-
- /**
- * Converts {@link TypeMirror} to a {@link dagger.internal.codegen.serialization.TypeProto} and
- * back to a {@link TypeMirror}. Asserts that the round-trip conversion is lossless.
- */
- private void assertProtoConversionEquality(TypeMirror typeMirror) {
- TypeProto toProto = TypeProtoConverter.toProto(typeMirror);
- TypeMirror fromProto = typeProtoConverter.fromProto(toProto);
- assertWithMessage("expected: %s\nactual : %s", typeMirror, fromProto)
- .that(types.isSameType(typeMirror, fromProto))
- .isTrue();
- }
-}
diff --git a/javatests/dagger/internal/codegen/ValidationReportTest.java b/javatests/dagger/internal/codegen/ValidationReportTest.java
index b0f2f2fe1..d1c3a2e3c 100644
--- a/javatests/dagger/internal/codegen/ValidationReportTest.java
+++ b/javatests/dagger/internal/codegen/ValidationReportTest.java
@@ -23,7 +23,8 @@ import static com.google.testing.compile.Compiler.javac;
import com.google.common.collect.ImmutableSet;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
-import dagger.internal.codegen.ValidationReport.Builder;
+import dagger.internal.codegen.validation.ValidationReport;
+import dagger.internal.codegen.validation.ValidationReport.Builder;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
diff --git a/javatests/dagger/internal/codegen/javapoet/BUILD b/javatests/dagger/internal/codegen/javapoet/BUILD
index 99c11bda7..438d3779b 100644
--- a/javatests/dagger/internal/codegen/javapoet/BUILD
+++ b/javatests/dagger/internal/codegen/javapoet/BUILD
@@ -15,11 +15,11 @@
# Description:
# Tests for dagger.internal.codegen.javapoet
-package(default_visibility = ["//:src"])
-
load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
load("//:test_defs.bzl", "GenJavaTests")
+package(default_visibility = ["//:src"])
+
GenJavaTests(
name = "javapoet_tests",
srcs = glob(["*.java"]),
@@ -28,10 +28,10 @@ GenJavaTests(
deps = [
"//java/dagger/internal/codegen/javapoet",
"//java/dagger/internal/codegen/langmodel",
- "@google_bazel_common//third_party/java/auto:common",
"@google_bazel_common//third_party/java/compile_testing",
"@google_bazel_common//third_party/java/javapoet",
"@google_bazel_common//third_party/java/junit",
"@google_bazel_common//third_party/java/truth",
+ "@maven//:com_google_auto_auto_common",
],
)
diff --git a/javatests/dagger/lint/BUILD b/javatests/dagger/lint/BUILD
new file mode 100644
index 000000000..268340d5e
--- /dev/null
+++ b/javatests/dagger/lint/BUILD
@@ -0,0 +1,33 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Description:
+# Tests for the Dagger Lint Rules
+
+load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_test")
+
+package(default_visibility = ["//:src"])
+
+kt_jvm_test(
+ name = "DaggerKotlinIssueDetectorTest",
+ srcs = ["DaggerKotlinIssueDetectorTest.kt"],
+ deps = [
+ "@google_bazel_common//third_party/java/junit",
+ "//java/dagger/lint:lint-artifact-lib",
+ "@maven//:com_android_tools_lint_lint_checks",
+ "@maven//:com_android_tools_lint_lint_tests",
+ "@maven//:com_android_tools_testutils",
+ "@maven//:org_jetbrains_kotlin_kotlin_stdlib",
+ ],
+)
diff --git a/javatests/dagger/lint/DaggerKotlinIssueDetectorTest.kt b/javatests/dagger/lint/DaggerKotlinIssueDetectorTest.kt
new file mode 100644
index 000000000..f11239234
--- /dev/null
+++ b/javatests/dagger/lint/DaggerKotlinIssueDetectorTest.kt
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dagger.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@Suppress("UnstableApiUsage")
+@RunWith(JUnit4::class)
+class DaggerKotlinIssueDetectorTest : LintDetectorTest() {
+
+ private companion object {
+ private val javaxInjectStubs = kotlin(
+ """
+ package javax.inject
+
+ annotation class Inject
+ annotation class Qualifier
+ """
+ ).indented()
+
+ private val daggerStubs = kotlin(
+ """
+ package dagger
+
+ annotation class Provides
+ annotation class Module
+ """
+ ).indented()
+
+ // For some reason in Bazel the stdlib dependency on the classpath isn't visible to the
+ // LintTestTask, so we just include it ourselves here for now.
+ private val jvmStaticStubs = kotlin(
+ """
+ package kotlin.jvm
+
+ annotation class JvmStatic
+ """
+ ).indented()
+ }
+
+ override fun getDetector(): Detector = DaggerKotlinIssueDetector()
+
+ override fun getIssues(): List<Issue> = DaggerKotlinIssueDetector.issues
+
+ @Test
+ fun simpleSmokeTestForQualifiersAndProviders() {
+ lint()
+ .allowMissingSdk()
+ .files(
+ javaxInjectStubs,
+ daggerStubs,
+ jvmStaticStubs,
+ kotlin(
+ """
+ package foo
+ import javax.inject.Inject
+ import javax.inject.Qualifier
+ import kotlin.jvm.JvmStatic
+ import dagger.Provides
+ import dagger.Module
+
+ @Qualifier
+ annotation class MyQualifier
+
+ class InjectedTest {
+ // This should fail because of `:field`
+ @Inject
+ @field:MyQualifier
+ lateinit var prop: String
+
+ // This is fine!
+ @Inject
+ @MyQualifier
+ lateinit var prop2: String
+ }
+
+ @Module
+ object ObjectModule {
+ // This should fail because it uses `@JvmStatic`
+ @JvmStatic
+ @Provides
+ fun provideFoo(): String {
+
+ }
+
+ // This is fine!
+ @Provides
+ fun provideBar(): String {
+
+ }
+ }
+
+ @Module
+ class ClassModule {
+ companion object {
+ // This should fail because the companion object is part of ClassModule, so this is unnecessary.
+ @JvmStatic
+ @Provides
+ fun provideBaz(): String {
+
+ }
+ }
+ }
+
+ @Module
+ class ClassModuleQualified {
+ companion object {
+ // This should fail because the companion object is part of ClassModule, so this is unnecessary.
+ // This specifically tests a fully qualified annotation
+ @kotlin.jvm.JvmStatic
+ @Provides
+ fun provideBaz(): String {
+
+ }
+ }
+ }
+
+ @Module
+ class ClassModule2 {
+ // This should fail because the companion object is part of ClassModule
+ @Module
+ companion object {
+ @Provides
+ fun provideBaz(): String {
+
+ }
+ }
+ }
+
+ @Module
+ class ClassModule2Qualified {
+ // This should fail because the companion object is part of ClassModule
+ // This specifically tests a fully qualified annotation
+ @dagger.Module
+ companion object {
+ @Provides
+ fun provideBaz(): String {
+
+ }
+ }
+ }
+
+ // This is correct as of Dagger 2.26!
+ @Module
+ class ClassModule3 {
+ companion object {
+ @Provides
+ fun provideBaz(): String {
+
+ }
+ }
+ }
+
+ class ClassModule4 {
+ // This is should fail because this should be extracted to a standalone object.
+ @Module
+ companion object {
+ @Provides
+ fun provideBaz(): String {
+
+ }
+ }
+ }
+ """
+ ).indented()
+ )
+ .allowCompilationErrors(false)
+ .run()
+ .expect(
+ """
+ src/foo/MyQualifier.kt:14: Warning: Redundant 'field:' used for Dagger qualifier annotation. [FieldSiteTargetOnQualifierAnnotation]
+ @field:MyQualifier
+ ~~~~~~~~~~~~~~~~~~
+ src/foo/MyQualifier.kt:26: Warning: @JvmStatic used for @Provides function in an object class [JvmStaticProvidesInObjectDetector]
+ @JvmStatic
+ ~~~~~~~~~~
+ src/foo/MyQualifier.kt:43: Warning: @JvmStatic used for @Provides function in an object class [JvmStaticProvidesInObjectDetector]
+ @JvmStatic
+ ~~~~~~~~~~
+ src/foo/MyQualifier.kt:56: Warning: @JvmStatic used for @Provides function in an object class [JvmStaticProvidesInObjectDetector]
+ @kotlin.jvm.JvmStatic
+ ~~~~~~~~~~~~~~~~~~~~~
+ src/foo/MyQualifier.kt:66: Warning: Module companion objects should not be annotated with @Module. [ModuleCompanionObjects]
+ // This should fail because the companion object is part of ClassModule
+ ^
+ src/foo/MyQualifier.kt:78: Warning: Module companion objects should not be annotated with @Module. [ModuleCompanionObjects]
+ // This should fail because the companion object is part of ClassModule
+ ^
+ src/foo/MyQualifier.kt:101: Warning: Module companion objects should not be annotated with @Module. [ModuleCompanionObjects]
+ // This is should fail because this should be extracted to a standalone object.
+ ^
+ 0 errors, 7 warnings
+ """.trimIndent()
+ )
+ .expectFixDiffs(
+ """
+ Fix for src/foo/MyQualifier.kt line 14: Remove 'field:':
+ @@ -14 +14
+ - @field:MyQualifier
+ + @MyQualifier
+ Fix for src/foo/MyQualifier.kt line 26: Remove @JvmStatic:
+ @@ -26 +26
+ - @JvmStatic
+ +
+ Fix for src/foo/MyQualifier.kt line 43: Remove @JvmStatic:
+ @@ -43 +43
+ - @JvmStatic
+ +
+ Fix for src/foo/MyQualifier.kt line 56: Remove @JvmStatic:
+ @@ -56 +56
+ - @kotlin.jvm.JvmStatic
+ +
+ Fix for src/foo/MyQualifier.kt line 66: Remove @Module:
+ @@ -67 +67
+ - @Module
+ +
+ Fix for src/foo/MyQualifier.kt line 78: Remove @Module:
+ @@ -80 +80
+ - @dagger.Module
+ +
+ Fix for src/foo/MyQualifier.kt line 101: Remove @Module:
+ @@ -102 +102
+ - @Module
+ +
+ """.trimIndent()
+ )
+ }
+}
diff --git a/javatests/dagger/producers/BUILD b/javatests/dagger/producers/BUILD
index 1e1bd6603..9404d85ac 100644
--- a/javatests/dagger/producers/BUILD
+++ b/javatests/dagger/producers/BUILD
@@ -15,8 +15,6 @@
# Description:
# Tests for dagger.producers
-package(default_visibility = ["//:src"])
-
load(
"//:build_defs.bzl",
"DOCLINT_HTML_AND_SYNTAX",
@@ -25,14 +23,17 @@ load(
)
load("//:test_defs.bzl", "GenJavaTests")
+package(default_visibility = ["//:src"])
+
GenJavaTests(
name = "producers_tests",
srcs = glob(["**/*.java"]),
functional = 0,
javacopts = SOURCE_7_TARGET_7 + DOCLINT_REFERENCES + DOCLINT_HTML_AND_SYNTAX,
deps = [
+ "//java/dagger/internal/guava:collect",
+ "//java/dagger/internal/guava:concurrent",
"//java/dagger/producers",
- "@google_bazel_common//third_party/java/guava",
"@google_bazel_common//third_party/java/guava:testlib",
"@google_bazel_common//third_party/java/junit",
"@google_bazel_common//third_party/java/mockito",
diff --git a/javatests/dagger/producers/internal/AbstractProducesMethodProducerTest.java b/javatests/dagger/producers/internal/AbstractProducesMethodProducerTest.java
index b29cb3c0d..af1350447 100644
--- a/javatests/dagger/producers/internal/AbstractProducesMethodProducerTest.java
+++ b/javatests/dagger/producers/internal/AbstractProducesMethodProducerTest.java
@@ -18,7 +18,8 @@ package dagger.producers.internal;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
@@ -55,7 +56,7 @@ public class AbstractProducesMethodProducerTest {
public void initMocks() {
MockitoAnnotations.initMocks(this);
monitor = Mockito.mock(ProducerMonitor.class, Mockito.CALLS_REAL_METHODS);
- when(componentMonitor.producerMonitorFor(any(ProducerToken.class))).thenReturn(monitor);
+ when(componentMonitor.producerMonitorFor(nullable(ProducerToken.class))).thenReturn(monitor);
componentMonitorProvider =
new Provider<ProductionComponentMonitor>() {
@Override
diff --git a/javatests/dagger/producers/monitoring/TimingProductionComponentMonitorTest.java b/javatests/dagger/producers/monitoring/TimingProductionComponentMonitorTest.java
index 449b5a6e7..5c443a4a0 100644
--- a/javatests/dagger/producers/monitoring/TimingProductionComponentMonitorTest.java
+++ b/javatests/dagger/producers/monitoring/TimingProductionComponentMonitorTest.java
@@ -16,7 +16,7 @@
package dagger.producers.monitoring;
-import static org.mockito.Mockito.any;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
diff --git a/javatests/dagger/producers/monitoring/TimingRecordersTest.java b/javatests/dagger/producers/monitoring/TimingRecordersTest.java
index 4e5d74f0a..eefcb01c8 100644
--- a/javatests/dagger/producers/monitoring/TimingRecordersTest.java
+++ b/javatests/dagger/producers/monitoring/TimingRecordersTest.java
@@ -17,8 +17,9 @@
package dagger.producers.monitoring;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyLong;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -96,7 +97,8 @@ public final class TimingRecordersTest {
public void singleRecorder_nullProducerTimingRecorder() {
when(mockProductionComponentTimingRecorderFactory.create(any(Object.class)))
.thenReturn(mockProductionComponentTimingRecorder);
- when(mockProductionComponentTimingRecorder.producerTimingRecorderFor(any(ProducerToken.class)))
+ when(mockProductionComponentTimingRecorder.producerTimingRecorderFor(
+ nullable(ProducerToken.class)))
.thenReturn(null);
ProductionComponentTimingRecorder.Factory factory =
TimingRecorders.delegatingProductionComponentTimingRecorderFactory(
@@ -110,7 +112,8 @@ public final class TimingRecordersTest {
public void singleRecorder_throwingProductionComponentTimingRecorder() {
when(mockProductionComponentTimingRecorderFactory.create(any(Object.class)))
.thenReturn(mockProductionComponentTimingRecorder);
- when(mockProductionComponentTimingRecorder.producerTimingRecorderFor(any(ProducerToken.class)))
+ when(mockProductionComponentTimingRecorder.producerTimingRecorderFor(
+ nullable(ProducerToken.class)))
.thenThrow(new RuntimeException("monkey"));
ProductionComponentTimingRecorder.Factory factory =
TimingRecorders.delegatingProductionComponentTimingRecorderFactory(
@@ -220,7 +223,8 @@ public final class TimingRecordersTest {
.thenReturn(mockProductionComponentTimingRecorderA);
when(mockProductionComponentTimingRecorderFactoryB.create(any(Object.class))).thenReturn(null);
when(mockProductionComponentTimingRecorderFactoryC.create(any(Object.class))).thenReturn(null);
- when(mockProductionComponentTimingRecorderA.producerTimingRecorderFor(any(ProducerToken.class)))
+ when(mockProductionComponentTimingRecorderA.producerTimingRecorderFor(
+ nullable(ProducerToken.class)))
.thenReturn(mockProducerTimingRecorderA);
ProductionComponentTimingRecorder.Factory factory =
TimingRecorders.delegatingProductionComponentTimingRecorderFactory(
@@ -249,7 +253,8 @@ public final class TimingRecordersTest {
.thenThrow(new RuntimeException("monkey"));
when(mockProductionComponentTimingRecorderFactoryC.create(any(Object.class)))
.thenThrow(new RuntimeException("monkey"));
- when(mockProductionComponentTimingRecorderA.producerTimingRecorderFor(any(ProducerToken.class)))
+ when(mockProductionComponentTimingRecorderA.producerTimingRecorderFor(
+ nullable(ProducerToken.class)))
.thenReturn(mockProducerTimingRecorderA);
ProductionComponentTimingRecorder.Factory factory =
TimingRecorders.delegatingProductionComponentTimingRecorderFactory(
@@ -340,7 +345,8 @@ public final class TimingRecordersTest {
private void setUpNormalSingleRecorder() {
when(mockProductionComponentTimingRecorderFactory.create(any(Object.class)))
.thenReturn(mockProductionComponentTimingRecorder);
- when(mockProductionComponentTimingRecorder.producerTimingRecorderFor(any(ProducerToken.class)))
+ when(mockProductionComponentTimingRecorder.producerTimingRecorderFor(
+ nullable(ProducerToken.class)))
.thenReturn(mockProducerTimingRecorder);
}
@@ -351,11 +357,14 @@ public final class TimingRecordersTest {
.thenReturn(mockProductionComponentTimingRecorderB);
when(mockProductionComponentTimingRecorderFactoryC.create(any(Object.class)))
.thenReturn(mockProductionComponentTimingRecorderC);
- when(mockProductionComponentTimingRecorderA.producerTimingRecorderFor(any(ProducerToken.class)))
+ when(mockProductionComponentTimingRecorderA.producerTimingRecorderFor(
+ nullable(ProducerToken.class)))
.thenReturn(mockProducerTimingRecorderA);
- when(mockProductionComponentTimingRecorderB.producerTimingRecorderFor(any(ProducerToken.class)))
+ when(mockProductionComponentTimingRecorderB.producerTimingRecorderFor(
+ nullable(ProducerToken.class)))
.thenReturn(mockProducerTimingRecorderB);
- when(mockProductionComponentTimingRecorderC.producerTimingRecorderFor(any(ProducerToken.class)))
+ when(mockProductionComponentTimingRecorderC.producerTimingRecorderFor(
+ nullable(ProducerToken.class)))
.thenReturn(mockProducerTimingRecorderC);
}
}
diff --git a/javatests/dagger/producers/monitoring/internal/MonitorsTest.java b/javatests/dagger/producers/monitoring/internal/MonitorsTest.java
index 47ccccb2c..ada4e335b 100644
--- a/javatests/dagger/producers/monitoring/internal/MonitorsTest.java
+++ b/javatests/dagger/producers/monitoring/internal/MonitorsTest.java
@@ -17,7 +17,8 @@
package dagger.producers.monitoring.internal;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -87,7 +88,7 @@ public final class MonitorsTest {
public void singleMonitor_nullProducerMonitor() {
when(mockProductionComponentMonitorFactory.create(any(Object.class)))
.thenReturn(mockProductionComponentMonitor);
- when(mockProductionComponentMonitor.producerMonitorFor(any(ProducerToken.class)))
+ when(mockProductionComponentMonitor.producerMonitorFor(nullable(ProducerToken.class)))
.thenReturn(null);
ProductionComponentMonitor.Factory factory =
Monitors.delegatingProductionComponentMonitorFactory(
@@ -103,7 +104,7 @@ public final class MonitorsTest {
.thenReturn(mockProductionComponentMonitor);
doThrow(new RuntimeException("monkey"))
.when(mockProductionComponentMonitor)
- .producerMonitorFor(any(ProducerToken.class));
+ .producerMonitorFor(nullable(ProducerToken.class));
ProductionComponentMonitor.Factory factory =
Monitors.delegatingProductionComponentMonitorFactory(
ImmutableList.of(mockProductionComponentMonitorFactory));
@@ -164,7 +165,9 @@ public final class MonitorsTest {
doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).requested();
doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).methodStarting();
doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).methodFinished();
- doThrow(new RuntimeException("monkey")).when(mockProducerMonitor).succeeded(any(Object.class));
+ doThrow(new RuntimeException("monkey"))
+ .when(mockProducerMonitor)
+ .succeeded(nullable(Object.class));
ProductionComponentMonitor.Factory factory =
Monitors.delegatingProductionComponentMonitorFactory(
ImmutableList.of(mockProductionComponentMonitorFactory));
@@ -252,7 +255,7 @@ public final class MonitorsTest {
.thenReturn(mockProductionComponentMonitorA);
when(mockProductionComponentMonitorFactoryB.create(any(Object.class))).thenReturn(null);
when(mockProductionComponentMonitorFactoryC.create(any(Object.class))).thenReturn(null);
- when(mockProductionComponentMonitorA.producerMonitorFor(any(ProducerToken.class)))
+ when(mockProductionComponentMonitorA.producerMonitorFor(nullable(ProducerToken.class)))
.thenReturn(mockProducerMonitorA);
ProductionComponentMonitor.Factory factory =
Monitors.delegatingProductionComponentMonitorFactory(
@@ -288,7 +291,7 @@ public final class MonitorsTest {
doThrow(new RuntimeException("monkey"))
.when(mockProductionComponentMonitorFactoryC)
.create(any(Object.class));
- when(mockProductionComponentMonitorA.producerMonitorFor(any(ProducerToken.class)))
+ when(mockProductionComponentMonitorA.producerMonitorFor(nullable(ProducerToken.class)))
.thenReturn(mockProducerMonitorA);
ProductionComponentMonitor.Factory factory =
Monitors.delegatingProductionComponentMonitorFactory(
@@ -390,7 +393,9 @@ public final class MonitorsTest {
doThrow(new RuntimeException("monkey")).when(mockProducerMonitorA).requested();
doThrow(new RuntimeException("monkey")).when(mockProducerMonitorA).methodStarting();
doThrow(new RuntimeException("monkey")).when(mockProducerMonitorB).methodFinished();
- doThrow(new RuntimeException("monkey")).when(mockProducerMonitorC).succeeded(any(Object.class));
+ doThrow(new RuntimeException("monkey"))
+ .when(mockProducerMonitorC)
+ .succeeded(nullable(Object.class));
ProductionComponentMonitor.Factory factory =
Monitors.delegatingProductionComponentMonitorFactory(
ImmutableList.of(
@@ -465,7 +470,7 @@ public final class MonitorsTest {
private void setUpNormalSingleMonitor() {
when(mockProductionComponentMonitorFactory.create(any(Object.class)))
.thenReturn(mockProductionComponentMonitor);
- when(mockProductionComponentMonitor.producerMonitorFor(any(ProducerToken.class)))
+ when(mockProductionComponentMonitor.producerMonitorFor(nullable(ProducerToken.class)))
.thenReturn(mockProducerMonitor);
}
@@ -476,11 +481,11 @@ public final class MonitorsTest {
.thenReturn(mockProductionComponentMonitorB);
when(mockProductionComponentMonitorFactoryC.create(any(Object.class)))
.thenReturn(mockProductionComponentMonitorC);
- when(mockProductionComponentMonitorA.producerMonitorFor(any(ProducerToken.class)))
+ when(mockProductionComponentMonitorA.producerMonitorFor(nullable(ProducerToken.class)))
.thenReturn(mockProducerMonitorA);
- when(mockProductionComponentMonitorB.producerMonitorFor(any(ProducerToken.class)))
+ when(mockProductionComponentMonitorB.producerMonitorFor(nullable(ProducerToken.class)))
.thenReturn(mockProducerMonitorB);
- when(mockProductionComponentMonitorC.producerMonitorFor(any(ProducerToken.class)))
+ when(mockProductionComponentMonitorC.producerMonitorFor(nullable(ProducerToken.class)))
.thenReturn(mockProducerMonitorC);
}
}
diff --git a/javatests/dagger/spi/BUILD b/javatests/dagger/spi/BUILD
index a94461b07..849037b6f 100644
--- a/javatests/dagger/spi/BUILD
+++ b/javatests/dagger/spi/BUILD
@@ -15,8 +15,6 @@
# Description:
# Tests for the Dagger SPI
-package(default_visibility = ["//:src"])
-
load("//:test_defs.bzl", "GenJavaTests")
load(
"//:build_defs.bzl",
@@ -24,6 +22,8 @@ load(
"DOCLINT_REFERENCES",
)
+package(default_visibility = ["//:src"])
+
GenJavaTests(
name = "spi_tests",
srcs = glob(["*.java"]),
@@ -32,11 +32,11 @@ GenJavaTests(
deps = [
"//java/dagger:core",
"//java/dagger/internal/codegen:processor",
- "//java/dagger/model",
+ "//java/dagger/internal/guava:base",
+ "//java/dagger/internal/guava:collect",
"//java/dagger/spi",
"@google_bazel_common//third_party/java/auto:service",
"@google_bazel_common//third_party/java/compile_testing",
- "@google_bazel_common//third_party/java/guava",
"@google_bazel_common//third_party/java/jsr330_inject",
"@google_bazel_common//third_party/java/junit",
"@google_bazel_common//third_party/java/truth",
diff --git a/javatests/dagger/spi/FailingPlugin.java b/javatests/dagger/spi/FailingPlugin.java
index 8fd0e359d..2d13e2176 100644
--- a/javatests/dagger/spi/FailingPlugin.java
+++ b/javatests/dagger/spi/FailingPlugin.java
@@ -83,7 +83,6 @@ public final class FailingPlugin implements BindingGraphPlugin {
.forEach(
edge -> diagnosticReporter.reportDependency(ERROR, edge, "Bad Dependency: %s", edge));
}
-
}
@Override
diff --git a/javatests/dagger/spi/SpiPluginTest.java b/javatests/dagger/spi/SpiPluginTest.java
index 613a91507..1df8cd1b0 100644
--- a/javatests/dagger/spi/SpiPluginTest.java
+++ b/javatests/dagger/spi/SpiPluginTest.java
@@ -53,7 +53,9 @@ public final class SpiPluginTest {
javac()
.withProcessors(new ComponentProcessor())
.withOptions(
- "-Aerror_on_binding=java.lang.Integer", "-Adagger.fullBindingGraphValidation=ERROR")
+ "-Aerror_on_binding=java.lang.Integer",
+ "-Adagger.fullBindingGraphValidation=ERROR",
+ "-Adagger.pluginsVisitFullBindingGraphs=ENABLED")
.compile(module);
assertThat(compilation).failed();
assertThat(compilation)
@@ -97,7 +99,7 @@ public final class SpiPluginTest {
.hadErrorContaining(
message(
"[FailingPlugin] Bad Binding: @Inject test.Foo()",
- " test.Foo is provided at",
+ " test.Foo is requested at",
" test.TestComponent.foo()"))
.inFile(component)
.onLineContaining("interface TestComponent");
@@ -185,7 +187,7 @@ public final class SpiPluginTest {
.hadErrorContaining(
message(
"[FailingPlugin] Bad Dependency: test.TestComponent.entryPoint() (entry point)",
- " test.EntryPoint is provided at",
+ " test.EntryPoint is requested at",
" test.TestComponent.entryPoint()"))
.inFile(component)
.onLineContaining("interface TestComponent");
@@ -195,7 +197,7 @@ public final class SpiPluginTest {
"[FailingPlugin] Bad Dependency: test.EntryPoint(…, dup1, …)",
" test.Duplicated is injected at",
" test.EntryPoint(…, dup1, …)",
- " test.EntryPoint is provided at",
+ " test.EntryPoint is requested at",
" test.TestComponent.entryPoint()"))
.inFile(component)
.onLineContaining("interface TestComponent");
@@ -205,7 +207,7 @@ public final class SpiPluginTest {
"[FailingPlugin] Bad Dependency: test.EntryPoint(…, dup2)",
" test.Duplicated is injected at",
" test.EntryPoint(…, dup2)",
- " test.EntryPoint is provided at",
+ " test.EntryPoint is requested at",
" test.TestComponent.entryPoint()"))
.inFile(component)
.onLineContaining("interface TestComponent");
@@ -220,7 +222,7 @@ public final class SpiPluginTest {
" test.Foo(inFooDep)",
" test.Foo is injected at",
" test.EntryPoint(foo, …)",
- " test.EntryPoint is provided at",
+ " test.EntryPoint is requested at",
" test.TestComponent.entryPoint()",
"The following other entry points also depend on it:",
" test.TestComponent.chain()"))
@@ -280,7 +282,7 @@ public final class SpiPluginTest {
message(
"[FailingPlugin] Bad Dependency: "
+ "test.TestSubcomponent.childEntryPoint() (entry point)",
- " test.EntryPoint is provided at",
+ " test.EntryPoint is requested at",
" test.TestSubcomponent.childEntryPoint()"
+ " [test.TestComponent → test.TestSubcomponent]"))
.inFile(component)
@@ -295,7 +297,7 @@ public final class SpiPluginTest {
"[FailingPlugin] Bad Dependency: test.EntryPoint(foo)",
" test.Foo is injected at",
" test.EntryPoint(foo)",
- " test.EntryPoint is provided at",
+ " test.EntryPoint is requested at",
" test.TestSubcomponent.childEntryPoint() "
+ "[test.TestComponent → test.TestSubcomponent]"))
.inFile(component)
@@ -472,7 +474,7 @@ public final class SpiPluginTest {
" test.Chain2(chain)",
" test.Chain2 is injected at",
" test.Chain1(chain)",
- " test.Chain1 is provided at",
+ " test.Chain1 is requested at",
" test.TestComponent.chain()",
"The following other entry points also depend on it:",
" test.TestSubcomponent.exposedOnSubcomponent() "
diff --git a/lib/NOTICE b/lib/NOTICE
deleted file mode 100644
index d64569567..000000000
--- a/lib/NOTICE
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/lib/auto-common-0.10-sources.jar b/lib/auto-common-0.10-sources.jar
deleted file mode 100644
index 59bf056ce..000000000
--- a/lib/auto-common-0.10-sources.jar
+++ /dev/null
Binary files differ
diff --git a/lib/auto-common-0.10-sources.jar.asc b/lib/auto-common-0.10-sources.jar.asc
deleted file mode 100644
index ecc33399c..000000000
--- a/lib/auto-common-0.10-sources.jar.asc
+++ /dev/null
@@ -1,11 +0,0 @@
------BEGIN PGP SIGNATURE-----
-Version: GnuPG v1
-
-iQEcBAABAgAGBQJaYLeuAAoJEF4feafCmGYeVzIH/A0rkeHli229WFOj5c3HnFqK
-hbyy32JRw8GZXTqNFamkuxWDF03jHwQn0ymp0nxRQ+I3JRTzbfuTnT73e+kiNSI8
-02PowzbrghgsuaifiOGpHaO/FRSBaexzjE1TbZncO6jM8RIg1J7GPqgUenRwA5YT
-7VL7Ig+5G9bXOZMcQ4OuHwqi2O1rSfnSDDIFGlDqmKNiJWHi4KijxNred9CvUGeW
-7zFBOzQkqUqm7Vs2MHmBvNM79fcD1F1SFa2I7+p6/oD1ecC0TAHLdlLhR1/sLBaG
-iw3uQeeflVZ6ilzJg93Rl2kNUw77nnywkt12SY76j1tpF5Ny2l8tid3bBrQEUPE=
-=0e1a
------END PGP SIGNATURE-----
diff --git a/lib/auto-common-0.10-sources.jar.sha1 b/lib/auto-common-0.10-sources.jar.sha1
deleted file mode 100644
index b9b3bee4b..000000000
--- a/lib/auto-common-0.10-sources.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-913c8de9604380c6e135086132adb26c77fa6c53 \ No newline at end of file
diff --git a/lib/auto-common-0.10.jar b/lib/auto-common-0.10.jar
deleted file mode 100644
index 8cbfa7295..000000000
--- a/lib/auto-common-0.10.jar
+++ /dev/null
Binary files differ
diff --git a/lib/auto-common-0.10.jar.asc b/lib/auto-common-0.10.jar.asc
deleted file mode 100644
index 6d81bdf4b..000000000
--- a/lib/auto-common-0.10.jar.asc
+++ /dev/null
@@ -1,11 +0,0 @@
------BEGIN PGP SIGNATURE-----
-Version: GnuPG v1
-
-iQEcBAABAgAGBQJaYLeuAAoJEF4feafCmGYe/1gIAInqdd+9NqxfOKRw6ujpL3nT
-XmmsP9gR/Tvvi3xRKj8PjcO9ydO2IfQ9ySAS6qbKuS9SIQn+Plq+6E+rwYkKy5t/
-MZ7Ff59XgMW0uxFEA2zUbcprgQyR6M4A31MYVmKuZ2fGBs5OsoN0+sZPCDmo4EzN
-TIgZ/LdNUWXLIsIRAKzCnUkYjlnNHKcJbW527obNH0UF/TBARrEAtmfCbKT6f91/
-zl1GtnxNR/9LVRxErFHQPcHrRxVP72xRHjdTCJHsL0t5cJJPr9maqkXq8DAyyptU
-XcjisvelIubTqnBDXcqJBIM3beGYPa5rot+3NjnNbylbfSqMED1dSGJL/fypY/U=
-=5BoX
------END PGP SIGNATURE-----
diff --git a/lib/auto-common-0.10.jar.sha1 b/lib/auto-common-0.10.jar.sha1
deleted file mode 100644
index 2056a80b3..000000000
--- a/lib/auto-common-0.10.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-c8f153ebe04a17183480ab4016098055fb474364 \ No newline at end of file
diff --git a/lib/auto-factory-1.0-beta6-sources.jar b/lib/auto-factory-1.0-beta6-sources.jar
deleted file mode 100644
index 56c69090e..000000000
--- a/lib/auto-factory-1.0-beta6-sources.jar
+++ /dev/null
Binary files differ
diff --git a/lib/auto-factory-1.0-beta6-sources.jar.asc b/lib/auto-factory-1.0-beta6-sources.jar.asc
deleted file mode 100644
index be05c6e97..000000000
--- a/lib/auto-factory-1.0-beta6-sources.jar.asc
+++ /dev/null
@@ -1,11 +0,0 @@
------BEGIN PGP SIGNATURE-----
-
-iQEzBAABCgAdFiEEMoi4voUS1sDKGFJoxR5svH/0bwsFAlvN1rYACgkQxR5svH/0
-bwsyngf8C60gBeqMcbTJ05w6IccmLONiVEBfXXVASLGxJRW6A4C6CZCOaeu/JTGV
-RlyKSL4RxT6LdN4IE1k/b+SWwhAGxES6n2J1G9TFq/SiRSEjPL2a41qRynjXo9Sx
-D/jWX4flGS4fT229EDx18d+Vy3So5+jPNLN7KyB4E9yBZfnTTmeENsgvs0Y1jw5D
-MNK73Nwwg+f7R42J+07FauRU8YXPYcG1UYZcnmAeHNDmwfL1UOsfSzrglHbU59mw
-MrupMb3h5VSBZcqknQUBbPQUUWo7e+jSJZKDB750CxBNSBfF3tN2D1vJ1/ynCIDA
-i+iY39xggnIbyKTPj1/aOGTBVfiVJQ==
-=xs9M
------END PGP SIGNATURE-----
diff --git a/lib/auto-factory-1.0-beta6-sources.jar.sha1 b/lib/auto-factory-1.0-beta6-sources.jar.sha1
deleted file mode 100644
index 9b5c0a806..000000000
--- a/lib/auto-factory-1.0-beta6-sources.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-fab31580230ac3001579238a8857a74bd045e3b9 \ No newline at end of file
diff --git a/lib/auto-factory-1.0-beta6.jar b/lib/auto-factory-1.0-beta6.jar
deleted file mode 100644
index e47130f2e..000000000
--- a/lib/auto-factory-1.0-beta6.jar
+++ /dev/null
Binary files differ
diff --git a/lib/auto-factory-1.0-beta6.jar.asc b/lib/auto-factory-1.0-beta6.jar.asc
deleted file mode 100644
index 5da4253a6..000000000
--- a/lib/auto-factory-1.0-beta6.jar.asc
+++ /dev/null
@@ -1,11 +0,0 @@
------BEGIN PGP SIGNATURE-----
-
-iQEzBAABCgAdFiEEMoi4voUS1sDKGFJoxR5svH/0bwsFAlvN1rIACgkQxR5svH/0
-bwt0VAf/S4AELEOE7cdcSkSF8vNCUhHgJbyRIb9SLT9Jh6ZXt0sVt1ZMB4Jcx9WL
-GfIwCAZhPcidTF3yVRvK0Sj2zHq53bGNsejL5BLO73oSoVowv7NV96v42zbBtMDU
-K6AI8G7lXEHYDlW4gwiyz/5CjP8VYSJgQ5vuC81rNL0vrBY0S30MlzHmCLjCvE2b
-DpV+thGWluhStwJRwvQArtUDgXYW6BPGetls/Mr0LKiSvz9uS9EvRvcQHiiwqxPu
-Jj5vheKyyeaRq7BOOswoUvfidr+UrNBt/Jit3L2kuo5n4n7sfb6irR610X8mMMN/
-kx5cA78f+p7XHbLZh6w3pySp+HCvKw==
-=1KAq
------END PGP SIGNATURE-----
diff --git a/lib/auto-factory-1.0-beta6.jar.sha1 b/lib/auto-factory-1.0-beta6.jar.sha1
deleted file mode 100644
index 57a5e6195..000000000
--- a/lib/auto-factory-1.0-beta6.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-58c804763a4d80c0884ac8a740fcff4d61da72bc \ No newline at end of file
diff --git a/lib/auto-service-1.0-rc5-sources.jar b/lib/auto-service-1.0-rc5-sources.jar
deleted file mode 100644
index 8a05c9d38..000000000
--- a/lib/auto-service-1.0-rc5-sources.jar
+++ /dev/null
Binary files differ
diff --git a/lib/auto-service-1.0-rc5-sources.jar.asc b/lib/auto-service-1.0-rc5-sources.jar.asc
deleted file mode 100644
index 08ea73224..000000000
--- a/lib/auto-service-1.0-rc5-sources.jar.asc
+++ /dev/null
@@ -1,11 +0,0 @@
------BEGIN PGP SIGNATURE-----
-
-iQEzBAABCgAdFiEEMoi4voUS1sDKGFJoxR5svH/0bwsFAlyZH5AACgkQxR5svH/0
-bwuBWwf/QMOlEF0td8ykweHGoOt5B6JmBLk4lPNhZDZdGmvqn+RzbWHo7F5QoBL6
-RQ+Wn97UDroLrIeMRsWvOzL/dA/kMhE2tNX8OKP1kaU7d1ahSkY6PRrPrdcf34IA
-Ku62psSXM5L78HdmhfVpf6UBsa3QJ9ZtLrBMjubxJ7aVrHCTIPP8obVgfgFWHnZD
-nGiynMUNXT6H+L9mDtkWHnVEkC07VBTCFr+6HA3TIf76KQ8LiIj6h+7l6KlPz48h
-eyaElzUUn5deTIG7jrPC2SaVhC0fKu/j1ZazN8tj/25+v9Q9mgZieYYEtiKjrEJU
-tli80ajN26E2NduLldkSVPt6N1Nf/w==
-=6nn7
------END PGP SIGNATURE-----
diff --git a/lib/auto-service-1.0-rc5-sources.jar.sha1 b/lib/auto-service-1.0-rc5-sources.jar.sha1
deleted file mode 100644
index 3638b53c2..000000000
--- a/lib/auto-service-1.0-rc5-sources.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-76bf7fbfc5a924f13115005a134546c4e2d1b245 \ No newline at end of file
diff --git a/lib/auto-service-1.0-rc5.jar b/lib/auto-service-1.0-rc5.jar
deleted file mode 100644
index 8c43e8ff2..000000000
--- a/lib/auto-service-1.0-rc5.jar
+++ /dev/null
Binary files differ
diff --git a/lib/auto-service-1.0-rc5.jar.2 b/lib/auto-service-1.0-rc5.jar.2
deleted file mode 100644
index 8c43e8ff2..000000000
--- a/lib/auto-service-1.0-rc5.jar.2
+++ /dev/null
Binary files differ
diff --git a/lib/auto-service-1.0-rc5.jar.asc b/lib/auto-service-1.0-rc5.jar.asc
deleted file mode 100644
index e1d016391..000000000
--- a/lib/auto-service-1.0-rc5.jar.asc
+++ /dev/null
@@ -1,11 +0,0 @@
------BEGIN PGP SIGNATURE-----
-
-iQEzBAABCgAdFiEEMoi4voUS1sDKGFJoxR5svH/0bwsFAlyZH48ACgkQxR5svH/0
-bwuZSAgAgmJ8gEx+MLtdt8IJY0ZGZtzntCOv2kTmieTQdwLKbmEc/WeQBXZeAWjb
-xKctEnesbNGwJY5jpPBiQH0nDd0MyIOc25gCvug2ezveo9eNe9ptOQFi+4gsG3mv
-0SGD9ZnRkzW8wNyMMWdBUJYdGPJp/FshsOVajBVsMDSev3OBxw8qfT4ZqhTO3LN6
-UnFydeqtbukqBiQRBrWEO7zXDmeHP+6GMWOD4Tkt60VRrSo7Sk6WeUkSJcB0rrmw
-+/blQ3jBlN2/ummOc1dWUu7EyBoWhJhChQDQAuwev5oMMyQnhTLiFcHPvoKWFTlO
-GsBFENSqqxlW4j2ZYpoMX42inuaqbQ==
-=AMUZ
------END PGP SIGNATURE-----
diff --git a/lib/auto-service-1.0-rc5.jar.sha1 b/lib/auto-service-1.0-rc5.jar.sha1
deleted file mode 100644
index 2edbbf411..000000000
--- a/lib/auto-service-1.0-rc5.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-d25246bae325b4bcc63b55d6d782515fac32215a \ No newline at end of file
diff --git a/lib/auto-service-annotations-1.0-rc5-sources.jar b/lib/auto-service-annotations-1.0-rc5-sources.jar
deleted file mode 100644
index 72fd4a9a8..000000000
--- a/lib/auto-service-annotations-1.0-rc5-sources.jar
+++ /dev/null
Binary files differ
diff --git a/lib/auto-service-annotations-1.0-rc5-sources.jar.asc b/lib/auto-service-annotations-1.0-rc5-sources.jar.asc
deleted file mode 100644
index 00586237b..000000000
--- a/lib/auto-service-annotations-1.0-rc5-sources.jar.asc
+++ /dev/null
@@ -1,11 +0,0 @@
------BEGIN PGP SIGNATURE-----
-
-iQEzBAABCgAdFiEEMoi4voUS1sDKGFJoxR5svH/0bwsFAlyZH4IACgkQxR5svH/0
-bwtnEwgAntYeAk/i4kaPhOdKCni4pZpkJ3WcEaDIiZROIvxn3GLFxDp1+WaFOuZS
-kYrqKLmDJdOwEK31FGP/XjsedmB6PP90pp01apaw2XS7pAzXQyNGMOypgCntJsN6
-ZkQYNLum8HQ6XXvEQh9j0eKU/9D+x7DRaxQZ69GkjFS1EwpytEYIEdFsjvHz3SYW
-G15++0H5+l7ZyNjXCs//x6tQ73nqhEu6UylwR4A9YvDuZhpMoFPNvB1+L6mlARgi
-cDRtm8hGLhsNeIYqGmbUEzwwKtKY/n7NwJX1zpXAg86/9E8R1xdbdzfGL6ctqQal
-w3WSYcNnmiZQfPinZuGHnrqDk/rN2w==
-=lgp6
------END PGP SIGNATURE-----
diff --git a/lib/auto-service-annotations-1.0-rc5-sources.jar.sha1 b/lib/auto-service-annotations-1.0-rc5-sources.jar.sha1
deleted file mode 100644
index fc01aa365..000000000
--- a/lib/auto-service-annotations-1.0-rc5-sources.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-a2e50e3ba1f9a88f89142e7ea9a0f5380574f4e4 \ No newline at end of file
diff --git a/lib/auto-service-annotations-1.0-rc5.jar b/lib/auto-service-annotations-1.0-rc5.jar
deleted file mode 100644
index fbc08abae..000000000
--- a/lib/auto-service-annotations-1.0-rc5.jar
+++ /dev/null
Binary files differ
diff --git a/lib/auto-service-annotations-1.0-rc5.jar.asc b/lib/auto-service-annotations-1.0-rc5.jar.asc
deleted file mode 100644
index b7e43cec7..000000000
--- a/lib/auto-service-annotations-1.0-rc5.jar.asc
+++ /dev/null
@@ -1,11 +0,0 @@
------BEGIN PGP SIGNATURE-----
-
-iQEzBAABCgAdFiEEMoi4voUS1sDKGFJoxR5svH/0bwsFAlyZH4EACgkQxR5svH/0
-bwtNcwf+IvcoHSI7AvrM+z6Lhqes8wEdU9Q59ixakvEos/umjYwCXoGJyAG/w8N5
-4woRDJRg8KgWC0Kls7CGs5ZwplQTLRte/SmXWux8CV0/3v6OlIP4voWvXBueqKST
-gNN/Miy0Qx5uX1Qwh0inqlFF7jOj+IuTofQ4ZM3UjZPLVYhSfS/wTwLmNWNawQq7
-b/PkxaYgC1RuNv9onA8Vru2UCIlH8NFP9iPqPMwhRVVxoSaZzo5NxrRNRPZUfPzF
-GUH/SRDOfXHJ1joeZRK21s8adOGzZZJsSxxZ393xx1jl5qLdoowjTtENgc9gOSQG
-FzxOfWCoWncsJP10KfyYRV70JnJMrQ==
-=dWz/
------END PGP SIGNATURE-----
diff --git a/lib/auto-service-annotations-1.0-rc5.jar.sha1 b/lib/auto-service-annotations-1.0-rc5.jar.sha1
deleted file mode 100644
index 4c48b47e9..000000000
--- a/lib/auto-service-annotations-1.0-rc5.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-6ea999af2b6262a7179a09c51a3d54e7b40a3833 \ No newline at end of file
diff --git a/lib/auto-value-1.6.5-sources.jar b/lib/auto-value-1.6.5-sources.jar
deleted file mode 100644
index 7d08fa773..000000000
--- a/lib/auto-value-1.6.5-sources.jar
+++ /dev/null
Binary files differ
diff --git a/lib/auto-value-1.6.5-sources.jar.asc b/lib/auto-value-1.6.5-sources.jar.asc
deleted file mode 100644
index 36551c134..000000000
--- a/lib/auto-value-1.6.5-sources.jar.asc
+++ /dev/null
@@ -1,12 +0,0 @@
------BEGIN PGP SIGNATURE-----
-Comment: GPGTools - https://gpgtools.org
-
-iQEzBAABCgAdFiEEx75bzJ/sFVGM/aiCsPNxD6ZJAOcFAlyuMe0ACgkQsPNxD6ZJ
-AOc+4wgAkYRN5mdnQ54oLb/YGL9jnU7tdcFnq73ddH9wJH9cTGE8dAHIlNT4lEjy
-ligUXdi6YbuAC2Fjs7nMZiSOjEEDlKRozbwE82WOiIdV/MltMJgFCdBbVpszHFmu
-NWxDq5KocdgLODHwmIWJ0Xey6M1rjVZEshNf/U70604eEbGl9Hu0aTVFFVcASMM5
-CC38/X/G72Ja1Cz404WqGpmAsrHgDHdcPUVT3xxDReV1B+yPKxazzuKZA8/+KkWP
-RddLifE3W2NYnLVX49DS9iWCBiEbh5P6jtHvQSiyXIawexhQJGhjQMwHoi0+jHJd
-INp5YyrB64PHqaSgtuXhwgTfXmxkMA==
-=5Cw9
------END PGP SIGNATURE-----
diff --git a/lib/auto-value-1.6.5-sources.jar.md5 b/lib/auto-value-1.6.5-sources.jar.md5
deleted file mode 100644
index ca18ea4b6..000000000
--- a/lib/auto-value-1.6.5-sources.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-4a433a939d3f77766c785288d5c24c10 \ No newline at end of file
diff --git a/lib/auto-value-1.6.5-sources.jar.sha1 b/lib/auto-value-1.6.5-sources.jar.sha1
deleted file mode 100644
index 17f16a87c..000000000
--- a/lib/auto-value-1.6.5-sources.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-bfc251753f9bbdd8855825361d5f8c7fec8a1471 \ No newline at end of file
diff --git a/lib/auto-value-1.6.5.jar b/lib/auto-value-1.6.5.jar
deleted file mode 100644
index be17cd173..000000000
--- a/lib/auto-value-1.6.5.jar
+++ /dev/null
Binary files differ
diff --git a/lib/auto-value-1.6.5.jar.asc b/lib/auto-value-1.6.5.jar.asc
deleted file mode 100644
index f0cdc3936..000000000
--- a/lib/auto-value-1.6.5.jar.asc
+++ /dev/null
@@ -1,12 +0,0 @@
------BEGIN PGP SIGNATURE-----
-Comment: GPGTools - https://gpgtools.org
-
-iQEzBAABCgAdFiEEx75bzJ/sFVGM/aiCsPNxD6ZJAOcFAlyuMewACgkQsPNxD6ZJ
-AOd5ygf8DOjgClYv8hCaiHiu2mDwFV4s24SbyvSZrAeBrHOoY6/E7leSqilJ+mF9
-3kjDv/VtHTv9Ds2cX2daH5hnvuYIwiCzdze3ZWdKJ+PtWsKpqEAevfEMRmSPPUV4
-ZZ5Zd6VZspD+rUViKfwF8ZW1zjKjdcIPbhuiYR4TF718kPL8WCV2bGruYidjYBoe
-hzLUUqY0rv+IwV+OycbyZwtmx1DVCTkYlDdepSyswr2RGb/UFIf44E0koboI+Xue
-6nF+w6AaOaufjdqe2ObIWNa2tsPTj4KABh3tHmOMs35x367/PzwQmh0I25gFAQrX
-KYo6dYVZeLWmNpdIJxeMIZRok0MAKA==
-=KnCZ
------END PGP SIGNATURE-----
diff --git a/lib/auto-value-1.6.5.jar.md5 b/lib/auto-value-1.6.5.jar.md5
deleted file mode 100644
index 11a5e9d59..000000000
--- a/lib/auto-value-1.6.5.jar.md5
+++ /dev/null
@@ -1 +0,0 @@
-d3730b8e2f9c6f62153181618a13f3a2 \ No newline at end of file
diff --git a/lib/auto-value-1.6.5.jar.sha1 b/lib/auto-value-1.6.5.jar.sha1
deleted file mode 100644
index edf62d845..000000000
--- a/lib/auto-value-1.6.5.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-816872c85048f36a67a276ef7a49cc2e4595711c \ No newline at end of file
diff --git a/lib/auto-value-annotations-1.6.5-sources.jar b/lib/auto-value-annotations-1.6.5-sources.jar
deleted file mode 100644
index b1a8b08b4..000000000
--- a/lib/auto-value-annotations-1.6.5-sources.jar
+++ /dev/null
Binary files differ
diff --git a/lib/auto-value-annotations-1.6.5-sources.jar.asc b/lib/auto-value-annotations-1.6.5-sources.jar.asc
deleted file mode 100644
index 81a451545..000000000
--- a/lib/auto-value-annotations-1.6.5-sources.jar.asc
+++ /dev/null
@@ -1,12 +0,0 @@
------BEGIN PGP SIGNATURE-----
-Comment: GPGTools - https://gpgtools.org
-
-iQEzBAABCgAdFiEEx75bzJ/sFVGM/aiCsPNxD6ZJAOcFAlyuMdUACgkQsPNxD6ZJ
-AOfNYwgAqoJ9hi7JyS1iwf646TPLPbtRaaRzl/w9hrZntVlMiHL5Ov+LHq6WW1tS
-IWqz9K6dbEDW4O3WbNXUsiWTGzUvkNc/bjQOY4MTCthTj/LO9PqHVSDGgWo/Y37c
-jazTbUNxctFSziu9mVZdw1VdCI65LVCbhSaOeIc7Msmr/ALtZ1pBGdJ8dzxuC3Dl
-BeT2khADj7C13Kicu7QtZfZypbTqB6JnjAwMO56c4pRviIqRpJsOKHbJM1KBxms+
-6JGv63XTBP22f83XX38wAAPocT5NZBn+lZcDcbC6Mh0NIhaISdx73ujWtTwHX5fC
-x0NYKIIBdyEb5GkPeD1D/IjB4w8o5w==
-=Hbou
------END PGP SIGNATURE-----
diff --git a/lib/auto-value-annotations-1.6.5-sources.jar.sha1 b/lib/auto-value-annotations-1.6.5-sources.jar.sha1
deleted file mode 100644
index 897752c29..000000000
--- a/lib/auto-value-annotations-1.6.5-sources.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-3499fd80025705c502699d1154c4b9631cb7a95e \ No newline at end of file
diff --git a/lib/auto-value-annotations-1.6.5.jar b/lib/auto-value-annotations-1.6.5.jar
deleted file mode 100644
index f68a01367..000000000
--- a/lib/auto-value-annotations-1.6.5.jar
+++ /dev/null
Binary files differ
diff --git a/lib/auto-value-annotations-1.6.5.jar.asc b/lib/auto-value-annotations-1.6.5.jar.asc
deleted file mode 100644
index 13569957a..000000000
--- a/lib/auto-value-annotations-1.6.5.jar.asc
+++ /dev/null
@@ -1,12 +0,0 @@
------BEGIN PGP SIGNATURE-----
-Comment: GPGTools - https://gpgtools.org
-
-iQEzBAABCgAdFiEEx75bzJ/sFVGM/aiCsPNxD6ZJAOcFAlyuMdQACgkQsPNxD6ZJ
-AOeeQQf/cRfXuh8RCDR5WWAHPXpK9JzA5OEEvtXl13uSqpcU+f2/APNOmG9tJz2B
-53bp6d18knaoSAWtC1Hl2kM8eZPYNlpB+MC8VJ+/JAZMf2arLMKGuvii9eYmGrvJ
-kXfRHqWgZQMQIKrd2ne/PMgnmixLPSF6HtyFh2JN/LbjR7ipQR3VAJe89QGmIFFf
-njSptIgKuDauQrjq//yXpWRyhUNheAH42y3aIaUvydcSOLsGIqQfTJLOe3sSMxlU
-d0fJWP47uW04y2j6kXv4SYKXGnBrv2M+c/6YRriPv69tSBc+2DY6SpMZsYdV9Car
-pDM+7m5lxCN8mI/KRRNcXaSR9GLJ2A==
-=ey6m
------END PGP SIGNATURE-----
diff --git a/lib/auto-value-annotations-1.6.5.jar.sha1 b/lib/auto-value-annotations-1.6.5.jar.sha1
deleted file mode 100644
index 858c96e71..000000000
--- a/lib/auto-value-annotations-1.6.5.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-c3dad10377f0e2242c9a4b88e9704eaf79103679 \ No newline at end of file
diff --git a/lib/google-java-format-1.7-all-deps.jar b/lib/google-java-format-1.7-all-deps.jar
deleted file mode 100644
index e2d40de46..000000000
--- a/lib/google-java-format-1.7-all-deps.jar
+++ /dev/null
Binary files differ
diff --git a/lib/google-java-format-1.7-all-deps.jar.asc b/lib/google-java-format-1.7-all-deps.jar.asc
deleted file mode 100644
index 73ae0e2a1..000000000
--- a/lib/google-java-format-1.7-all-deps.jar.asc
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN PGP SIGNATURE-----
-
-iQIzBAABCgAdFiEE53QXrBlBYKP6vQSWmiWcfuY2xe0FAlw2hwoACgkQmiWcfuY2
-xe22Ww//YIHLWoQApuSmLdq4A1fcNOYbSpm6J9I2J1i2VoK84XSXP1XJTWsoYNbx
-Y3Xx6I8p28AxH7LOWcdtds9enbF06blzz+1CxuSxvN32M7DDursiihsUYU9wmZVU
-7V1ZYBtaYTYYw8C5U+UchcIVJrHdG9KrAiBcn1s76gigNAtCYV/1PyryupS1wcDZ
-bdj48mI6adcnIpV+9D9W4rLuXw2Hg+ewktXTj3HYqkg+5Nvc/zP9ueBvqEsyh5Iz
-3+69egblhuN7gRaEWarlYfJ2n/kF9T/UMAkQrWD8+9cOgUcgTHkNADNOLpmDH6t7
-OYvZhgwQT920ZJgGP8e3DGyvMxBMxlr19ciTT19HL5qglyhFm/SeHvFdpaHkIjb9
-CmASWrlkALdEv2gPx+m79EJifyK5tYtViZgLhfjwYkS/opw5TfJX0+Ngh9ehgune
-c6TgEIzRQAEQ/9QoVk2crGT/lhXnJI3Zid7cSP2/fRdui91KaKGi9qFvXyJf7XKI
-vH0De+e3yZN/GXLYyQSUzmdQU8HMF9iuc81CT1ANgU+e9HU5k9FWSPHrA3gN7CKt
-/a3qIsIBpcdKe9zcPxSoEqRzVNept4IhszgiIVxZPNrPShzCRbS1sGURoOTbSFSP
-+aKWNFoswX/RANGQVajOyfF5mJ3lB4N0u2eI0HIR3NzNx0HN+2Q=
-=+/96
------END PGP SIGNATURE-----
diff --git a/lib/google-java-format-1.7-all-deps.jar.sha1 b/lib/google-java-format-1.7-all-deps.jar.sha1
deleted file mode 100644
index b247a7c85..000000000
--- a/lib/google-java-format-1.7-all-deps.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-b6d34a51e579b08db7c624505bdf9af4397f1702 \ No newline at end of file
diff --git a/lib/google-java-format-1.7-sources.jar b/lib/google-java-format-1.7-sources.jar
deleted file mode 100644
index 70fca1be0..000000000
--- a/lib/google-java-format-1.7-sources.jar
+++ /dev/null
Binary files differ
diff --git a/lib/google-java-format-1.7-sources.jar.asc b/lib/google-java-format-1.7-sources.jar.asc
deleted file mode 100644
index 713333c51..000000000
--- a/lib/google-java-format-1.7-sources.jar.asc
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN PGP SIGNATURE-----
-
-iQIzBAABCgAdFiEE53QXrBlBYKP6vQSWmiWcfuY2xe0FAlw2hwsACgkQmiWcfuY2
-xe1j6BAAsDY9DTTcjRtrT6AiZchELcJleOdqK3TRdbl193Mtw5eR+BdKqtcLZSmZ
-1EqYNcldOu9wgbZFpFL99xWNAPOt8tTU9hTsZvrRN0e5CwKXrlJl0MeJi/6yYquG
-FerYGZuDMMEza1q9D2TG5erLOjDEWVLjkSs3jw8mSyMvN0OJFhhMrU4sqhlXMGNV
-lDxeYXLXr+QtIi0tXAUQ5XSwtR7eukSevA7n3zQJKfYMlFbG82lMGJccfazJpdPf
-cJLAGY9/q3RVZx5vk00kz3sDmL+rMU01Lh+CHwT9D8ASRfzjkssHuKaMYBysuh7s
-YZ6X3Txvtkzf3fJjYl5jID3jEui82iR5jv5ncZFZFCsZsTRM0sLAP5/KeX128dHM
-fJzVH4GIUvOqt9aIDKQ+3aNsARsIH2Z2AHymUxVZoP33V9HoC8x0ghxM0xohvkh5
-L+VgUOEjekOneqWD1QEF+HD7S3hpy36+g37PYGcQRRaCIDn00Se+UbJ9RZrUVdvu
-JgmVXcpagrLmcSnkG265zdDW9Vu7vjAKhxjLXmbE0aowzdo/HZ+u3R2hpZbOyv33
-NmakhTwaUtQtDm34GVPqoypnu85va27vxVDQgTsk3im9M1l/UP65NidPuuSk7iOI
-UECD7wc6Ddh80jmDlXdlxSNG+zzGp95gS5UVoDXgwNEok7UgrY4=
-=zj6k
------END PGP SIGNATURE-----
diff --git a/lib/google-java-format-1.7-sources.jar.sha1 b/lib/google-java-format-1.7-sources.jar.sha1
deleted file mode 100644
index 31d633739..000000000
--- a/lib/google-java-format-1.7-sources.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-dbb14f20004dfaf01a5ce9f5e75c5993f18cc0a5 \ No newline at end of file
diff --git a/lib/google-java-format-1.7.jar b/lib/google-java-format-1.7.jar
deleted file mode 100644
index 953541cfa..000000000
--- a/lib/google-java-format-1.7.jar
+++ /dev/null
Binary files differ
diff --git a/lib/google-java-format-1.7.jar.asc b/lib/google-java-format-1.7.jar.asc
deleted file mode 100644
index 3642d6941..000000000
--- a/lib/google-java-format-1.7.jar.asc
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN PGP SIGNATURE-----
-
-iQIzBAABCgAdFiEE53QXrBlBYKP6vQSWmiWcfuY2xe0FAlw2hwkACgkQmiWcfuY2
-xe1uIhAAt9sWjStHkfHmWNFd5exYsjBmN0eObgOU5A386j5Q5P4hZjFDYsDvKlVo
-DWZKI2A9ZvpisuI083DwGJCXvbtD1WICh5Xx1B2tK2oCgl5bYiU88LCw5jS9F2IZ
-thPFhFDMIw4tQZp2J4o+itO7gtoUMfN5J9Y7ZcOWG60F3ouGc6vd8PnMk/CfxNZ0
-Zy21dkBzJVJsNghUWsHfP6WS2ySVBNQaarY+/4IuIrBVqRNZBhD+qEtZRG3mwc4X
-ydd4hCvoAQ7lysrdZEMDRx0qLQoqEWyfNFX7b8X/Oi6Ape765mD3Ss1WFgsjLqo+
-kt4Ge09f4QOCtMP95M8AnnHGjpVuibC0tb2E7qXfcrMWf+EX8ksFJKwaXZQfG4lv
-7kxZDTeeDdU6SUakiwfkEKN6s4v0MrGi8TM+776gLihcD/tx09555h+EwXrbp3IE
-1SWJCiYCuaqe1UXNEsZGYKv72GkPSEjY813Tn3endvGXX+QrLCU6DrLRyy1IWFKJ
-Yu6THeT0Z+M6h+oTO0ug2gJ6Ur7apQC4JBIi12XE+QhwuTuV0byiUArmO+3elLH4
-Nd9O0yFhKClaJ+ye+X+XLYkIjh+IJGFmbUahvCFuMMKuwbFRHwn5pw0DOJvE7qjg
-dBZhwjzKN8RHzik5nFwzhBBIzj3UglacGkUPFv7JW1TqcY2X6kE=
-=VIxo
------END PGP SIGNATURE-----
diff --git a/lib/google-java-format-1.7.jar.sha1 b/lib/google-java-format-1.7.jar.sha1
deleted file mode 100644
index d0a76c2c3..000000000
--- a/lib/google-java-format-1.7.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-97cb6afc835d65682edc248e19170a8e4ecfe4c4 \ No newline at end of file
diff --git a/lib/javax.inject-1-sources.jar b/lib/javax.inject-1-sources.jar
deleted file mode 100644
index 0c980535d..000000000
--- a/lib/javax.inject-1-sources.jar
+++ /dev/null
Binary files differ
diff --git a/lib/javax.inject-1-sources.jar.sha1 b/lib/javax.inject-1-sources.jar.sha1
deleted file mode 100644
index b69cdf0ad..000000000
--- a/lib/javax.inject-1-sources.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-a00123f261762a7c5e0ec916a2c7c8298d29c400 /home/maven/repository-staging/to-ibiblio/maven2/javax/inject/javax.inject/1/javax.inject-1-sources.jar
diff --git a/lib/javax.inject-1.jar b/lib/javax.inject-1.jar
deleted file mode 100644
index b2a9d0bf7..000000000
--- a/lib/javax.inject-1.jar
+++ /dev/null
Binary files differ
diff --git a/lib/javax.inject-1.jar.sha1 b/lib/javax.inject-1.jar.sha1
deleted file mode 100644
index 41e75ef21..000000000
--- a/lib/javax.inject-1.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-6975da39a7040257bd51d21a231b76c915872d38 /home/maven/repository-staging/to-ibiblio/maven2/javax/inject/javax.inject/1/javax.inject-1.jar
diff --git a/lint-baseline.xml b/lint-baseline.xml
new file mode 100644
index 000000000..cf790c246
--- /dev/null
+++ b/lint-baseline.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+
+ <issue
+ id="NewApi"
+ message="Multi-catch with these reflection exceptions requires API level 19 (current min is 14) because they get compiled to the common but new super type `ReflectiveOperationException`. As a workaround either create individual catch statements, or catch `Exception`."
+ errorLine1=" } catch (ClassNotFoundException"
+ errorLine2=" ^">
+ <location
+ file="external/dagger2/java/dagger/hilt/android/internal/testing/TestApplicationComponentManager.java"
+ line="85"
+ column="14"/>
+ </issue>
+
+</issues>
diff --git a/test_defs.bzl b/test_defs.bzl
index 15ab299f3..d16ad3660 100644
--- a/test_defs.bzl
+++ b/test_defs.bzl
@@ -12,24 +12,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+"""This file defines constants useful across the Dagger tests."""
+
+load("@rules_java//java:defs.bzl", "java_library", "java_test")
# Defines a set of build variants and the list of extra javacopts to build with.
# The key will be appended to the generated test names to ensure uniqueness.
BUILD_VARIANTS = {
"FastInit": ["-Adagger.fastInit=enabled"],
- "AheadOfTimeSubcomponents": ["-Adagger.experimentalAheadOfTimeSubcomponents=enabled"],
- "FastInitAndAheadOfTimeSubcomponents": [
- "-Adagger.fastInit=enabled",
- "-Adagger.experimentalAheadOfTimeSubcomponents=enabled",
- ],
- "AheadOfTimeSubcomponents_ForceUseSerializedComponentImplementations": [
- "-Adagger.experimentalAheadOfTimeSubcomponents=enabled",
- "-Adagger.forceUseSerializedComponentImplementations=enabled",
- ],
}
# TODO(ronshapiro): convert this to use bazel_common
-# TODO(user): split into two functions for functional vs non-functional tests?
+# TODO(bcorso): split into two functions for functional vs non-functional tests?
def GenJavaTests(
name,
srcs,
@@ -41,8 +35,8 @@ def GenJavaTests(
test_javacopts = None,
functional = True):
_GenTests(
- native.java_library,
- native.java_test,
+ java_library,
+ java_test,
name,
srcs,
deps,
@@ -190,6 +184,5 @@ def _gen_tests(
**test_kwargs
)
-
def _hjar_test(name, tags):
pass
diff --git a/tools/BUILD b/tools/BUILD
index 7b3cc6c6f..83d504803 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -15,8 +15,30 @@
# Description:
# Tools for Dagger
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+load(":maven_info.bzl", "maven_info_tests")
+
package(default_visibility = ["//:src"])
+maven_info_tests()
+
exports_files([
"pom-template.xml",
])
+
+bzl_library(
+ name = "maven_bzl",
+ srcs = ["maven.bzl"],
+ deps = [":maven_info_bzl"],
+)
+
+bzl_library(
+ name = "maven_info_bzl",
+ srcs = ["maven_info.bzl"],
+ deps = ["@bazel_skylib//lib:unittest"],
+)
+
+bzl_library(
+ name = "dejetify_bzl",
+ srcs = ["dejetify.bzl"],
+)
diff --git a/tools/dejetify.bzl b/tools/dejetify.bzl
new file mode 100644
index 000000000..4f0b0ced5
--- /dev/null
+++ b/tools/dejetify.bzl
@@ -0,0 +1,47 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Macro for producing dejetified artifacts.
+"""
+
+# See: https://developer.android.com/studio/command-line/jetifier
+JETIFIER_STANDALONE = "https://dl.google.com/dl/android/studio/jetifier-zips/1.0.0-beta08/jetifier-standalone.zip"
+
+def dejetified_library(name, input, output):
+ _dejetify_library(name, input, output)
+
+def _dejetify_library(name, input, output):
+ """Generates a dejetified library artifact.
+
+ A dejetified artifact is one that has been transformed to migrate its
+ AndroidX APIs to the support equivalents.
+
+ Args:
+ name: The name of the target.
+ input: The android_library input, e.g. ":myLibrary.aar".
+ output: The name of the output artifact, e.g. "dejetified-myLibrary.aar".
+ """
+ native.genrule(
+ name = name,
+ srcs = [input],
+ outs = [output],
+ cmd = """
+ TEMP="$$(mktemp -d)"
+ curl {tool_link} --output $$TEMP/jetifier-standalone.zip
+ unzip $$TEMP/jetifier-standalone.zip -d $$TEMP/
+ $$TEMP/jetifier-standalone/bin/jetifier-standalone -r \
+ -l info -i $< -o $@
+ """.format(tool_link = JETIFIER_STANDALONE),
+ local = 1,
+ )
diff --git a/tools/maven.bzl b/tools/maven.bzl
index 2ac035919..2c648a218 100644
--- a/tools/maven.bzl
+++ b/tools/maven.bzl
@@ -16,6 +16,9 @@
"""
load("@google_bazel_common//tools/maven:pom_file.bzl", default_pom_file = "pom_file")
+load(":maven_info.bzl", "MavenInfo", "collect_maven_info")
+load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
+load("@google_bazel_common//tools/jarjar:jarjar.bzl", "jarjar_library")
def pom_file(name, targets, artifact_name, artifact_id, packaging = None, **kwargs):
default_pom_file(
@@ -35,4 +38,366 @@ def pom_file(name, targets, artifact_name, artifact_id, packaging = None, **kwar
**kwargs
)
-POM_VERSION = "2.23.1"
+def gen_maven_artifact(
+ name,
+ artifact_name,
+ artifact_coordinates,
+ artifact_target,
+ artifact_target_libs = None,
+ artifact_target_maven_deps = None,
+ artifact_target_maven_deps_banned = None,
+ testonly = 0,
+ pom_name = "pom",
+ packaging = None,
+ javadoc_srcs = None,
+ javadoc_root_packages = None,
+ javadoc_exclude_packages = None,
+ javadoc_android_api_level = None,
+ shaded_deps = None,
+ shaded_rules = None,
+ manifest = None,
+ lint_deps = None,
+ proguard_specs = None):
+ _gen_maven_artifact(
+ name,
+ artifact_name,
+ artifact_coordinates,
+ artifact_target,
+ artifact_target_libs,
+ artifact_target_maven_deps,
+ artifact_target_maven_deps_banned,
+ testonly,
+ pom_name,
+ packaging,
+ javadoc_srcs,
+ javadoc_root_packages,
+ javadoc_exclude_packages,
+ javadoc_android_api_level,
+ shaded_deps,
+ shaded_rules,
+ manifest,
+ lint_deps,
+ proguard_specs
+ )
+
+def _gen_maven_artifact(
+ name,
+ artifact_name,
+ artifact_coordinates,
+ artifact_target,
+ artifact_target_libs,
+ artifact_target_maven_deps,
+ artifact_target_maven_deps_banned,
+ testonly,
+ pom_name,
+ packaging,
+ javadoc_srcs,
+ javadoc_root_packages,
+ javadoc_exclude_packages,
+ javadoc_android_api_level,
+ shaded_deps,
+ shaded_rules,
+ manifest,
+ lint_deps,
+ proguard_specs):
+ """Generates the files required for a maven artifact.
+
+ This macro generates the following targets:
+ * ":pom": The pom file for the given target and deps
+ * ":<NAME>": The artifact file for the given target and deps
+ * ":<NAME>-src": The sources jar file for the given target and deps
+ * ":<NAME>-javadoc": The javadocs jar file for the given target and deps
+
+ This macro also validates a few things. First, it validates that the
+ given "target" is a maven artifact (i.e. the "tags" attribute contains
+ "maven_coordinates=..."). Second, it calculates the list of transitive
+ dependencies of the target that are not owned by another maven artifact,
+ and validates that the given "deps" matches exactly.
+
+ Args:
+ name: The name associated with the various output targets.
+ artifact_target: The target containing the maven_coordinates.
+ artifact_name: The name of the maven artifact.
+ artifact_coordinates: The coordinates of the maven artifact in the
+ form: "<group_id>:<artifact_id>:<version>"
+ artifact_target_libs: The set of transitive libraries of the target.
+ artifact_target_maven_deps: The required maven deps of the target.
+ artifact_target_maven_deps_banned: The banned maven deps of the target.
+ testonly: True if the jar should be testonly.
+ packaging: The packaging of the maven artifact. E.g. "aar"
+ pom_name: The name of the pom file (or "pom" if absent).
+ javadoc_srcs: The srcs for the javadocs.
+ javadoc_root_packages: The root packages for the javadocs.
+ javadoc_exclude_packages: The packages to exclude from the javadocs.
+ javadoc_android_api_level: The android api level for the javadocs.
+ shaded_deps: The shaded deps for the jarjar.
+ shaded_rules: The shaded rules for the jarjar.
+ manifest: The AndroidManifest.xml to bundle in when packaing an 'aar'.
+ lint_deps: The lint targets to be bundled in when packaging an 'aar'.
+ proguard_specs: The proguard spec files to be bundled in when packaging an 'aar'
+ """
+
+ _validate_maven_deps(
+ name = name + "-validation",
+ testonly = 1,
+ target = artifact_target,
+ expected_artifact = artifact_coordinates,
+ expected_libs = artifact_target_libs,
+ expected_maven_deps = artifact_target_maven_deps,
+ banned_maven_deps = artifact_target_maven_deps_banned,
+ )
+
+ shaded_deps = shaded_deps or []
+ shaded_rules = shaded_rules or []
+ artifact_targets = [artifact_target] + (artifact_target_libs or [])
+ lint_deps = lint_deps or []
+
+ # META-INF resources files that can be combined by appending lines.
+ merge_meta_inf_files = [
+ "gradle/incremental.annotation.processors",
+ ]
+
+ artifact_id = artifact_coordinates.split(":")[1]
+ pom_file(
+ name = pom_name,
+ testonly = testonly,
+ artifact_id = artifact_id,
+ artifact_name = artifact_name,
+ packaging = packaging,
+ targets = artifact_targets,
+ )
+
+ if (packaging == "aar"):
+ jarjar_library(
+ name = name + "-classes",
+ testonly = testonly,
+ jars = artifact_targets + shaded_deps,
+ rules = shaded_rules,
+ merge_meta_inf_files = merge_meta_inf_files,
+ )
+ if lint_deps:
+ # jarjar all lint artifacts since an aar only contains a single lint.jar.
+ jarjar_library(
+ name = name + "-lint",
+ jars = lint_deps,
+ )
+ lint_jar_name = name + "-lint.jar"
+ else:
+ lint_jar_name = None
+
+ if proguard_specs:
+ # Concatenate all proguard rules since an aar only contains a single proguard.txt
+ native.genrule(
+ name = name + "-proguard",
+ srcs = proguard_specs,
+ outs = [name + "-proguard.txt"],
+ cmd = "cat $(SRCS) > $@",
+ )
+ proguard_file = name + "-proguard.txt"
+ else:
+ proguard_file = None
+
+ _package_android_library(
+ name = name + "-android-lib",
+ manifest = manifest,
+ classesJar = name + "-classes.jar",
+ lintJar = lint_jar_name,
+ proguardSpec = proguard_file,
+ )
+
+ # Copy intermediate outputs to final one.
+ native.genrule(
+ name = name,
+ srcs = [name + "-android-lib"],
+ outs = [name + ".aar"],
+ cmd = "cp $< $@",
+ )
+ else:
+ jarjar_library(
+ name = name,
+ testonly = testonly,
+ jars = artifact_targets + shaded_deps,
+ rules = shaded_rules,
+ merge_meta_inf_files = merge_meta_inf_files,
+ )
+
+ jarjar_library(
+ name = name + "-src",
+ testonly = testonly,
+ jars = [_src_jar(dep) for dep in artifact_targets],
+ merge_meta_inf_files = merge_meta_inf_files,
+ )
+
+ if javadoc_srcs != None:
+ javadoc_library(
+ name = name + "-javadoc",
+ srcs = javadoc_srcs,
+ testonly = testonly,
+ root_packages = javadoc_root_packages,
+ exclude_packages = javadoc_exclude_packages,
+ android_api_level = javadoc_android_api_level,
+ deps = artifact_targets,
+ )
+ else:
+ # Build an empty javadoc because Sonatype requires javadocs
+ # even if the jar is empty.
+ # https://central.sonatype.org/pages/requirements.html#supply-javadoc-and-sources
+ native.java_binary(
+ name = name + "-javadoc",
+ )
+
+def _src_jar(target):
+ if target.startswith(":"):
+ target = Label("//" + native.package_name() + target)
+ else:
+ target = Label(target)
+ return "//%s:lib%s-src.jar" % (target.package, target.name)
+
+def _validate_maven_deps_impl(ctx):
+ """Validates the given Maven target and deps
+
+ Validates that the given "target" is a maven artifact (i.e. the "tags"
+ attribute contains "maven_coordinates=..."). Second, it calculates the
+ list of transitive dependencies of the target that are not owned by
+ another maven artifact, and validates that the given "deps" matches
+ exactly.
+ """
+ target = ctx.attr.target
+ artifact = target[MavenInfo].artifact
+ if not artifact:
+ fail("\t[Error]: %s is not a maven artifact" % target.label)
+
+ if artifact != ctx.attr.expected_artifact:
+ fail(
+ "\t[Error]: %s expected artifact, %s, but was: %s" % (
+ target.label,
+ ctx.attr.expected_artifact,
+ artifact,
+ ),
+ )
+
+ all_transitive_deps = target[MavenInfo].all_transitive_deps.to_list()
+ maven_nearest_artifacts = target[MavenInfo].maven_nearest_artifacts.to_list()
+ maven_transitive_deps = target[MavenInfo].maven_transitive_deps.to_list()
+
+ expected_libs = [dep.label for dep in getattr(ctx.attr, "expected_libs", [])]
+ actual_libs = [dep for dep in all_transitive_deps if dep not in maven_transitive_deps]
+ _validate_list("artifact_target_libs", actual_libs, expected_libs)
+
+ expected_maven_deps = [dep for dep in getattr(ctx.attr, "expected_maven_deps", [])]
+ actual_maven_deps = [_strip_artifact_version(artifact) for artifact in maven_nearest_artifacts]
+ _validate_list(
+ "artifact_target_maven_deps",
+ actual_maven_deps,
+ expected_maven_deps,
+ ctx.attr.banned_maven_deps,
+ )
+
+def _validate_list(name, actual_list, expected_list, banned_list = []):
+ missing = sorted(['"{}",'.format(x) for x in actual_list if x not in expected_list])
+ if missing:
+ fail("\t[Error]: Found missing {}: \n\t\t".format(name) + "\n\t\t".join(missing))
+
+ extra = sorted(['"{}",'.format(x) for x in expected_list if x not in actual_list])
+ if extra:
+ fail("\t[Error]: Found extra {}: \n\t\t".format(name) + "\n\t\t".join(extra))
+
+ banned = sorted(['"{}",'.format(x) for x in actual_list if x in banned_list])
+ if banned:
+ fail("\t[Error]: Found banned {}: \n\t\t".format(name) + "\n\t\t".join(banned))
+
+def _strip_artifact_version(artifact):
+ return artifact.rsplit(":", 1)[0]
+
+_validate_maven_deps = rule(
+ implementation = _validate_maven_deps_impl,
+ attrs = {
+ "target": attr.label(
+ doc = "The target to generate a maven artifact for.",
+ aspects = [collect_maven_info],
+ mandatory = True,
+ ),
+ "expected_artifact": attr.string(
+ doc = "The artifact name of the target.",
+ mandatory = True,
+ ),
+ "expected_libs": attr.label_list(
+ doc = "The set of transitive libraries of the target, if any.",
+ ),
+ "expected_maven_deps": attr.string_list(
+ doc = "The required maven dependencies of the target, if any.",
+ ),
+ "banned_maven_deps": attr.string_list(
+ doc = "The required maven dependencies of the target, if any.",
+ ),
+ },
+)
+
+def _package_android_library_impl(ctx):
+ """A very, very simple Android Library (aar) packaging rule.
+
+ This rule only support packaging simple android libraries. No resources
+ support, assets, extra libs, nor jni. This rule is needed because
+ there is no 'JarJar equivalent' for AARs and some of our artifacts are
+ composed of sources spread across multiple android_library targets.
+
+ See: https://developer.android.com/studio/projects/android-library.html#aar-contents
+ """
+ inputs = [ctx.file.manifest, ctx.file.classesJar]
+ if ctx.file.lintJar:
+ inputs.append(ctx.file.lintJar)
+ if ctx.file.proguardSpec:
+ inputs.append(ctx.file.proguardSpec)
+
+ ctx.actions.run_shell(
+ inputs = inputs,
+ outputs = [ctx.outputs.aar],
+ command = """
+ TMPDIR="$(mktemp -d)"
+ cp {manifest} $TMPDIR/AndroidManifest.xml
+ cp {classesJar} $TMPDIR/classes.jar
+ if [[ -a {lintJar} ]]; then
+ cp {lintJar} $TMPDIR/lint.jar
+ fi
+ if [[ -a {proguardSpec} ]]; then
+ cp {proguardSpec} $TMPDIR/proguard.txt
+ fi
+ touch $TMPDIR/R.txt
+ zip -j {outputFile} $TMPDIR/*
+ """.format(
+ manifest = ctx.file.manifest.path,
+ classesJar = ctx.file.classesJar.path,
+ lintJar = ctx.file.lintJar.path if ctx.file.lintJar else "none",
+ proguardSpec = ctx.file.proguardSpec.path if ctx.file.proguardSpec else "none",
+ outputFile = ctx.outputs.aar.path,
+ ),
+ )
+
+_package_android_library = rule(
+ implementation = _package_android_library_impl,
+ attrs = {
+ "manifest": attr.label(
+ doc = "The AndroidManifest.xml file.",
+ allow_single_file = True,
+ mandatory = True,
+ ),
+ "classesJar": attr.label(
+ doc = "The classes.jar file.",
+ allow_single_file = True,
+ mandatory = True,
+ ),
+ "lintJar": attr.label(
+ doc = "The lint.jar file.",
+ allow_single_file = True,
+ mandatory = False,
+ ),
+ "proguardSpec": attr.label(
+ doc = "The proguard.txt file.",
+ allow_single_file = True,
+ mandatory = False,
+ ),
+ },
+ outputs = {
+ "aar": "%{name}.aar",
+ },
+)
diff --git a/tools/maven_info.bzl b/tools/maven_info.bzl
new file mode 100644
index 000000000..5ebed3f65
--- /dev/null
+++ b/tools/maven_info.bzl
@@ -0,0 +1,243 @@
+# Copyright (C) 2019 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Skylark rules to collect Maven artifacts information.
+"""
+
+load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest")
+
+# TODO(b/142057516): Unfork this file once we've settled on a more general API.
+MavenInfo = provider(
+ fields = {
+ "artifact": """
+ The Maven coordinate for the artifact that is exported by this target, if one exists.
+ """,
+ "has_srcs": """
+ True if this library contains srcs..
+ """,
+ "all_transitive_deps": """
+ All transitive deps of the target with srcs.
+ """,
+ "maven_nearest_artifacts": """
+ The nearest maven deps of the target.
+ """,
+ "maven_transitive_deps": """
+ All transitive deps that are included in some maven dependency.
+ """,
+ },
+)
+
+_EMPTY_MAVEN_INFO = MavenInfo(
+ artifact = None,
+ has_srcs = False,
+ maven_nearest_artifacts = depset(),
+ maven_transitive_deps = depset(),
+ all_transitive_deps = depset(),
+)
+
+_MAVEN_COORDINATES_PREFIX = "maven_coordinates="
+
+def _collect_maven_info_impl(target, ctx):
+ tags = getattr(ctx.rule.attr, "tags", [])
+ srcs = getattr(ctx.rule.attr, "srcs", [])
+ deps = getattr(ctx.rule.attr, "deps", [])
+ exports = getattr(ctx.rule.attr, "exports", [])
+
+ artifact = None
+ for tag in tags:
+ if tag in ("maven:compile_only", "maven:shaded"):
+ return [_EMPTY_MAVEN_INFO]
+ if tag.startswith(_MAVEN_COORDINATES_PREFIX):
+ artifact = tag[len(_MAVEN_COORDINATES_PREFIX):]
+
+ all_deps = [dep.label for dep in (deps + exports) if dep[MavenInfo].has_srcs]
+ all_transitive_deps = [dep[MavenInfo].all_transitive_deps for dep in (deps + exports)]
+
+ maven_artifacts = []
+ maven_nearest_artifacts = []
+ maven_deps = []
+ maven_transitive_deps = []
+ for dep in (deps + exports):
+ # If the dep is itself a maven artifact, add it and all of its transitive deps.
+ # Otherwise, just propagate its transitive maven deps.
+ if dep[MavenInfo].artifact or dep[MavenInfo] == _EMPTY_MAVEN_INFO:
+ if (dep[MavenInfo].artifact):
+ maven_artifacts.append(dep[MavenInfo].artifact)
+ maven_deps.append(dep.label)
+ maven_transitive_deps.append(dep[MavenInfo].all_transitive_deps)
+ else:
+ maven_nearest_artifacts.append(dep[MavenInfo].maven_nearest_artifacts)
+ maven_transitive_deps.append(dep[MavenInfo].maven_transitive_deps)
+
+ return [MavenInfo(
+ artifact = artifact,
+ has_srcs = len(srcs) > 0,
+ maven_nearest_artifacts = depset(maven_artifacts, transitive = maven_nearest_artifacts),
+ maven_transitive_deps = depset(maven_deps, transitive = maven_transitive_deps),
+ all_transitive_deps = depset(all_deps, transitive = all_transitive_deps),
+ )]
+
+collect_maven_info = aspect(
+ attr_aspects = [
+ "deps",
+ "exports",
+ ],
+ doc = """
+ Collects the Maven information for targets, their dependencies, and their transitive exports.
+ """,
+ implementation = _collect_maven_info_impl,
+)
+
+def _fake_java_library(name, deps = None, exports = None, is_artifact = True):
+ src_file = ["%s.java" % name]
+ native.genrule(
+ name = "%s_source_file" % name,
+ outs = src_file,
+ cmd = "echo 'package pkg; class %s {}' > $@" % name,
+ )
+ native.java_library(
+ name = name,
+ srcs = src_file,
+ tags = ["maven_coordinates=%s:_:_" % name] if is_artifact else [],
+ deps = deps or [],
+ exports = exports or [],
+ )
+
+def _maven_info_test_impl(ctx):
+ env = unittest.begin(ctx)
+ asserts.equals(
+ env,
+ expected = ctx.attr.artifact if ctx.attr.artifact else None,
+ actual = ctx.attr.target[MavenInfo].artifact,
+ msg = "MavenInfo.artifact",
+ )
+ asserts.equals(
+ env,
+ expected = sorted([ctx.label.relative(dep) for dep in ctx.attr.maven_transitive_deps]),
+ actual = sorted(ctx.attr.target[MavenInfo].maven_transitive_deps.to_list()),
+ msg = "MavenInfo.maven_transitive_deps",
+ )
+ asserts.equals(
+ env,
+ expected = sorted([ctx.label.relative(dep) for dep in ctx.attr.all_transitive_deps]),
+ actual = sorted(ctx.attr.target[MavenInfo].all_transitive_deps.to_list()),
+ msg = "MavenInfo.all_transitive_deps",
+ )
+ return unittest.end(env)
+
+_maven_info_test = unittest.make(
+ _maven_info_test_impl,
+ attrs = {
+ "target": attr.label(aspects = [collect_maven_info]),
+ "artifact": attr.string(),
+ "maven_transitive_deps": attr.string_list(),
+ "all_transitive_deps": attr.string_list(),
+ },
+)
+
+def maven_info_tests():
+ """Tests for `pom_file` and `MavenInfo`.
+ """
+ _fake_java_library(name = "A")
+ _fake_java_library(
+ name = "DepOnA",
+ deps = [":A"],
+ )
+
+ _maven_info_test(
+ name = "a_test",
+ target = ":A",
+ artifact = "A:_:_",
+ maven_transitive_deps = [],
+ all_transitive_deps = [],
+ )
+
+ _maven_info_test(
+ name = "dependencies_test",
+ target = ":DepOnA",
+ artifact = "DepOnA:_:_",
+ maven_transitive_deps = [":A"],
+ all_transitive_deps = [":A"],
+ )
+
+ _fake_java_library(
+ name = "ExportsA",
+ exports = [":A"],
+ )
+
+ _maven_info_test(
+ name = "exports_test",
+ target = ":ExportsA",
+ artifact = "ExportsA:_:_",
+ maven_transitive_deps = [":A"],
+ all_transitive_deps = [":A"],
+ )
+
+ _fake_java_library(
+ name = "TransitiveExports",
+ exports = [":ExportsA"],
+ )
+
+ _maven_info_test(
+ name = "transitive_exports_test",
+ target = ":TransitiveExports",
+ artifact = "TransitiveExports:_:_",
+ maven_transitive_deps = [":ExportsA", ":A"],
+ all_transitive_deps = [":ExportsA", ":A"],
+ )
+
+ _fake_java_library(
+ name = "TransitiveDeps",
+ deps = [":ExportsA"],
+ )
+
+ _maven_info_test(
+ name = "transitive_deps_test",
+ target = ":TransitiveDeps",
+ artifact = "TransitiveDeps:_:_",
+ maven_transitive_deps = [":ExportsA", ":A"],
+ all_transitive_deps = [":ExportsA", ":A"],
+ )
+
+ _fake_java_library(name = "Node1", is_artifact = False)
+ _maven_info_test(
+ name = "test_node1",
+ target = ":Node1",
+ maven_transitive_deps = [],
+ all_transitive_deps = [],
+ )
+
+ _fake_java_library(name = "Node2_Artifact", deps = [":Node1"])
+ _maven_info_test(
+ name = "test_node2",
+ target = ":Node2_Artifact",
+ artifact = "Node2_Artifact:_:_",
+ maven_transitive_deps = [],
+ all_transitive_deps = [":Node1"],
+ )
+
+ _fake_java_library(name = "Node3", deps = [":Node2_Artifact"], is_artifact = False)
+ _maven_info_test(
+ name = "test_node3",
+ target = ":Node3",
+ maven_transitive_deps = [":Node1", ":Node2_Artifact"],
+ all_transitive_deps = [":Node1", ":Node2_Artifact"],
+ )
+
+ _fake_java_library(name = "Node4", deps = [":Node3"], is_artifact = False)
+ _maven_info_test(
+ name = "test_node4",
+ target = ":Node4",
+ maven_transitive_deps = [":Node1", ":Node2_Artifact"],
+ all_transitive_deps = [":Node1", ":Node2_Artifact", ":Node3"],
+ )
diff --git a/tools/pom-template.xml b/tools/pom-template.xml
index 39ed62d1d..68b6ac02c 100644
--- a/tools/pom-template.xml
+++ b/tools/pom-template.xml
@@ -16,7 +16,6 @@
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
-
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
@@ -32,7 +31,7 @@
<packaging>{packaging}</packaging>
<scm>
- <url>http://github.com/google/dagger/</url>
+ <url>https://github.com/google/dagger/</url>
<connection>scm:git:git://github.com/google/dagger.git</connection>
<developerConnection>scm:git:ssh://git@github.com/google/dagger.git</developerConnection>
<tag>HEAD</tag>
@@ -40,22 +39,21 @@
<issueManagement>
<system>GitHub Issues</system>
- <url>http://github.com/google/dagger/issues</url>
+ <url>https://github.com/google/dagger/issues</url>
</issueManagement>
<licenses>
<license>
<name>Apache 2.0</name>
- <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ <url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
</license>
</licenses>
<organization>
<name>Google, Inc.</name>
- <url>http://www.google.com</url>
+ <url>https://www.google.com</url>
</organization>
-
<dependencies>
-{generated_bzl_deps}
+ {generated_bzl_deps}
</dependencies>
</project>
diff --git a/util/deploy-dagger.sh b/util/deploy-dagger.sh
new file mode 100755
index 000000000..ddf492659
--- /dev/null
+++ b/util/deploy-dagger.sh
@@ -0,0 +1,127 @@
+#!/bin/bash
+
+set -eu
+
+readonly MVN_GOAL="$1"
+readonly VERSION_NAME="$2"
+shift 2
+readonly EXTRA_MAVEN_ARGS=("$@")
+
+# Builds and deploys the given artifacts to a configured maven goal.
+# @param {string} library the library to deploy.
+# @param {string} pomfile the pom file to deploy.
+# @param {string} srcjar the sources jar of the library. This is an optional
+# parameter, if provided then javadoc must also be provided.
+# @param {string} javadoc the java doc jar of the library. This is an optional
+# parameter, if provided then srcjar must also be provided.
+_deploy() {
+ local library=$1
+ local pomfile=$2
+ local srcjar=$3
+ local javadoc=$4
+ bash $(dirname $0)/deploy-library.sh \
+ "$library" \
+ "$pomfile" \
+ "$srcjar" \
+ "$javadoc" \
+ "$MVN_GOAL" \
+ "$VERSION_NAME" \
+ "${EXTRA_MAVEN_ARGS[@]:+${EXTRA_MAVEN_ARGS[@]}}"
+}
+
+_deploy \
+ java/dagger/libcore.jar \
+ java/dagger/pom.xml \
+ java/dagger/libcore-src.jar \
+ java/dagger/core-javadoc.jar
+
+_deploy \
+ gwt/libgwt.jar \
+ gwt/pom.xml \
+ gwt/libgwt.jar \
+ gwt/libgwt.jar
+
+_deploy \
+ java/dagger/internal/codegen/artifact.jar \
+ java/dagger/internal/codegen/pom.xml \
+ java/dagger/internal/codegen/artifact-src.jar \
+ java/dagger/internal/codegen/artifact-javadoc.jar
+
+_deploy \
+ java/dagger/producers/artifact.jar \
+ java/dagger/producers/pom.xml \
+ java/dagger/producers/artifact-src.jar \
+ java/dagger/producers/artifact-javadoc.jar
+
+_deploy \
+ java/dagger/spi/artifact.jar \
+ java/dagger/spi/pom.xml \
+ java/dagger/spi/artifact-src.jar \
+ java/dagger/spi/artifact-javadoc.jar
+
+_deploy \
+ java/dagger/android/android.aar \
+ java/dagger/android/pom.xml \
+ java/dagger/android/libandroid-src.jar \
+ java/dagger/android/android-javadoc.jar
+
+_deploy \
+ java/dagger/android/android-legacy.aar \
+ java/dagger/android/legacy-pom.xml \
+ "" \
+ ""
+
+# b/37741866 and https://github.com/google/dagger/issues/715
+_deploy \
+ java/dagger/android/libandroid.jar \
+ java/dagger/android/jarimpl-pom.xml \
+ java/dagger/android/libandroid-src.jar \
+ java/dagger/android/android-javadoc.jar
+
+_deploy \
+ java/dagger/android/support/support.aar \
+ java/dagger/android/support/pom.xml \
+ java/dagger/android/support/libsupport-src.jar \
+ java/dagger/android/support/support-javadoc.jar
+
+_deploy \
+ java/dagger/android/support/support-legacy.aar \
+ java/dagger/android/support/legacy-pom.xml \
+ "" \
+ ""
+
+_deploy \
+ shaded_android_processor.jar \
+ java/dagger/android/processor/pom.xml \
+ java/dagger/android/processor/libprocessor-src.jar \
+ java/dagger/android/processor/processor-javadoc.jar
+
+_deploy \
+ java/dagger/grpc/server/libserver.jar \
+ java/dagger/grpc/server/server-pom.xml \
+ java/dagger/grpc/server/libserver-src.jar \
+ java/dagger/grpc/server/javadoc.jar
+
+_deploy \
+ java/dagger/grpc/server/libannotations.jar \
+ java/dagger/grpc/server/annotations-pom.xml \
+ java/dagger/grpc/server/libannotations-src.jar \
+ java/dagger/grpc/server/javadoc.jar
+
+_deploy \
+ shaded_grpc_server_processor.jar \
+ java/dagger/grpc/server/processor/pom.xml \
+ java/dagger/grpc/server/processor/libprocessor-src.jar \
+ java/dagger/grpc/server/processor/javadoc.jar
+
+_deploy \
+ java/dagger/lint/lint-artifact.jar \
+ java/dagger/lint/lint-pom.xml \
+ java/dagger/lint/lint-artifact-src.jar \
+ java/dagger/lint/lint-artifact-javadoc.jar
+
+_deploy \
+ java/dagger/lint/lint-android-artifact.aar \
+ java/dagger/lint/lint-android-pom.xml \
+ "" \
+ ""
diff --git a/util/deploy-hilt.sh b/util/deploy-hilt.sh
new file mode 100755
index 000000000..3c25c10b7
--- /dev/null
+++ b/util/deploy-hilt.sh
@@ -0,0 +1,88 @@
+#!/bin/bash
+
+set -eu
+
+readonly MVN_GOAL="$1"
+readonly VERSION_NAME="$2"
+shift 2
+readonly EXTRA_MAVEN_ARGS=("$@")
+
+# Builds and deploys the given artifacts to a configured maven goal.
+# @param {string} library the library to deploy.
+# @param {string} pomfile the pom file to deploy.
+# @param {string} srcjar the sources jar of the library. This is an optional
+# parameter, if provided then javadoc must also be provided.
+# @param {string} javadoc the java doc jar of the library. This is an optional
+# parameter, if provided then srcjar must also be provided.
+_deploy() {
+ local library=$1
+ local pomfile=$2
+ local srcjar=$3
+ local javadoc=$4
+ bash $(dirname $0)/deploy-library.sh \
+ "$library" \
+ "$pomfile" \
+ "$srcjar" \
+ "$javadoc" \
+ "$MVN_GOAL" \
+ "$VERSION_NAME" \
+ "${EXTRA_MAVEN_ARGS[@]:+${EXTRA_MAVEN_ARGS[@]}}"
+}
+
+_deploy \
+ java/dagger/hilt/android/artifact.aar \
+ java/dagger/hilt/android/pom.xml \
+ java/dagger/hilt/android/artifact-src.jar \
+ java/dagger/hilt/android/artifact-javadoc.jar
+
+_deploy \
+ java/dagger/hilt/android/testing/artifact.aar \
+ java/dagger/hilt/android/testing/pom.xml \
+ java/dagger/hilt/android/testing/artifact-src.jar \
+ java/dagger/hilt/android/testing/artifact-javadoc.jar
+
+_deploy \
+ java/dagger/hilt/processor/artifact.jar \
+ java/dagger/hilt/processor/pom.xml \
+ java/dagger/hilt/processor/artifact-src.jar \
+ java/dagger/hilt/processor/artifact-javadoc.jar
+
+_deploy \
+ java/dagger/hilt/android/processor/artifact.jar \
+ java/dagger/hilt/android/processor/pom.xml \
+ java/dagger/hilt/android/processor/artifact-src.jar \
+ java/dagger/hilt/android/processor/artifact-javadoc.jar
+
+_deploy \
+ java/dagger/hilt/artifact-core.jar \
+ java/dagger/hilt/pom.xml \
+ java/dagger/hilt/artifact-core-src.jar \
+ java/dagger/hilt/artifact-core-javadoc.jar
+
+# Builds and deploy the Gradle plugin.
+_deploy_plugin() {
+ local plugindir=java/dagger/hilt/android/plugin
+ ./$plugindir/gradlew -p $plugindir --no-daemon clean \
+ publishAllPublicationsToMavenRepository -PPublishVersion="$VERSION_NAME"
+ local outdir=$plugindir/build/repo/com/google/dagger/hilt-android-gradle-plugin/$VERSION_NAME
+ # When building '-SNAPSHOT' versions in gradle, the filenames replaces
+ # '-SNAPSHOT' with timestamps, so we need to disambiguate by finding each file
+ # to deploy. See: https://stackoverflow.com/questions/54182823/
+ local suffix
+ if [[ "$VERSION_NAME" == *"-SNAPSHOT" ]]; then
+ # Gets the timestamp part out of the name to be used as suffix.
+ # Timestamp format is ########.######-#.
+ suffix=$(find $outdir -name "*.pom" | grep -Eo '[0-9]{8}\.[0-9]{6}-[0-9]{1}')
+ else
+ suffix=$VERSION_NAME
+ fi
+ mvn "$MVN_GOAL" \
+ -Dfile="$(find $outdir -name "*-$suffix.jar")" \
+ -DpomFile="$(find $outdir -name "*-$suffix.pom")" \
+ -Dsources="$(find $outdir -name "*-$suffix-sources.jar")" \
+ -Djavadoc="$(find $outdir -name "*-$suffix-javadoc.jar")" \
+ "${EXTRA_MAVEN_ARGS[@]:+${EXTRA_MAVEN_ARGS[@]}}"
+}
+
+# Gradle Plugin is built with Gradle, but still deployed via Maven (mvn)
+_deploy_plugin
diff --git a/util/deploy-library.sh b/util/deploy-library.sh
new file mode 100755
index 000000000..a744402ba
--- /dev/null
+++ b/util/deploy-library.sh
@@ -0,0 +1,92 @@
+#!/bin/bash
+
+set -eu
+
+# Builds and deploys the given artifacts to a configured maven goal.
+# @param {string} library the library to deploy.
+# @param {string} pomfile the pom file to deploy.
+# @param {string} srcjar the sources jar of the library. This is an optional
+# parameter, if provided then javadoc must also be provided.
+# @param {string} javadoc the java doc jar of the library. This is an optional
+# parameter, if provided then srcjar must also be provided.
+deploy_library() {
+ local library=$1
+ local pomfile=$2
+ local srcjar=$3
+ local javadoc=$4
+ local mvn_goal=$5
+ local version_name=$6
+ shift 6
+ local extra_maven_args=("$@")
+
+ bazel build --define=pom_version="$version_name" \
+ $library $pomfile
+
+ # TODO(bcorso): Consider moving this into the "gen_maven_artifact" macro, this
+ # requires having the version checked-in for the build system.
+ add_tracking_version \
+ $(bazel_output_file $library) \
+ $(bazel_output_file $pomfile) \
+ $version_name
+
+ if [ -n "$srcjar" ] && [ -n "$javadoc" ] ; then
+ bazel build --define=pom_version="$version_name" \
+ $srcjar $javadoc
+ mvn $mvn_goal \
+ -Dfile=$(bazel_output_file $library) \
+ -Djavadoc=$(bazel_output_file $javadoc) \
+ -DpomFile=$(bazel_output_file $pomfile) \
+ -Dsources=$(bazel_output_file $srcjar) \
+ "${extra_maven_args[@]:+${extra_maven_args[@]}}"
+ else
+ mvn $mvn_goal \
+ -Dfile=$(bazel_output_file $library) \
+ -DpomFile=$(bazel_output_file $pomfile) \
+ "${extra_maven_args[@]:+${extra_maven_args[@]}}"
+ fi
+}
+
+bazel_output_file() {
+ local library=$1
+ local output_file=bazel-bin/$library
+ if [[ ! -e $output_file ]]; then
+ output_file=bazel-genfiles/$library
+ fi
+ if [[ ! -e $output_file ]]; then
+ echo "Could not find bazel output file for $library"
+ exit 1
+ fi
+ echo -n $output_file
+}
+
+add_tracking_version() {
+ local library=$1
+ local pomfile=$2
+ local version_name=$3
+ local group_id=$(find_pom_value $pomfile "groupId")
+ local artifact_id=$(find_pom_value $pomfile "artifactId")
+ local temp_dir=$(mktemp -d)
+ local version_file="META-INF/${group_id}_${artifact_id}.version"
+ mkdir -p "$temp_dir/META-INF/"
+ echo $version_name >> "$temp_dir/$version_file"
+ if [[ $library =~ \.jar$ ]]; then
+ jar uf $library -C $temp_dir $version_file
+ elif [[ $library =~ \.aar$ ]]; then
+ unzip $library classes.jar -d $temp_dir
+ jar uf $temp_dir/classes.jar -C $temp_dir $version_file
+ jar uf $library -C $temp_dir classes.jar
+ else
+ echo "Could not add tracking version file to $library"
+ exit 1
+ fi
+}
+
+find_pom_value() {
+ local pomfile=$1
+ local attribute=$2
+ # Using Python here because `mvn help:evaluate` doesn't work with our gen pom
+ # files since they don't include the aar packaging plugin.
+ python $(dirname $0)/find_pom_value.py $pomfile $attribute
+}
+
+deploy_library "$@"
diff --git a/util/deploy-to-maven-central.sh b/util/deploy-to-maven-central.sh
index 1f721a162..de2b49c48 100755
--- a/util/deploy-to-maven-central.sh
+++ b/util/deploy-to-maven-central.sh
@@ -6,50 +6,69 @@ if [ $# -lt 2 ]; then
echo "usage $0 <ssl-key> <version-name> [<param> ...]"
exit 1;
fi
-key=$1
-version_name=$2
+readonly KEY=$1
+readonly VERSION_NAME=$2
shift 2
-if [[ ! "$version_name" =~ ^2\. ]]; then
+if [[ ! "$VERSION_NAME" =~ ^2\. ]]; then
echo 'Version name must begin with "2."'
exit 2
fi
-if [[ "$version_name" =~ " " ]]; then
+if [[ "$VERSION_NAME" =~ " " ]]; then
echo "Version name must not have any spaces"
exit 3
fi
-bazel test //...
+bash $(dirname $0)/run-local-tests.sh
-bash $(dirname $0)/execute-deploy.sh \
+# Note: we detach from head before making any sed changes to avoid commiting
+# a particular version to master. This sed change used to be done at the very
+# end of the script, but with the introduction of "-alpha" to the Hilt
+# artifacts, we need to do the sed replacement before deploying the artifacts to
+# maven. Note, that this sed replacement is only done for versioned releases.
+# HEAD-SNAPSHOT and LOCAL_SNAPSHOT versions of Hilt artifacts do not contain
+# "-alpha".
+git checkout --detach
+
+# Set the version string that is used as a tag in all of our libraries. If
+# another repo depends on a versioned tag of Dagger, their java_library.tags
+# should match the versioned release.
+sed -i s/'#ALPHA_POSTFIX'/'+ "-alpha"'/g build_defs.bzl
+sed -i s/'${project.version}'/"${VERSION_NAME}"/g build_defs.bzl
+
+bash $(dirname $0)/deploy-dagger.sh \
"gpg:sign-and-deploy-file" \
- "$version_name" \
+ "$VERSION_NAME" \
"-DrepositoryId=sonatype-nexus-staging" \
"-Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/" \
- "-Dgpg.keyname=${key}"
+ "-Dgpg.keyname=${KEY}"
+
+bash $(dirname $0)/deploy-hilt.sh \
+ "gpg:sign-and-deploy-file" \
+ "${VERSION_NAME}-alpha" \
+ "-DrepositoryId=sonatype-nexus-staging" \
+ "-Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/" \
+ "-Dgpg.keyname=${KEY}"
+
+# Note: We avoid commiting until after deploying in case deploying fails and
+# we need to run the script again.
+git commit -m "${VERSION_NAME} release" build_defs.bzl
+git tag -a -m "Dagger ${VERSION_NAME}" dagger-"${VERSION_NAME}"
+git push origin tag dagger-"${VERSION_NAME}"
+
+# Switch back to the original HEAD
+git checkout -
# Publish javadocs to gh-pages
bazel build //:user-docs.jar
git clone --quiet --branch gh-pages \
https://github.com/google/dagger gh-pages > /dev/null
cd gh-pages
-unzip ../bazel-bin/user-docs.jar -d api/$version_name
-rm -rf api/$version_name/META-INF/
-git add api/$version_name
-git commit -m "$version_name docs"
+unzip ../bazel-bin/user-docs.jar -d api/$VERSION_NAME
+rm -rf api/$VERSION_NAME/META-INF/
+git add api/$VERSION_NAME
+git commit -m "$VERSION_NAME docs"
git push origin gh-pages
cd ..
rm -rf gh-pages
-
-git checkout --detach
-# Set the version string that is used as a tag in all of our libraries. If another repo depends on
-# a versioned tag of Dagger, their java_library.tags should match the versioned release.
-sed -i s/'${project.version}'/"${version_name}"/g tools/maven.bzl
-git commit -m "${version_name} release" tools/maven.bzl
-
-git tag -a -m "Dagger ${version_name}" dagger-"${version_name}"
-git push origin tag dagger-"${version_name}"
-
-# Switch back to the original HEAD
-git checkout -
diff --git a/util/execute-deploy.sh b/util/execute-deploy.sh
deleted file mode 100755
index 115346809..000000000
--- a/util/execute-deploy.sh
+++ /dev/null
@@ -1,110 +0,0 @@
-#!/bin/bash
-
-set -eu
-
-readonly MVN_GOAL="$1"
-readonly VERSION_NAME="$2"
-shift 2
-readonly EXTRA_MAVEN_ARGS=("$@")
-
-bazel_output_file() {
- local library=$1
- local output_file=bazel-bin/$library
- if [[ ! -e $output_file ]]; then
- output_file=bazel-genfiles/$library
- fi
- if [[ ! -e $output_file ]]; then
- echo "Could not find bazel output file for $library"
- exit 1
- fi
- echo -n $output_file
-}
-
-deploy_library() {
- local library=$1
- local srcjar=$2
- local javadoc=$3
- local pomfile=$4
- bazel build --define=pom_version="$VERSION_NAME" \
- $library $srcjar $javadoc $pomfile
-
- mvn $MVN_GOAL \
- -Dfile=$(bazel_output_file $library) \
- -Djavadoc=$(bazel_output_file $javadoc) \
- -DpomFile=$(bazel_output_file $pomfile) \
- -Dsources=$(bazel_output_file $srcjar) \
- "${EXTRA_MAVEN_ARGS[@]:+${EXTRA_MAVEN_ARGS[@]}}"
-}
-
-deploy_library \
- java/dagger/libcore.jar \
- java/dagger/libcore-src.jar \
- java/dagger/core-javadoc.jar \
- java/dagger/pom.xml
-
-deploy_library \
- gwt/libgwt.jar \
- gwt/libgwt.jar \
- gwt/libgwt.jar \
- gwt/pom.xml
-
-deploy_library \
- shaded_compiler.jar \
- shaded_compiler_src.jar \
- java/dagger/internal/codegen/codegen-javadoc.jar \
- java/dagger/internal/codegen/pom.xml
-
-deploy_library \
- java/dagger/producers/libproducers.jar \
- java/dagger/producers/libproducers-src.jar \
- java/dagger/producers/producers-javadoc.jar \
- java/dagger/producers/pom.xml
-
-deploy_library \
- shaded_spi.jar \
- shaded_spi_src.jar \
- spi-javadoc.jar \
- java/dagger/spi/pom.xml
-
-deploy_library \
- java/dagger/android/android.aar \
- java/dagger/android/libandroid-src.jar \
- java/dagger/android/android-javadoc.jar \
- java/dagger/android/pom.xml
-
-# b/37741866 and https://github.com/google/dagger/issues/715
-deploy_library \
- java/dagger/android/libandroid.jar \
- java/dagger/android/libandroid-src.jar \
- java/dagger/android/android-javadoc.jar \
- java/dagger/android/jarimpl-pom.xml
-
-deploy_library \
- java/dagger/android/support/support.aar \
- java/dagger/android/support/libsupport-src.jar \
- java/dagger/android/support/support-javadoc.jar \
- java/dagger/android/support/pom.xml
-
-deploy_library \
- shaded_android_processor.jar \
- java/dagger/android/processor/libprocessor-src.jar \
- java/dagger/android/processor/processor-javadoc.jar \
- java/dagger/android/processor/pom.xml
-
-deploy_library \
- java/dagger/grpc/server/libserver.jar \
- java/dagger/grpc/server/libserver-src.jar \
- java/dagger/grpc/server/javadoc.jar \
- java/dagger/grpc/server/server-pom.xml
-
-deploy_library \
- java/dagger/grpc/server/libannotations.jar \
- java/dagger/grpc/server/libannotations-src.jar \
- java/dagger/grpc/server/javadoc.jar \
- java/dagger/grpc/server/annotations-pom.xml
-
-deploy_library \
- shaded_grpc_server_processor.jar \
- java/dagger/grpc/server/processor/libprocessor-src.jar \
- java/dagger/grpc/server/processor/javadoc.jar \
- java/dagger/grpc/server/processor/pom.xml
diff --git a/util/find_pom_value.py b/util/find_pom_value.py
new file mode 100644
index 000000000..c662b0e07
--- /dev/null
+++ b/util/find_pom_value.py
@@ -0,0 +1,19 @@
+"""Find value of a Maven pom attribute given a pom file.
+
+ Usage:
+ python find_pom_value.py <pom-file> <pom-attribute>
+"""
+import sys
+import xml.etree.ElementTree as Xml
+
+
+def main(argv):
+ pom_file = argv[1]
+ pom_attribute = argv[2]
+ print(
+ Xml.ElementTree(file=pom_file).findtext(
+ "{http://maven.apache.org/POM/4.0.0}%s" % pom_attribute))
+
+
+if __name__ == "__main__":
+ main(sys.argv)
diff --git a/util/generate-latest-docs.sh b/util/generate-latest-docs.sh
index 8e9304c87..c5f71fd21 100755
--- a/util/generate-latest-docs.sh
+++ b/util/generate-latest-docs.sh
@@ -1,30 +1,35 @@
-# see http://benlimmer.com/2013/12/26/automatically-publish-javadoc-to-gh-pages-with-travis-ci/ for details
+set -eux
-set -eu
-
-if [ "$TRAVIS_REPO_SLUG" == "google/dagger" ] && \
- [ "$TRAVIS_JDK_VERSION" == "$JDK_FOR_PUBLISHING" ] && \
- [ "$TRAVIS_PULL_REQUEST" == "false" ] && \
- [ "$TRAVIS_BRANCH" == "master" ]; then
+if [ "$GITHUB_REPOSITORY" == "google/dagger" ] && \
+ [ "$GITHUB_EVENT_NAME" == "push" ] && \
+ [ "$GITHUB_REF" == "refs/heads/master" ]; then
echo -e "Publishing javadoc...\n"
bazel build //:user-docs.jar
JAVADOC_JAR="$(pwd)/bazel-bin/user-docs.jar"
cd $HOME
- git clone --quiet --branch=gh-pages https://${GH_TOKEN}@github.com/google/dagger gh-pages > /dev/null
+ git clone --quiet --branch=gh-pages https://x-access-token:${GH_TOKEN}@github.com/google/dagger gh-pages > /dev/null
cd gh-pages
- git config --global user.email "travis@travis-ci.org"
- git config --global user.name "travis-ci"
+ git config --global user.email "dagger-dev+github@google.com"
+ git config --global user.name "Dagger Team"
git rm -rf api/latest
mkdir -p api
unzip "$JAVADOC_JAR" -d api/latest
rm -rf api/latest/META-INF/
git add -f api/latest
- git commit -m "Latest javadoc on successful travis build $TRAVIS_BUILD_NUMBER auto-pushed to gh-pages"
- git push -fq origin gh-pages > /dev/null
- echo -e "Published Javadoc to gh-pages.\n"
+ # Check if there are any changes before committing, otherwise attempting
+ # to commit will fail the build (https://stackoverflow.com/a/2659808).
+ if [[ $(git diff-index --quiet HEAD --) || $? == 1 ]]; then
+ # The exist status is 1, meaning there are changes to commit
+ git commit -m "Latest javadoc on successful Github build $GITHUB_WORKFLOW/$GITHUB_RUN_ID auto-pushed to gh-pages"
+ git push -fq origin gh-pages > /dev/null
+ echo -e "Published Javadoc to gh-pages.\n"
+ else
+ # The exist status is 0, meaning there are no changes to commit
+ echo -e "Skipping publishing docs since no changes were detected."
+ fi
else
- echo -e "Not publishing docs for jdk=${TRAVIS_JDK_VERSION} and branch=${TRAVIS_BRANCH}"
+ echo -e "Not publishing docs for branch=${$GITHUB_REF}"
fi
diff --git a/util/install-local-snapshot.sh b/util/install-local-snapshot.sh
index 8f77a41fb..be6030c74 100755
--- a/util/install-local-snapshot.sh
+++ b/util/install-local-snapshot.sh
@@ -4,8 +4,43 @@ set -eu
echo -e "Installing maven snapshot locally...\n"
-bash $(dirname $0)/execute-deploy.sh \
+bash $(dirname $0)/deploy-dagger.sh \
+ "install:install-file" \
+ "LOCAL-SNAPSHOT"
+
+bash $(dirname $0)/deploy-hilt.sh \
"install:install-file" \
"LOCAL-SNAPSHOT"
echo -e "Installed local snapshot"
+
+verify_version_file() {
+ local m2_repo=$(mvn help:evaluate -Dexpression=settings.localRepository -q -DforceStdout)
+ local group_path=com/google/dagger
+ local artifact_id=$1
+ local type=$2
+ local version="LOCAL-SNAPSHOT"
+ local temp_dir=$(mktemp -d)
+ local content
+ if [ $type = "jar" ]; then
+ unzip $m2_repo/$group_path/$artifact_id/$version/$artifact_id-$version.jar \
+ META-INF/com.google.dagger_$artifact_id.version \
+ -d $temp_dir
+ elif [ $type = "aar" ]; then
+ unzip $m2_repo/$group_path/$artifact_id/$version/$artifact_id-$version.aar \
+ classes.jar \
+ -d $temp_dir
+ unzip $temp_dir/classes.jar \
+ META-INF/com.google.dagger_$artifact_id.version \
+ -d $temp_dir
+ fi
+ local content=$(cat $temp_dir/META-INF/com.google.dagger_${artifact_id}.version)
+ if [[ $content != $version ]]; then
+ echo "Version file failed verification for artifact: $artifact_id"
+ exit 1
+ fi
+}
+
+# Verify tracking version file in Dagger and Dagger Android
+verify_version_file "dagger" "jar"
+verify_version_file "dagger-android" "aar"
diff --git a/util/publish-snapshot-on-commit.sh b/util/publish-snapshot-on-commit.sh
index 944a2c3f7..63cc3eaa1 100755
--- a/util/publish-snapshot-on-commit.sh
+++ b/util/publish-snapshot-on-commit.sh
@@ -1,14 +1,20 @@
# see https://coderwall.com/p/9b_lfq
-set -eu
+set -eux
-if [ "$TRAVIS_REPO_SLUG" == "google/dagger" ] && \
- [ "$TRAVIS_JDK_VERSION" == "$JDK_FOR_PUBLISHING" ] && \
- [ "$TRAVIS_PULL_REQUEST" == "false" ] && \
- [ "$TRAVIS_BRANCH" == "master" ]; then
+if [ "$GITHUB_REPOSITORY" == "google/dagger" ] && \
+ [ "$GITHUB_EVENT_NAME" == "push" ] && \
+ [ "$GITHUB_REF" == "refs/heads/master" ]; then
echo -e "Publishing maven snapshot...\n"
- bash $(dirname $0)/execute-deploy.sh \
+ bash $(dirname $0)/deploy-dagger.sh \
+ "deploy:deploy-file" \
+ "HEAD-SNAPSHOT" \
+ "-DrepositoryId=sonatype-nexus-snapshots" \
+ "-Durl=https://oss.sonatype.org/content/repositories/snapshots" \
+ "--settings=$(dirname $0)/settings.xml"
+
+ bash $(dirname $0)/deploy-hilt.sh \
"deploy:deploy-file" \
"HEAD-SNAPSHOT" \
"-DrepositoryId=sonatype-nexus-snapshots" \
@@ -17,5 +23,5 @@ if [ "$TRAVIS_REPO_SLUG" == "google/dagger" ] && \
echo -e "Published maven snapshot"
else
- echo -e "Not publishing snapshot for jdk=${TRAVIS_JDK_VERSION} and branch=${TRAVIS_BRANCH}"
+ echo -e "Not publishing snapshot for branch=${$GITHUB_REF}"
fi
diff --git a/util/run-local-emulator-tests.sh b/util/run-local-emulator-tests.sh
new file mode 100755
index 000000000..7fc77620f
--- /dev/null
+++ b/util/run-local-emulator-tests.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+set -ex
+
+readonly GRADLE_PROJECTS=(
+ "javatests/artifacts/hilt-android/simple"
+ "javatests/artifacts/hilt-android/simpleKotlin"
+)
+for project in "${GRADLE_PROJECTS[@]}"; do
+ echo "Running gradle Android emulator tests for $project"
+ ./$project/gradlew -p $project connectedAndroidTest --no-daemon --stacktrace
+done
+
+# Run emulator tests in a project with configuration cache enabled
+# TODO(danysantiago): Once AGP 4.2.0 is stable, remove these project and enable
+# config cache in the other test projects.
+readonly CONFIG_CACHE_PROJECT="javatests/artifacts/hilt-android/gradleConfigCache"
+./$CONFIG_CACHE_PROJECT/gradlew -p $CONFIG_CACHE_PROJECT connectedAndroidTest --no-daemon --stacktrace --configuration-cache
diff --git a/util/run-local-gradle-android-tests.sh b/util/run-local-gradle-android-tests.sh
new file mode 100755
index 000000000..ba51078e7
--- /dev/null
+++ b/util/run-local-gradle-android-tests.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+set -ex
+
+readonly AGP_VERSION_INPUT=$1
+readonly ANDROID_GRADLE_PROJECTS=(
+ "java/dagger/example/gradle/android/simple"
+ "javatests/artifacts/dagger-android/simple"
+ "javatests/artifacts/hilt-android/simple"
+ "javatests/artifacts/hilt-android/simpleKotlin"
+)
+for project in "${ANDROID_GRADLE_PROJECTS[@]}"; do
+ echo "Running gradle tests for $project with AGP $AGP_VERSION_INPUT"
+ AGP_VERSION=$AGP_VERSION_INPUT ./$project/gradlew -p $project buildDebug --no-daemon --stacktrace
+ AGP_VERSION=$AGP_VERSION_INPUT ./$project/gradlew -p $project testDebug --no-daemon --stacktrace
+done
+
+# Run gradle tests in a project with configuration cache enabled
+# TODO(danysantiago): Once AGP 4.2.0 is stable, remove these project and enable
+# config cache in the other test projects.
+readonly CONFIG_CACHE_PROJECT="javatests/artifacts/hilt-android/gradleConfigCache"
+./$CONFIG_CACHE_PROJECT/gradlew -p $CONFIG_CACHE_PROJECT assembleDebug --no-daemon --stacktrace --configuration-cache
diff --git a/util/run-local-gradle-tests.sh b/util/run-local-gradle-tests.sh
new file mode 100755
index 000000000..b88ccabc7
--- /dev/null
+++ b/util/run-local-gradle-tests.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+set -ex
+
+readonly GRADLE_PROJECTS=(
+ "java/dagger/example/gradle/simple"
+ "java/dagger/hilt/android/plugin"
+ "javatests/artifacts/dagger/simple"
+)
+for project in "${GRADLE_PROJECTS[@]}"; do
+ echo "Running gradle tests for $project"
+ ./$project/gradlew -p $project build --no-daemon --stacktrace
+ ./$project/gradlew -p $project test --no-daemon --stacktrace
+done
diff --git a/util/run-local-tests.sh b/util/run-local-tests.sh
new file mode 100755
index 000000000..ea78671fe
--- /dev/null
+++ b/util/run-local-tests.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+set -ex
+
+# These jobs should match .github/workflows/ci.yml. We can't run this script
+# directly in Github since it's too slow for a single job.
+
+# Run local bazel tests
+bazel test --test_output=errors //...
+
+# Install local maven artifacts.
+util/install-local-snapshot.sh
+
+# Run local mvn tests
+pushd examples/maven && mvn compile && popd
+
+# Run local gradle tests
+util/run-local-gradle-tests.sh
+util/run-local-gradle-android-tests.sh "4.1.0"
+util/run-local-gradle-android-tests.sh "4.2.0-beta04"
+
diff --git a/workspace_defs.bzl b/workspace_defs.bzl
new file mode 100644
index 000000000..c017b76f9
--- /dev/null
+++ b/workspace_defs.bzl
@@ -0,0 +1,292 @@
+# Copyright (C) 2020 The Google Bazel Common Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""A macro to configure Dagger deps within a workspace"""
+
+load("//:build_defs.bzl", "POM_VERSION", "POM_VERSION_ALPHA")
+
+_DAGGER_VERSION = POM_VERSION
+_HILT_VERSION = POM_VERSION_ALPHA
+
+DAGGER_ARTIFACTS = [
+ "com.google.dagger:dagger:" + _DAGGER_VERSION,
+ "com.google.dagger:dagger-compiler:" + _DAGGER_VERSION,
+ "com.google.dagger:dagger-producers:" + _DAGGER_VERSION,
+ "com.google.dagger:dagger-spi:" + _DAGGER_VERSION,
+]
+
+DAGGER_ANDROID_ARTIFACTS = [
+ "com.google.dagger:dagger-android-processor:" + _DAGGER_VERSION,
+ "com.google.dagger:dagger-android-support:" + _DAGGER_VERSION,
+ "com.google.dagger:dagger-android:" + _DAGGER_VERSION,
+]
+
+HILT_ANDROID_ARTIFACTS = [
+ "androidx.test:core:1.1.0", # Export for ApplicationProvider
+ "javax.annotation:jsr250-api:1.0", # Export for @Generated
+ "androidx.annotation:annotation:1.1.0", # Export for @CallSuper/@Nullable
+ "com.google.dagger:dagger:" + _DAGGER_VERSION,
+ "com.google.dagger:dagger-compiler:" + _DAGGER_VERSION,
+ "com.google.dagger:hilt-android:" + _HILT_VERSION,
+ "com.google.dagger:hilt-android-testing:" + _HILT_VERSION,
+ "com.google.dagger:hilt-android-compiler:" + _HILT_VERSION,
+]
+
+DAGGER_REPOSITORIES = [
+ "https://maven.google.com",
+ "https://repo1.maven.org/maven2",
+]
+
+DAGGER_ANDROID_REPOSITORIES = DAGGER_REPOSITORIES
+
+HILT_ANDROID_REPOSITORIES = DAGGER_REPOSITORIES
+
+# https://github.com/bazelbuild/buildtools/blob/master/WARNINGS.md#unnamed-macro
+# buildifier: disable=unnamed-macro
+def dagger_rules(repo_name = "@maven"):
+ """Defines the Dagger targets with proper exported dependencies and plugins.
+
+ The targets will be of the form ":<artifact-id>".
+
+ Args:
+ repo_name: The name of the dependency repository (default is "@maven").
+ """
+ native.java_library(
+ name = "dagger",
+ exported_plugins = [":dagger-compiler"],
+ visibility = ["//visibility:public"],
+ exports = [
+ "%s//:com_google_dagger_dagger" % repo_name,
+ "%s//:javax_inject_javax_inject" % repo_name,
+ ],
+ )
+
+ native.java_plugin(
+ name = "dagger-compiler",
+ generates_api = 1,
+ processor_class = "dagger.internal.codegen.ComponentProcessor",
+ deps = [
+ "%s//:com_google_dagger_dagger_compiler" % repo_name,
+ ],
+ )
+
+ native.java_library(
+ name = "dagger-producers",
+ visibility = ["//visibility:public"],
+ exports = [
+ ":dagger",
+ "%s//:com_google_dagger_dagger_producers" % repo_name,
+ ],
+ )
+
+ native.java_library(
+ name = "dagger-spi",
+ visibility = ["//visibility:public"],
+ exports = [
+ "%s//:com_google_dagger_dagger_spi" % repo_name,
+ ],
+ )
+
+# https://github.com/bazelbuild/buildtools/blob/master/WARNINGS.md#unnamed-macro
+# buildifier: disable=unnamed-macro
+def dagger_android_rules(repo_name = "@maven"):
+ """Defines the Dagger Android targets with proper exported dependencies and plugins.
+
+ The targets will be of the form ":<artifact-id>".
+
+ Args:
+ repo_name: The name of the dependency repository (default is "@maven").
+ """
+
+ # https://github.com/bazelbuild/buildtools/blob/master/WARNINGS.md#native-android
+ # buildifier: disable=native-android
+ native.android_library(
+ name = "dagger-android",
+ exported_plugins = [":dagger-android-processor"],
+ visibility = ["//visibility:public"],
+ exports = [
+ "%s//:com_google_dagger_dagger_android" % repo_name,
+ ],
+ )
+
+ # https://github.com/bazelbuild/buildtools/blob/master/WARNINGS.md#native-android
+ # buildifier: disable=native-android
+ native.android_library(
+ name = "dagger-android-support",
+ exported_plugins = [":dagger-android-processor"],
+ visibility = ["//visibility:public"],
+ exports = [
+ ":dagger-android",
+ "%s//:com_google_dagger_dagger_android_support" % repo_name,
+ ],
+ )
+
+ native.java_plugin(
+ name = "dagger-android-processor",
+ generates_api = 1,
+ processor_class = "dagger.android.processor.AndroidProcessor",
+ deps = [
+ "%s//:com_google_dagger_dagger_android_processor" % repo_name,
+ ],
+ )
+
+# https://github.com/bazelbuild/buildtools/blob/master/WARNINGS.md#unnamed-macro
+# buildifier: disable=unnamed-macro
+def hilt_android_rules(repo_name = "@maven"):
+ """Defines the Hilt Android targets with proper exported dependencies and plugins.
+
+ The targets will be of the form ":<artifact-id>".
+
+ Args:
+ repo_name: The name of the dependency repository (default is "@maven").
+ """
+
+ # https://github.com/bazelbuild/buildtools/blob/master/WARNINGS.md#native-android
+ # buildifier: disable=native-android
+ native.android_library(
+ name = "hilt-android",
+ exported_plugins = [
+ ":hilt_dagger_compiler",
+ ":hilt_android_entry_point_processor",
+ ":hilt_aggregated_deps_processor",
+ ":hilt_alias_of_processor",
+ ":hilt_define_component_processor",
+ ":hilt_generates_root_input_processor",
+ ":hilt_originating_element_processor",
+ ":hilt_root_processor",
+ ":hilt_view_model_processor",
+ ],
+ visibility = ["//visibility:public"],
+ exports = [
+ "%s//:com_google_dagger_dagger" % repo_name, # For Dagger APIs
+ "%s//:javax_inject_javax_inject" % repo_name, # For @Inject
+ "%s//:androidx_annotation_annotation" % repo_name, # For @CallSuper
+ "%s//:com_google_dagger_hilt_android" % repo_name,
+ "%s//:javax_annotation_jsr250_api" % repo_name, # For @Generated
+ ],
+ )
+
+ # This target is same as dagger-compiler, but we're redefining it here
+ # so that users don't have to call dagger_rules() first.
+ native.java_plugin(
+ name = "hilt_dagger_compiler",
+ generates_api = 1,
+ processor_class = "dagger.internal.codegen.ComponentProcessor",
+ deps = [
+ "%s//:com_google_dagger_dagger_compiler" % repo_name,
+ ],
+ )
+
+ native.java_plugin(
+ name = "hilt_android_entry_point_processor",
+ generates_api = 1,
+ processor_class = "dagger.hilt.android.processor.internal.androidentrypoint.AndroidEntryPointProcessor",
+ deps = ["%s//:com_google_dagger_hilt_android_compiler" % repo_name],
+ )
+
+ native.java_plugin(
+ name = "hilt_aggregated_deps_processor",
+ generates_api = 1,
+ processor_class = "dagger.hilt.processor.internal.aggregateddeps.AggregatedDepsProcessor",
+ deps = ["%s//:com_google_dagger_hilt_android_compiler" % repo_name],
+ )
+
+ native.java_plugin(
+ name = "hilt_alias_of_processor",
+ generates_api = 1,
+ processor_class = "dagger.hilt.processor.internal.aliasof.AliasOfProcessor",
+ deps = ["%s//:com_google_dagger_hilt_android_compiler" % repo_name],
+ )
+
+ native.java_plugin(
+ name = "hilt_define_component_processor",
+ generates_api = 1,
+ processor_class = "dagger.hilt.processor.internal.definecomponent.DefineComponentProcessor",
+ deps = ["%s//:com_google_dagger_hilt_android_compiler" % repo_name],
+ )
+
+ native.java_plugin(
+ name = "hilt_generates_root_input_processor",
+ generates_api = 1,
+ processor_class = "dagger.hilt.processor.internal.generatesrootinput.GeneratesRootInputProcessor",
+ deps = ["%s//:com_google_dagger_hilt_android_compiler" % repo_name],
+ )
+
+ native.java_plugin(
+ name = "hilt_originating_element_processor",
+ generates_api = 1,
+ processor_class = "dagger.hilt.processor.internal.originatingelement.OriginatingElementProcessor",
+ deps = ["%s//:com_google_dagger_hilt_android_compiler" % repo_name],
+ )
+
+ native.java_plugin(
+ name = "hilt_root_processor",
+ generates_api = 1,
+ processor_class = "dagger.hilt.processor.internal.root.RootProcessor",
+ deps = ["%s//:com_google_dagger_hilt_android_compiler" % repo_name],
+ )
+
+ native.java_plugin(
+ name = "hilt_view_model_processor",
+ generates_api = 1,
+ processor_class = "dagger.hilt.android.processor.internal.viewmodel.ViewModelProcessor",
+ deps = ["%s//:com_google_dagger_hilt_android_compiler" % repo_name],
+ )
+
+ # https://github.com/bazelbuild/buildtools/blob/master/WARNINGS.md#native-android
+ # buildifier: disable=native-android
+ native.android_library(
+ name = "hilt-android-testing",
+ testonly = 1,
+ exported_plugins = [
+ ":hilt_bind_value_processor",
+ ":hilt_custom_test_application_processor",
+ ":hilt_testroot_processor",
+ ":hilt_uninstall_modules_processor",
+ ],
+ visibility = ["//visibility:public"],
+ exports = [
+ ":hilt-android",
+ "%s//:androidx_test_core" % repo_name, # For ApplicationProvider
+ "%s//:com_google_dagger_hilt_android_testing" % repo_name,
+ ],
+ )
+
+ native.java_plugin(
+ name = "hilt_bind_value_processor",
+ generates_api = 1,
+ processor_class = "dagger.hilt.android.processor.internal.bindvalue.BindValueProcessor",
+ deps = ["%s//:com_google_dagger_hilt_android_compiler" % repo_name],
+ )
+
+ native.java_plugin(
+ name = "hilt_custom_test_application_processor",
+ generates_api = 1,
+ processor_class = "dagger.hilt.android.processor.internal.customtestapplication.CustomTestApplicationProcessor",
+ deps = ["%s//:com_google_dagger_hilt_android_compiler" % repo_name],
+ )
+
+ native.java_plugin(
+ name = "hilt_testroot_processor",
+ generates_api = 1,
+ processor_class = "dagger.hilt.android.processor.internal.testroot.TestRootProcessor",
+ deps = ["%s//:com_google_dagger_hilt_android_compiler" % repo_name],
+ )
+
+ native.java_plugin(
+ name = "hilt_uninstall_modules_processor",
+ generates_api = 1,
+ processor_class = "dagger.hilt.android.processor.internal.uninstallmodules.UninstallModulesProcessor",
+ deps = ["%s//:com_google_dagger_hilt_android_compiler" % repo_name],
+ )