Snap for 7478028 from 313f77a7c9ef6b6f5e4c83e6ebbadb13699f6cb1 to mainline-adbd-release

Change-Id: Ia38bbd38424f7ca7e6810117cb5e3201f9af507e
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..328d4b1
--- /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 fabb38f..1f7ead8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,8 @@
 
 gen-external-apklibs
 
-/bazel-*
+bazel-*
 
 *.pyc
+
+.gradle
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 9015f2e..0000000
--- 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 27a05c5..1bdbd1b 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"],
+package {
+    default_visibility: [":__subpackages__"],
+    default_applicable_licenses: ["external_dagger2_license"],
 }
 
-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",
+// 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-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"],
-}
-
-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",
+    license_text: [
+        "LICENSE.txt",
+        "NOTICE",
     ],
 }
 
 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",
-    ],
-}
-
-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 @@
     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 @@
         "dagger2",
         "dagger2-android-annotation-stubs",
         "guava",
+        "jsr330",
     ],
 
     java_version: "1.8",
+    sdk_version: "core_current",
 }
 
 // build dagger2 compiler plugin
@@ -147,8 +114,16 @@
 
 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 @@
 
     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_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 @@
     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 8becb3e..015e849 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(
+    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 @@
     ],
 )
 
-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 = SHADE_RULES,
+    rules = [
+        "rule com.google.auto.common.** dagger.android.shaded.auto.common.@1",
+    ],
 )
 
 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 = SHADE_RULES,
+    rules = [
+        "rule com.google.auto.common.** dagger.grpc.shaded.auto.common.@1",
+    ],
 )
 
 # 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 @@
         "//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 45f15c4..54fb50a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -33,11 +33,19 @@
 *   [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 59b9e25..5eef5c3 100644
--- a/METADATA
+++ b/METADATA
@@ -11,7 +11,7 @@
     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 a9ebc26..393e2e7 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 @@
 
 ### 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 @@
 </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 @@
 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 @@
 }
 ```
 
-### 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 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 8758b38..6c2b6b0 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(
-    name = "zlib",
-    actual = "@bazel_tools//third_party/zlib",
+# 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",
+    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 21aeb46..d4a0290 100755
--- a/android-annotation-stubs/gen_annotations.sh
+++ b/android-annotation-stubs/gen_annotations.sh
@@ -59,6 +59,8 @@
 
 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 83e3590..ef86328 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 0bd7402..932ca08 100644
--- a/build_defs.bzl
+++ b/build_defs.bzl
@@ -24,3 +24,8 @@
     "-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 0000000..5cd2290
--- /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 0000000..0f9a621
--- /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 0000000..23f93b1
--- /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 0000000..4de78ae
--- /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 0000000..10c1849
--- /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 0000000..796638d
--- /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 0000000..981a41a
--- /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 0000000..9f9df50
--- /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 0000000..4a627cf
--- /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 0000000..6509c80
--- /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 0000000..7af63b9
--- /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 0000000..624fdf7
--- /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 0000000..0709222
--- /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 0000000..3af7052
--- /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 0000000..a786737
--- /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 0000000..e890401
--- /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 0000000..0ff7212
--- /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 0000000..122bec0
--- /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 0000000..0ad3903
--- /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 0000000..9982fbd
--- /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 0000000..5f4e162
--- /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 0000000..6d89270
--- /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 0000000..a196d99
--- /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 0000000..723ab91
--- /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 0000000..16d2bdb
--- /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 0000000..20c1f27
--- /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 0000000..567c7aa
--- /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 0000000..3d6d1f2
--- /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 0000000..fb8c969
--- /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 0000000..712b21c
--- /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 0000000..202fed3
--- /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 0000000..d94c33f
--- /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 0000000..be21b96
--- /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 c12050d..0000000
--- 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 ffc99dc..0000000
--- 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 7a2b9a6..0000000
--- 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 6410336..0000000
--- 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 e50d249..0000000
--- 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 fbab399..0000000
--- 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 b5ddb6b..0000000
--- 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 e394349..0000000
--- 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 df00b86..0000000
--- 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 c9f9828..0000000
--- 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 da73013..5aa9110 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 3485431..aaebade 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 @@
     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 a60be3b..5d7827b 100644
--- a/java/dagger/Provides.java
+++ b/java/dagger/Provides.java
@@ -30,11 +30,11 @@
  *
  * <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 2f4fb4c..f3296c6 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 @@
    * 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 @@
    * 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 @@
    * 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 @@
    * 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 d4a5d72..ff4a1f6 100644
--- a/java/dagger/android/AndroidInjectionKey.java
+++ b/java/dagger/android/AndroidInjectionKey.java
@@ -28,11 +28,7 @@
  * #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 a88d16e..f0cea41 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 @@
     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 @@
     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 43708f3..9f37223 100644
--- a/java/dagger/android/DaggerActivity.java
+++ b/java/dagger/android/DaggerActivity.java
@@ -19,7 +19,7 @@
 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 d39aa86..2efd724 100644
--- a/java/dagger/android/DaggerBroadcastReceiver.java
+++ b/java/dagger/android/DaggerBroadcastReceiver.java
@@ -19,7 +19,7 @@
 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 4aad485..e1d3f54 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 136bbad..0000000
--- 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 b2aa992..0000000
--- 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 997ddb8..0000000
--- 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 564f32d..0000000
--- 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 d1c6a6c..0000000
--- 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 f30b92c..1425350 100644
--- a/java/dagger/android/internal/AndroidInjectionKeys.java
+++ b/java/dagger/android/internal/AndroidInjectionKeys.java
@@ -20,6 +20,7 @@
  * 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 0000000..46bbb66
--- /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 0000000..a11d0c0
--- /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 0000000..49274e9
--- /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 5c17341..ad7f08e 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 @@
   }
 
   @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 5754143..7e63c76 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 @@
     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 @@
     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 bd8ffbf..0000000
--- 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 1624345..c77e297 100644
--- a/java/dagger/android/support/AndroidSupportInjection.java
+++ b/java/dagger/android/support/AndroidSupportInjection.java
@@ -20,8 +20,7 @@
 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 @@
    * 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 Object findHasSupportFragmentInjector(Fragment fragment) {
+  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 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 749d541..03846b5 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 @@
     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 @@
     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 ccc4faa..da5b173 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 @@
 
   @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 1efaeec..7a5a76d 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 69b90bc..8b0bf23 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 332cdaa..8874511 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 @@
 
   @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 e80609e..0000000
--- 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 0000000..59d8eec
--- /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 0000000..4eba9a7
--- /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 0000000..fadd46d
--- /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 0000000..9b8b7f0
--- /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 e98fe9b..bc0be48 100644
--- a/java/dagger/errorprone/AndroidSupportInjectionModuleMigrator.java
+++ b/java/dagger/errorprone/AndroidSupportInjectionModuleMigrator.java
@@ -19,7 +19,6 @@
 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 @@
 /** 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 408925a..9c3707f 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 @@
     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 0000000..1973229
--- /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 711fb1e..0000000
--- 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 7396744..0000000
--- 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 40ac8ee..0000000
--- 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 f2aab2d..0000000
--- 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 c52bb98..0000000
--- 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 ae3d42d..0000000
--- 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 37add1f..0000000
--- 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 c4ba1fd..0000000
--- 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 0000000..3ff5a75
--- /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 0000000..87e5e01
--- /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 0000000..d1ff505
--- /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 0000000..29bd6f5
--- /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 0000000..a3ff0d0
--- /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 0000000..be5cbe6
--- /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 0000000..3b52d73
--- /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 0000000..a449703
--- /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 0000000..ef75220
--- /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 0000000..266af12
--- /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 0000000..f6c0eda
--- /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 0000000..9a5e2e2
--- /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 0000000..f39d585
--- /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 0000000..801d827
--- /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 0000000..bf4be67
--- /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 0000000..4978cb5
--- /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 0000000..811d4b3
--- /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 0000000..d41c559
--- /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 0000000..bbb0ed1
--- /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 0000000..3d8de8a
--- /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 0000000..22faba5
--- /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 0000000..81e5f65
--- /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 0000000..3c22941
--- /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 0000000..bee4450
--- /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 0000000..7d65989
--- /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 0000000..7972844
--- /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 0000000..7e0cf04
--- /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 0000000..c473b68
--- /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 0000000..2e6d3f4
--- /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 0000000..b0d6d0a
--- /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 0000000..9991c50
--- /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 0000000..a6f2e01
--- /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 0000000..d8f6346
--- /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 0000000..cc666d2
--- /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 0000000..f6317c2
--- /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 0000000..30a973e
--- /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 0000000..734590a
--- /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 0000000..e2e34ea
--- /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 0000000..18a547b
--- /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 0000000..f45fd41
--- /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 0000000..50f8f0b
--- /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 0000000..2d8d1e4
--- /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 0000000..5c2d1cf
--- /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 0000000..4d9ca16
--- /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 0000000..b0d6d0a
--- /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 0000000..c5a07bc
--- /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 0000000..11552f8
--- /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 0000000..c9e59ef
--- /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 0000000..5c2d1cf
--- /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 0000000..4d9ca16
--- /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 0000000..b0d6d0a
--- /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 84b4a87..bd889c3 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 1c57807..d9a2f97 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 @@
     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 @@
     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 5fe8d5c..a864f36 100644
--- a/java/dagger/grpc/server/README.md
+++ b/java/dagger/grpc/server/README.md
@@ -7,4 +7,4 @@
 
 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 ce02b06..dd33651 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 @@
     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 @@
     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 e361fdb..9b74454 100644
--- a/java/dagger/grpc/server/processor/GrpcServiceProcessor.java
+++ b/java/dagger/grpc/server/processor/GrpcServiceProcessor.java
@@ -40,7 +40,7 @@
 /**
  * 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 0000000..94af18a
--- /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 0000000..fcd572c
--- /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 0000000..e216e77
--- /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 0000000..32e2312
--- /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 0000000..ffd786f
--- /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 0000000..9148c82
--- /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 0000000..911e466
--- /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 0000000..5e2d7cd
--- /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 0000000..8c56116
--- /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 0000000..3a7b087
--- /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 0000000..47d7e67
--- /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 0000000..6145af1
--- /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 0000000..0c2ca0f
--- /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 0000000..2b02498
--- /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 0000000..78f48cc
--- /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 0000000..b3c340e
--- /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 0000000..3a307d1
--- /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 0000000..6b088e6
--- /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 0000000..0a3f6ea
--- /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 0000000..1f5b420
--- /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 0000000..2ba2133
--- /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 0000000..be5f2e8
--- /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 0000000..9ac3686
--- /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 0000000..2a4c194
--- /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 0000000..b3687ee
--- /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 0000000..28a7223
--- /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 0000000..cbf8f7f
--- /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 0000000..daf046b
--- /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 0000000..86460d8
--- /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 0000000..662c5b4
--- /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 0000000..b4aec95
--- /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 0000000..d933c81
--- /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 0000000..f508e48
--- /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 0000000..4f8ab14
--- /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 0000000..70e2e57
--- /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 0000000..a163d8c
--- /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 0000000..6c35600
--- /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 0000000..173b163
--- /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 0000000..3969b73
--- /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 0000000..8a23af5
--- /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 0000000..466856c
--- /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 0000000..f1b550c
--- /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 0000000..7f1fcfc
--- /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 0000000..7abe3c2
--- /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 0000000..7585bbb
--- /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 0000000..0ebd150
--- /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 0000000..51d60b2
--- /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 0000000..73daf8f
--- /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 0000000..f1e5f27
--- /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 0000000..29b5f70
--- /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 0000000..0234ffe
--- /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 0000000..c0916a4
--- /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 0000000..462aefc
--- /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 0000000..f7919a9
--- /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 0000000..03b9ae9
--- /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 0000000..0fd3db3
--- /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 0000000..84ca99a
--- /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 0000000..bf52431
--- /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 0000000..646c51b
--- /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 0000000..5c2d1cf
--- /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 0000000..4d9ca16
--- /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 0000000..b0d6d0a
--- /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 0000000..2ae0b0a
--- /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 0000000..bff4797
--- /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 0000000..081c7ad
--- /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 0000000..2e274ab
--- /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 0000000..8a85040
--- /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 0000000..9ee904d
--- /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 0000000..765fb36
--- /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 0000000..564f05a
--- /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 0000000..4fe168d
--- /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 0000000..a061ab5
--- /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 0000000..0d7637d
--- /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 0000000..c1e4f27
--- /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 0000000..d52ff71
--- /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 0000000..7a02482
--- /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 0000000..646c51b
--- /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 0000000..5c2d1cf
--- /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 0000000..4d9ca16
--- /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 0000000..b0d6d0a
--- /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 0000000..e42f9d1
--- /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 0000000..57c84e3
--- /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 0000000..d5b11f5
--- /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 0000000..5ea09db
--- /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 0000000..110b3fe
--- /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 0000000..0e282be
--- /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 0000000..11cc89f
--- /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 0000000..9b95d12
--- /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 0000000..f2e37b6
--- /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 0000000..e9acbb9
--- /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 0000000..b15a8cc
--- /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 0000000..b3ecfc2
--- /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 0000000..becce8c
--- /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 0000000..9967053
--- /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 0000000..427822d
--- /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 0000000..3b0d3bc
--- /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 0000000..5d54e9c
--- /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 0000000..ef7aa3b
--- /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 0000000..2d4a72b
--- /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 0000000..bb645db
--- /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 0000000..3bc8df1
--- /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 0000000..471c31c
--- /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 0000000..de702a9
--- /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 0000000..40dd60a
--- /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 0000000..d5e7f15
--- /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 0000000..cb2ece0
--- /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 0000000..b877f6f
--- /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 0000000..087a7ee
--- /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 0000000..080e203
--- /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 0000000..af4ebc2
--- /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 0000000..a36e237
--- /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 0000000..b0bc361
--- /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 0000000..c3bbf0c
--- /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 0000000..17c40b3
--- /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 0000000..1878870
--- /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 0000000..a8695c4
--- /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 0000000..c7ff5c9
--- /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 0000000..4eed6fb
--- /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 0000000..e39073f
--- /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 0000000..7055f91
--- /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 0000000..fd80510
--- /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 0000000..198ec8a
--- /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 0000000..edd3d3d
--- /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 0000000..ade47c3
--- /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 0000000..14c1387
--- /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 0000000..f9d0ad8
--- /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 0000000..e37225a
--- /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 0000000..894f7ab
--- /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 0000000..7fd2c83
--- /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 0000000..274ecbe
--- /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 0000000..5c2d1cf
--- /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 0000000..4d9ca16
--- /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 0000000..b0d6d0a
--- /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 0000000..e066f48
--- /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 0000000..015bb76
--- /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 0000000..9bb5160
--- /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 0000000..7a33836
--- /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 0000000..e26edb5
--- /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 0000000..84b35b1
--- /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 0000000..39a2d3a
--- /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 0000000..e5a101e
--- /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 0000000..1580431
--- /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 0000000..5d2b9df
--- /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 0000000..e2d2a7b
--- /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 0000000..e704d4a
--- /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 0000000..95e9356
--- /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 0000000..3031528
--- /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 0000000..a4967e3
--- /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 0000000..f7397fc
--- /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 0000000..dde8858
--- /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 0000000..afc0da2
--- /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 0000000..55780cc
--- /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 0000000..285d5ba
--- /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 0000000..d1c08f0
--- /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 0000000..b472bfb
--- /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 0000000..5bac8ac
--- /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 0000000..29060e0
--- /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 0000000..a6e5d93
--- /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 0000000..f7793be
--- /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 0000000..d3297c3
--- /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 0000000..dd23236
--- /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 0000000..5a92241
--- /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 0000000..6141250
--- /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 0000000..2d58766
--- /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 0000000..f256985
--- /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 0000000..a003ab5
--- /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 0000000..0b84002
--- /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 0000000..75292b8
--- /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 0000000..e116c44
--- /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 0000000..915ae51
--- /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 0000000..aaf8b89
--- /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 0000000..e5a2cdd
--- /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 0000000..ce9ad14
--- /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 0000000..e5868f8
--- /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 0000000..7bb9b9a
--- /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 0000000..f16e06d
--- /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 0000000..55e9ddc
--- /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 0000000..f8e9f60
--- /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 0000000..4ef479f
--- /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 0000000..daadd03
--- /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 0000000..577410d
--- /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 0000000..e9e217d
--- /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 0000000..a80a215
--- /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 0000000..412b09a
--- /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 0000000..cbc51b3
--- /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 0000000..108a15d
--- /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 0000000..bf50288
--- /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 0000000..060b077
--- /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 0000000..e9721d5
--- /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 0000000..51b7ef4
--- /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 0000000..de8e3f7
--- /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 0000000..fd1a6c8
--- /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 0000000..73c4606
--- /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 0000000..e92f0f0
--- /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 0000000..b45925f
--- /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 0000000..789fbfe
--- /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 0000000..846f7d2
--- /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 0000000..97ebe52
--- /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 0000000..a8e57dc
--- /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 0000000..cfcc40e
--- /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 0000000..226ef75
--- /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 0000000..26b45ec
--- /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 0000000..2cbd744
--- /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 0000000..c61325d
--- /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 0000000..9f128f7
--- /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 0000000..e74ac9e
--- /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 0000000..bc6c75e
--- /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 0000000..84205f2
--- /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 0000000..8861125
--- /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 0000000..abf7c0a
--- /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 0000000..6f8f989
--- /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 0000000..26ec669
--- /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 0000000..47db287
--- /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 0000000..620e0c5
--- /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 0000000..c0164f8
--- /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 0000000..b1dfcb3
--- /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 0000000..690a55c
--- /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 0000000..36a4410
--- /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 0000000..32a34f7
--- /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 0000000..fa15f92
--- /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 0000000..97eb4cb
--- /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 0000000..925a19f
--- /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 0000000..6480c10
--- /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 0000000..fae8e7d
--- /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 0000000..c88182b
--- /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 0000000..c53a430
--- /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 0000000..b6bf709
--- /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 0000000..48dc8cd
--- /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 0000000..07e8fe7
--- /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 0000000..3c8cb18
--- /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 0000000..dc245d1
--- /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 0000000..3967e1b
--- /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 0000000..4d85d37
--- /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 0000000..bd837a3
--- /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 0000000..f65e2df
--- /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 0000000..76319b9
--- /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 0000000..b2a84db
--- /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 0000000..796dfce
--- /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 0000000..25ea704
--- /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 0000000..3e96ed4
--- /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 0000000..b1d56c4
--- /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 0000000..25555fb
--- /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 0000000..d5da760
--- /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 0000000..8e54ac4
--- /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 0000000..d4917f8
--- /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 0000000..b68d4f1
--- /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 0000000..a14410d
--- /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 0000000..acb6252
--- /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 0000000..bc269e3
--- /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 0000000..568b060
--- /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 0000000..87adcf3
--- /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 0000000..584d8f9
--- /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 0000000..baff1d8
--- /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 0000000..d961768
--- /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 0000000..4961cd5
--- /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 0000000..234ea7b
--- /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 0000000..5d3cef9
--- /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 0000000..3a4bf1e
--- /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 0000000..fab4d19
--- /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 0000000..6d2137a
--- /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 0000000..661dd8a
--- /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 0000000..6f8f67e
--- /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 0000000..ec5ddf8
--- /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 0000000..460e8a9
--- /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 0000000..b1578da
--- /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 0000000..b33c19d
--- /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 0000000..cc955ef
--- /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 0000000..bcc2dfe
--- /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 0000000..58c938f
--- /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 0000000..ebbc941
--- /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 0000000..23ed148
--- /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 0000000..6dac59f
--- /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 0000000..66f1751
--- /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 0000000..18ff0bd
--- /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 0000000..6efd643
--- /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 0000000..75c4c15
--- /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 0000000..930a72e
--- /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 0000000..d3f90f2
--- /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 0000000..7edec1c
--- /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 0000000..0f10f39
--- /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 0000000..aa69e40
--- /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 0000000..e0bfca9
--- /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 0000000..0b33011
--- /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 0000000..e3865a6
--- /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 0000000..c51f350
--- /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 0000000..f24ffb7
--- /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 0000000..6588e09
--- /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 0000000..5c53894
--- /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 0000000..139592e
--- /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 0000000..50dbcf6
--- /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 0000000..723cf04
--- /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 0000000..709eb7f
--- /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 0000000..981636d
--- /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 0000000..45cdd22
--- /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 0000000..c2c55e6
--- /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 0000000..4c43f1d
--- /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 0000000..8f8c948
--- /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 0000000..3545231
--- /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 0000000..284f8cd
--- /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 0000000..e5a83b6
--- /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 0000000..fc97fed
--- /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 0000000..561beb7
--- /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 0000000..3ea1c5f
--- /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 0000000..dfe1389
--- /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 0000000..5837343
--- /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 1ca0fbb..0000000
--- 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 996cd1d..0000000
--- 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 0000000..55d3285
--- /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 25e2b5b..12824d1 100644
--- a/java/dagger/internal/MapBuilder.java
+++ b/java/dagger/internal/MapBuilder.java
@@ -50,11 +50,9 @@
   }
 
   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 8eb0783..39748c9 100644
--- a/java/dagger/internal/MapFactory.java
+++ b/java/dagger/internal/MapFactory.java
@@ -66,11 +66,13 @@
       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 993d150..0000000
--- 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 1e658e4..0000000
--- 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 983321e..0000000
--- 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 714a353..5c2d740 100644
--- a/java/dagger/internal/Preconditions.java
+++ b/java/dagger/internal/Preconditions.java
@@ -51,6 +51,34 @@
   }
 
   /**
+   * 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 41a2fc7..d65e7cb 100644
--- a/java/dagger/internal/SetBuilder.java
+++ b/java/dagger/internal/SetBuilder.java
@@ -61,13 +61,12 @@
   }
 
   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 273f552..0000000
--- 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 8d729e2..0000000
--- 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 282d8ca..0000000
--- 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 fc30eaa..0000000
--- 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 bad9636..0000000
--- 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 0000000..abc0436
--- /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 0000000..4f2f5b7
--- /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 0000000..3173987
--- /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 824a72e..be970be 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
 
+load("@rules_java//java:defs.bzl", "java_library", "java_plugin")
+load(
+    "//:build_defs.bzl",
+    "POM_VERSION",
+)
+load("//tools:maven.bzl", "gen_maven_artifact")
+
 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",
-    ],
-)
-
-# 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",
-    ],
-)
-
-# 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",
+    exports = [
+        "@google_bazel_common//third_party/java/jsr250_annotations",  # Export for @Generated
     ],
-)
-
-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",
+        ":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_library(
-    name = "kythe",
-    srcs = KYTHE_SRCS,
-    plugins = [":component-codegen"],
-    deps = [
-        ":base",
-        ":binding",
-        ":javac",
-        ":javac-plugin-module",
-        ":kythe_plugin",
-        ":processor",
-        "//java/dagger:core",
-        "//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 = "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",
+    name = "package_info",
+    srcs = ["package-info.java"],
+    tags = ["maven:merged"],
+    deps = ["@google_bazel_common//third_party/java/error_prone:annotations"],
+)
+
+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 @@
     ],
     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 c0f6b71..0000000
--- 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 c9520cd..0000000
--- 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 d850165..0000000
--- 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 0051912..0000000
--- 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 65200f7..0000000
--- 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 564b412..0000000
--- 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 2f548b2..0000000
--- 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 a2cc799..0000000
--- 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 d96da8a..0000000
--- 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 e2c3812..0000000
--- 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 129647f..0000000
--- 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 63e1fa2..0000000
--- 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 df17b15..0000000
--- 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 e6c4f8e..0000000
--- 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 21c05cc..0000000
--- 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 28a272d..0000000
--- 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 a7da092..0000000
--- 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 27067aa..0000000
--- 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 37109c7..0000000
--- 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 9249c8e..0000000
--- 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 1a491c7..0000000
--- 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 b2dc8d8..0000000
--- 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 4c222a9..0000000
--- 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 e198c3a..0000000
--- 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 e1c9d73..0000000
--- 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 acecc9e..0000000
--- 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 a5e0219..0000000
--- 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 66ce3ef..0000000
--- 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 bc3cbf8..0000000
--- 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 a8f2ece..0000000
--- 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 37cf1e8..0000000
--- 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 31bbfab..0000000
--- 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 f6ec7a2..0000000
--- 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 a5eb680..0000000
--- 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 4f06b90..0000000
--- 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 dc203de..0000000
--- 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 c55dadd..0000000
--- 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 769cc4c..0000000
--- 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 7d87eac..0000000
--- 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 8f85b3a..0000000
--- 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 330ec2d..0000000
--- 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 d1e5333..0000000
--- 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 47857ca..260f65a 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 @@
  * 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);
+    }
+    if (!disjoint(annotations, rootComponentCreatorAnnotations())) {
+      processRootCreator(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);
+  private void processRootComponent(TypeElement element) {
+    ValidationReport<TypeElement> validationReport = componentValidator.validate(element);
+    validationReport.printMessagesTo(messager);
+    if (validationReport.isClean()) {
+      componentGenerator.generate(
+          componentDescriptorFactory.rootComponentDescriptor(element), messager);
     }
   }
 
-  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);
-    }
-
-    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 340da14..0000000
--- 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 7579ac9..0000000
--- 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 9578ece..0000000
--- 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 e98d595..0000000
--- 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 8e5b9ba..0000000
--- 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 7e7bbd8..0000000
--- 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 3a31ec7..0000000
--- 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 645c94f..3125ce7 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 @@
   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 @@
       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 @@
     this.componentDescriptorFactory = componentDescriptorFactory;
     this.bindingGraphFactory = bindingGraphFactory;
     this.componentGenerator = componentGenerator;
-    this.bindingGraphConverter = bindingGraphConverter;
     this.bindingGraphValidator = bindingGraphValidator;
-    this.compilerOptions = compilerOptions;
   }
 
   @Override
@@ -95,27 +87,6 @@
   }
 
   @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 @@
     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 @@
     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 void processCreator(Element creator) {
+    creatorValidator.validate(MoreElements.asType(creator)).printMessagesTo(messager);
   }
 
-  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 boolean isComponentValid(Element component) {
+    ValidationReport<?> report = componentValidator.validate(asType(component));
+    report.printMessagesTo(messager);
+    return report.isClean();
   }
 
-  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 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 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 @@
     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 541a4ab..e392252 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 @@
  *
  * <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 @@
   @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 @@
 
   @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 @@
       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 @@
     static ImmutableList<ProcessingStep> processingSteps(
         MapKeyProcessingStep mapKeyProcessingStep,
         InjectProcessingStep injectProcessingStep,
+        AssistedInjectProcessingStep assistedInjectProcessingStep,
+        AssistedFactoryProcessingStep assistedFactoryProcessingStep,
+        AssistedProcessingStep assistedProcessingStep,
         MonitoringModuleProcessingStep monitoringModuleProcessingStep,
         MultibindingAnnotationsProcessingStep multibindingAnnotationsProcessingStep,
         BindsInstanceProcessingStep bindsInstanceProcessingStep,
@@ -170,15 +174,14 @@
       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 @@
 
   @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 b8c6049..0000000
--- 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 3bed5da..0000000
--- 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 d6aa053..0000000
--- 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 b25c01b..0000000
--- 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 4ab58c9..0000000
--- 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 cc1efd2..0000000
--- 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 7739915..0000000
--- 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 a9bba29..0000000
--- 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 0958bc8..0000000
--- 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 66b5289..0000000
--- 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 c78d60d..0000000
--- 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 dda6c11..0000000
--- 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 5a685ef..0000000
--- 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 790ec76..0000000
--- 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 51f6fc3..0000000
--- 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 e14fbb7..0000000
--- 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 66f41d1..0000000
--- 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 50d17a6..0000000
--- 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 c41cf2c..0000000
--- 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 8cdf6d1..0000000
--- 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 67991de..0000000
--- 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 7524cdf..0000000
--- 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 d5d4b62..0000000
--- 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 64b0845..0000000
--- 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 b42f9c4..0000000
--- 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 8532481..0000000
--- 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 3ad12e2..0000000
--- 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 25becaf..0000000
--- 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 1a99818..0000000
--- 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 21f2d32..0000000
--- 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 e611d22..0000000
--- 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 4f2f622..0000000
--- 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 45e99a8..0000000
--- 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 8502ecb..0000000
--- 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 f657cee..0000000
--- 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 3eed45f..0000000
--- 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 40d7606..0000000
--- 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 4195e1a..0000000
--- 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 d367bc5..0000000
--- 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 9ff254e..0000000
--- 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 53d4f9a..0000000
--- 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 4a1deda..0000000
--- 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 feea7a0..0000000
--- 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 de2ada0..0000000
--- 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 a3de083..0000000
--- 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 6f62d66..0000000
--- 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 4c45630..0000000
--- 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 f4e3779..0000000
--- 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 7746692..0000000
--- 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 19d2eda..0000000
--- 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 1b14a7e..0000000
--- 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 aa3c461..0000000
--- 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 34d8f6d..0000000
--- 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 5c36322..0000000
--- 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 69b107c..0000000
--- 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 19c4d19..0000000
--- 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 d649e46..0000000
--- 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 2840d75..0000000
--- 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 45dc391..0000000
--- 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 4563362..0000000
--- 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 183d162..0000000
--- 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 be8c975..5374592 100644
--- a/java/dagger/internal/codegen/InjectProcessingStep.java
+++ b/java/dagger/internal/codegen/InjectProcessingStep.java
@@ -18,6 +18,10 @@
 
 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 @@
 // 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 @@
 
   @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 d3c4ce8..0000000
--- 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 521ad43..0000000
--- 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 2785b18..0000000
--- 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 d61aaa5..0000000
--- 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 2d2e8cb..0000000
--- 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 d09f91d..0000000
--- 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 74c4366..0000000
--- 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 c9b26d7..0000000
--- 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 bc5b66e..0000000
--- 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 b6ac27f..0000000
--- 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 407f208..0000000
--- 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 670fda7..0000000
--- 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 526c493..0000000
--- 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 187b792..0000000
--- 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 ce27877..0000000
--- 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 19975a7..50693da 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.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 @@
  * 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 f2568ef..0000000
--- 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 d8da6af..0000000
--- 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 346d271..0000000
--- 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 73ecdbf..0000000
--- 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 c42c7c9..0000000
--- 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 4918fa1..0000000
--- 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 e9a8ffc..0000000
--- 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 1e04669..0000000
--- 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 036315e..0000000
--- 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 4360af7..0000000
--- 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 8e863e5..0000000
--- 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 2120e80..0000000
--- 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 ef5c9c5..0000000
--- 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 012d5b0..0000000
--- 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 610d052..0000000
--- 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 f39361d..0000000
--- 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 412acae..0000000
--- 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 76bcb8b..0000000
--- 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 ead708d..0000000
--- 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 7e43ec1..0000000
--- 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 907466b..0000000
--- 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 27dd071..0000000
--- 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 5f0d687..0000000
--- 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 ac7a4b7..0000000
--- 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 161b47b..0000000
--- 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 c93d8ce..0000000
--- 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 27cd531..2bf3354 100644
--- a/java/dagger/internal/codegen/ModuleProcessingStep.java
+++ b/java/dagger/internal/codegen/ModuleProcessingStep.java
@@ -28,7 +28,19 @@
 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 @@
   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 @@
       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 @@
     this.moduleConstructorProxyGenerator = moduleConstructorProxyGenerator;
     this.inaccessibleMapKeyProxyGenerator = inaccessibleMapKeyProxyGenerator;
     this.delegateDeclarationFactory = delegateDeclarationFactory;
+    this.metadataUtil = metadataUtil;
   }
 
   @Override
@@ -95,23 +110,38 @@
     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 6426e69..0000000
--- 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 094bf34..0000000
--- 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 1738fe9..0000000
--- 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 55ae579..0000000
--- 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 92825a0..0000000
--- 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 84a4d94..0000000
--- 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 b478704..0000000
--- 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 2bb0a7e..0000000
--- 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 c3724dc..0000000
--- 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 f0523eb..0000000
--- 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 abe161a..0000000
--- 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 bc97d30..0000000
--- 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 1452c3f..0000000
--- 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 b26ab91..0000000
--- 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 dbb0e37..0000000
--- 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 51c9939..0000000
--- 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 ba9e25f..0000000
--- 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 0fdbf68..0000000
--- 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 1021c35..0000000
--- 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 2d2b583..0000000
--- 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 5d4ba18..0000000
--- 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 c30e67a..0000000
--- 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 482c123..0000000
--- 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 cf2475d..0000000
--- 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 1730574..8a535fe 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 @@
 
 /** 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 @@
   }
 
   @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 105452a..0000000
--- 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 b56cc30..2373cd2 100644
--- a/java/dagger/internal/codegen/ProcessingRoundCacheModule.java
+++ b/java/dagger/internal/codegen/ProcessingRoundCacheModule.java
@@ -18,6 +18,14 @@
 
 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 @@
 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 @@
 
   @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 1dd7a1c..0000000
--- 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 87b5a4a..0000000
--- 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 3f2bef4..0000000
--- 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 aca9756..0000000
--- 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 18818d5..0000000
--- 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 bf45948..0000000
--- 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 a22f21c..0000000
--- 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 60166de..0000000
--- 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 01e71ae..0000000
--- 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 306cb13..0000000
--- 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 2fb1a0e..0000000
--- 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 6eb92ac..0000000
--- 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 aa17f5e..0000000
--- 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 814995a..0000000
--- 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 252f712..0000000
--- 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 5efb85f..0000000
--- 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 9712091..0000000
--- 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 e4fa584..0000000
--- 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 505c8ea..0000000
--- 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 909d7ae..0000000
--- 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 805ac7b..0000000
--- 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 7f043db..0000000
--- 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 07c1c68..0000000
--- 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 7dddc2f..0000000
--- 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 c32262a..426afae 100644
--- a/java/dagger/internal/codegen/SourceFileGeneratorsModule.java
+++ b/java/dagger/internal/codegen/SourceFileGeneratorsModule.java
@@ -18,48 +18,49 @@
 
 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
+abstract class SourceFileGeneratorsModule {
+
+  @Provides
+  static SourceFileGenerator<ProvisionBinding> factoryGenerator(
+      FactoryGenerator generator, CompilerOptions compilerOptions) {
+    return hjarWrapper(generator, compilerOptions);
   }
 
-  @Module
-  class ProvisionModule extends GeneratorModule<ProvisionBinding, FactoryGenerator> {}
+  @Provides
+  static SourceFileGenerator<ProductionBinding> producerFactoryGenerator(
+      ProducerFactoryGenerator generator, CompilerOptions compilerOptions) {
+    return hjarWrapper(generator, compilerOptions);
+  }
 
-  @Module
-  class ProductionModule extends GeneratorModule<ProductionBinding, ProducerFactoryGenerator> {}
+  @Provides
+  static SourceFileGenerator<MembersInjectionBinding> membersInjectorGenerator(
+      MembersInjectorGenerator generator, CompilerOptions compilerOptions) {
+    return hjarWrapper(generator, compilerOptions);
+  }
 
-  @Module
-  class MembersInjectionModule
-      extends GeneratorModule<MembersInjectionBinding, MembersInjectorGenerator> {}
-
-  @Module
-  class ComponentModule extends GeneratorModule<BindingGraph, ComponentGenerator> {}
-
-  // 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 fd93d0d..0000000
--- 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 a8f13e1..a4f537b 100644
--- a/java/dagger/internal/codegen/SpiModule.java
+++ b/java/dagger/internal/codegen/SpiModule.java
@@ -24,6 +24,7 @@
 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 @@
   @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 c97024e..0000000
--- 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 b415d3f..0000000
--- 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 5677857..0000000
--- 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 0c6a006..0000000
--- 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 f2ffd83..0000000
--- 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 29633d9..0000000
--- 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 3f59b24..0000000
--- 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 4f456f2..0000000
--- 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 306c05d..0000000
--- 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 00769b2..0000000
--- 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 c703bd8..0000000
--- 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 11c48b3..0000000
--- 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 2b7b02c..0000000
--- 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 1869b7c..0000000
--- 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 f6a4b3f..0000000
--- 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 d7c3252..0000000
--- 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 5d19dc1..0000000
--- 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 0000000..0bcbb12
--- /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 0000000..61e9482
--- /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 0000000..c9b3580
--- /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 0000000..c046daa
--- /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 0000000..93e445c
--- /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 0000000..f85fbfd
--- /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 0000000..c5e2357
--- /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 0000000..4cd54a3
--- /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 0000000..a25f996
--- /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 0000000..93c265a
--- /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 0000000..4e2307a
--- /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 0000000..1688736
--- /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 0000000..234ecc1
--- /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 0000000..1ee50da
--- /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 0000000..424f92a
--- /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 0000000..1505682
--- /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 0000000..95d5ef4
--- /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 0000000..f2c39ce
--- /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 0000000..a75a6d3
--- /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 0000000..9690691
--- /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 0000000..d595bcb
--- /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 0000000..5553dd8
--- /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 0000000..02348a4
--- /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 0000000..c1ffe47
--- /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 0000000..e92b8ab
--- /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 0000000..de0aea5
--- /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 0000000..8d6ee5d
--- /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 0000000..ff8db8f
--- /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 0000000..0d4eef6
--- /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 0000000..712260f
--- /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 0000000..8476497
--- /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 0000000..6f2fc80
--- /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 0000000..4936a05
--- /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 0000000..b882b37
--- /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 0000000..2c15e26
--- /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 0000000..78d440d
--- /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 0000000..d61d9cf
--- /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 0000000..7f5ea54
--- /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 0000000..f3e0a1b
--- /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 0000000..c056588
--- /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 0000000..6297188
--- /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 0000000..5ea30ed
--- /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 0000000..b2581d6
--- /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 0000000..a7e4cc4
--- /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 0000000..f13aa50
--- /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 0000000..1cb3d7c
--- /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 0000000..0947c25
--- /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 0000000..fa24b56
--- /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 0000000..539a66a
--- /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 0000000..1942e8c
--- /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 0000000..b6c3c38
--- /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 0000000..f11517e
--- /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 0000000..707de4c
--- /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 0000000..888dec2
--- /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 0000000..e01d22e
--- /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 0000000..8962ade
--- /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 0000000..3b0b73f
--- /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 0000000..6b160b6
--- /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 0000000..85463af
--- /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 0000000..5203130
--- /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 0000000..748755e
--- /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 0000000..7a6f8d2
--- /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 0000000..d923666
--- /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 0000000..9ac0efa
--- /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 0000000..7c00401
--- /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 0000000..ec7d79d
--- /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 0000000..3dd1016
--- /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 0000000..97c680f
--- /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 0000000..c471f94
--- /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 0000000..6b52f04
--- /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 0000000..f1d3f8d
--- /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 0000000..d7ba7bc
--- /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 0000000..ad85a68
--- /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 0000000..c917dd6
--- /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 0000000..74301fe
--- /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 0000000..3b6d9d9
--- /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 0000000..93e79b5
--- /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 0000000..4f1f3ef
--- /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 0000000..da35b51
--- /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 0000000..a1f1848
--- /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 0000000..bc89ebb
--- /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 0000000..08e2c3e
--- /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 0000000..5c4da51
--- /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 0000000..a01dbaf
--- /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 0000000..fe1c3e0
--- /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 0000000..481c6d8
--- /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 0000000..7334cd9
--- /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 0000000..9800b15
--- /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 0000000..7d742f9
--- /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 0000000..bf83a69
--- /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 0000000..2527d3c
--- /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 0000000..b69dc65
--- /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 ad9761d..0000000
--- 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 0000000..ef39b34
--- /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 0000000..a0d1cda
--- /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 0000000..d989679
--- /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 0000000..a86cc1b
--- /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 0000000..06d15a2
--- /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 0000000..39c4728
--- /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 0000000..d7be4ca
--- /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 0000000..d898d4d
--- /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 0000000..e1b35da
--- /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 0000000..e04ee14
--- /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 0000000..84179d6
--- /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 0000000..b386a4f
--- /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 0000000..04cb80f
--- /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 0000000..fdfcc9d
--- /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 0000000..5584372
--- /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 0000000..99b05a4
--- /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 0000000..7919e47
--- /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 273e472..0000000
--- 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 0000000..468a685
--- /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 0000000..7b01676
--- /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 0000000..587445b
--- /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 0000000..136280d
--- /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 0000000..57494a2
--- /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 0000000..b8cb37c
--- /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 0000000..214191a
--- /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 cc0d7de..b66d266 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 f829d49..ddb9f88 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 9301dbc..71f03f0 100644
--- a/java/dagger/internal/codegen/javapoet/TypeNames.java
+++ b/java/dagger/internal/codegen/javapoet/TypeNames.java
@@ -25,6 +25,7 @@
 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.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 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 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 0000000..d1c5458
--- /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 0000000..296da44
--- /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 0000000..1515469
--- /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 0000000..76d28f0
--- /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 0000000..9e8dea1
--- /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 0000000..4c6f85e
--- /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
similarity index 100%
rename from java/dagger/internal/codegen/kythe_plugin_deploy.jar
rename to 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 16fa5d8..670f4aa 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 873ad3d..12cec31 100644
--- a/java/dagger/internal/codegen/langmodel/DaggerElements.java
+++ b/java/dagger/internal/codegen/langmodel/DaggerElements.java
@@ -28,6 +28,7 @@
 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.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.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 @@
   }
 
   /**
+   * 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 @@
     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 e588fbd..fb291db 100644
--- a/java/dagger/internal/codegen/langmodel/DaggerTypes.java
+++ b/java/dagger/internal/codegen/langmodel/DaggerTypes.java
@@ -83,7 +83,7 @@
    * @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 @@
     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 c8cc404..4c478d6 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 2bc02b4..0000000
--- 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 1449e9d..0000000
--- 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 e6c9577..0000000
--- 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 0000000..140afd2
--- /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 0000000..602157b
--- /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 0000000..4557745
--- /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 0000000..16ef78f
--- /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 0000000..99e86e7
--- /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 0000000..10aec06
--- /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 0000000..81349b9
--- /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 0000000..08afbc8
--- /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 0000000..283af7d
--- /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 0000000..6d144bd
--- /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 0000000..24d65a9
--- /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 0000000..0e79b91
--- /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 0000000..6d2dd18
--- /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 0000000..cb776e1
--- /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 0000000..a6d9a3f
--- /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 0000000..aa20232
--- /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 0000000..1861636
--- /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 0000000..b3322e5
--- /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 0000000..a9c650a
--- /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 0000000..68c9604
--- /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 0000000..7d7b9e3
--- /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 0000000..35ae7c7
--- /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 0000000..06f68f6
--- /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 0000000..3a164ca
--- /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 0000000..240f9d0
--- /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 0000000..6aa5147
--- /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 0000000..afa6270
--- /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 0000000..02ac06a
--- /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 0000000..2c72e5f
--- /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 0000000..5d4c64e
--- /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 0000000..fd75eac
--- /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 0000000..8829667
--- /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 0000000..6b7c720
--- /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 0000000..1606ebe
--- /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 0000000..6b4f303
--- /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 0000000..57867f1
--- /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 0000000..5fa0270
--- /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 0000000..620b0f0
--- /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 0000000..7f37375
--- /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 0000000..96e6340
--- /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 0000000..b1237ca
--- /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 0000000..d0c481c
--- /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 0000000..dcb88b3
--- /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 0000000..bd45f60
--- /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 0000000..5f1f0bd
--- /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 0000000..f9d218b
--- /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 0000000..ef69124
--- /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 0000000..9fa10c6
--- /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 0000000..7fa9aa3
--- /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 0000000..53914b6
--- /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 0000000..299f279
--- /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 0000000..13008b8
--- /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 0000000..653a7a2
--- /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 0000000..3361132
--- /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 0000000..a7f9556
--- /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 0000000..5ac1e8f
--- /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 0000000..5a40c02
--- /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 0000000..6e5dca8
--- /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 0000000..70edd1a
--- /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 0000000..c17ff8e
--- /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 0000000..56a6ef3
--- /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 0000000..cc0e136
--- /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 0000000..2df6a99
--- /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 0000000..5a4b6f1
--- /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 0000000..dcd02fa
--- /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 0000000..d527359
--- /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 0000000..7cebc10
--- /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 0000000..b5135b0
--- /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 0000000..c2f9893
--- /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 0000000..a7d6685
--- /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 0000000..255e85d
--- /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 0000000..104d48a
--- /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 0000000..b04e21b
--- /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 0000000..abf9d03
--- /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 0000000..3d602b3
--- /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 0000000..df618b0
--- /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 0000000..534c057
--- /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 0000000..9c0c74a
--- /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 0000000..afd0e99
--- /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 0000000..fcd9b56
--- /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 0000000..64e008e
--- /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 0000000..4faf3fa
--- /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 0000000..2d80963
--- /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 0000000..593921e
--- /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 0000000..20b946e
--- /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 0000000..80888df
--- /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 0000000..c1fcaf7
--- /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 0000000..b6502ea
--- /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 0000000..0d1ccf3
--- /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 0000000..c58f4e0
--- /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 0000000..5e52923
--- /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 0000000..9b0d4e8
--- /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 0000000..0d3f7e8
--- /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 0000000..400c6a2
--- /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 0000000..0b2e11a
--- /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 0000000..754f909
--- /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 0000000..f2062f4
--- /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 0000000..82e6628
--- /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 0000000..3099048
--- /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 0000000..fa0037b
--- /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 0000000..d38b9d9
--- /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 0000000..ce71907
--- /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 0000000..f07b882
--- /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 0000000..0bd4dcc
--- /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 0000000..f75cee1
--- /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 0000000..3e0ffec
--- /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 0000000..113e85c
--- /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 0000000..f3fdbd3
--- /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 5fe0db3..0be8fc5 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 @@
     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 @@
     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 748bf38..1ccba43 100644
--- a/java/dagger/model/BindingGraph.java
+++ b/java/dagger/model/BindingGraph.java
@@ -20,12 +20,10 @@
 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 @@
  *   <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 @@
  *
  * <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 @@
    *     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 @@
 
   /**
    * 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 @@
   }
 
   /** Returns the dependency edges. */
-  public final ImmutableSet<DependencyEdge> dependencyEdges() {
+  public ImmutableSet<DependencyEdge> dependencyEdges() {
     return dependencyEdgeStream().collect(toImmutableSet());
   }
 
@@ -197,14 +187,14 @@
    * 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 @@
    * 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 @@
    * 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 @@
    * 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 @@
   }
 
   /** 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 @@
    *
    * @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 @@
   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 @@
   }
 
   /** 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 c450475..85d4df8 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 @@
  * 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 20d7b42..9bef8fc 100644
--- a/java/dagger/model/BindingKind.java
+++ b/java/dagger/model/BindingKind.java
@@ -25,6 +25,15 @@
   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 a9d5f19..9c9f44a 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 dc17c1d..2f032cd 100644
--- a/java/dagger/model/testing/BindingGraphSubject.java
+++ b/java/dagger/model/testing/BindingGraphSubject.java
@@ -18,7 +18,7 @@
 
 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 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 @@
   }
 
   /** 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 ad065a1..41762e6 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 @@
     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 5721569..0000000
--- 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 c63e108..ebb90cf 100644
--- a/java/dagger/producers/monitoring/TimingProducerMonitor.java
+++ b/java/dagger/producers/monitoring/TimingProducerMonitor.java
@@ -25,6 +25,7 @@
  * 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 5fbe3e3..be89319 100644
--- a/java/dagger/producers/monitoring/TimingRecorders.java
+++ b/java/dagger/producers/monitoring/TimingRecorders.java
@@ -30,6 +30,7 @@
  */
 // 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 9c04582..cbec465 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 0000000..20e6a3d
--- /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 0000000..6750d0c
--- /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 0000000..9543e0e
--- /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 0000000..9b282de
--- /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 0000000..20a3734
--- /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 0000000..0f765b5
--- /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 0000000..d74a501
--- /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 0000000..5e04071
--- /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 0000000..39a88e3
--- /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 0000000..dcc7bcb
--- /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 0000000..2f861b1
--- /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 0000000..d6bc0ac
--- /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 0000000..3cecc4c
--- /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 0000000..579f478
--- /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 0000000..024da5c
--- /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 0000000..8c179e5
--- /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 0000000..2d8d1e4
--- /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 0000000..5c2d1cf
--- /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 0000000..4d9ca16
--- /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 0000000..b0d6d0a
--- /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 0000000..c5a07bc
--- /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 0000000..97c966e
--- /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 0000000..5c2d1cf
--- /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 0000000..4d9ca16
--- /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 0000000..b0d6d0a
--- /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 0000000..eb4946f
--- /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 0000000..f136101
--- /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 0000000..43ccd1d
--- /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 0000000..9099db4
--- /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 0000000..9fce7cd
--- /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 0000000..ce1fb71
--- /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 0000000..1cbd4b0
--- /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 0000000..969e183
--- /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 0000000..0011bde
--- /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 0000000..cb5a8df
--- /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 0000000..05afdbf
--- /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 0000000..527d887
--- /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 0000000..3b0cdde
--- /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 0000000..1813493
--- /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 0000000..62d4c05
--- /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 0000000..4d9ca16
--- /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 0000000..fbd7c51
--- /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 0000000..a9f778a
--- /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 0000000..d0d5fee
--- /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 0000000..98b79e1
--- /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 0000000..6ee721c
--- /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 0000000..14d33b0
--- /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 0000000..0b12b13
--- /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 0000000..9965b2e
--- /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 0000000..9a63f17
--- /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 0000000..5d16cd4
--- /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 0000000..3c7d1f3
--- /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 0000000..19f0679
--- /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 0000000..26d52df
--- /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 0000000..b786106
--- /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 0000000..c8f9b8a
--- /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 0000000..b49e666
--- /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 0000000..e77f605
--- /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 0000000..81afb6e
--- /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 0000000..6e5ad6a
--- /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 0000000..1ba3c2f
--- /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 0000000..e1f4421
--- /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 0000000..f2e2e77
--- /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 0000000..a4f900d
--- /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 0000000..0234ffe
--- /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 0000000..770fabd
--- /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 0000000..e5ed046
--- /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 0000000..49a304e
--- /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 0000000..57fd803
--- /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 0000000..22f815c
--- /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 0000000..419e539
--- /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 0000000..0e772a8
--- /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 0000000..fe583e5
--- /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 0000000..c1931ac
--- /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 0000000..d55bb5a
--- /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 0000000..c1ca22f
--- /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 0000000..d513f37
--- /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 0000000..99adcfd
--- /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 0000000..646c51b
--- /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 0000000..5c2d1cf
--- /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 0000000..4d9ca16
--- /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 0000000..b0d6d0a
--- /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 0000000..d67be3b
--- /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 0000000..baded09
--- /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 0000000..f6a628e
--- /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 0000000..bad895d
--- /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 0000000..383c4be
--- /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 0000000..c60ba73
--- /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 0000000..cb10c8b
--- /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 0000000..ecfaf6e
--- /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 0000000..d790265
--- /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 0000000..29ffc9e
--- /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 0000000..6786e9d
--- /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 0000000..7ac4ebf
--- /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 0000000..679a0d9
--- /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 0000000..480aea1
--- /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 0000000..8d98e92
--- /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 0000000..c24c6b7
--- /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 0000000..cdf6a65
--- /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 0000000..0a0270e
--- /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 0000000..ca700b2
--- /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 0000000..b830f31
--- /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 0000000..59b79bb
--- /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 0000000..b22e588
--- /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 0000000..c638706
--- /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 0000000..bd7f8a7
--- /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 0000000..7e7e9ae
--- /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 0000000..352e48c
--- /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 0000000..0d7637d
--- /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 0000000..c1e4f27
--- /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 0000000..43a1abc
--- /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 0000000..10e6515
--- /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 0000000..1e04606
--- /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 0000000..0234ffe
--- /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 0000000..d594476
--- /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 0000000..3a1923a
--- /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 0000000..4ea4c09
--- /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 0000000..ae2a5e3
--- /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 0000000..a2fcb73
--- /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 0000000..508e7d5
--- /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 0000000..306c171
--- /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 0000000..0d2bc61
--- /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 0000000..646c51b
--- /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 0000000..5c2d1cf
--- /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 0000000..4d9ca16
--- /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 0000000..b0d6d0a
--- /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 0000000..7c8462f
--- /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 0000000..8989ebb
--- /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 0000000..cea0913
--- /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 3debec5..c6ab360 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 @@
     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 19b6e84..7e60ebd 100644
--- a/javatests/dagger/android/AndroidInjectionTest.java
+++ b/javatests/dagger/android/AndroidInjectionTest.java
@@ -18,6 +18,7 @@
 
 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.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 @@
     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 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 @@
     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 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 @@
   }
 
   @Test
-  public void injectActivity_applicationDoesntImplementHasActivityInjector() {
+  public void injectActivity_applicationDoesntImplementHasAndroidInjector() {
     Activity activity = Robolectric.setupActivity(Activity.class);
 
     try {
@@ -139,21 +140,15 @@
     }
   }
 
-  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 @@
       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 @@
       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 0000000..0f51e49
--- /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 5bc3f45..38efb3a 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 @@
     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 d0306b8..37d3d61 100644
--- a/javatests/dagger/android/DispatchingAndroidInjectorTest.java
+++ b/javatests/dagger/android/DispatchingAndroidInjectorTest.java
@@ -29,9 +29,7 @@
 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 1a45bdd..0000000
--- 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 8a9c16e..d7b210c 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 51e9992..25c5e94 100644
--- a/javatests/dagger/android/support/AndroidSupportInjectionTest.java
+++ b/javatests/dagger/android/support/AndroidSupportInjectionTest.java
@@ -20,15 +20,15 @@
 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 @@
   }
 
   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 @@
       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 6d8f43b..6bbfaa1 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 @@
         "//: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 2e40a35..0b5f4cd 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 130b971..1ae1108 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 @@
     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 @@
     ],
 )
 
-load("//:test_defs.bzl", "GenRobolectricTests")
-
 GenRobolectricTests(
     name = "functional_tests",
     srcs = glob(["*Test.java"]),
@@ -48,9 +49,9 @@
         "//: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 c5cb150..73bb98a 100644
--- a/javatests/dagger/android/support/functional/InjectorsTest.java
+++ b/javatests/dagger/android/support/functional/InjectorsTest.java
@@ -20,12 +20,12 @@
 
 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 @@
     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 1668ce6..aff8961 100644
--- a/javatests/dagger/android/support/functional/TestContentProvider.java
+++ b/javatests/dagger/android/support/functional/TestContentProvider.java
@@ -19,7 +19,7 @@
 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 f417bec..8e6a555 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 @@
     ),
     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 812cd04..d514337 100644
--- a/javatests/dagger/functional/BoundedGenerics.java
+++ b/javatests/dagger/functional/BoundedGenerics.java
@@ -19,6 +19,7 @@
 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 0000000..502c51d
--- /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 f53c0f8..40ef546 100644
--- a/javatests/dagger/functional/Generic2.java
+++ b/javatests/dagger/functional/Generic2.java
@@ -18,6 +18,7 @@
 
 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 c3f38b4..0e3d41b 100644
--- a/javatests/dagger/functional/GenericNoDeps.java
+++ b/javatests/dagger/functional/GenericNoDeps.java
@@ -18,6 +18,7 @@
 
 import javax.inject.Inject;
 
+
 class GenericNoDeps<T> {
   
   @Inject GenericNoDeps() {}
diff --git a/javatests/dagger/functional/MultibindingTest.java b/javatests/dagger/functional/MultibindingTest.java
index d99e46d..3da3901 100644
--- a/javatests/dagger/functional/MultibindingTest.java
+++ b/javatests/dagger/functional/MultibindingTest.java
@@ -80,7 +80,7 @@
 
   @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 2ea01ec..14ab134 100644
--- a/javatests/dagger/functional/NeedsFactory.java
+++ b/javatests/dagger/functional/NeedsFactory.java
@@ -19,11 +19,19 @@
 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 efbd73d..1ae797e 100644
--- a/javatests/dagger/functional/NeedsProviderOfFactory.java
+++ b/javatests/dagger/functional/NeedsProviderOfFactory.java
@@ -26,7 +26,8 @@
   static class InjectsProviderOfFactory {
     @Inject
     InjectsProviderOfFactory(
-        Provider<NeedsProviderOfFactory_SomethingFactory> provider) {}
+        Provider<NeedsProviderOfFactory_SomethingFactory> somethingFactoryProvider,
+        Provider<SomethingFactoryImpl> somethingFactoryImplProvider) {}
   }
 
   @Component
@@ -34,6 +35,12 @@
     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 20a89d4..0000000
--- 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 ae6b3a4..0000000
--- 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 1813ad2..0000000
--- 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 7084f83..0000000
--- 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 853e22b..0000000
--- 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 f995789..0000000
--- 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 2723ac0..0000000
--- 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 689689c..0000000
--- 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 06cebb9..0000000
--- 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 2eee3c9..0000000
--- 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 c629f6e..0000000
--- 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 b5ced6f..0000000
--- 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 1d69723..0000000
--- 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 d908b1c..0000000
--- 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 0000000..78495b7
--- /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 0000000..d586c61
--- /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 0000000..d079430
--- /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 0000000..3176add
--- /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 0000000..91f7e42
--- /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 0000000..9b794fd
--- /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 0000000..952b94d
--- /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 0000000..5e663e7
--- /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 0000000..3c3421d
--- /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 0000000..e78873d
--- /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 0000000..7c597f8
--- /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 0000000..16d420a
--- /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 0000000..b27b029
--- /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 0000000..644cf44
--- /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 0000000..a519b3f
--- /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 0000000..e093365
--- /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 0000000..a7efe57
--- /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 e1d5227..e8c3ca6 100644
--- a/javatests/dagger/functional/binds/SimpleBindingModule.java
+++ b/javatests/dagger/functional/binds/SimpleBindingModule.java
@@ -20,6 +20,7 @@
 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 @@
 
 @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 @@
   @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 07ebbc8..9b1447c 100644
--- a/javatests/dagger/functional/builder/BuildMethodCovariantReturnInherited.java
+++ b/javatests/dagger/functional/builder/BuildMethodCovariantReturnInherited.java
@@ -18,6 +18,7 @@
 
 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 33d3816..d47c892 100644
--- a/javatests/dagger/functional/builder/BuilderBindsInstanceParameterTest.java
+++ b/javatests/dagger/functional/builder/BuilderBindsInstanceParameterTest.java
@@ -18,6 +18,7 @@
 
 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 9563103..949514e 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 f4faeab..e17d619 100644
--- a/javatests/dagger/functional/cycle/Cycles.java
+++ b/javatests/dagger/functional/cycle/Cycles.java
@@ -97,7 +97,7 @@
       this.sProvider = sProvider;
     }
   }
-
+  
   static class X {
     public final Y y;
 
@@ -143,10 +143,10 @@
     A a();
 
     C c();
-
+    
     ChildCycleComponent child();
   }
-
+  
   @Module
   static class CycleModule {
     @Provides
@@ -160,12 +160,12 @@
   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 b77ee3e..d77a713 100644
--- a/javatests/dagger/functional/cycle/DoubleCheckCycleTest.java
+++ b/javatests/dagger/functional/cycle/DoubleCheckCycleTest.java
@@ -181,6 +181,7 @@
                 })
             .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 66492eb..ed8efd3 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 6b76ba3..5e35c75 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 0000000..529eb69
--- /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 0000000..591c5d6
--- /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 0000000..6602f10
--- /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 0000000..156f436
--- /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 0000000..3bc2e4e
--- /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 0000000..a37be44
--- /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 0000000..e0ebf3c
--- /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 0000000..f927186
--- /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 0000000..1eaa3f5
--- /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 0000000..e390aba
--- /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 0000000..9aeb2d8
--- /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 0000000..5d151fc
--- /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 0000000..31a45c7
--- /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 0000000..4a1d6e2
--- /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 0000000..c240fd3
--- /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 3519348..4a08455 100644
--- a/javatests/dagger/functional/membersinject/MembersInjectGenericParent.java
+++ b/javatests/dagger/functional/membersinject/MembersInjectGenericParent.java
@@ -18,6 +18,7 @@
 
 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 d60a7d1..6e182dd 100644
--- a/javatests/dagger/functional/membersinject/MembersInjectModule.java
+++ b/javatests/dagger/functional/membersinject/MembersInjectModule.java
@@ -26,7 +26,7 @@
   
   @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 0617fd5..31e4f45 100644
--- a/javatests/dagger/functional/membersinject/MembersInjectTest.java
+++ b/javatests/dagger/functional/membersinject/MembersInjectTest.java
@@ -18,12 +18,15 @@
 
 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 @@
     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 0000000..7cd97d9
--- /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 38f7d97..474cb80 100644
--- a/javatests/dagger/functional/modules/ModuleIncludesTest.java
+++ b/javatests/dagger/functional/modules/ModuleIncludesTest.java
@@ -19,6 +19,7 @@
 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 @@
   @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 0000000..60b4482
--- /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 0000000..47d2441
--- /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 7c5fd29..9eade52 100644
--- a/javatests/dagger/functional/modules/subpackage/PublicModule.java
+++ b/javatests/dagger/functional/modules/subpackage/PublicModule.java
@@ -19,7 +19,7 @@
 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 f2b5598..801d120 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.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 @@
 
 @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 f188405..70260e7 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.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 @@
 
 @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 36c9701..f69b3cb 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("//: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 0000000..d4ad9f0
--- /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 c85e342..6df526e 100644
--- a/javatests/dagger/functional/producers/ProducerFactoryTest.java
+++ b/javatests/dagger/functional/producers/ProducerFactoryTest.java
@@ -18,7 +18,7 @@
 
 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 543835f..32007a9 100644
--- a/javatests/dagger/functional/producers/monitoring/MonitoringTest.java
+++ b/javatests/dagger/functional/producers/monitoring/MonitoringTest.java
@@ -18,7 +18,7 @@
 
 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 fa5c6ee..a5eb8a3 100644
--- a/javatests/dagger/functional/producers/multibindings/MultibindingComponent.java
+++ b/javatests/dagger/functional/producers/multibindings/MultibindingComponent.java
@@ -30,6 +30,7 @@
 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 fffaddb..8119bdd 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 f49083b..10745d7 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 c34de0a..380322b 100644
--- a/javatests/dagger/functional/subcomponent/SubcomponentTest.java
+++ b/javatests/dagger/functional/subcomponent/SubcomponentTest.java
@@ -50,6 +50,7 @@
     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 7cb4fce..846ceb0 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 7526bf0..9cad2a0 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("//: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 1e8c2e6..0603801 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 0000000..5ce0a2a
--- /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 0000000..7fb2320
--- /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 0000000..bf2ed4c
--- /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 0000000..af535f5
--- /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 0000000..3af5be6
--- /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 0000000..25a1abd
--- /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 0000000..c024a9c
--- /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 0000000..e53d9e2
--- /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 0000000..739c87c
--- /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 0000000..030efd9
--- /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 0000000..713cd22
--- /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 0000000..7c3e45f
--- /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 0000000..b3eaeab
--- /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 0000000..b5c22c1
--- /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 0000000..e6e1cfb
--- /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 0000000..0feaa4e
--- /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 0000000..cad41aa
--- /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 0000000..b36a71d
--- /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 0000000..f52b051
--- /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 0000000..ce5cc0a
--- /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 0000000..901c7a3
--- /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 0000000..90b0596
--- /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 0000000..8ea715d
--- /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 0000000..c5dacd9
--- /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 0000000..fb62c3d
--- /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 5598ff2..c55bee3 100644
--- a/javatests/dagger/internal/MapProviderFactoryTest.java
+++ b/javatests/dagger/internal/MapProviderFactoryTest.java
@@ -46,6 +46,7 @@
     MapProviderFactory.<String, Integer>builder(1).put("Hello", null);
   }
 
+
   @Test
   public void iterationOrder() {
     Provider<Integer> p1 = incrementingIntegerProvider(10);
@@ -74,6 +75,7 @@
         .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 bb967b1..0000000
--- 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 1bd221a..0000000
--- 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 fda5859..0000000
--- 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 0000000..bf61412
--- /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 0000000..2bcfedc
--- /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 0000000..d752321
--- /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 0000000..47e2171
--- /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 ad214ff..979ae71 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")
 
-GenJavaTests(
-    name = "compiler_tests",
-    srcs = glob(["*.java"]),
-    functional = False,
-    javacopts = DOCLINT_HTML_AND_SYNTAX,
+package(default_visibility = ["//:src"])
+
+kt_jvm_library(
+    name = "kotlin_sources",
+    srcs = [
+        "KotlinInjectedQualifier.kt",
+        "KotlinObjectWithMemberInjection.kt",
+    ],
     deps = [
         "//java/dagger:core",
-        "//java/dagger/internal/codegen:base",
-        "//java/dagger/internal/codegen:binding",
-        "//java/dagger/internal/codegen:binding_graph_validation",
+    ],
+)
+
+# 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/codegen:validation",
-        "//java/dagger/internal/codegen:writing",
+        "//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"],
+        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:package_info",
+        "//java/dagger/internal/codegen:processor",
+        "//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 0000000..aa01b3a
--- /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 6ba9e3e..ab26010 100644
--- a/javatests/dagger/internal/codegen/BindsMethodValidationTest.java
+++ b/javatests/dagger/internal/codegen/BindsMethodValidationTest.java
@@ -25,7 +25,6 @@
 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 @@
 
   @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 28b75be..bfc3617 100644
--- a/javatests/dagger/internal/codegen/BindsMissingDelegateValidationTest.java
+++ b/javatests/dagger/internal/codegen/BindsMissingDelegateValidationTest.java
@@ -97,7 +97,7 @@
         .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 fa44a6e..ca3e5e4 100644
--- a/javatests/dagger/internal/codegen/BindsOptionalOfMethodValidationTest.java
+++ b/javatests/dagger/internal/codegen/BindsOptionalOfMethodValidationTest.java
@@ -37,7 +37,7 @@
 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 23aa312..86ce53d 100644
--- a/javatests/dagger/internal/codegen/CompilerMode.java
+++ b/javatests/dagger/internal/codegen/CompilerMode.java
@@ -23,9 +23,6 @@
 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 3e08f63..26796bf 100644
--- a/javatests/dagger/internal/codegen/Compilers.java
+++ b/javatests/dagger/internal/codegen/Compilers.java
@@ -19,42 +19,60 @@
 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 f280bdd..acf5437 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 @@
             "",
             "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 @@
             "  }",
             "}");
     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 @@
             "  }",
             "}");
     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 @@
             "  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 @@
             "  }",
             "}");
     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 @@
             "  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 @@
             "  }",
             "}");
     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 @@
             "  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 @@
             "}");
 
     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 @@
             "}");
 
     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 6f4ea8d..0a7e40e 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 @@
 /** 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 @@
             "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 @@
 
   @Test
   public void testCanInstantiateModulesUserCannotSet() {
+    assume().that(compilerType).isEqualTo(JAVAC);
     JavaFileObject module =
         JavaFileObjects.forSourceLines(
             "test.TestModule",
@@ -149,7 +161,7 @@
             "",
             IMPORT_GENERATED_ANNOTATION,
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "final class DaggerTestComponent implements TestComponent {",
             "  private final TestModule testModule;",
             "",
@@ -310,6 +322,7 @@
 
   @Test
   public void testCreatorWithBindsInstanceNoStaticCreateGenerated() {
+    assume().that(compilerType).isEqualTo(JAVAC);
     JavaFileObject componentFile =
         javaFileBuilder("test.SimpleComponent")
             .addLines(
@@ -347,7 +360,7 @@
                 "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 @@
 
   @Test
   public void testCreatorWithPrimitiveBindsInstance() {
+    assume().that(compilerType).isEqualTo(JAVAC);
     JavaFileObject componentFile =
         javaFileBuilder("test.SimpleComponent")
             .addLines(
@@ -448,7 +462,7 @@
                 "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 @@
 
   @Test
   public void testMultipleSettersPerTypeFails() {
+    assume().that(compilerType).isEqualTo(JAVAC);
     JavaFileObject moduleFile =
         JavaFileObjects.forSourceLines(
             "test.TestModule",
@@ -837,6 +852,7 @@
 
   @Test
   public void testMultipleSettersPerTypeIncludingResolvedGenericsFails() {
+    assume().that(compilerType).isEqualTo(JAVAC);
     JavaFileObject moduleFile =
         JavaFileObjects.forSourceLines(
             "test.TestModule",
@@ -900,6 +916,7 @@
 
   @Test
   public void testExtraSettersFails() {
+    assume().that(compilerType).isEqualTo(JAVAC);
     JavaFileObject componentFile =
         javaFileBuilder("test.SimpleComponent")
             .addLines(
@@ -1032,6 +1049,7 @@
 
   @Test
   public void covariantFactoryMethodReturnType() {
+    assume().that(compilerType).isEqualTo(JAVAC);
     JavaFileObject foo =
         JavaFileObjects.forSourceLines(
             "test.Foo",
@@ -1072,6 +1090,7 @@
 
   @Test
   public void covariantFactoryMethodReturnType_hasNewMethod() {
+    assume().that(compilerType).isEqualTo(JAVAC);
     JavaFileObject foo =
         JavaFileObjects.forSourceLines(
             "test.Foo",
@@ -1133,6 +1152,7 @@
 
   @Test
   public void covariantFactoryMethodReturnType_hasNewMethod_factoryMethodInherited() {
+    assume().that(compilerType).isEqualTo(JAVAC);
     JavaFileObject foo =
         JavaFileObjects.forSourceLines(
             "test.Foo",
@@ -1250,4 +1270,13 @@
         .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 2ee120e..8ad4322 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 @@
 
   /** 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 0000000..bbfa51c
--- /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 403498b..0f155b9 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 @@
             "",
             "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 @@
             "  }",
             "}");
     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 @@
             "  }",
             "}");
     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 @@
             "  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 19eabac..6fb1820 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 @@
     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 2b79b6f..eee6a0c 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.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 @@
         "}");
 
     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 @@
         "  OuterClass outerClass();",
         "}");
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(
+        compilerWithOptions(
                 compilerMode.javacopts().append("-Adagger.privateMemberValidation=WARNING"))
             .compile(outerClass, componentFile);
     assertThat(compilation).failed();
@@ -185,7 +182,7 @@
                 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 @@
             .build();
 
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(injectableTypeFile, componentFile);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -302,7 +298,7 @@
             .addLines(
                 "package test;",
                 "",
-                GENERATED_ANNOTATION,
+                GENERATED_CODE_ANNOTATIONS,
                 "final class DaggerSimpleComponent implements SimpleComponent {")
             .addLinesIn(
                 FAST_INIT_MODE,
@@ -387,8 +383,7 @@
                 "  }")
             .build();
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(injectableTypeFile, componentFile);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -422,7 +417,7 @@
             .addLines(
                 "package test;",
                 "",
-                GENERATED_ANNOTATION,
+                GENERATED_CODE_ANNOTATIONS,
                 "final class DaggerOuterType_SimpleComponent",
                 "    implements OuterType.SimpleComponent {",
                 "  private DaggerOuterType_SimpleComponent() {}",
@@ -446,7 +441,7 @@
             .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 @@
                 "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 @@
                 "    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 @@
             .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 @@
             .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 @@
         "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 @@
         "  }",
         "}");
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(
                 always,
                 testModule,
@@ -789,7 +781,7 @@
         "@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 @@
             "  ChildComponent childComponent();",
             "}");
     assertThat(
-            daggerCompiler().withOptions(compilerMode.javacopts()).compile(subcomponent, component))
+            compilerWithOptions(compilerMode.javacopts()).compile(subcomponent, component))
         .failed();
     assertThat(
             daggerCompiler(
@@ -893,7 +885,7 @@
             "import dagger.internal.Preconditions;",
             IMPORT_GENERATED_ANNOTATION,
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "final class DaggerParent implements Parent {",
             "",
             "  private DaggerParent() {}",
@@ -927,8 +919,7 @@
             "  }",
             "}");
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(component, module, subcomponent);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -962,8 +953,7 @@
         "  BClass bClass();",
         "}");
     assertThat(
-            daggerCompiler()
-                .withOptions(compilerMode.javacopts())
+            compilerWithOptions(compilerMode.javacopts())
                 .compile(aModule, aClass, bClass, component))
         .succeeded();
   }
@@ -1007,7 +997,7 @@
                 "",
                 "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 @@
             .build();
 
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(injectableTypeFile, injectedTypeFile, componentFile);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -1064,7 +1053,7 @@
             "test.DaggerSimpleComponent",
             "package test;",
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "final class DaggerSimpleComponent implements SimpleComponent {",
             "  private Provider<SimpleComponent> simpleComponentProvider;",
             "",
@@ -1084,8 +1073,7 @@
             "  }",
             "}");
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(injectableTypeFile, componentFile);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -1129,7 +1117,7 @@
                 "",
                 "import com.google.errorprone.annotations.CanIgnoreReturnValue;",
                 "",
-                GENERATED_ANNOTATION,
+                GENERATED_CODE_ANNOTATIONS,
                 "final class DaggerSimpleComponent implements SimpleComponent {",
                 "  @Override",
                 "  public SomeInjectedType createAndInject() {",
@@ -1147,8 +1135,7 @@
             .build();
 
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(injectableTypeFile, injectedTypeFile, componentFile);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -1198,11 +1185,9 @@
             .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 @@
                 "    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 @@
                 "  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 @@
                 "    ",
                 "    @Override()",
                 "    public A get() {",
-                "      return Preconditions.checkNotNull(",
-                "          aComponent.a(), " + NPE_FROM_COMPONENT_METHOD + ");",
+                "      return Preconditions.checkNotNullFromComponent(aComponent.a());",
                 "    }",
                 "  }",
                 "}")
@@ -1277,9 +1254,8 @@
                 "      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 @@
                 "  }")
             .build();
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(aFile, bFile, aComponentFile, bComponentFile);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -1343,7 +1318,7 @@
             "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 @@
             "  }",
             "}");
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(aFile, otherAFile, moduleFile, otherModuleFile, componentFile);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -1469,7 +1443,7 @@
             "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 @@
             "  @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 @@
                 "",
                 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 @@
             .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 @@
             "",
             IMPORT_GENERATED_ANNOTATION,
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "final class DaggerSimpleComponent implements SimpleComponent {",
             "  private DaggerSimpleComponent() {}",
             "",
@@ -1663,8 +1633,7 @@
             "  }",
             "}");
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(
                 injectableTypeFile,
                 componentSupertypeAFile,
@@ -1707,7 +1676,7 @@
             "test.DaggerSimpleComponent",
             "package test;",
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "final class DaggerSimpleComponent implements SimpleComponent {",
             "  @Override",
             "  public SomeInjectableType someInjectableType() {",
@@ -1715,8 +1684,7 @@
             "  }",
             "}");
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(injectableTypeFile, componentSupertype, depComponentFile);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -1763,8 +1731,7 @@
         "  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 @@
             "  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 @@
             "  @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 @@
             "import dagger.internal.Preconditions;",
             IMPORT_GENERATED_ANNOTATION,
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "final class DaggerParent implements Parent {",
             "  private DaggerParent() {",
             "  }",
@@ -2111,8 +2078,7 @@
             "}");
 
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(foo, module, component, prunedSubcomponent);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -2175,7 +2141,7 @@
     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 @@
   @Test
   public void nullIncorrectlyReturnedFromNonNullableInlinedProvider() {
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(
                 JavaFileObjects.forSourceLines(
                     "test.TestModule",
@@ -2225,7 +2190,7 @@
                 "test.TestModule_NonNullableStringFactory",
                 "package test;",
                 "",
-                GENERATED_ANNOTATION,
+                GENERATED_CODE_ANNOTATIONS,
                 "public final class TestModule_NonNullableStringFactory",
                 "    implements Factory<String> {",
                 "  @Override",
@@ -2234,8 +2199,8 @@
                 "  }",
                 "",
                 "  public static String nonNullableString() {",
-                "    return Preconditions.checkNotNull(",
-                "        TestModule.nonNullableString(), " + NPE_FROM_PROVIDES_METHOD + ");",
+                "    return Preconditions.checkNotNullFromProvides(",
+                "        TestModule.nonNullableString());",
                 "  }",
                 "}"));
 
@@ -2245,7 +2210,7 @@
             .addLines(
                 "package test;",
                 "",
-                GENERATED_ANNOTATION,
+                GENERATED_CODE_ANNOTATIONS,
                 "final class DaggerTestComponent implements TestComponent {",
                 "  @Override",
                 "  public String nonNullableString() {",
@@ -2274,8 +2239,7 @@
   @Test
   public void nullCheckingIgnoredWhenProviderReturnsPrimitive() {
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(
                 JavaFileObjects.forSourceLines(
                     "test.TestModule",
@@ -2316,7 +2280,7 @@
                 "test.TestModule_PrimitiveIntegerFactory",
                 "package test;",
                 "",
-                GENERATED_ANNOTATION,
+                GENERATED_CODE_ANNOTATIONS,
                 "public final class TestModule_PrimitiveIntegerFactory",
                 "    implements Factory<Integer> {",
                 "",
@@ -2336,7 +2300,7 @@
             .addLines(
                 "package test;",
                 "",
-                GENERATED_ANNOTATION,
+                GENERATED_CODE_ANNOTATIONS,
                 "final class DaggerTestComponent implements TestComponent {",
                 "  @Override",
                 "  public Integer nonNullableInteger() {",
@@ -2412,9 +2376,9 @@
         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 @@
         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 @@
             "test.DaggerTestComponent",
             "package test;",
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "final class DaggerTestComponent implements TestComponent {",
             "  @Override",
             "  public Injected injected() {",
@@ -2622,7 +2586,7 @@
             "test.DaggerTestComponent",
             "package test;",
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "final class DaggerTestComponent implements TestComponent {",
             "  @Override",
             "  public String unqualified() {",
@@ -2676,7 +2640,7 @@
                 "",
                 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 85e2d7b..049ff85 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 @@
             "  }",
             "}");
     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 @@
                 "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 @@
             "  long l();",
             "}");
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(module, otherPackageModule, component);
     assertThat(compilation).succeeded();
     JavaFileObject generatedComponent =
@@ -174,7 +172,7 @@
             "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 @@
             "}");
 
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(dependency, component, subcomponent);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -247,7 +244,7 @@
                 "test.DaggerTestComponent",
                 "package test;",
                 "",
-                GENERATED_ANNOTATION,
+                GENERATED_CODE_ANNOTATIONS,
                 "final class DaggerTestComponent implements TestComponent {",
                 "  private final Dep dep;",
                 "",
@@ -267,14 +264,13 @@
                 "",
                 "  @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 @@
                 "test.DaggerTestComponent",
                 "package test;",
                 "",
-                GENERATED_ANNOTATION,
+                GENERATED_CODE_ANNOTATIONS,
                 "final class DaggerTestComponent implements TestComponent {",
                 "  private final ParentModule parentModule;",
                 "",
@@ -370,15 +366,15 @@
                 "  }",
                 "",
                 "  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 @@
                 "test.DaggerTestComponent",
                 "package test;",
                 "",
-                GENERATED_ANNOTATION,
+                GENERATED_CODE_ANNOTATIONS,
                 "final class DaggerTestComponent implements TestComponent {",
                 "  private final ParentModule parentModule;",
                 "",
@@ -425,8 +421,7 @@
                 "}");
     }
     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 0000000..fc59c92
--- /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 169a318..6c642a4 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 @@
             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 @@
                 "    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 1fcf7bc..470b1ed 100644
--- a/javatests/dagger/internal/codegen/DaggerModuleMethodSubject.java
+++ b/javatests/dagger/internal/codegen/DaggerModuleMethodSubject.java
@@ -36,7 +36,7 @@
 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 2f4aecf..bc729fc 100644
--- a/javatests/dagger/internal/codegen/DelegateBindingExpressionTest.java
+++ b/javatests/dagger/internal/codegen/DelegateBindingExpressionTest.java
@@ -19,8 +19,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.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 @@
                 .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 @@
                     "    return (RegularScoped) local;",
                     "  }",
                     "",
-                    "  private ReusableScoped getReusableScoped() {",
+                    "  private ReusableScoped reusableScoped() {",
                     "    Object local = reusableScoped;",
                     "    if (local == null) {",
                     "      local = new ReusableScoped();",
@@ -223,14 +223,14 @@
                 .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 @@
                     "    return (RegularScoped) local;",
                     "  }",
                     "",
-                    "  private ReusableScoped getReusableScoped() {",
+                    "  private ReusableScoped reusableScoped() {",
                     "    Object local = reusableScoped;",
                     "    if (local == null) {",
                     "      local = new ReusableScoped();",
@@ -299,14 +299,14 @@
                 .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 @@
                     "    return (RegularScoped) local;",
                     "  }",
                     "",
-                    "  private ReusableScoped getReusableScoped() {",
+                    "  private ReusableScoped reusableScoped() {",
                     "    Object local = reusableScoped;",
                     "    if (local == null) {",
                     "      local = new ReusableScoped();",
@@ -390,8 +390,7 @@
             "  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 @@
                 .addLines(
                     "package test;",
                     "",
-                    GENERATED_ANNOTATION,
+                    GENERATED_CODE_ANNOTATIONS,
                     "final class DaggerTestComponent implements TestComponent {")
                 .addLinesIn(
                     DEFAULT_MODE,
@@ -422,7 +421,7 @@
                     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 @@
                     "",
                     "  @Override",
                     "  public Supertype supertype() {",
-                    "    return (Supertype) getSubtype();",
+                    "    return (Supertype) subtype();",
                     "  }")
                 .build());
   }
@@ -499,8 +498,7 @@
             "  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 @@
                 .addLines(
                     "package test;",
                     "",
-                    GENERATED_ANNOTATION,
+                    GENERATED_CODE_ANNOTATIONS,
                     "final class DaggerTestComponent implements TestComponent {")
                 .addLinesIn(
                     DEFAULT_MODE,
@@ -528,7 +526,7 @@
                     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 @@
                     "",
                     "  @Override",
                     "  public UsesSupertype usesSupertype() {",
-                    "    return UsesSupertype_Factory.newInstance(getSubtype());",
+                    "    return UsesSupertype_Factory.newInstance(subtype());",
                     "  }")
                 .build());
   }
@@ -590,8 +588,7 @@
             "}");
 
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(module, component);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -602,7 +599,7 @@
                 .addLines(
                     "package test;",
                     "",
-                    GENERATED_ANNOTATION,
+                    GENERATED_CODE_ANNOTATIONS,
                     "final class DaggerTestComponent implements TestComponent {")
                 .addLinesIn(
                     DEFAULT_MODE,
@@ -620,7 +617,7 @@
                     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 @@
                     "",
                     "  @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 @@
             "}");
 
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(module, component);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -704,7 +700,7 @@
                 .addLines(
                     "package test;",
                     "",
-                    GENERATED_ANNOTATION,
+                    GENERATED_CODE_ANNOTATIONS,
                     "final class DaggerTestComponent implements TestComponent {")
                 .addLinesIn(
                     DEFAULT_MODE,
@@ -721,7 +717,7 @@
                     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 @@
                     "",
                     "  @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 @@
             "}");
 
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(supertype, injectableSubtype, module, component);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -810,7 +805,7 @@
                 .addLines(
                     "package test;",
                     "",
-                    GENERATED_ANNOTATION,
+                    GENERATED_CODE_ANNOTATIONS,
                     "final class DaggerRequestsSubtypeAsProvider",
                     "    implements RequestsSubtypeAsProvider {")
                 .addLinesIn(
@@ -824,7 +819,7 @@
                     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 @@
                     "",
                     "  @Override",
                     "  public Provider<Supertype> supertypeProvider() {",
-                    "    return getSubtypeProvider();",
+                    "    return subtypeProvider();",
                     "  }",
                     "",
                     "  private final class SwitchingProvider<T> implements Provider<T> {",
@@ -888,11 +883,10 @@
             "@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 @@
                 .addLines(
                     "package test;",
                     "",
-                    GENERATED_ANNOTATION,
+                    GENERATED_CODE_ANNOTATIONS,
                     "final class DaggerTestComponent implements TestComponent {")
                 .addLinesIn(
                     DEFAULT_MODE,
@@ -919,7 +913,7 @@
                     "  }",
                     "",
                     "  @Override",
-                    "  public Provider<Object> getObject() {",
+                    "  public Provider<Object> object() {",
                     "    return bindStringProvider;",
                     "  }",
                     "}")
@@ -929,7 +923,7 @@
                     "  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 @@
                     "    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 @@
                     "  }",
                     "",
                     "  @Override",
-                    "  public Provider<Object> getObject() {",
+                    "  public Provider<Object> object() {",
                     "    Object local = bindStringProvider;",
                     "    if (local == null) {",
                     "      local = new SwitchingProvider<>(0);",
@@ -967,7 +961,7 @@
                     "    @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 @@
 
   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 4a4e0a5..22547b5 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 @@
         .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 @@
   @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 @@
         .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 @@
         .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 @@
         .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 @@
         .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 @@
         .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 @@
         .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 @@
         .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 @@
         .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 @@
                 "    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 a2da92f..9b0dde0 100644
--- a/javatests/dagger/internal/codegen/DiagnosticFormattingTest.java
+++ b/javatests/dagger/internal/codegen/DiagnosticFormattingTest.java
@@ -18,6 +18,7 @@
 
 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 14a6fb9..fbda59d 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 @@
         "}");
 
     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 @@
             "}");
 
     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 @@
       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 @@
             "}");
 
     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 @@
       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 @@
             "}");
 
     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 @@
             "}");
 
     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 @@
             "}");
 
     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 @@
             "}");
 
     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 @@
             "}");
 
     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 @@
             "}");
 
     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 @@
             "}");
 
     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 @@
             "}");
 
     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 @@
     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 @@
             "}");
 
     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 @@
     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 0000000..e97e360
--- /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 58ddb82..6857039 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 @@
             "",
             IMPORT_GENERATED_ANNOTATION,
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "final class DaggerSimpleComponent implements SimpleComponent {",
             "  private DaggerSimpleComponent() {}",
             "",
@@ -112,8 +112,7 @@
             "}");
 
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(injectedType, dependsOnInjected, componentFile);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -185,7 +184,7 @@
                 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 @@
                 "    return new Builder().build();",
                 "  }",
                 "",
-                "  private ScopedType getScopedType() {",
+                "  private ScopedType scopedType() {",
                 "    Object local = scopedType;",
                 "    if (local instanceof MemoizedSentinel) {",
                 "      synchronized (local) {",
@@ -214,11 +213,11 @@
                 "    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 @@
                 "",
                 "  @Override",
                 "  public NeedsProvider needsProvider() {",
-                "    return new NeedsProvider(getDependsOnScopedProvider());",
+                "    return new NeedsProvider(dependsOnScopedProvider());",
                 "  }",
                 "",
                 "  static final class Builder {",
@@ -250,7 +249,7 @@
                 "    @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 @@
                 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 @@
                 "}");
     }
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(scopedType, dependsOnScoped, componentFile, needsProvider);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -379,7 +377,7 @@
                 "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 @@
                 "    return new Builder().build();",
                 "  }",
                 "",
-                "  private ScopedType getScopedType() {",
+                "  private ScopedType scopedType() {",
                 "    Object local = scopedType;",
                 "    if (local instanceof MemoizedSentinel) {",
                 "      synchronized (local) {",
@@ -425,7 +423,7 @@
                 "",
                 "    @Override",
                 "    public DependsOnScoped dependsOnScoped() {",
-                "      return new DependsOnScoped(DaggerSimpleComponent.this.getScopedType());",
+                "      return new DependsOnScoped(DaggerSimpleComponent.this.scopedType());",
                 "    }",
                 "  }",
                 "}");
@@ -440,7 +438,7 @@
                 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 @@
                 "}");
     }
     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 cbc8c03..be495d7 100644
--- a/javatests/dagger/internal/codegen/FrameworkFieldTest.java
+++ b/javatests/dagger/internal/codegen/FrameworkFieldTest.java
@@ -24,6 +24,7 @@
 
 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 3e6e3ad..e27534d 100644
--- a/javatests/dagger/internal/codegen/FrameworkTypeMapperTest.java
+++ b/javatests/dagger/internal/codegen/FrameworkTypeMapperTest.java
@@ -23,6 +23,8 @@
 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 944e307..b56bc04 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 @@
   // 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 @@
   @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 @@
   @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 @@
   @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 @@
         .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 @@
   @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 @@
 
     // 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 @@
   // 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 @@
   @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 @@
   @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 @@
   // 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 @@
   @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 @@
   @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 @@
   @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 @@
   @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 @@
   // 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 @@
   @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 @@
   @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 @@
   @Test
   public void bothAliasesDifferentValues() {
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(
+        compilerWithOptions(
                 "-Adagger.moduleBindingValidation=NONE",
                 "-Adagger.fullBindingGraphValidation=ERROR")
             .compile(MODULE_WITH_ERRORS);
@@ -462,8 +525,7 @@
   @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 2aa17ac..f9a1b70 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 @@
         + "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 @@
       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 e6e9706..579e87a 100644
--- a/javatests/dagger/internal/codegen/InjectConstructorFactoryGeneratorTest.java
+++ b/javatests/dagger/internal/codegen/InjectConstructorFactoryGeneratorTest.java
@@ -20,8 +20,9 @@
 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 @@
             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 @@
             "",
             "  @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 @@
             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 @@
             "",
             "  @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 @@
             "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 @@
             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 @@
             "",
             "  @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 @@
             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 @@
             "",
             "  @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 @@
             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 @@
     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 @@
         "  @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 @@
         "  }",
         "}");
     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 @@
         "  }",
         "}");
     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 @@
         "  @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 @@
         "  @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 @@
         "  @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 @@
         "  @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 @@
         .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 @@
             IMPORT_GENERATED_ANNOTATION,
             "import javax.inject.Provider;",
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "public final class InjectConstructor_Factory ",
             "    implements Factory<InjectConstructor> {",
             "",
@@ -1032,7 +1083,7 @@
             "  }",
             "",
             "  @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 @@
             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 @@
             IMPORT_GENERATED_ANNOTATION,
             "import javax.inject.Provider;",
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "public final class InjectConstructor_Factory ",
             "    implements Factory<InjectConstructor> {",
             "",
@@ -1129,7 +1191,7 @@
             "  }",
             "",
             "  @Override public InjectConstructor get() {",
-            "    return new InjectConstructor(objectsProvider.get());",
+            "    return newInstance(objectsProvider.get());",
             "  }",
             "",
             "  public static InjectConstructor_Factory create(",
@@ -1170,7 +1232,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "import javax.inject.Provider;",
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "public final class InjectConstructor_Factory ",
             "    implements Factory<InjectConstructor> {",
             "",
@@ -1181,7 +1243,7 @@
             "  }",
             "",
             "  @Override public InjectConstructor get() {",
-            "    return new InjectConstructor(factoryProvider.get());",
+            "    return newInstance(factoryProvider.get());",
             "  }",
             "",
             "  public static InjectConstructor_Factory create(",
@@ -1227,7 +1289,7 @@
             "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 @@
             "  }",
             "",
             "  @Override public InjectConstructor get() {",
-            "    return new InjectConstructor(factoryProvider.get());",
+            "    return newInstance(factoryProvider.get());",
             "  }",
             "",
             "  public static InjectConstructor_Factory create(",
@@ -1285,7 +1347,7 @@
             IMPORT_GENERATED_ANNOTATION,
             "import javax.inject.Provider;",
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "public final class InjectConstructor_Factory ",
             "    implements Factory<InjectConstructor> {",
             "",
@@ -1300,8 +1362,7 @@
             "  }",
             "",
             "  @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 @@
             "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 @@
             "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 d526ec9..fdf62cb 100644
--- a/javatests/dagger/internal/codegen/KeyFactoryTest.java
+++ b/javatests/dagger/internal/codegen/KeyFactoryTest.java
@@ -24,8 +24,11 @@
 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.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 @@
 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 @@
     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 @@
       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 0000000..05e2758
--- /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 0000000..619c1dd
--- /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 ad48712..ed9dd65 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 @@
                 "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 @@
                 "  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 @@
                 "    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 @@
                 "  }",
                 "",
                 "  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 @@
                 "      switch (id) {",
                 "        case 0:",
                 "            return (T) DaggerTestComponent.this",
-                "                 .getMapOfPathEnumAndProviderOfHandler();",
+                "                 .mapOfPathEnumAndProviderOfHandler();",
                 "        case 1:",
                 "            return (T) MapModuleOne_ProvideAdminHandlerFactory",
                 "                .provideAdminHandler(DaggerTestComponent.this.mapModuleOne);",
@@ -205,7 +206,7 @@
                 "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 @@
                 "}");
     }
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(
                 mapModuleOneFile,
                 mapModuleTwoFile,
@@ -357,7 +357,7 @@
                 "test.DaggerTestComponent",
                 "package test;",
                 "",
-                GENERATED_ANNOTATION,
+                GENERATED_CODE_ANNOTATIONS,
                 "final class DaggerTestComponent implements TestComponent {",
                 "  private Provider<Map<Class<?>, Integer>> mapOfClassOfAndIntegerProvider;",
                 "",
@@ -367,7 +367,7 @@
                 "  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 @@
                 "",
                 "  @Override",
                 "  public Object inaccessibleEnum() {",
-                "    return getMapOfPackagePrivateEnumAndInteger();",
+                "    return mapOfPackagePrivateEnumAndInteger();",
                 "  }",
                 "",
                 "  @Override",
@@ -443,7 +443,7 @@
                 "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 @@
                 "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 @@
                 "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 @@
                 "  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 @@
                 "    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 @@
                 "  }",
                 "",
                 "  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 @@
                 "      switch (id) {",
                 "        case 0:",
                 "            return (T) DaggerTestComponent.this",
-                "                 .getMapOfStringAndProviderOfHandler();",
+                "                 .mapOfStringAndProviderOfHandler();",
                 "        case 1:",
                 "            return (T) MapModuleOne_ProvideAdminHandlerFactory",
                 "                .provideAdminHandler(DaggerTestComponent.this.mapModuleOne);",
@@ -613,7 +613,7 @@
                 "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 @@
                 "}");
     }
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(
                 mapModuleOneFile,
                 mapModuleTwoFile,
@@ -737,7 +736,7 @@
                 "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 @@
                 "    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 @@
                 "    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 @@
                 "  }",
                 "",
                 "  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 @@
                 "      switch (id) {",
                 "        case 0:",
                 "            return (T) DaggerTestComponent.this",
-                "                 .getMapOfWrappedClassKeyAndProviderOfHandler();",
+                "                 .mapOfWrappedClassKeyAndProviderOfHandler();",
                 "        case 1:",
                 "            return (T) MapModuleOne_ProvideAdminHandlerFactory",
                 "                .provideAdminHandler(DaggerTestComponent.this.mapModuleOne);",
@@ -823,7 +822,7 @@
                 "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 @@
                 "}");
     }
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(
                 mapModuleOneFile,
                 mapModuleTwoFile,
@@ -951,14 +949,14 @@
                 "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 @@
                 "    @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 @@
                 "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 @@
                 "}");
     }
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(
                 mapModuleOneFile,
                 mapModuleTwoFile,
@@ -1080,7 +1077,7 @@
             "test.DaggerTestComponent",
             "package test;",
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "final class DaggerTestComponent implements TestComponent {",
             "  private final MapModule mapModule;",
             "",
@@ -1090,8 +1087,7 @@
             "  }",
             "}");
     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 11f6bc1..3f33bd5 100644
--- a/javatests/dagger/internal/codegen/MapBindingExpressionTest.java
+++ b/javatests/dagger/internal/codegen/MapBindingExpressionTest.java
@@ -20,8 +20,8 @@
 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 @@
                 "",
                 "import dagger.internal.MapBuilder;",
                 "",
-                GENERATED_ANNOTATION,
+                GENERATED_CODE_ANNOTATIONS,
                 "final class DaggerTestComponent implements TestComponent {")
             .addLinesIn(
                 FAST_INIT_MODE,
@@ -101,7 +101,7 @@
                 "  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 @@
                 "    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 @@
                 "    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 @@
                 "    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 @@
                 "        0, MapModule_ProvideIntFactory.create());")
             .addLinesIn(
                 FAST_INIT_MODE,
-                "        0, getProvideIntProvider());")
+                "        0, provideIntProvider());")
             .addLines(
                 "  }",
                 "",
@@ -183,9 +183,9 @@
                 "        .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 @@
             "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 @@
             "test.DaggerParent",
             "package test;",
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "final class DaggerParent implements Parent {",
             "  private final ParentModule parentModule;",
             "",
@@ -360,7 +360,7 @@
   }
 
   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 aa1a715..69ab5a7 100644
--- a/javatests/dagger/internal/codegen/MapBindingExpressionWithGuavaTest.java
+++ b/javatests/dagger/internal/codegen/MapBindingExpressionWithGuavaTest.java
@@ -20,8 +20,7 @@
 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 @@
             .addLines(
                 "package test;",
                 "",
-                GENERATED_ANNOTATION,
+                GENERATED_CODE_ANNOTATIONS,
                 "final class DaggerTestComponent implements TestComponent {")
             .addLinesIn(
                 FAST_INIT_MODE,
@@ -137,7 +136,7 @@
                 "  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 @@
                 "    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 @@
                 "    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 @@
                 "    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 @@
                 "        0, MapModule_ProvideIntFactory.create());")
             .addLinesIn(
                 FAST_INIT_MODE, //
-                "        0, getProvideIntProvider());")
+                "        0, provideIntProvider());")
             .addLines(
                 "  }",
                 "",
@@ -218,9 +217,9 @@
                 "      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 @@
                 "    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 @@
                 "      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 @@
                 "      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 @@
                 "          .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 @@
                 "}")
             .build();
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(mapModuleFile, componentFile, subcomponentModuleFile, subcomponent);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -403,7 +401,7 @@
             "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 @@
             "  }",
             "}");
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(module, inaccessible, usesInaccessible, componentFile);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -466,7 +463,7 @@
             "test.DaggerParent",
             "package test;",
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "final class DaggerParent implements Parent {",
             "  private final ParentModule parentModule;",
             "",
@@ -482,7 +479,7 @@
             "}");
 
     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 @@
             "",
             "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 746c60d..ffdb2bc 100644
--- a/javatests/dagger/internal/codegen/MapKeyProcessorTest.java
+++ b/javatests/dagger/internal/codegen/MapKeyProcessorTest.java
@@ -18,7 +18,7 @@
 
 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 @@
             "import com.google.auto.value.AutoAnnotation;",
             IMPORT_GENERATED_ANNOTATION,
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "public final class PathKeyCreator {",
             "  private PathKeyCreator() {}",
             "",
@@ -122,7 +122,7 @@
             "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 04b0986..753fb53 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 @@
 @RunWith(JUnit4.class)
 public class MapMultibindingValidationTest {
   @Test
-  public void duplicateMapKeys() {
+  public void duplicateMapKeys_UnwrappedMapKey() {
     JavaFileObject module =
         JavaFileObjects.forSourceLines(
             "test.MapModule",
@@ -65,18 +67,19 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 edaedaf..ef69c71 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.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 @@
             "test.DaggerTestComponent",
             "package test;",
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "final class DaggerTestComponent implements TestComponent {",
             "  @Override",
             "  public Child child() {",
@@ -95,8 +95,7 @@
             "  }",
             "}");
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(childFile, parentFile, componentFile);
 
     assertThat(compilation).succeeded();
@@ -147,7 +146,7 @@
             "",
             "import com.google.errorprone.annotations.CanIgnoreReturnValue;",
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "final class DaggerTestComponent implements TestComponent {",
             "  @Override",
             "  public Child child() {",
@@ -161,8 +160,7 @@
             "  }",
             "}");
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(childFile, parentFile, depFile, componentFile);
 
     assertThat(compilation).succeeded();
@@ -184,44 +182,47 @@
         "",
         " @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 @@
         "",
         "  @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 @@
             "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 @@
         .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 @@
             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;",
+            "  private final Provider<String> stringProvider2;",
+            "  private final Provider<String> stringProvider3;",
+            "  private final Provider<String> stringProvider4;",
             "",
-            "  public MethodInjection_MembersInjector(Provider<String> stringProvider) {",
+            "  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 @@
             "    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 @@
         "  @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 @@
         "  @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 @@
         "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 @@
           "    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 @@
             "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 @@
             "    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 @@
             "}");
 
     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 @@
             "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 @@
             "    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 @@
         "    @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 @@
         "  }",
         "}");
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(
+        compilerWithOptions(
                 compilerMode.javacopts().append("-Adagger.privateMemberValidation=WARNING"))
             .compile(file);
     assertThat(compilation).succeeded();
@@ -1013,7 +1151,7 @@
         "    @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 @@
             "  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 @@
             "  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 @@
             "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 @@
             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 @@
             "  }",
             "}");
     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 @@
             "}");
 
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(foo, inaccessible, usesInaccessible, component);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -1233,28 +1399,33 @@
                 "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 @@
             "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 @@
             "}");
 
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(inaccessible, inaccessiblesModule, usesInaccessibles, component);
     assertThat(compilation).succeeded();
     JavaFileObject generatedComponent =
@@ -1375,13 +1545,13 @@
                 "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 @@
                 "    UsesInaccessibles_MembersInjector.injectInaccessibles(")
             .addLinesIn(
                 FAST_INIT_MODE,
-                "        instance, (List) getListOfInaccessible());")
+                "        instance, (List) listOfInaccessible());")
             .addLinesIn(
                 DEFAULT_MODE,
                 "        instance, (List) inaccessiblesProvider.get());")
@@ -1488,8 +1658,7 @@
             "}");
 
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(foo, supertype, subtype, injectsSubtype, component);
     assertThat(compilation).succeeded();
     JavaFileObject generatedComponent =
@@ -1505,15 +1674,15 @@
             "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 @@
         .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 68d5daa..d8cd6d9 100644
--- a/javatests/dagger/internal/codegen/MembersInjectionValidationTest.java
+++ b/javatests/dagger/internal/codegen/MembersInjectionValidationTest.java
@@ -236,4 +236,177 @@
     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 687c29a..6d2f989 100644
--- a/javatests/dagger/internal/codegen/MethodSignatureFormatterTest.java
+++ b/javatests/dagger/internal/codegen/MethodSignatureFormatterTest.java
@@ -22,12 +22,20 @@
 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 @@
 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 @@
     }
   }
 
+  @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 @@
     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 e2f9634..3c066ba 100644
--- a/javatests/dagger/internal/codegen/MissingBindingSuggestionsTest.java
+++ b/javatests/dagger/internal/codegen/MissingBindingSuggestionsTest.java
@@ -18,6 +18,7 @@
 
 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 @@
     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 @@
         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 8eaa8d5..1aabd48 100644
--- a/javatests/dagger/internal/codegen/MissingBindingValidationTest.java
+++ b/javatests/dagger/internal/codegen/MissingBindingValidationTest.java
@@ -56,8 +56,9 @@
         "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 @@
             "}");
     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 @@
             "}");
     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 @@
 
     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 @@
 
     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 @@
 
     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 @@
             "}");
     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 @@
 
     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 @@
     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 @@
 
     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 @@
 
     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 @@
 
     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 @@
     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 @@
             "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 @@
 
     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 @@
 
     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 50c41ed..cbda1b8 100644
--- a/javatests/dagger/internal/codegen/ModuleFactoryGeneratorTest.java
+++ b/javatests/dagger/internal/codegen/ModuleFactoryGeneratorTest.java
@@ -24,9 +24,8 @@
 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 @@
             "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 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 @@
             "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 @@
             "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 @@
             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 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 @@
             "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 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 @@
             "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 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 @@
             "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 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 @@
         .onLine(6);
   }
 
+
   @Test
   public void enclosedInPrivateModule() {
     JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.Enclosing",
@@ -886,7 +883,7 @@
             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 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 @@
             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 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 @@
             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 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 @@
             "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 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 @@
             "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 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 @@
             "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 @@
             "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 @@
             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 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 @@
                 "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 static TestModule_GetFactory create() {",
-                "    return INSTANCE;",
+                "    return InstanceHolder.INSTANCE;",
                 "  }",
                 "",
                 "  public static int proxyGet() {",
@@ -1433,7 +1429,7 @@
                 "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 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 649649a..162c8c0 100644
--- a/javatests/dagger/internal/codegen/ModuleValidationTest.java
+++ b/javatests/dagger/internal/codegen/ModuleValidationTest.java
@@ -342,10 +342,18 @@
             "",
             "@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 2bf9494..152adb0 100644
--- a/javatests/dagger/internal/codegen/MultibindingTest.java
+++ b/javatests/dagger/internal/codegen/MultibindingTest.java
@@ -131,7 +131,7 @@
     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 @@
     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 24d5636..0000000
--- 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 b765166..60b6898 100644
--- a/javatests/dagger/internal/codegen/OptionalBindingRequestFulfillmentTest.java
+++ b/javatests/dagger/internal/codegen/OptionalBindingRequestFulfillmentTest.java
@@ -20,7 +20,7 @@
 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 @@
                 "",
                 "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 @@
                 "        Maybe_MaybeModule_ProvideMaybeFactory.create()));")
             .addLinesIn(
                 FAST_INIT_MODE, //
-                "        getMaybeProvider()));")
+                "        maybeProvider()));")
             .addLines(
                 "  }",
                 "",
@@ -250,7 +250,7 @@
             "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 @@
             "  @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 1755101..4e158ef 100644
--- a/javatests/dagger/internal/codegen/OptionalBindingTest.java
+++ b/javatests/dagger/internal/codegen/OptionalBindingTest.java
@@ -89,7 +89,7 @@
     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 0000000..b766463
--- /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 0b6ef8f..d41bc2d 100644
--- a/javatests/dagger/internal/codegen/ProducerModuleFactoryGeneratorTest.java
+++ b/javatests/dagger/internal/codegen/ProducerModuleFactoryGeneratorTest.java
@@ -214,6 +214,7 @@
         .onLine(6);
   }
 
+
   @Test
   public void enclosedInPrivateModule() {
     JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.Enclosing",
@@ -369,8 +370,8 @@
             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 @@
             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 9a852c7..797fe2e 100644
--- a/javatests/dagger/internal/codegen/ProductionComponentProcessorTest.java
+++ b/javatests/dagger/internal/codegen/ProductionComponentProcessorTest.java
@@ -19,8 +19,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 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 @@
         "  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 @@
         "@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 @@
         "@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 @@
             .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 @@
                 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 @@
                 "    return new Builder().build();",
                 "  }",
                 "",
-                "  private Executor getProductionImplementationExecutor() {",
+                "  private Executor productionImplementationExecutor() {",
                 "    Object local = productionImplementationExecutor;",
                 "    if (local instanceof MemoizedSentinel) {",
                 "      synchronized (local) {",
@@ -295,7 +295,7 @@
                 "    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 @@
                 "    return (Provider<Executor>) local;",
                 "  }",
                 "",
-                "  private ProductionComponentMonitor getProductionComponentMonitor() {",
+                "  private ProductionComponentMonitor productionComponentMonitor() {",
                 "    Object local = productionComponentMonitor;",
                 "    if (local instanceof MemoizedSentinel) {",
                 "      synchronized (local) {",
@@ -325,7 +325,7 @@
                 "  }",
                 "",
                 "  private Provider<ProductionComponentMonitor>",
-                "      getProductionComponentMonitorProvider() {",
+                "      productionComponentMonitorProvider() {",
                 "      Object local = monitorProvider;",
                 "      if (local == null) {",
                 "        local = new SwitchingProvider<>(1);",
@@ -334,11 +334,11 @@
                 "      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 @@
                 "      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 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 @@
                 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 @@
         "  }",
         "}");
     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 @@
             "  ProductionScoped productionScoped();",
             "}");
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(productionScoped, parent, child);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -651,7 +650,7 @@
             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 @@
                     "      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 8453e03..3e416e9 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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 9efcc2a..58ab2af 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 @@
     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 @@
     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 @@
     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 @@
   @Test
   public void fullBindingGraphValidationDoesNotReportForOneScope() {
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(
+        compilerWithOptions(
                 "-Adagger.fullBindingGraphValidation=ERROR",
                 "-Adagger.moduleHasDifferentScopesValidation=ERROR")
             .compile(
@@ -286,8 +285,7 @@
   @Test
   public void fullBindingGraphValidationDoesNotReportInjectBindings() {
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(
+        compilerWithOptions(
                 "-Adagger.fullBindingGraphValidation=ERROR",
                 "-Adagger.moduleHasDifferentScopesValidation=ERROR")
             .compile(
@@ -384,9 +382,8 @@
   }
 
   @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 @@
         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 @@
   }
 
   @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 @@
                 "    @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 3fb0e9c..e143370 100644
--- a/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentTest.java
+++ b/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentTest.java
@@ -18,8 +18,8 @@
 
 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 @@
             "",
             "import dagger.internal.SetBuilder;",
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "final class DaggerTestComponent implements TestComponent {",
             "  @Override",
             "  public Set<String> strings() {",
@@ -191,9 +191,9 @@
             "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 UsesInaccessible usesInaccessible() {",
             "    return UsesInaccessible_Factory.newInstance(",
             "        (Set) Collections.emptySet(),",
-            "        (Set) getSetOfInaccessible2());",
+            "        (Set) setOfInaccessible2());",
             "  }",
             "}");
     Compilation compilation =
@@ -266,7 +266,7 @@
             "import java.util.Set;",
             IMPORT_GENERATED_ANNOTATION,
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "final class DaggerParent implements Parent {",
             "  private DaggerParent() {}",
             "",
@@ -316,7 +316,7 @@
   }
 
   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 7a47393..2e3686f 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 @@
             "",
             "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 @@
             "  }",
             "}");
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(emptySetModuleFile, setModuleFile, componentFile);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -203,9 +202,9 @@
             "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 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 @@
             "",
             "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 @@
             "  }",
             "}");
     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 @@
             "import java.util.Set;",
             IMPORT_GENERATED_ANNOTATION,
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "final class DaggerTestComponent implements TestComponent, "
                 + "CancellationListener {",
             "  private DaggerTestComponent() {}",
@@ -346,14 +344,14 @@
             "    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 @@
             "}");
 
     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 c7fe998..248e2ef 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 5dab4c4..0fd7e08 100644
--- a/javatests/dagger/internal/codegen/SubcomponentBuilderValidationTest.java
+++ b/javatests/dagger/internal/codegen/SubcomponentBuilderValidationTest.java
@@ -18,11 +18,12 @@
 
 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 de0067f..ea0b4cc 100644
--- a/javatests/dagger/internal/codegen/SubcomponentCreatorRequestFulfillmentTest.java
+++ b/javatests/dagger/internal/codegen/SubcomponentCreatorRequestFulfillmentTest.java
@@ -21,14 +21,15 @@
 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 @@
             "",
             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 b5753d4..371253a 100644
--- a/javatests/dagger/internal/codegen/SubcomponentCreatorValidationTest.java
+++ b/javatests/dagger/internal/codegen/SubcomponentCreatorValidationTest.java
@@ -18,18 +18,19 @@
 
 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 @@
     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 ad08160..19a27ff 100644
--- a/javatests/dagger/internal/codegen/SubcomponentValidationTest.java
+++ b/javatests/dagger/internal/codegen/SubcomponentValidationTest.java
@@ -19,8 +19,9 @@
 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 @@
         "  }",
         "}");
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(componentFile, childComponentFile, moduleFile);
     assertThat(compilation).failed();
     assertThat(compilation)
@@ -152,16 +152,15 @@
             "  }",
             "}");
     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 @@
         "@Subcomponent",
         "interface ChildComponent {}");
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(componentFile, childComponentFile);
     assertThat(compilation).failed();
     assertThat(compilation)
@@ -221,8 +219,7 @@
         "@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 @@
         "@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 @@
         "",
         "@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 @@
         "@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 @@
         "",
         "@Subcomponent(modules = ChildModule.class)",
         "interface ChildComponent {",
-        "  Object getObject();",
+        "  Object object();",
         "}");
     JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.ChildModule",
         "package test;",
@@ -362,8 +357,7 @@
         "  @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 @@
             "@Component",
             "interface ParentComponent {",
             "  ChildComponent childComponent();",
-            "  Dep1 getDep1();",
-            "  Dep2 getDep2();",
+            "  Dep1 dep1();",
+            "  Dep2 dep2();",
             "}");
     JavaFileObject childComponentFile =
         JavaFileObjects.forSourceLines(
@@ -395,7 +389,7 @@
             "",
             "@Subcomponent(modules = ChildModule.class)",
             "interface ChildComponent {",
-            "  Object getObject();",
+            "  Object object();",
             "}");
     JavaFileObject childModuleFile =
         JavaFileObjects.forSourceLines(
@@ -463,7 +457,7 @@
             .addLines(
                 "package test;",
                 "",
-                GENERATED_ANNOTATION,
+                GENERATED_CODE_ANNOTATIONS,
                 "final class DaggerParentComponent implements ParentComponent {")
             .addLinesIn(
                 DEFAULT_MODE,
@@ -475,7 +469,7 @@
                 "")
             .addLines(
                 "  @Override", //
-                "  public Dep1 getDep1() {")
+                "  public Dep1 dep1() {")
             .addLinesIn(
                 FAST_INIT_MODE,
                 "   Object local = dep1;",
@@ -496,7 +490,7 @@
                 "  }", //
                 "",
                 "  @Override",
-                "  public Dep2 getDep2() {")
+                "  public Dep2 dep2() {")
             .addLinesIn(
                 FAST_INIT_MODE,
                 "   Object local = dep2;",
@@ -545,34 +539,34 @@
                 "")
             .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 @@
             .build();
 
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(
                 parentComponentFile,
                 childComponentFile,
@@ -663,7 +656,7 @@
             "",
             "import test.subpackage.Sub;",
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "final class DaggerParentComponent implements ParentComponent {",
             "  @Override",
             "  public Foo.Sub newInstanceSubcomponent() {",
@@ -700,8 +693,7 @@
             "  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 @@
             "test.DaggerParentComponent",
             "package test;",
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "final class DaggerParentComponent implements ParentComponent {",
             "  @Override",
             "  public Sub newSubcomponent() {",
@@ -772,7 +764,7 @@
             "  }",
             "}");
     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 @@
         JavaFileObjects.forSourceLines(
             "DaggerParentComponent",
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "final class DaggerParentComponent implements ParentComponent {",
             "  @Override",
             "  public Sub newSubcomponent() {",
@@ -835,7 +827,7 @@
             "");
 
     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 @@
             "",
             "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 @@
             "}");
 
     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 @@
             "test.DaggerC",
             "package test;",
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "final class DaggerC implements C {",
             "  @Override",
             "  public Foo.C newInstanceC() {",
@@ -956,8 +948,7 @@
             "}");
 
     Compilation compilation =
-        daggerCompiler()
-            .withOptions(compilerMode.javacopts())
+        compilerWithOptions(compilerMode.javacopts())
             .compile(parent, subcomponentWithSameSimpleNameAsParent);
     assertThat(compilation).succeeded();
     assertThat(compilation)
@@ -1007,7 +998,7 @@
             "",
             IMPORT_GENERATED_ANNOTATION,
             "",
-            GENERATED_ANNOTATION,
+            GENERATED_CODE_ANNOTATIONS,
             "final class DaggerC implements C {",
             "  @Override",
             "  public C.Foo.Sub.Builder fooBuilder() {",
@@ -1019,7 +1010,7 @@
             "    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 @@
             "  }",
             "}");
     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 @@
             "}");
 
     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 2898f2e..593ad49 100644
--- a/javatests/dagger/internal/codegen/SwitchingProviderTest.java
+++ b/javatests/dagger/internal/codegen/SwitchingProviderTest.java
@@ -18,7 +18,7 @@
 
 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 @@
         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 @@
                 "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 @@
                 "",
                 "  @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 @@
                 "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 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 @@
                 "test.DaggerTestComponent",
                 "package test;",
                 "",
-                GENERATED_ANNOTATION,
+                GENERATED_CODE_ANNOTATIONS,
                 "final class DaggerTestComponent implements TestComponent {",
                 "  @Override",
                 "  public Provider<Set<String>> setProvider() {",
@@ -469,7 +469,7 @@
                 "test.DaggerTestComponent",
                 "package test;",
                 "",
-                GENERATED_ANNOTATION,
+                GENERATED_CODE_ANNOTATIONS,
                 "final class DaggerTestComponent implements TestComponent {",
                 "  private Provider<MembersInjector<Foo>> fooMembersInjectorProvider;",
                 "",
@@ -540,7 +540,7 @@
                 "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 8c8628c..0000000
--- 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 b0f2f2f..d1c3a2e 100644
--- a/javatests/dagger/internal/codegen/ValidationReportTest.java
+++ b/javatests/dagger/internal/codegen/ValidationReportTest.java
@@ -23,7 +23,8 @@
 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 99c11bd..438d377 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 @@
     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 0000000..268340d
--- /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 0000000..f112392
--- /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 1e1bd66..9404d85 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("//: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 b29cb3c..af13504 100644
--- a/javatests/dagger/producers/internal/AbstractProducesMethodProducerTest.java
+++ b/javatests/dagger/producers/internal/AbstractProducesMethodProducerTest.java
@@ -18,7 +18,8 @@
 
 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 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 449b5a6..5c443a4 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 4e5d74f..eefcb01 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 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 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 @@
         .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 @@
         .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 @@
   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 @@
         .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 47ccccb..ada4e33 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 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 @@
         .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 @@
     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 @@
         .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 @@
     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 @@
     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 @@
   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 @@
         .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 a94461b..849037b 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 @@
     "DOCLINT_REFERENCES",
 )
 
+package(default_visibility = ["//:src"])
+
 GenJavaTests(
     name = "spi_tests",
     srcs = glob(["*.java"]),
@@ -32,11 +32,11 @@
     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 8fd0e35..2d13e21 100644
--- a/javatests/dagger/spi/FailingPlugin.java
+++ b/javatests/dagger/spi/FailingPlugin.java
@@ -83,7 +83,6 @@
           .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 613a915..1df8cd1 100644
--- a/javatests/dagger/spi/SpiPluginTest.java
+++ b/javatests/dagger/spi/SpiPluginTest.java
@@ -53,7 +53,9 @@
         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 @@
         .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 @@
         .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 @@
                 "[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 @@
                 "[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 @@
                 "        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 @@
             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 @@
                 "[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 @@
                 "        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 d645695..0000000
--- 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 59bf056..0000000
--- 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 ecc3339..0000000
--- 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 b9b3bee..0000000
--- 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 8cbfa72..0000000
--- 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 6d81bdf..0000000
--- 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 2056a80..0000000
--- 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 56c6909..0000000
--- 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 be05c6e..0000000
--- 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 9b5c0a8..0000000
--- 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 e47130f..0000000
--- 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 5da4253..0000000
--- 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 57a5e61..0000000
--- 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 8a05c9d..0000000
--- 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 08ea732..0000000
--- 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 3638b53..0000000
--- 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 8c43e8f..0000000
--- 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 8c43e8f..0000000
--- 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 e1d0163..0000000
--- 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 2edbbf4..0000000
--- 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 72fd4a9..0000000
--- 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 0058623..0000000
--- 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 fc01aa3..0000000
--- 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 fbc08ab..0000000
--- 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 b7e43ce..0000000
--- 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 4c48b47..0000000
--- 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 7d08fa7..0000000
--- 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 36551c1..0000000
--- 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 ca18ea4..0000000
--- 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 17f16a8..0000000
--- 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 be17cd1..0000000
--- 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 f0cdc39..0000000
--- 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 11a5e9d..0000000
--- 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 edf62d8..0000000
--- 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 b1a8b08..0000000
--- 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 81a4515..0000000
--- 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 897752c..0000000
--- 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 f68a013..0000000
--- 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 1356995..0000000
--- 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 858c96e..0000000
--- 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 e2d40de..0000000
--- 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 73ae0e2..0000000
--- 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 b247a7c..0000000
--- 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 70fca1b..0000000
--- 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 713333c..0000000
--- 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 31d6337..0000000
--- 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 953541c..0000000
--- 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 3642d69..0000000
--- 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 d0a76c2..0000000
--- 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 0c98053..0000000
--- 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 b69cdf0..0000000
--- 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 b2a9d0b..0000000
--- 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 41e75ef..0000000
--- 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 0000000..cf790c2
--- /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 15ab299..d16ad36 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 @@
         test_javacopts = None,
         functional = True):
     _GenTests(
-        native.java_library,
-        native.java_test,
+        java_library,
+        java_test,
         name,
         srcs,
         deps,
@@ -190,6 +184,5 @@
             **test_kwargs
         )
 
-
 def _hjar_test(name, tags):
     pass
diff --git a/tools/BUILD b/tools/BUILD
index 7b3cc6c..83d5048 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 0000000..4f0b0ce
--- /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 2ac0359..2c648a2 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 @@
         **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 0000000..5ebed3f
--- /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 39ed62d..68b6ac0 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 0000000..ddf4926
--- /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 0000000..3c25c10
--- /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 0000000..a744402
--- /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 1f721a1..de2b49c 100755
--- a/util/deploy-to-maven-central.sh
+++ b/util/deploy-to-maven-central.sh
@@ -6,50 +6,69 @@
   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 1153468..0000000
--- 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 0000000..c662b0e
--- /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 8e9304c..c5f71fd 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 8f77a41..be6030c 100755
--- a/util/install-local-snapshot.sh
+++ b/util/install-local-snapshot.sh
@@ -4,8 +4,43 @@
 
 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 944a2c3..63cc3ea 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 @@
 
   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 0000000..7fc7762
--- /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 0000000..ba51078
--- /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 0000000..b88ccab
--- /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 0000000..ea78671
--- /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 0000000..c017b76
--- /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],
+    )