Snap for 10447354 from cd3b5ac62e070c8a3bccc1a0495eaa742438c252 to mainline-networking-release

Change-Id: If38505d23a28c208702fc914f438416e64289ca5
diff --git a/.github/actions/artifact-android-emulator-tests/action.yml b/.github/actions/artifact-android-emulator-tests/action.yml
new file mode 100644
index 0000000..a57fd08
--- /dev/null
+++ b/.github/actions/artifact-android-emulator-tests/action.yml
@@ -0,0 +1,39 @@
+name: 'Artifact Android emulator tests'
+description: 'Runs Android emulator tests on the Dagger LOCAL-SNAPSHOT artifacts.'
+
+inputs:
+  api-level:
+    description: 'The version of Android emulator API to test with.'
+    required: true
+
+runs:
+  using: "composite"
+  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 ${{ inputs.api-level }})'
+      uses: reactivecircus/android-emulator-runner@v2
+      with:
+        api-level: ${{ inputs.api-level }}
+        target: google_apis
+        script: ./util/run-local-emulator-tests.sh
+    - name: 'Upload test reports (API ${{ inputs.api-level }})'
+      if: ${{ always() }}
+      uses: actions/upload-artifact@v2
+      with:
+        name: androidTests-report-api-${{ inputs.api-level }}
+        path: ${{ github.workspace }}/**/build/reports/androidTests/connected/*
diff --git a/.github/actions/artifact-android-local-tests/action.yml b/.github/actions/artifact-android-local-tests/action.yml
new file mode 100644
index 0000000..090bbb5
--- /dev/null
+++ b/.github/actions/artifact-android-local-tests/action.yml
@@ -0,0 +1,36 @@
+name: 'Artifact Android local tests'
+description: 'Runs Android local tests on the Dagger LOCAL-SNAPSHOT artifacts.'
+
+inputs:
+  agp:
+    description: 'The version of AGP to test with.'
+    required: true
+
+runs:
+  using: "composite"
+  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 ${{ inputs.agp }})'
+      run: ./util/run-local-gradle-android-tests.sh "${{ inputs.agp }}"
+      shell: bash
+    - name: 'Upload test reports (AGP ${{ inputs.agp }})'
+      if: ${{ always() }}
+      uses: actions/upload-artifact@v2
+      with:
+        name: tests-reports-agp-${{ inputs.agp }}
+        path: ${{ github.workspace }}/**/build/reports/tests/*
diff --git a/.github/actions/artifact-java-local-tests/action.yml b/.github/actions/artifact-java-local-tests/action.yml
new file mode 100644
index 0000000..eb31897
--- /dev/null
+++ b/.github/actions/artifact-java-local-tests/action.yml
@@ -0,0 +1,25 @@
+name: 'Artifact Java local tests'
+description: 'Runs java local tests on the Dagger LOCAL-SNAPSHOT artifacts.'
+
+runs:
+  using: "composite"
+  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
diff --git a/.github/actions/bazel-build/action.yml b/.github/actions/bazel-build/action.yml
new file mode 100644
index 0000000..bc875c7
--- /dev/null
+++ b/.github/actions/bazel-build/action.yml
@@ -0,0 +1,39 @@
+name: 'Bazel build'
+description: 'Builds artifacts and creates the Dagger local snapshots.'
+
+runs:
+  using: "composite"
+  steps:
+    - name: 'Install Java ${{ env.USE_JAVA_VERSION }}'
+      uses: actions/setup-java@v2
+      with:
+        distribution: '${{ env.USE_JAVA_DISTRIBUTION }}'
+        java-version: '${{ env.USE_JAVA_VERSION }}'
+    - name: 'Check out repository'
+      uses: actions/checkout@v2
+    - name: 'Cache Bazel files'
+      uses: actions/cache@v2
+      with:
+        path: ~/.cache/bazel
+        key: ${{ runner.os }}-bazel-build-${{ github.sha }}
+        restore-keys: |
+          ${{ runner.os }}-bazel-build-
+    - name: 'Java build'
+      run: bazel build //java/...
+      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
+    - name: 'Clean bazel cache'
+      # According to the documentation, we should be able to exclude these via
+      # the actions/cache path, e.g. "!~/.cache/bazel/*/*/external/" but that
+      # doesn't seem to work.
+      run: |
+        rm -rf $(bazel info repository_cache)
+        rm -rf ~/.cache/bazel/*/*/external/
+      shell: bash
diff --git a/.github/actions/bazel-test/action.yml b/.github/actions/bazel-test/action.yml
new file mode 100644
index 0000000..0fc5a1c
--- /dev/null
+++ b/.github/actions/bazel-test/action.yml
@@ -0,0 +1,55 @@
+name: 'Bazel test'
+description: 'Runs Bazel tests.'
+
+runs:
+  using: "composite"
+  steps:
+    - name: 'Install Java ${{ env.USE_JAVA_VERSION }}'
+      uses: actions/setup-java@v2
+      with:
+        distribution: '${{ env.USE_JAVA_DISTRIBUTION }}'
+        java-version: '${{ env.USE_JAVA_VERSION }}'
+    - 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
+        # Note: we could use the same key as bazel-build, but we separate them
+        # so that bazel-build's cache is smaller (~200Mb vs ~900Mb) and faster
+        # to load than this cache since it's the bottleneck of all other steps
+        key: ${{ runner.os }}-bazel-test-${{ github.sha }}
+        restore-keys: |
+          ${{ runner.os }}-bazel-test-
+    - 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: 'Run Bazel examples'
+      run: cd examples/bazel; bazel test --test_output=errors //...
+      shell: bash
+    - name: 'Clean bazel cache'
+      # According to the documentation, we should be able to exclude these via
+      # the actions/cache path, e.g. "!~/.cache/bazel/*/*/external/" but that
+      # doesn't seem to work.
+      run: |
+        rm -rf $(bazel info repository_cache)
+        rm -rf ~/.cache/bazel/*/*/external/
+      shell: bash
diff --git a/.github/actions/build-gradle-plugin/action.yml b/.github/actions/build-gradle-plugin/action.yml
new file mode 100644
index 0000000..6a388d4
--- /dev/null
+++ b/.github/actions/build-gradle-plugin/action.yml
@@ -0,0 +1,56 @@
+name: 'Build Hilt Gradle plugin'
+description: 'Builds the Hilt Gradle plugin.'
+
+inputs:
+  agp:
+    description: 'The version of AGP to build with.'
+    required: true
+
+runs:
+  using: "composite"
+  steps:
+    - name: 'Install Java ${{ env.USE_JAVA_VERSION }}'
+      uses: actions/setup-java@v2
+      with:
+          distribution: '${{ env.USE_JAVA_DISTRIBUTION }}'
+          java-version: '${{ env.USE_JAVA_VERSION }}'
+    - 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-build-${{ github.sha }}
+        restore-keys: |
+          ${{ runner.os }}-bazel-build-
+    - 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: 'Build and install Hilt Gradle plugin local snapshot'
+      run: ./util/deploy-hilt-gradle-plugin.sh "install:install-file" "LOCAL-SNAPSHOT"
+      shell: bash
+      env:
+        AGP_VERSION: '${{ inputs.agp }}'
+    - name: 'Clean bazel cache'
+      # According to the documentation, we should be able to exclude these via
+      # the actions/cache path, e.g. "!~/.cache/bazel/*/*/external/" but that
+      # doesn't seem to work.
+      run: |
+        rm -rf $(bazel info repository_cache)
+        rm -rf ~/.cache/bazel/*/*/external/
+      shell: bash
diff --git a/.github/actions/prechecks/action.yml b/.github/actions/prechecks/action.yml
new file mode 100644
index 0000000..1365184
--- /dev/null
+++ b/.github/actions/prechecks/action.yml
@@ -0,0 +1,20 @@
+name: 'Performs prechecks before running other actions.'
+description: 'Validates that the Dagger version in the config.yml file is the latest version of Dagger released.'
+
+runs:
+  using: "composite"
+  steps:
+    # Cancel previous runs on the same branch to avoid unnecessary parallel
+    # runs of the same job. See https://github.com/google/go-github/pull/1821
+    - name: Cancel previous
+      uses: styfle/cancel-workflow-action@0.8.0
+      with:
+        access_token: ${{ github.token }}
+    - name: 'Check out gh-pages repository'
+      uses: actions/checkout@v2
+      with:
+        ref: 'refs/heads/gh-pages'
+        path: gh-pages
+    - name: 'Validate latest Dagger version'
+      run: ./gh-pages/.github/scripts/validate-latest-dagger-version.sh gh-pages/_config.yml
+      shell: bash
diff --git a/.github/actions/test-gradle-plugin/action.yml b/.github/actions/test-gradle-plugin/action.yml
new file mode 100644
index 0000000..93c6af7
--- /dev/null
+++ b/.github/actions/test-gradle-plugin/action.yml
@@ -0,0 +1,54 @@
+name: 'Test Hilt Gradle plugin'
+description: 'Tests the Hilt Gradle plugin.'
+
+runs:
+  using: "composite"
+  steps:
+    - name: 'Install Java ${{ env.USE_JAVA_VERSION }}'
+      uses: actions/setup-java@v2
+      with:
+          distribution: '${{ env.USE_JAVA_DISTRIBUTION }}'
+          java-version: '${{ env.USE_JAVA_VERSION }}'
+    - 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-build-${{ github.sha }}
+        restore-keys: |
+          ${{ runner.os }}-bazel-build-
+    - 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: 'Build and test Hilt Gradle plugin'
+      run: ./java/dagger/hilt/android/plugin/gradlew -p java/dagger/hilt/android/plugin clean test  --continue --no-daemon --stacktrace
+      shell: bash
+    - name: 'Clean bazel cache'
+      # According to the documentation, we should be able to exclude these via
+      # the actions/cache path, e.g. "!~/.cache/bazel/*/*/external/" but that
+      # doesn't seem to work.
+      run: |
+        rm -rf $(bazel info repository_cache)
+        rm -rf ~/.cache/bazel/*/*/external/
+      shell: bash
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 6c818cc..d5c35d6 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -9,182 +9,106 @@
       - master
 
 env:
+  USE_JAVA_DISTRIBUTION: 'zulu'
   # Our Bazel builds currently rely on JDK 8.
   USE_JAVA_VERSION: '8'
-  # Our Bazel builds currently rely on 3.7.1. The version is set via
+  # Our Bazel builds currently rely on 4.2.1. The version is set via
   # baselisk by USE_BAZEL_VERSION: https://github.com/bazelbuild/bazelisk.
-  USE_BAZEL_VERSION: '3.7.1'
+  USE_BAZEL_VERSION: '4.2.1'
 
 jobs:
   validate-latest-dagger-version:
     name: 'Validate Dagger version'
     runs-on: ubuntu-latest
     steps:
-      # Cancel previous runs on the same branch to avoid unnecessary parallel
-      # runs of the same job. See https://github.com/google/go-github/pull/1821
-      - name: Cancel previous
-        uses: styfle/cancel-workflow-action@0.8.0
-        with:
-          access_token: ${{ github.token }}
-      - name: 'Check out gh-pages repository'
-        uses: actions/checkout@v2
-        with:
-          ref: 'refs/heads/gh-pages'
-          path: gh-pages
-      - name: 'Validate latest Dagger version'
-        run: ./gh-pages/.github/scripts/validate-latest-dagger-version.sh gh-pages/_config.yml
+      - uses: actions/checkout@v2
+      - uses: ./.github/actions/prechecks
+  bazel-build:
+    name: 'Bazel build'
+    needs: validate-latest-dagger-version
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: ./.github/actions/bazel-build
   bazel-test:
     name: 'Bazel tests'
     needs: validate-latest-dagger-version
     runs-on: ubuntu-latest
     steps:
-      - name: 'Install Java ${{ env.USE_JAVA_VERSION }}'
-        uses: actions/setup-java@v1
-        with:
-          java-version: '${{ env.USE_JAVA_VERSION }}'
-      - 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 //... --keep_going
-        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
+      - uses: actions/checkout@v2
+      - uses: ./.github/actions/bazel-test
   artifact-java-local-tests:
     name: 'Artifact Java local tests'
-    needs: bazel-test
+    needs: bazel-build
     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
+      - uses: actions/checkout@v2
+      - uses: ./.github/actions/artifact-java-local-tests
+  test-gradle-plugin:
+    name: 'Test Hilt Gradle plugin'
+    needs: bazel-build
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: ./.github/actions/test-gradle-plugin
   artifact-android-local-tests:
     name: 'Artifact Android local tests (AGP ${{ matrix.agp }})'
-    needs: bazel-test
+    needs: bazel-build
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        agp: ['4.1.0', '4.2.0-beta04']
+        agp: ['4.1.0', '4.2.0', '7.0.0']
     steps:
-      - name: 'Check out repository'
-        uses: actions/checkout@v2
-      - name: 'Cache Gradle files'
-        uses: actions/cache@v2
+      - uses: actions/checkout@v2
+      - uses: ./.github/actions/artifact-android-local-tests
         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
-      - name: 'Upload test reports (AGP ${{ matrix.agp }})'
-        if: ${{ always() }}
-        uses: actions/upload-artifact@v2
-        with:
-          name: tests-reports-agp-${{ matrix.agp }}
-          path: ${{ github.workspace }}/**/build/reports/tests/*
+          agp: '${{ matrix.agp }}'
   artifact-android-emulator-tests:
     name: 'Artifact Android emulator tests (API 30)'
-    needs: bazel-test
+    needs: bazel-build
     # 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
+      - uses: actions/checkout@v2
+      - uses: ./.github/actions/artifact-android-emulator-tests
         timeout-minutes: 25
         with:
-          api-level: 30
-          target: google_apis
-          script: ./util/run-local-emulator-tests.sh
-      - name: 'Upload test reports (API 30)'
-        if: ${{ always() }}
-        uses: actions/upload-artifact@v2
+          api-level: '30'
+  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-build
+    # It's recommended to run emulator tests on macOS
+    # See https://github.com/marketplace/actions/android-emulator-runner
+    runs-on: macos-latest
+    strategy:
+      matrix: # Run on 16 (PreL), 21 (L), and 26 (O).
+        api-level: [16, 21, 26]
+    steps:
+      - uses: actions/checkout@v2
+      - uses: ./.github/actions/artifact-android-emulator-tests
+        timeout-minutes: 25
         with:
-          name: androidTests-reports-api-30
-          path: ${{ github.workspace }}/**/build/reports/androidTests/connected/*
+          api-level: '${{ matrix.api-level }}'
   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]
+    needs: [bazel-test, artifact-java-local-tests, artifact-android-local-tests, test-gradle-plugin]
     if: github.event_name == 'push' && github.repository == 'google/dagger' && github.ref == 'refs/heads/master'
     runs-on: ubuntu-latest
     steps:
       - name: 'Install Java ${{ env.USE_JAVA_VERSION }}'
-        uses: actions/setup-java@v1
+        uses: actions/setup-java@v2
         with:
+          distribution: '${{ env.USE_JAVA_DISTRIBUTION }}'
           java-version: '${{ env.USE_JAVA_VERSION }}'
+          server-id: sonatype-nexus-snapshots
+          server-username: CI_DEPLOY_USERNAME
+          server-password: CI_DEPLOY_PASSWORD
       - name: 'Check out repository'
         uses: actions/checkout@v2
       - name: 'Cache local Maven repository'
@@ -200,9 +124,9 @@
         uses: actions/cache@v2
         with:
           path: ~/.cache/bazel
-          key: ${{ runner.os }}-bazel-${{ github.sha }}
+          key: ${{ runner.os }}-bazel-build-${{ github.sha }}
           restore-keys: |
-            ${{ runner.os }}-bazel-
+            ${{ runner.os }}-bazel-build-
       - name: 'Cache Gradle files'
         uses: actions/cache@v2
         with:
@@ -218,50 +142,33 @@
         env:
           GH_TOKEN: ${{ github.token }}
       - name: 'Publish latest snapshot'
-        run: ./util/publish-snapshot-on-commit.sh
+        run: |
+          util/deploy-all.sh \
+            "deploy:deploy-file" \
+            "HEAD-SNAPSHOT" \
+            "-DrepositoryId=sonatype-nexus-snapshots" \
+            "-Durl=https://oss.sonatype.org/content/repositories/snapshots"
         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
+      - name: 'Clean bazel cache'
+        # According to the documentation, we should be able to exclude these via
+        # the actions/cache path, e.g. "!~/.cache/bazel/*/*/external/" but that
+        # doesn't seem to work.
+        run: |
+          rm -rf $(bazel info repository_cache)
+          rm -rf ~/.cache/bazel/*/*/external/
+        shell: bash
+  build-gradle-plugin-latest-agp:
+    name: 'Build Hilt Gradle plugin against latest AGP version'
+    # We only run this on master push (essentially a postsubmit) since we
+    # don't want this job to prevent merges
     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: # Run on 16 (PreL), 21 (L), and 26 (O).
-        api-level: [16, 21, 26]
+    needs: bazel-build
+    runs-on: ubuntu-latest
     steps:
-      - name: 'Check out repository'
-        uses: actions/checkout@v2
-      - name: 'Cache Gradle files'
-        uses: actions/cache@v2
+      - uses: actions/checkout@v2
+      - uses: ./.github/actions/build-gradle-plugin
         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
-      - name: 'Upload test reports (API ${{ matrix.api-level }})'
-        if: ${{ always() }}
-        uses: actions/upload-artifact@v2
-        with:
-          name: androidTests-report-api-${{ matrix.api-level }}
-          path: ${{ github.workspace }}/**/build/reports/androidTests/connected/*
+          agp: '+'
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..3359c71
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,142 @@
+name: Dagger Release
+
+on:
+  workflow_dispatch:
+    inputs:
+      dagger_release_version:
+        description: 'The Dagger version to use in this release.'
+        required: true
+
+env:
+  USE_JAVA_DISTRIBUTION: 'zulu'
+  # Our Bazel builds currently rely on JDK 8.
+  USE_JAVA_VERSION: '8'
+  # Our Bazel builds currently rely on 4.2.1. The version is set via
+  # baselisk by USE_BAZEL_VERSION: https://github.com/bazelbuild/bazelisk.
+  USE_BAZEL_VERSION: '4.2.1'
+  DAGGER_RELEASE_VERSION: "${{ github.event.inputs.dagger_release_version }}"
+
+# TODO(bcorso):Convert these jobs into local composite actions to share with the
+# continuous integration workflow.
+jobs:
+  validate-latest-dagger-version:
+    name: 'Validate Dagger version'
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: ./.github/actions/prechecks
+  bazel-build:
+    name: 'Bazel build'
+    needs: validate-latest-dagger-version
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: ./.github/actions/bazel-build
+  bazel-test:
+    name: 'Bazel tests'
+    needs: validate-latest-dagger-version
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: ./.github/actions/bazel-test
+  artifact-java-local-tests:
+    name: 'Artifact Java local tests'
+    needs: bazel-build
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: ./.github/actions/artifact-java-local-tests
+  test-gradle-plugin:
+    name: 'Test Hilt Gradle plugin'
+    needs: bazel-build
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: ./.github/actions/test-gradle-plugin
+  artifact-android-local-tests:
+    name: 'Artifact Android local tests (AGP ${{ matrix.agp }})'
+    needs: bazel-build
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        agp: ['4.1.0', '4.2.0', '7.0.0']
+    steps:
+      - uses: actions/checkout@v2
+      - uses: ./.github/actions/artifact-android-local-tests
+        with:
+          agp: '${{ matrix.agp }}'
+  publish-artifacts:
+    name: 'Publish Artifact'
+    needs: [bazel-test, artifact-java-local-tests, artifact-android-local-tests, test-gradle-plugin]
+    runs-on: ubuntu-latest
+    steps:
+      - name: 'Install Java ${{ env.USE_JAVA_VERSION }}'
+        uses: actions/setup-java@v2
+        with:
+          distribution: '${{ env.USE_JAVA_DISTRIBUTION }}'
+          java-version: '${{ env.USE_JAVA_VERSION }}'
+          server-id: sonatype-nexus-staging
+          server-username: CI_DEPLOY_USERNAME
+          server-password: CI_DEPLOY_PASSWORD
+          gpg-private-key: ${{ secrets.CI_GPG_PRIVATE_KEY }}
+          gpg-passphrase: CI_GPG_PASSPHRASE
+      - 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-build-${{ github.sha }}
+          restore-keys: |
+            ${{ runner.os }}-bazel-build-
+      - 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 artifacts
+        run: |
+          util/deploy-all.sh \
+            "gpg:sign-and-deploy-file" \
+            "${{ env.DAGGER_RELEASE_VERSION }}" \
+            "-DrepositoryId=sonatype-nexus-staging" \
+            "-Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/"
+        shell: bash
+        env:
+          CI_DEPLOY_USERNAME: ${{ secrets.CI_DEPLOY_USERNAME }}
+          CI_DEPLOY_PASSWORD: ${{ secrets.CI_DEPLOY_PASSWORD }}
+          CI_GPG_PASSPHRASE: ${{ secrets.CI_GPG_PASSPHRASE }}
+      - name: 'Set git credentials'
+        run: |
+            git config --global user.email "dagger-dev+github@google.com"
+            git config --global user.name "Dagger Team"
+        shell: bash
+      - name: 'Publish tagged release'
+        run: util/publish-tagged-release.sh ${{ env.DAGGER_RELEASE_VERSION }}
+        shell: bash
+      - name: 'Publish tagged docs'
+        run: util/publish-tagged-docs.sh ${{ env.DAGGER_RELEASE_VERSION }}
+        shell: bash
+        env:
+          GH_TOKEN: ${{ github.token }}
+      - name: 'Clean bazel cache'
+        # According to the documentation, we should be able to exclude these via
+        # the actions/cache path, e.g. "!~/.cache/bazel/*/*/external/" but that
+        # doesn't seem to work.
+        run: |
+          rm -rf $(bazel info repository_cache)
+          rm -rf ~/.cache/bazel/*/*/external/
+        shell: bash
diff --git a/Android.bp b/Android.bp
index 17db0d7..61b133d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -45,7 +45,6 @@
     ],
     license_text: [
         "LICENSE.txt",
-        "NOTICE",
     ],
 }
 
@@ -85,9 +84,18 @@
         "com.android.adservices",
         "com.android.extservices",
         "com.android.ondevicepersonalization",
+        "com.android.healthfitness",
+        "com.android.devicelock",
     ],
 
     sdk_version: "core_current",
+
+    errorprone: {
+        javacflags: [
+            "-Xep:FormatStringAnnotation:WARN",
+            "-Xep:NoCanIgnoreReturnValueOnClasses:WARN",
+        ],
+    },
 }
 
 // build dagger2 producers library
@@ -126,6 +134,16 @@
     jarjar_rules: "jarjar-rules.txt",
 }
 
+// Dagger distributes its own copy of androidx.room.compiler.processing
+// while the API is unstable.  There are shading rules in jarjar-rules.txt
+// to prevent conflicts with official version.  When this is removed
+// in favor of the official version the shading rules should also be
+// removed.
+java_import_host {
+    name: "dagger2-room-compiler-processing",
+    jars: ["java/dagger/internal/codegen/xprocessing/xprocessing.jar"],
+}
+
 java_library_host {
     name: "dagger2-compiler-lib",
     use_tools_jar: true,
@@ -136,6 +154,7 @@
 
         "java/dagger/model/*.java",
         "java/dagger/spi/*.java",
+        "java/dagger/spi/model/*.java",
     ],
 
     exclude_srcs: [
@@ -151,11 +170,13 @@
         "auto_common",
         "dagger2",
         "dagger2-producers",
+        "dagger2-room-compiler-processing",
         "google_java_format",
         "guava",
         "javapoet",
         "jsr330",
         "kotlin-stdlib",
+        "kotlin-stdlib-jdk8",
         "kotlinx_metadata_jvm",
     ],
 
@@ -193,6 +214,13 @@
         "--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
         "--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
     ],
+
+    errorprone: {
+        javacflags: [
+            "-Xep:FormatStringAnnotation:WARN",
+            "-Xep:NoCanIgnoreReturnValueOnClasses:WARN",
+        ],
+    },
 }
 
 // Compile  dummy implementations of annotations used by dagger2 but not
@@ -230,7 +258,9 @@
         "hilt_generates_root_input_processor",
     ],
     apex_available: [
+        "//apex_available:platform",
         "com.android.ondevicepersonalization",
+        "com.android.healthfitness",
     ],
 }
 
@@ -242,18 +272,22 @@
 
     srcs: [
         "java/dagger/hilt/android/*.java",
+        "java/dagger/hilt/android/*.kt",
         "java/dagger/hilt/android/components/*.java",
+        "java/dagger/hilt/android/flags/*.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/legacy/*.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",
         "java/dagger/hilt/internal/aggregatedroot/*.java",
+        "java/dagger/hilt/internal/componenttreedeps/*.java",
         "java/dagger/hilt/internal/processedrootsentinel/*.java",
     ],
     manifest: "java/dagger/hilt/android/AndroidManifest.xml",
@@ -282,17 +316,22 @@
     ],
     exported_plugins: [
         "dagger2-compiler",
+
         "hilt_android_entry_point_processor",
         "hilt_aggregated_deps_processor",
         "hilt_alias_of_processor",
+        "hilt_component_tree_deps_processor",
         "hilt_define_component_processor",
+        "hilt_early_entry_point_processor",
         "hilt_generates_root_input_processor",
         "hilt_originating_element_processor",
         "hilt_root_processor",
         "hilt_viewmodel_processor",
     ],
     apex_available: [
+        "//apex_available:platform",
         "com.android.ondevicepersonalization",
+        "com.android.healthfitness",
     ],
 }
 
@@ -302,7 +341,10 @@
 
     srcs: [
         "java/dagger/hilt/android/internal/testing/*.java",
+        "java/dagger/hilt/android/internal/testing/root/*.java",
+        "java/dagger/hilt/android/internal/uninstallmodules/*.java",
         "java/dagger/hilt/android/testing/*.java",
+        "java/dagger/hilt/testing/*.java",
     ],
     manifest: "java/dagger/hilt/android/testing/AndroidManifest.xml",
     static_libs: [
@@ -315,6 +357,7 @@
         "android-support-multidex",
         "jsr305",
         "dagger2",
+        "hilt_android",
         "hilt_core",
         "junit",
     ],
@@ -327,13 +370,18 @@
     ],
     exported_plugins: [
         "dagger2-compiler",
+
         "hilt_android_entry_point_processor",
         "hilt_aggregated_deps_processor",
+        "hilt_alias_of_processor",
+        "hilt_component_tree_deps_processor",
         "hilt_define_component_processor",
+        "hilt_early_entry_point_processor",
         "hilt_generates_root_input_processor",
         "hilt_originating_element_processor",
         "hilt_root_processor",
-	"hilt_viewmodel_processor",
+        "hilt_viewmodel_processor",
+
         "hilt_custom_test_application_processor",
         "hilt_bindvalue_processor",
         "hilt_uninstall_modules_processor",
@@ -370,12 +418,24 @@
 }
 
 java_plugin {
+    name: "hilt_component_tree_deps_processor",
+    generates_api: true,
+    processor_class: "dagger.hilt.processor.internal.root.ComponentTreeDepsProcessor",
+}
+
+java_plugin {
     name: "hilt_define_component_processor",
     generates_api: true,
     processor_class: "dagger.hilt.processor.internal.definecomponent.DefineComponentProcessor",
 }
 
 java_plugin {
+    name: "hilt_early_entry_point_processor",
+    generates_api: true,
+    processor_class: "dagger.hilt.processor.internal.earlyentrypoint.EarlyEntryPointProcessor",
+}
+
+java_plugin {
     name: "hilt_originating_element_processor",
     generates_api: true,
     processor_class: "dagger.hilt.processor.internal.originatingelement.OriginatingElementProcessor",
@@ -420,6 +480,7 @@
         "java/dagger/hilt/android/processor/**/*.kt",
         "java/dagger/hilt/codegen/*.java",
         "java/dagger/hilt/processor/internal/**/*.java",
+        "java/dagger/hilt/processor/internal/**/*.kt",
     ],
     plugins: [
         "auto_service_plugin",
diff --git a/BUILD b/BUILD
index b2495d7..8692ea1 100644
--- a/BUILD
+++ b/BUILD
@@ -79,7 +79,7 @@
     name = "shaded_android_processor",
     jars = [
         "//java/dagger/android/processor",
-        "@maven//:com_google_auto_auto_common",
+        "//third_party/java/auto:common",
     ],
     rules = [
         "rule com.google.auto.common.** dagger.android.shaded.auto.common.@1",
@@ -90,7 +90,7 @@
     name = "shaded_grpc_server_processor",
     jars = [
         "//java/dagger/grpc/server/processor",
-        "@maven//:com_google_auto_auto_common",
+        "//third_party/java/auto:common",
     ],
     rules = [
         "rule com.google.auto.common.** dagger.grpc.shaded.auto.common.@1",
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 54fb50a..f0748e0 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -28,6 +28,10 @@
 
 Dagger is built with [`bazel`](https://bazel.build).
 
+Ensure that Dagger is checked out on a case-sensitive filesystem. On a
+case-insensitive file system (e.g. Windows or MacOS by default) some tasks that
+attempt to delete the `build/` folder will also delete the bazel `BUILD` files.
+
 ### Building Dagger from the command line
 
 *   [Install Bazel](https://docs.bazel.build/versions/master/install.html)
diff --git a/LICENSE b/LICENSE
new file mode 120000
index 0000000..85de3d4
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1 @@
+LICENSE.txt
\ No newline at end of file
diff --git a/METADATA b/METADATA
index 5eef5c3..c9c7925 100644
--- a/METADATA
+++ b/METADATA
@@ -11,7 +11,7 @@
     type: GIT
     value: "https://github.com/google/dagger"
   }
-  version: "dagger-2.19.1"
-  last_upgrade_date { year: 2020 month: 11 day: 18 }
+  version: "dagger-2.41"
+  last_upgrade_date { year: 2022 month: 04 day: 04 }
   license_type: NOTICE
 }
diff --git a/NOTICE b/NOTICE
deleted file mode 100644
index d645695..0000000
--- a/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/OWNERS b/OWNERS
index 87a5dbe..fcafdd6 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1 +1,3 @@
 include platform/libcore:/OWNERS
+
+ccross@android.com
diff --git a/README.md b/README.md
index 393e2e7..fce9032 100644
--- a/README.md
+++ b/README.md
@@ -39,8 +39,8 @@
 
 load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
 
-DAGGER_TAG = "2.28.1"
-DAGGER_SHA = "9e69ab2f9a47e0f74e71fe49098bea908c528aa02fa0c5995334447b310d0cdd"
+DAGGER_TAG = "2.40.5"
+DAGGER_SHA = "5a6923e56edbc1e34c8089ecab5338a1b8ddb79a3a54b6c86cdcf31212680d32"
 http_archive(
     name = "dagger",
     strip_prefix = "dagger-dagger-%s" % DAGGER_TAG,
diff --git a/WORKSPACE b/WORKSPACE
index d49c0e2..d37a5a3 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -15,6 +15,39 @@
 load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
 
 #############################
+# Upgrade java_tools version
+#############################
+
+# These targets added per instructions at
+# https://github.com/bazelbuild/java_tools/releases/tag/javac11_v10.7
+http_archive(
+    name = "remote_java_tools_linux",
+    sha256 = "cf57fc238ed5c24c718436ab4178ade5eb838fe56e7c32c4fafe0b6fbdaec51f",
+    urls = [
+        "https://mirror.bazel.build/bazel_java_tools/releases/javac11/v10.7/java_tools_javac11_linux-v10.7.zip",
+        "https://github.com/bazelbuild/java_tools/releases/download/javac11_v10.7/java_tools_javac11_linux-v10.7.zip",
+    ],
+)
+
+http_archive(
+    name = "remote_java_tools_windows",
+    sha256 = "a0fc3a3be3ea01a4858d12f56892dd663c02f218104e8c1dc9f3e90d5e583bcb",
+    urls = [
+        "https://mirror.bazel.build/bazel_java_tools/releases/javac11/v10.7/java_tools_javac11_windows-v10.7.zip",
+        "https://github.com/bazelbuild/java_tools/releases/download/javac11_v10.7/java_tools_javac11_windows-v10.7.zip",
+    ],
+)
+
+http_archive(
+    name = "remote_java_tools_darwin",
+    sha256 = "51a4cf424d3b26d6c42703cf2d80002f1489ba0d28c939519c3bb9c3d6ee3720",
+    urls = [
+        "https://mirror.bazel.build/bazel_java_tools/releases/javac11/v10.7/java_tools_javac11_darwin-v10.7.zip",
+        "https://github.com/bazelbuild/java_tools/releases/download/javac11_v10.7/java_tools_javac11_darwin-v10.7.zip",
+    ],
+)
+
+#############################
 # Load nested repository
 #############################
 
@@ -31,9 +64,9 @@
 
 http_archive(
     name = "google_bazel_common",
-    sha256 = "d8aa0ef609248c2a494d5dbdd4c89ef2a527a97c5a87687e5a218eb0b77ff640",
-    strip_prefix = "bazel-common-4a8d451e57fb7e1efecbf9495587a10684a19eb2",
-    urls = ["https://github.com/google/bazel-common/archive/4a8d451e57fb7e1efecbf9495587a10684a19eb2.zip"],
+    sha256 = "8b6aebdc095c8448b2f6a72bb8eae4a563891467e2d20c943f21940b1c444e38",
+    strip_prefix = "bazel-common-3d0e5005cfcbee836e31695d4ab91b5328ccc506",
+    urls = ["https://github.com/google/bazel-common/archive/3d0e5005cfcbee836e31695d4ab91b5328ccc506.zip"],
 )
 
 load("@google_bazel_common//:workspace_defs.bzl", "google_common_workspace_rules")
@@ -87,36 +120,41 @@
 # Load Kotlin repository
 #############################
 
-RULES_KOTLIN_COMMIT = "2c283821911439e244285b5bfec39148e7d90e21"
+RULES_KOTLIN_COMMIT = "686f0f1cf3e1cc8c750688bb082316b3eadb3cb6"
 
-RULES_KOTLIN_SHA = "b04cd539e7e3571745179da95069586b6fa76a64306b24bb286154e652010608"
+RULES_KOTLIN_SHA = "1d8758bbf27400a5f9d40f01e4337f6834d2b7864df34e9aa5cf0a9ab6cc9241"
 
 http_archive(
-    name = "io_bazel_rules_kotlin",
+    name = "io_bazel_rules_kotlin_head",
     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")
+load("@io_bazel_rules_kotlin_head//src/main/starlark/release_archive:repository.bzl", "archive_repository")
 
-kt_download_local_dev_dependencies()
+archive_repository(
+    name = "io_bazel_rules_kotlin",
+    source_repository_name = "io_bazel_rules_kotlin_head",
+)
 
-load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kotlin_repositories")
+load("@io_bazel_rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories", "kotlinc_version")
 
-KOTLIN_VERSION = "1.4.20"
+KOTLIN_VERSION = "1.5.32"
 
-KOTLINC_RELEASE_SHA = "11db93a4d6789e3406c7f60b9f267eba26d6483dcd771eff9f85bb7e9837011f"
+KOTLINC_RELEASE_SHA = "2e728c43ee0bf819eae06630a4cbbc28ba2ed5b19a55ee0af96d2c0ab6b6c2a5"
 
-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_version(
+        release = KOTLIN_VERSION,
+        sha256 = KOTLINC_RELEASE_SHA,
+    ),
+)
 
-kotlin_repositories(compiler_release = KOTLINC_RELEASE)
+load("@io_bazel_rules_kotlin//kotlin:core.bzl", "kt_register_toolchains")
 
-register_toolchains("//:kotlin_toolchain")
+kt_register_toolchains()
 
 #############################
 # Load Maven dependencies
@@ -135,23 +173,39 @@
 
 load("@rules_jvm_external//:defs.bzl", "maven_install")
 
-ANDROID_LINT_VERSION = "26.6.2"
+ANDROID_LINT_VERSION = "30.1.0"
+
+AUTO_COMMON_VERSION = "1.2.1"
+
+# NOTE(bcorso): Even though we set the version here, our Guava version in
+#  processor code will use whatever version is built into JavaBuilder, which is
+#  tied to the version of Bazel we're using.
+GUAVA_VERSION = "27.1"
+
+GRPC_VERSION = "1.2.0"
+
+INCAP_VERSION = "0.2"
+
+BYTE_BUDDY_VERSION = "1.9.10"
+
+CHECKER_FRAMEWORK_VERSION = "2.5.3"
+
+ERROR_PRONE_VERSION = "2.3.2"
 
 maven_install(
     artifacts = [
         "androidx.annotation:annotation:1.1.0",
-        "androidx.appcompat:appcompat:1.2.0",
-        "androidx.activity:activity:1.2.2",
-        "androidx.fragment:fragment:1.3.2",
+        "androidx.appcompat:appcompat:1.3.1",
+        "androidx.activity:activity:1.3.1",
+        "androidx.fragment:fragment:1.3.6",
         "androidx.lifecycle:lifecycle-common:2.3.1",
         "androidx.lifecycle:lifecycle-viewmodel:2.3.1",
         "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.1",
         "androidx.multidex:multidex:2.0.1",
         "androidx.savedstate:savedstate:1.0.0",
-        "androidx.test:monitor:1.1.1",
-        "androidx.test:core:1.1.0",
-        "androidx.test.ext:junit:1.1.2",
-        "com.google.auto:auto-common:0.11",
+        "androidx.test:monitor:1.4.0",
+        "androidx.test:core:1.4.0",
+        "androidx.test.ext:junit:1.1.3",
         "com.android.support:appcompat-v7:25.0.0",
         "com.android.support:support-annotations:25.0.0",
         "com.android.support:support-fragment:25.0.0",
@@ -164,17 +218,54 @@
         "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",
+        "com.google.auto:auto-common:%s" % AUTO_COMMON_VERSION,
+        "com.google.auto.factory:auto-factory:1.0",
+        "com.google.auto.service:auto-service:1.0",
+        "com.google.auto.service:auto-service-annotations:1.0",
+        "com.google.auto.value:auto-value:1.6",
+        "com.google.auto.value:auto-value-annotations:1.6",
+        "com.google.code.findbugs:jsr305:3.0.1",
+        "com.google.devtools.ksp:symbol-processing-api:1.5.30-1.0.0",
+        "com.google.errorprone:error_prone_annotation:%s" % ERROR_PRONE_VERSION,
+        "com.google.errorprone:error_prone_annotations:%s" % ERROR_PRONE_VERSION,
+        "com.google.errorprone:error_prone_check_api:%s" % ERROR_PRONE_VERSION,
+        "com.google.googlejavaformat:google-java-format:1.5",
+        "com.google.guava:guava:%s-jre" % GUAVA_VERSION,
+        "com.google.guava:guava-testlib:%s-jre" % GUAVA_VERSION,
+        "com.google.guava:failureaccess:1.0.1",
+        "com.google.guava:guava-beta-checker:1.0",
+        "com.google.protobuf:protobuf-java:3.7.0",
+        "com.google.testing.compile:compile-testing:0.18",
+        "com.google.truth:truth:1.1",
+        "com.squareup:javapoet:1.13.0",
+        "io.grpc:grpc-context:%s" % GRPC_VERSION,
+        "io.grpc:grpc-core:%s" % GRPC_VERSION,
+        "io.grpc:grpc-netty:%s" % GRPC_VERSION,
+        "io.grpc:grpc-protobuf:%s" % GRPC_VERSION,
+        "jakarta.inject:jakarta.inject-api:2.0.1",
+        "javax.annotation:jsr250-api:1.0",
+        "javax.inject:javax.inject:1",
+        "javax.inject:javax.inject-tck:1",
         "junit:junit:4.13",
+        "net.bytebuddy:byte-buddy:%s" % BYTE_BUDDY_VERSION,
+        "net.bytebuddy:byte-buddy-agent:%s" % BYTE_BUDDY_VERSION,
+        "net.ltgt.gradle.incap:incap:%s" % INCAP_VERSION,
+        "net.ltgt.gradle.incap:incap-processor:%s" % INCAP_VERSION,
+        "org.checkerframework:checker-compat-qual:%s" % CHECKER_FRAMEWORK_VERSION,
+        "org.checkerframework:dataflow:%s" % CHECKER_FRAMEWORK_VERSION,
+        "org.checkerframework:javacutil:%s" % CHECKER_FRAMEWORK_VERSION,
+        "org.hamcrest:hamcrest-core:1.3",
         "org.jetbrains.kotlin:kotlin-stdlib:%s" % KOTLIN_VERSION,
-        "org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.2.0",
+        "org.jetbrains.kotlin:kotlin-stdlib-jdk8:%s" % KOTLIN_VERSION,
+        "org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.3.0",
+        "org.mockito:mockito-core:2.28.2",
+        "org.objenesis:objenesis:1.0",
         "org.robolectric:robolectric:4.4",
         "org.robolectric:shadows-framework:4.4",  # For ActivityController
     ],
     repositories = [
         "https://repo1.maven.org/maven2",
         "https://maven.google.com",
-        "https://jcenter.bintray.com/",  # Lint has one trove4j dependency in jCenter
     ],
 )
 
diff --git a/android-annotation-stubs/gen_annotations.sh b/android-annotation-stubs/gen_annotations.sh
index d4a0290..1aa5d27 100755
--- a/android-annotation-stubs/gen_annotations.sh
+++ b/android-annotation-stubs/gen_annotations.sh
@@ -58,9 +58,14 @@
  */
 
 package net.ltgt.gradle.incap;
+import java.util.Locale;
 public enum IncrementalAnnotationProcessorType {
   AGGREGATING,
   DYNAMIC,
-  ISOLATING
+  ISOLATING;
+
+  public String getProcessorOption() {
+    return "org.gradle.annotation.processing." + name().toLowerCase(Locale.ROOT);
+  }
 }
 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 ef86328..55a3a5e 100644
--- a/android-annotation-stubs/src/net/ltgt/gradle/incap/IncrementalAnnotationProcessorType.java
+++ b/android-annotation-stubs/src/net/ltgt/gradle/incap/IncrementalAnnotationProcessorType.java
@@ -15,8 +15,13 @@
  */
 
 package net.ltgt.gradle.incap;
+import java.util.Locale;
 public enum IncrementalAnnotationProcessorType {
   AGGREGATING,
   DYNAMIC,
-  ISOLATING
+  ISOLATING;
+
+  public String getProcessorOption() {
+    return "org.gradle.annotation.processing." + name().toLowerCase(Locale.ROOT);
+  }
 }
diff --git a/examples/bazel/WORKSPACE b/examples/bazel/WORKSPACE
index 23f93b1..6a1197f 100644
--- a/examples/bazel/WORKSPACE
+++ b/examples/bazel/WORKSPACE
@@ -19,7 +19,9 @@
 # Load Dagger repository
 ########################
 
-# TODO(bcorso): Replace with `http_archive` pointing to tagged released.
+# In a real project, this repository would use `http_archive` to link to a
+# tagged, released version of the Dagger, but we use `local_repository` so that
+# CI testing can test local changes to workspace_defs.bzl.
 local_repository(
     name = "dagger",
     path = "../../",
diff --git a/jarjar-rules.txt b/jarjar-rules.txt
index 57941b3..abb7fb2 100644
--- a/jarjar-rules.txt
+++ b/jarjar-rules.txt
@@ -1,3 +1,6 @@
 # shade guava to avoid conflicts with guava embedded in Error Prone.
 rule com.google.common.** com.google.dagger.common.@1
 rule com.google.auto.** com.google.dagger.auto.@1
+
+# shade local xprocessing.jar to avoid conflicts with upstream Xprocessing
+rule androidx.room.compiler.processing.** dagger.spi.shaded.androidx.room.compiler.processing.@1
diff --git a/java/dagger/BUILD b/java/dagger/BUILD
index aaebade..17c6c96 100644
--- a/java/dagger/BUILD
+++ b/java/dagger/BUILD
@@ -32,9 +32,9 @@
     srcs = glob(["**/*.java"]),
     javacopts = SOURCE_7_TARGET_7 + DOCLINT_HTML_AND_SYNTAX,
     tags = ["maven_coordinates=com.google.dagger:dagger:" + POM_VERSION],
-    exports = ["@google_bazel_common//third_party/java/jsr330_inject"],
+    exports = ["//third_party/java/jsr330_inject"],
     deps = [
-        "@google_bazel_common//third_party/java/jsr330_inject",
+        "//third_party/java/jsr330_inject",
     ],
 )
 
@@ -55,5 +55,5 @@
     srcs = [":javadoc-srcs"],
     exclude_packages = ["dagger.internal"],
     root_packages = ["dagger"],
-    deps = ["@google_bazel_common//third_party/java/jsr330_inject"],
+    deps = ["//third_party/java/jsr330_inject"],
 )
diff --git a/java/dagger/Component.java b/java/dagger/Component.java
index 709b0e6..be746e8 100644
--- a/java/dagger/Component.java
+++ b/java/dagger/Component.java
@@ -35,14 +35,16 @@
  * example, {@code @Component interface MyComponent {...}} will produce an implementation named
  * {@code DaggerMyComponent}.
  *
- * <a name="component-methods"></a>
+ * <p><a id="component-methods"></a>
+ *
  * <h2>Component methods</h2>
  *
  * <p>Every type annotated with {@code @Component} must contain at least one abstract component
  * method. Component methods may have any name, but must have signatures that conform to either
  * {@linkplain Provider provision} or {@linkplain MembersInjector members-injection} contracts.
  *
- * <a name="provision-methods"></a>
+ * <p><a id="provision-methods"></a>
+ *
  * <h3>Provision methods</h3>
  *
  * <p>Provision methods have no parameters and return an {@link Inject injected} or {@link Provides
@@ -68,7 +70,8 @@
  *   {@literal Lazy<SomeType>} getLazySomeType();
  * </code></pre>
  *
- * <a name="members-injection-methods"></a>
+ * <a id="members-injection-methods"></a>
+ *
  * <h3>Members-injection methods</h3>
  *
  * <p>Members-injection methods have a single parameter and inject dependencies into each of the
@@ -111,7 +114,8 @@
  *   }
  * </code></pre>
  *
- * <a name="instantiation"></a>
+ * <a id="instantiation"></a>
+ *
  * <h2>Instantiation</h2>
  *
  * <p>Component implementations are primarily instantiated via a generated <a
@@ -131,17 +135,17 @@
  * <p>Example of using a builder:
  *
  * <pre>{@code
- *   public static void main(String[] args) {
- *     OtherComponent otherComponent = ...;
- *     MyComponent component = DaggerMyComponent.builder()
- *         // required because component dependencies must be set
- *         .otherComponent(otherComponent)
- *         // required because FlagsModule has constructor parameters
- *         .flagsModule(new FlagsModule(args))
- *         // may be elided because a no-args constructor is visible
- *         .myApplicationModule(new MyApplicationModule())
- *         .build();
- *   }
+ * public static void main(String[] args) {
+ *   OtherComponent otherComponent = ...;
+ *   MyComponent component = DaggerMyComponent.builder()
+ *       // required because component dependencies must be set
+ *       .otherComponent(otherComponent)
+ *       // required because FlagsModule has constructor parameters
+ *       .flagsModule(new FlagsModule(args))
+ *       // may be elided because a no-args constructor is visible
+ *       .myApplicationModule(new MyApplicationModule())
+ *       .build();
+ * }
  * }</pre>
  *
  * <p>Example of using a factory:
@@ -162,7 +166,8 @@
  * SomeComponent.create()} and {@code SomeComponent.builder().build()} are both valid and
  * equivalent.
  *
- * <a name="scope"></a>
+ * <p><a id="scope"></a>
+ *
  * <h2>Scope</h2>
  *
  * <p>Each Dagger component can be associated with a scope by annotating it with the {@linkplain
@@ -184,14 +189,16 @@
  * self-contained implementations, exiting a scope is as simple as dropping all references to the
  * component instance.
  *
- * <a name="component-relationships"></a>
+ * <p><a id="component-relationships"></a>
+ *
  * <h2>Component relationships</h2>
  *
  * <p>While there is much utility in isolated components with purely unscoped bindings, many
  * applications will call for multiple components with multiple scopes to interact. Dagger provides
  * two mechanisms for relating components.
  *
- * <a name="subcomponents"></a>
+ * <p><a id="subcomponents"></a>
+ *
  * <h3>Subcomponents</h3>
  *
  * <p>The simplest way to relate two components is by declaring a {@link Subcomponent}. A
@@ -219,7 +226,8 @@
  *   }
  * </code></pre>
  *
- * <a name="component-dependencies"></a>
+ * <a id="component-dependencies"></a>
+ *
  * <h3>Component dependencies</h3>
  *
  * <p>While subcomponents are the simplest way to compose subgraphs of bindings, subcomponents are
diff --git a/java/dagger/Module.java b/java/dagger/Module.java
index bd32a1f..a3cdb54 100644
--- a/java/dagger/Module.java
+++ b/java/dagger/Module.java
@@ -23,18 +23,15 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
-/**
- * Annotates a class that contributes to the object graph.
- */
+/** Annotates a class that contributes to the object graph. */
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
 public @interface Module {
   /**
-   * Additional {@code @Module}-annotated classes from which this module is
-   * composed. The de-duplicated contributions of the modules in
-   * {@code includes}, and of their inclusions recursively, are all contributed
-   * to the object graph.
+   * Additional {@code @Module}-annotated classes from which this module is composed. The
+   * de-duplicated contributions of the modules in {@code includes}, and of their inclusions
+   * recursively, are all contributed to the object graph.
    */
   Class<?>[] includes() default {};
 
diff --git a/java/dagger/Provides.java b/java/dagger/Provides.java
index 5d7827b..011f990 100644
--- a/java/dagger/Provides.java
+++ b/java/dagger/Provides.java
@@ -33,14 +33,14 @@
  * <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 androidx.annotation.Nullable}.
+ * annotating the method with any {@code @Nullable} annotation like {@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
- * attempts to pair a {@code @Nullable} provision with a non-{@code @Nullable} injection site
- * will fail to compile.
+ * <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 attempts to pair
+ * a {@code @Nullable} provision with a non-{@code @Nullable} injection site will fail to compile.
  */
-@Documented @Target(METHOD) @Retention(RUNTIME)
-public @interface Provides {
-}
+@Documented
+@Target(METHOD)
+@Retention(RUNTIME)
+public @interface Provides {}
diff --git a/java/dagger/android/BUILD b/java/dagger/android/BUILD
index f0cea41..503a298 100644
--- a/java/dagger/android/BUILD
+++ b/java/dagger/android/BUILD
@@ -54,8 +54,8 @@
     ],
     deps = [
         "//:dagger_with_compiler",
-        "@google_bazel_common//third_party/java/auto:value",
-        "@google_bazel_common//third_party/java/error_prone:annotations",
+        "//third_party/java/auto:value",
+        "//third_party/java/error_prone:annotations",
         "@maven//:androidx_annotation_annotation",
     ],
 )
@@ -68,14 +68,6 @@
     targets = [":android"],
 )
 
-# b/37741866 and https://github.com/google/dagger/issues/715
-pom_file(
-    name = "jarimpl-pom",
-    artifact_id = "dagger-android-jarimpl",
-    artifact_name = "Dagger Android",
-    targets = [":android"],
-)
-
 dejetified_library(
     name = "dejetified-android",
     input = ":android.aar",
@@ -87,8 +79,8 @@
     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",
+        "//third_party/java/auto:value",
+        "//third_party/java/error_prone:annotations",
         "@maven//:com_android_support_support_annotations",
     ],
 )
diff --git a/java/dagger/android/internal/proguard/BUILD b/java/dagger/android/internal/proguard/BUILD
index a11d0c0..5a85279 100644
--- a/java/dagger/android/internal/proguard/BUILD
+++ b/java/dagger/android/internal/proguard/BUILD
@@ -25,7 +25,7 @@
     srcs = ["ProguardProcessor.java"],
     javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
     deps = [
-        "@google_bazel_common//third_party/java/auto:service",
+        "//third_party/java/auto:service",
     ],
 )
 
diff --git a/java/dagger/android/processor/AndroidInjectorDescriptor.java b/java/dagger/android/processor/AndroidInjectorDescriptor.java
index 3ec6613..0b9d74b 100644
--- a/java/dagger/android/processor/AndroidInjectorDescriptor.java
+++ b/java/dagger/android/processor/AndroidInjectorDescriptor.java
@@ -16,10 +16,8 @@
 
 package dagger.android.processor;
 
-import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
 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 dagger.internal.codegen.langmodel.DaggerElements.getAnnotatedAnnotations;
 import static java.util.stream.Collectors.toList;
 import static javax.lang.model.element.Modifier.ABSTRACT;
 
@@ -27,16 +25,13 @@
 import com.google.auto.common.MoreTypes;
 import com.google.auto.value.AutoValue;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
 import com.squareup.javapoet.AnnotationSpec;
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.TypeName;
-import dagger.Module;
-import dagger.android.ContributesAndroidInjector;
 import java.util.List;
 import java.util.Optional;
 import javax.annotation.processing.Messager;
-import javax.inject.Qualifier;
-import javax.inject.Scope;
 import javax.lang.model.element.AnnotationMirror;
 import javax.lang.model.element.AnnotationValue;
 import javax.lang.model.element.Element;
@@ -47,24 +42,24 @@
 import javax.tools.Diagnostic.Kind;
 
 /**
- * A descriptor of a generated {@link Module} and {@link dagger.Subcomponent} to be generated from a
- * {@link ContributesAndroidInjector} method.
+ * A descriptor of a generated {@link dagger.Module} and {@link dagger.Subcomponent} to be generated
+ * from a {@code ContributesAndroidInjector} method.
  */
 @AutoValue
 abstract class AndroidInjectorDescriptor {
-  /** The type to be injected; the return type of the {@link ContributesAndroidInjector} method. */
+  /** The type to be injected; the return type of the {@code ContributesAndroidInjector} method. */
   abstract ClassName injectedType();
 
   /** Scopes to apply to the generated {@link dagger.Subcomponent}. */
   abstract ImmutableSet<AnnotationSpec> scopes();
 
-  /** @see ContributesAndroidInjector#modules() */
+  /** See {@code ContributesAndroidInjector#modules()} */
   abstract ImmutableSet<ClassName> modules();
 
-  /** The {@link Module} that contains the {@link ContributesAndroidInjector} method. */
+  /** The {@link dagger.Module} that contains the {@code ContributesAndroidInjector} method. */
   abstract ClassName enclosingModule();
 
-  /** The method annotated with {@link ContributesAndroidInjector}. */
+  /** The method annotated with {@code ContributesAndroidInjector}. */
   abstract ExecutableElement method();
 
   @AutoValue.Builder
@@ -90,7 +85,7 @@
     }
 
     /**
-     * Validates a {@link ContributesAndroidInjector} method, returning an {@link
+     * Validates a {@code ContributesAndroidInjector} method, returning an {@link
      * AndroidInjectorDescriptor} if it is valid, or {@link Optional#empty()} otherwise.
      */
     Optional<AndroidInjectorDescriptor> createIfValid(ExecutableElement method) {
@@ -107,7 +102,7 @@
       AndroidInjectorDescriptor.Builder builder =
           new AutoValue_AndroidInjectorDescriptor.Builder().method(method);
       TypeElement enclosingElement = MoreElements.asType(method.getEnclosingElement());
-      if (!isAnnotationPresent(enclosingElement, Module.class)) {
+      if (!MoreDaggerElements.isAnnotationPresent(enclosingElement, TypeNames.MODULE)) {
         reporter.reportError("@ContributesAndroidInjector methods must be in a @Module");
       }
       builder.enclosingModule(ClassName.get(enclosingElement));
@@ -121,21 +116,26 @@
       }
 
       AnnotationMirror annotation =
-          getAnnotationMirror(method, ContributesAndroidInjector.class).get();
+          MoreDaggerElements.getAnnotationMirror(method, TypeNames.CONTRIBUTES_ANDROID_INJECTOR)
+              .get();
       for (TypeMirror module :
           getAnnotationValue(annotation, "modules").accept(new AllTypesVisitor(), null)) {
-        if (isAnnotationPresent(MoreTypes.asElement(module), Module.class)) {
+        if (MoreDaggerElements.isAnnotationPresent(MoreTypes.asElement(module), TypeNames.MODULE)) {
           builder.modulesBuilder().add((ClassName) TypeName.get(module));
         } else {
           reporter.reportError(String.format("%s is not a @Module", module), annotation);
         }
       }
 
-      for (AnnotationMirror scope : getAnnotatedAnnotations(method, Scope.class)) {
+      for (AnnotationMirror scope : Sets.union(
+          getAnnotatedAnnotations(method, TypeNames.SCOPE),
+          getAnnotatedAnnotations(method, TypeNames.SCOPE_JAVAX))) {
         builder.scopesBuilder().add(AnnotationSpec.get(scope));
       }
 
-      for (AnnotationMirror qualifier : getAnnotatedAnnotations(method, Qualifier.class)) {
+      for (AnnotationMirror qualifier : Sets.union(
+          getAnnotatedAnnotations(method, TypeNames.QUALIFIER),
+          getAnnotatedAnnotations(method, TypeNames.QUALIFIER_JAVAX))) {
         reporter.reportError(
             "@ContributesAndroidInjector methods cannot have qualifiers", qualifier);
       }
diff --git a/java/dagger/android/processor/AndroidMapKeyValidator.java b/java/dagger/android/processor/AndroidMapKeyValidator.java
index f6e808a..0213526 100644
--- a/java/dagger/android/processor/AndroidMapKeyValidator.java
+++ b/java/dagger/android/processor/AndroidMapKeyValidator.java
@@ -17,25 +17,19 @@
 package dagger.android.processor;
 
 import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
-import static com.google.auto.common.MoreElements.getAnnotationMirror;
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
 import static com.google.common.collect.Iterables.getOnlyElement;
 import static dagger.android.processor.AndroidMapKeys.injectedTypeFromMapKey;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotatedAnnotations;
 
-import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
+import com.google.auto.common.BasicAnnotationProcessor.Step;
 import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.SetMultimap;
-import dagger.Binds;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Sets;
+import com.squareup.javapoet.ClassName;
 import dagger.MapKey;
-import dagger.android.AndroidInjectionKey;
-import dagger.android.AndroidInjector;
-import dagger.multibindings.ClassKey;
-import java.lang.annotation.Annotation;
-import java.util.Set;
 import javax.annotation.processing.Messager;
-import javax.inject.Qualifier;
-import javax.inject.Scope;
 import javax.lang.model.element.AnnotationMirror;
 import javax.lang.model.element.Element;
 import javax.lang.model.element.ExecutableElement;
@@ -46,8 +40,13 @@
 import javax.lang.model.util.Types;
 import javax.tools.Diagnostic.Kind;
 
-/** Validates the correctness of {@link MapKey}s used with {@code dagger.android}. */
-final class AndroidMapKeyValidator implements ProcessingStep {
+/** Validates the correctness of {@link dagger.MapKey}s used with {@code dagger.android}. */
+final class AndroidMapKeyValidator implements Step {
+  private static final ImmutableMap<String, ClassName> SUPPORTED_ANNOTATIONS =
+      ImmutableMap.of(
+          TypeNames.ANDROID_INJECTION_KEY.toString(), TypeNames.ANDROID_INJECTION_KEY,
+          TypeNames.CLASS_KEY.toString(), TypeNames.CLASS_KEY);
+
   private final Elements elements;
   private final Types types;
   private final Messager messager;
@@ -59,16 +58,12 @@
   }
 
   @Override
-  public Set<? extends Class<? extends Annotation>> annotations() {
-    return ImmutableSet.<Class<? extends Annotation>>builder()
-        .add(AndroidInjectionKey.class)
-        .add(ClassKey.class)
-        .build();
+  public ImmutableSet<String> annotations() {
+    return SUPPORTED_ANNOTATIONS.keySet();
   }
 
   @Override
-  public Set<Element> process(
-      SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
+  public ImmutableSet<Element> process(ImmutableSetMultimap<String, Element> elementsByAnnotation) {
     ImmutableSet.Builder<Element> deferredElements = ImmutableSet.builder();
     elementsByAnnotation
         .entries()
@@ -83,8 +78,9 @@
     return deferredElements.build();
   }
 
-  private void validateMethod(Class<? extends Annotation> annotation, ExecutableElement method) {
-    if (!getAnnotatedAnnotations(method, Qualifier.class).isEmpty()) {
+  private void validateMethod(String annotation, ExecutableElement method) {
+    if (!Sets.union(getAnnotatedAnnotations(method, TypeNames.QUALIFIER),
+        getAnnotatedAnnotations(method, TypeNames.QUALIFIER_JAVAX)).isEmpty()) {
       return;
     }
 
@@ -94,7 +90,8 @@
       return;
     }
 
-    if (!getAnnotatedAnnotations(method, Scope.class).isEmpty()) {
+    if (!Sets.union(getAnnotatedAnnotations(method, TypeNames.SCOPE),
+          getAnnotatedAnnotations(method, TypeNames.SCOPE_JAVAX)).isEmpty()) {
       SuppressWarnings suppressedWarnings = method.getAnnotation(SuppressWarnings.class);
       if (suppressedWarnings == null
           || !ImmutableSet.copyOf(suppressedWarnings.value())
@@ -107,7 +104,7 @@
             Kind.ERROR,
             String.format(
                 "%s bindings should not be scoped. Scoping this method may leak instances of %s.",
-                AndroidInjector.Factory.class.getCanonicalName(),
+                TypeNames.ANDROID_INJECTOR_FACTORY.canonicalName(),
                 mapKeyValueElement.getQualifiedName()),
             method);
       }
@@ -117,7 +114,8 @@
 
     // @Binds methods should only have one parameter, but we can't guarantee the order of Processors
     // in javac, so do a basic check for valid form
-    if (isAnnotationPresent(method, Binds.class) && method.getParameters().size() == 1) {
+    if (MoreDaggerElements.isAnnotationPresent(method, TypeNames.BINDS)
+        && method.getParameters().size() == 1) {
       validateMapKeyMatchesBindsParameter(annotation, method);
     }
   }
@@ -138,10 +136,10 @@
   }
 
   /**
-   * A valid @Binds method could bind an {@link AndroidInjector.Factory} for one type, while giving
+   * A valid @Binds method could bind an {@code AndroidInjector.Factory} for one type, while giving
    * it a map key of a different type. The return type and parameter type would pass typical @Binds
-   * validation, but the map lookup in {@link dagger.android.DispatchingAndroidInjector} would
-   * retrieve the wrong injector factory.
+   * validation, but the map lookup in {@code DispatchingAndroidInjector} would retrieve the wrong
+   * injector factory.
    *
    * <pre>{@code
    * {@literal @Binds}
@@ -151,10 +149,10 @@
    *     BlueActivityComponent.Builder builder);
    * }</pre>
    */
-  private void validateMapKeyMatchesBindsParameter(
-      Class<? extends Annotation> annotation, ExecutableElement method) {
+  private void validateMapKeyMatchesBindsParameter(String annotation, ExecutableElement method) {
     TypeMirror parameterType = getOnlyElement(method.getParameters()).asType();
-    AnnotationMirror annotationMirror = getAnnotationMirror(method, annotation).get();
+    AnnotationMirror annotationMirror =
+        MoreDaggerElements.getAnnotationMirror(method, SUPPORTED_ANNOTATIONS.get(annotation)).get();
     TypeMirror mapKeyType =
         elements.getTypeElement(injectedTypeFromMapKey(annotationMirror).get()).asType();
     if (!types.isAssignable(parameterType, injectorFactoryOf(mapKeyType))) {
@@ -172,6 +170,6 @@
   }
 
   private TypeElement factoryElement() {
-    return elements.getTypeElement(AndroidInjector.Factory.class.getCanonicalName());
+    return elements.getTypeElement(TypeNames.ANDROID_INJECTOR_FACTORY.canonicalName());
   }
 }
diff --git a/java/dagger/android/processor/AndroidMapKeys.java b/java/dagger/android/processor/AndroidMapKeys.java
index fb1fc38..28da271 100644
--- a/java/dagger/android/processor/AndroidMapKeys.java
+++ b/java/dagger/android/processor/AndroidMapKeys.java
@@ -19,7 +19,6 @@
 import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
 
 import com.google.auto.common.MoreTypes;
-import dagger.android.AndroidInjectionKey;
 import java.util.Optional;
 import javax.lang.model.element.AnnotationMirror;
 import javax.lang.model.element.TypeElement;
@@ -27,7 +26,7 @@
 
 final class AndroidMapKeys {
   /**
-   * If {@code mapKey} is {@link AndroidInjectionKey}, returns the string value for the map key. If
+   * If {@code mapKey} is {@code AndroidInjectionKey}, returns the string value for the map key. If
    * it's {@link dagger.multibindings.ClassKey}, returns the fully-qualified class name of the
    * annotation value. Otherwise returns {@link Optional#empty()}.
    */
diff --git a/java/dagger/android/processor/AndroidProcessor.java b/java/dagger/android/processor/AndroidProcessor.java
index ad7f08e..357f7de 100644
--- a/java/dagger/android/processor/AndroidProcessor.java
+++ b/java/dagger/android/processor/AndroidProcessor.java
@@ -55,7 +55,7 @@
       "dagger.android.experimentalUseStringKeys";
 
   @Override
-  protected Iterable<? extends ProcessingStep> initSteps() {
+  protected Iterable<? extends Step> steps() {
     Filer filer = new FormattingFiler(processingEnv.getFiler());
     Messager messager = processingEnv.getMessager();
     Elements elements = processingEnv.getElementUtils();
diff --git a/java/dagger/android/processor/BUILD b/java/dagger/android/processor/BUILD
index 7e63c76..3ab6eee 100644
--- a/java/dagger/android/processor/BUILD
+++ b/java/dagger/android/processor/BUILD
@@ -15,7 +15,7 @@
 # Description:
 #   Public Dagger API for Android
 
-load("@rules_java//java:defs.bzl", "java_import", "java_library", "java_plugin")
+load("@rules_java//java:defs.bzl", "java_library", "java_plugin")
 load(
     "//:build_defs.bzl",
     "DOCLINT_HTML_AND_SYNTAX",
@@ -38,35 +38,20 @@
     javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
     tags = ["maven_coordinates=com.google.dagger:dagger-android-processor:" + POM_VERSION],
     deps = [
-        "//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",
-        "@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/internal/codegen/langmodel",
         "//java/dagger/spi",
-        # https://github.com/bazelbuild/bazel/issues/2517
-        ":dagger-android-jar",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:service",
+        "//third_party/java/auto:value",
+        "//third_party/java/google_java_format",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/incap",
+        "//third_party/java/javapoet",
     ],
 )
 
-# https://github.com/bazelbuild/bazel/issues/2517
-# This target serves two (related) purposes:
-# 1. Bazel does not allow a java_library to depend on an android_library, even if that java_library
-# will be used in a java_plugin.
-# 2. It stores the metadata for the "jarimpl" target that we use to work-around Gradle not loading
-# aar artifacts that are declared as deps of an annotation processor. Our pom.xml generator reads
-# the tags and includes them apppropriately.
-java_import(
-    name = "dagger-android-jar",
-    jars = ["//java/dagger/android:libandroid.jar"],
-    tags = ["maven_coordinates=com.google.dagger:dagger-android-jarimpl:" + POM_VERSION],
-    visibility = ["//visibility:private"],
-)
-
 pom_file(
     name = "pom",
     artifact_id = "dagger-android-processor",
diff --git a/java/dagger/android/processor/ContributesAndroidInjectorGenerator.java b/java/dagger/android/processor/ContributesAndroidInjectorGenerator.java
index 5c99fd4..f3f4d18 100644
--- a/java/dagger/android/processor/ContributesAndroidInjectorGenerator.java
+++ b/java/dagger/android/processor/ContributesAndroidInjectorGenerator.java
@@ -29,10 +29,10 @@
 import static javax.lang.model.element.Modifier.STATIC;
 import static javax.lang.model.util.ElementFilter.methodsIn;
 
-import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
+import com.google.auto.common.BasicAnnotationProcessor.Step;
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.SetMultimap;
+import com.google.common.collect.ImmutableSetMultimap;
 import com.squareup.javapoet.AnnotationSpec;
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.JavaFile;
@@ -41,26 +41,16 @@
 import com.squareup.javapoet.TypeName;
 import com.squareup.javapoet.TypeSpec;
 import com.squareup.javapoet.WildcardTypeName;
-import dagger.Binds;
-import dagger.Module;
-import dagger.Subcomponent;
-import dagger.android.AndroidInjectionKey;
-import dagger.android.AndroidInjector;
-import dagger.android.ContributesAndroidInjector;
 import dagger.android.processor.AndroidInjectorDescriptor.Validator;
-import dagger.multibindings.ClassKey;
-import dagger.multibindings.IntoMap;
 import java.io.IOException;
-import java.lang.annotation.Annotation;
-import java.util.Set;
 import javax.annotation.processing.Filer;
 import javax.lang.model.SourceVersion;
 import javax.lang.model.element.Element;
 import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.util.Elements;
 
-/** Generates the implementation specified in {@link ContributesAndroidInjector}. */
-final class ContributesAndroidInjectorGenerator implements ProcessingStep {
+/** Generates the implementation specified in {@code ContributesAndroidInjector}. */
+final class ContributesAndroidInjectorGenerator implements Step {
 
   private final AndroidInjectorDescriptor.Validator validator;
   private final Filer filer;
@@ -82,13 +72,12 @@
   }
 
   @Override
-  public Set<? extends Class<? extends Annotation>> annotations() {
-    return ImmutableSet.of(ContributesAndroidInjector.class);
+  public ImmutableSet<String> annotations() {
+    return ImmutableSet.of(TypeNames.CONTRIBUTES_ANDROID_INJECTOR.toString());
   }
 
   @Override
-  public Set<Element> process(
-      SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
+  public ImmutableSet<Element> process(ImmutableSetMultimap<String, Element> elementsByAnnotation) {
     ImmutableSet.Builder<Element> deferredElements = ImmutableSet.builder();
     for (ExecutableElement method : methodsIn(elementsByAnnotation.values())) {
       try {
@@ -118,7 +107,7 @@
         classBuilder(moduleName)
             .addOriginatingElement(descriptor.method())
             .addAnnotation(
-                AnnotationSpec.builder(Module.class)
+                AnnotationSpec.builder(TypeNames.MODULE)
                     .addMember("subcomponents", "$T.class", subcomponentName)
                     .build())
             .addModifiers(PUBLIC, ABSTRACT)
@@ -141,25 +130,24 @@
   private MethodSpec bindAndroidInjectorFactory(
       AndroidInjectorDescriptor descriptor, ClassName subcomponentBuilderName) {
     return methodBuilder("bindAndroidInjectorFactory")
-        .addAnnotation(Binds.class)
-        .addAnnotation(IntoMap.class)
+        .addAnnotation(TypeNames.BINDS)
+        .addAnnotation(TypeNames.INTO_MAP)
         .addAnnotation(androidInjectorMapKey(descriptor))
         .addModifiers(ABSTRACT)
         .returns(
-            parameterizedTypeName(
-                AndroidInjector.Factory.class,
-                WildcardTypeName.subtypeOf(TypeName.OBJECT)))
+            ParameterizedTypeName.get(
+                TypeNames.ANDROID_INJECTOR_FACTORY, WildcardTypeName.subtypeOf(TypeName.OBJECT)))
         .addParameter(subcomponentBuilderName, "builder")
         .build();
   }
 
   private AnnotationSpec androidInjectorMapKey(AndroidInjectorDescriptor descriptor) {
     if (useStringKeys) {
-      return AnnotationSpec.builder(AndroidInjectionKey.class)
+      return AnnotationSpec.builder(TypeNames.ANDROID_INJECTION_KEY)
           .addMember("value", "$S", descriptor.injectedType().toString())
           .build();
     }
-    return AnnotationSpec.builder(ClassKey.class)
+    return AnnotationSpec.builder(TypeNames.CLASS_KEY)
         .addMember("value", "$T.class", descriptor.injectedType())
         .build();
   }
@@ -168,7 +156,7 @@
       AndroidInjectorDescriptor descriptor,
       ClassName subcomponentName,
       ClassName subcomponentFactoryName) {
-    AnnotationSpec.Builder subcomponentAnnotation = AnnotationSpec.builder(Subcomponent.class);
+    AnnotationSpec.Builder subcomponentAnnotation = AnnotationSpec.builder(TypeNames.SUBCOMPONENT);
     for (ClassName module : descriptor.modules()) {
       subcomponentAnnotation.addMember("modules", "$T.class", module);
     }
@@ -177,7 +165,8 @@
         .addModifiers(PUBLIC)
         .addAnnotation(subcomponentAnnotation.build())
         .addAnnotations(descriptor.scopes())
-        .addSuperinterface(parameterizedTypeName(AndroidInjector.class, descriptor.injectedType()))
+        .addSuperinterface(
+            ParameterizedTypeName.get(TypeNames.ANDROID_INJECTOR, descriptor.injectedType()))
         .addType(subcomponentFactory(descriptor, subcomponentFactoryName))
         .build();
   }
@@ -185,15 +174,11 @@
   private TypeSpec subcomponentFactory(
       AndroidInjectorDescriptor descriptor, ClassName subcomponentFactoryName) {
     return interfaceBuilder(subcomponentFactoryName)
-        .addAnnotation(Subcomponent.Factory.class)
+        .addAnnotation(TypeNames.SUBCOMPONENT_FACTORY)
         .addModifiers(PUBLIC, STATIC)
         .addSuperinterface(
-            parameterizedTypeName(AndroidInjector.Factory.class, descriptor.injectedType()))
+            ParameterizedTypeName.get(
+                TypeNames.ANDROID_INJECTOR_FACTORY, descriptor.injectedType()))
         .build();
   }
-
-  private static ParameterizedTypeName parameterizedTypeName(
-      Class<?> clazz, TypeName... typeArguments) {
-    return ParameterizedTypeName.get(ClassName.get(clazz), typeArguments);
-  }
 }
diff --git a/java/dagger/android/processor/DuplicateAndroidInjectorsChecker.java b/java/dagger/android/processor/DuplicateAndroidInjectorsChecker.java
index a19c5ef..bcc8e5a 100644
--- a/java/dagger/android/processor/DuplicateAndroidInjectorsChecker.java
+++ b/java/dagger/android/processor/DuplicateAndroidInjectorsChecker.java
@@ -30,8 +30,6 @@
 import com.google.common.collect.Maps;
 import com.google.common.collect.Multimaps;
 import dagger.MapKey;
-import dagger.android.AndroidInjector;
-import dagger.android.DispatchingAndroidInjector;
 import dagger.model.Binding;
 import dagger.model.BindingGraph;
 import dagger.model.BindingKind;
@@ -43,13 +41,12 @@
 import java.util.Map;
 import java.util.Optional;
 import java.util.stream.Stream;
-import javax.inject.Provider;
 import javax.lang.model.element.AnnotationMirror;
 import javax.lang.model.type.TypeKind;
 import javax.lang.model.type.TypeMirror;
 
 /**
- * Validates that the two maps that {@link DispatchingAndroidInjector} injects have logically
+ * Validates that the two maps that {@code DispatchingAndroidInjector} injects have logically
  * different keys. If a contribution exists for the same {@code FooActivity} with
  * {@code @ActivityKey(FooActivity.class)} and
  * {@code @AndroidInjectionKey("com.example.FooActivity")}, report an error.
@@ -67,7 +64,7 @@
 
   private boolean isDispatchingAndroidInjector(Binding binding) {
     Key key = binding.key();
-    return MoreTypes.isTypeOf(DispatchingAndroidInjector.class, key.type())
+    return MoreDaggerTypes.isTypeOf(TypeNames.DISPATCHING_ANDROID_INJECTOR, key.type())
         && !key.qualifier().isPresent();
   }
 
@@ -118,12 +115,12 @@
             requestedBinding -> {
               TypeMirror valueType =
                   MoreTypes.asDeclared(requestedBinding.key().type()).getTypeArguments().get(1);
-              if (!MoreTypes.isTypeOf(Provider.class, valueType)
+              if (!MoreDaggerTypes.isTypeOf(TypeNames.PROVIDER, valueType)
                   || !valueType.getKind().equals(TypeKind.DECLARED)) {
                 return false;
               }
               TypeMirror providedType = MoreTypes.asDeclared(valueType).getTypeArguments().get(0);
-              return MoreTypes.isTypeOf(AndroidInjector.Factory.class, providedType);
+              return MoreDaggerTypes.isTypeOf(TypeNames.ANDROID_INJECTOR_FACTORY, providedType);
             });
   }
 
diff --git a/java/dagger/android/processor/MoreDaggerElements.java b/java/dagger/android/processor/MoreDaggerElements.java
new file mode 100644
index 0000000..06dea38
--- /dev/null
+++ b/java/dagger/android/processor/MoreDaggerElements.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.android.processor;
+
+import com.google.auto.common.MoreElements;
+import com.squareup.javapoet.ClassName;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+// TODO(bcorso): Dedupe with dagger/internal/codegen/langmodel/DaggerElements.java?
+// TODO(bcorso): Contribute upstream to auto common?
+/** Similar to auto common, but uses {@link ClassName} rather than {@link Class}. */
+final class MoreDaggerElements {
+  /**
+   * Returns {@code true} iff the given element has an {@link AnnotationMirror} whose {@linkplain
+   * AnnotationMirror#getAnnotationType() annotation type} has the same canonical name as that of
+   * {@code annotationClass}. This method is a safer alternative to calling {@link
+   * Element#getAnnotation} and checking for {@code null} as it avoids any interaction with
+   * annotation proxies.
+   */
+  public static boolean isAnnotationPresent(Element element, ClassName annotationName) {
+    return getAnnotationMirror(element, annotationName).isPresent();
+  }
+
+  /**
+   * Returns an {@link AnnotationMirror} for the annotation of type {@code annotationClass} on
+   * {@code element}, or {@link Optional#empty()} if no such annotation exists. This method is a
+   * safer alternative to calling {@link Element#getAnnotation} as it avoids any interaction with
+   * annotation proxies.
+   */
+  public static Optional<AnnotationMirror> getAnnotationMirror(
+      Element element, ClassName annotationName) {
+    String annotationClassName = annotationName.canonicalName();
+    for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
+      TypeElement annotationTypeElement =
+          MoreElements.asType(annotationMirror.getAnnotationType().asElement());
+      if (annotationTypeElement.getQualifiedName().contentEquals(annotationClassName)) {
+        return Optional.of(annotationMirror);
+      }
+    }
+    return Optional.empty();
+  }
+
+  private MoreDaggerElements() {}
+}
diff --git a/java/dagger/android/processor/MoreDaggerTypes.java b/java/dagger/android/processor/MoreDaggerTypes.java
new file mode 100644
index 0000000..4bde405
--- /dev/null
+++ b/java/dagger/android/processor/MoreDaggerTypes.java
@@ -0,0 +1,114 @@
+/*
+ * 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.android.processor;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.auto.common.MoreElements;
+import com.squareup.javapoet.ArrayTypeName;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.TypeName;
+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.NoType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleTypeVisitor8;
+
+// TODO(bcorso): Dedupe with dagger/internal/codegen/langmodel/DaggerTypes.java?
+// TODO(bcorso): Contribute upstream to auto common?
+/** Similar to auto common, but uses {@link ClassName} rather than {@link Class}. */
+final class MoreDaggerTypes {
+
+  /**
+   * Returns true if the raw type underlying the given {@link TypeMirror} represents the same raw
+   * type as the given {@link Class} and throws an IllegalArgumentException if the {@link
+   * TypeMirror} does not represent a type that can be referenced by a {@link Class}
+   */
+  public static boolean isTypeOf(final TypeName typeName, TypeMirror type) {
+    checkNotNull(typeName);
+    return type.accept(new IsTypeOf(typeName), null);
+  }
+
+  private static final class IsTypeOf extends SimpleTypeVisitor8<Boolean, Void> {
+    private final TypeName typeName;
+
+    IsTypeOf(TypeName typeName) {
+      this.typeName = typeName;
+    }
+
+    @Override
+    protected Boolean defaultAction(TypeMirror type, Void ignored) {
+      throw new IllegalArgumentException(type + " cannot be represented as a Class<?>.");
+    }
+
+    @Override
+    public Boolean visitNoType(NoType noType, Void p) {
+      if (noType.getKind().equals(TypeKind.VOID)) {
+        return typeName.equals(TypeName.VOID);
+      }
+      throw new IllegalArgumentException(noType + " cannot be represented as a Class<?>.");
+    }
+
+    @Override
+    public Boolean visitError(ErrorType errorType, Void p) {
+      return false;
+    }
+
+    @Override
+    public Boolean visitPrimitive(PrimitiveType type, Void p) {
+      switch (type.getKind()) {
+        case BOOLEAN:
+          return typeName.equals(TypeName.BOOLEAN);
+        case BYTE:
+          return typeName.equals(TypeName.BYTE);
+        case CHAR:
+          return typeName.equals(TypeName.CHAR);
+        case DOUBLE:
+          return typeName.equals(TypeName.DOUBLE);
+        case FLOAT:
+          return typeName.equals(TypeName.FLOAT);
+        case INT:
+          return typeName.equals(TypeName.INT);
+        case LONG:
+          return typeName.equals(TypeName.LONG);
+        case SHORT:
+          return typeName.equals(TypeName.SHORT);
+        default:
+          throw new IllegalArgumentException(type + " cannot be represented as a Class<?>.");
+      }
+    }
+
+    @Override
+    public Boolean visitArray(ArrayType array, Void p) {
+      return (typeName instanceof ArrayTypeName)
+          && isTypeOf(((ArrayTypeName) typeName).componentType, array.getComponentType());
+    }
+
+    @Override
+    public Boolean visitDeclared(DeclaredType type, Void ignored) {
+      TypeElement typeElement = MoreElements.asType(type.asElement());
+      return (typeName instanceof ClassName)
+          && typeElement.getQualifiedName().contentEquals(((ClassName) typeName).canonicalName());
+    }
+  }
+
+  private MoreDaggerTypes() {}
+}
diff --git a/java/dagger/android/processor/TypeNames.java b/java/dagger/android/processor/TypeNames.java
new file mode 100644
index 0000000..7325690
--- /dev/null
+++ b/java/dagger/android/processor/TypeNames.java
@@ -0,0 +1,54 @@
+/*
+ * 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.android.processor;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.TypeName;
+
+// TODO(bcorso): Dedupe with dagger/internal/codegen/javapoet/TypeNames.java?
+/** Common names and methods for JavaPoet {@link TypeName} and {@link ClassName} usage. */
+public final class TypeNames {
+
+  // Core Dagger classnames
+  public static final ClassName BINDS = ClassName.get("dagger", "Binds");
+  public static final ClassName CLASS_KEY = ClassName.get("dagger.multibindings", "ClassKey");
+  public static final ClassName INTO_MAP = ClassName.get("dagger.multibindings", "IntoMap");
+  public static final ClassName MAP_KEY = ClassName.get("dagger", "MapKey");
+  public static final ClassName MODULE = ClassName.get("dagger", "Module");
+  public static final ClassName SUBCOMPONENT = ClassName.get("dagger", "Subcomponent");
+  public static final ClassName SUBCOMPONENT_FACTORY = SUBCOMPONENT.nestedClass("Factory");
+
+  // Dagger.android classnames
+  public static final ClassName ANDROID_INJECTION_KEY =
+      ClassName.get("dagger.android", "AndroidInjectionKey");
+  public static final ClassName ANDROID_INJECTOR =
+      ClassName.get("dagger.android", "AndroidInjector");
+  public static final ClassName DISPATCHING_ANDROID_INJECTOR =
+      ClassName.get("dagger.android", "DispatchingAndroidInjector");
+  public static final ClassName ANDROID_INJECTOR_FACTORY = ANDROID_INJECTOR.nestedClass("Factory");
+  public static final ClassName CONTRIBUTES_ANDROID_INJECTOR =
+      ClassName.get("dagger.android", "ContributesAndroidInjector");
+
+  // Other classnames
+  public static final ClassName PROVIDER = ClassName.get("javax.inject", "Provider");
+  public static final ClassName QUALIFIER = ClassName.get("jakarta.inject", "Qualifier");
+  public static final ClassName QUALIFIER_JAVAX = ClassName.get("javax.inject", "Qualifier");
+  public static final ClassName SCOPE = ClassName.get("jakarta.inject", "Scope");
+  public static final ClassName SCOPE_JAVAX = ClassName.get("javax.inject", "Scope");
+
+  private TypeNames() {}
+}
diff --git a/java/dagger/android/support/BUILD b/java/dagger/android/support/BUILD
index 8afa703..80747a4 100644
--- a/java/dagger/android/support/BUILD
+++ b/java/dagger/android/support/BUILD
@@ -40,7 +40,7 @@
     deps = [
         "//:dagger_with_compiler",
         "//java/dagger/android",
-        "@google_bazel_common//third_party/java/error_prone:annotations",
+        "//third_party/java/error_prone:annotations",
         "@maven//:androidx_activity_activity",
         "@maven//:androidx_annotation_annotation",
         "@maven//:androidx_appcompat_appcompat",
@@ -71,7 +71,7 @@
     exports = [
         "//:dagger_with_compiler",
         "//java/dagger/android:legacy-deps",
-        "@google_bazel_common//third_party/java/error_prone:annotations",
+        "//third_party/java/error_prone:annotations",
         "@maven//:com_android_support_appcompat_v7",
         "@maven//:com_android_support_support_annotations",
         "@maven//:com_android_support_support_fragment",
diff --git a/java/dagger/errorprone/BUILD b/java/dagger/errorprone/BUILD
index 9c3707f..0bb288a 100644
--- a/java/dagger/errorprone/BUILD
+++ b/java/dagger/errorprone/BUILD
@@ -10,8 +10,8 @@
     srcs = glob(["*.java"]),
     deps = [
         "//java/dagger:core",
-        "//java/dagger/internal/guava:collect",
+        "//third_party/java/error_prone:check_api",
+        "//third_party/java/guava/collect",
         "@bazel_tools//tools/jdk:langtools-neverlink",
-        "@google_bazel_common//third_party/java/error_prone:check_api",
     ],
 )
diff --git a/java/dagger/example/gradle/android/simple/app/build.gradle b/java/dagger/example/gradle/android/simple/app/build.gradle
deleted file mode 100644
index d8f6346..0000000
--- a/java/dagger/example/gradle/android/simple/app/build.gradle
+++ /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.
- */
-
-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
deleted file mode 100644
index cc666d2..0000000
--- a/java/dagger/example/gradle/android/simple/app/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,30 +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.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/Model.java b/java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/Model.java
deleted file mode 100644
index 30a973e..0000000
--- a/java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/Model.java
+++ /dev/null
@@ -1,29 +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.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
deleted file mode 100644
index 734590a..0000000
--- a/java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/SimpleActivity.java
+++ /dev/null
@@ -1,73 +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.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
deleted file mode 100644
index e2e34ea..0000000
--- a/java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/SimpleApplication.java
+++ /dev/null
@@ -1,61 +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.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
deleted file mode 100644
index 18a547b..0000000
--- a/java/dagger/example/gradle/android/simple/app/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,30 +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"
-    />
-</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
deleted file mode 100644
index f45fd41..0000000
--- a/java/dagger/example/gradle/android/simple/app/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?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/gradle/wrapper/gradle-wrapper.jar b/java/dagger/example/gradle/android/simple/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index 5c2d1cf..0000000
--- a/java/dagger/example/gradle/android/simple/gradle/wrapper/gradle-wrapper.jar
+++ /dev/null
Binary files differ
diff --git a/java/dagger/example/gradle/android/simple/settings.gradle b/java/dagger/example/gradle/android/simple/settings.gradle
deleted file mode 100644
index c5a07bc..0000000
--- a/java/dagger/example/gradle/android/simple/settings.gradle
+++ /dev/null
@@ -1,2 +0,0 @@
-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
deleted file mode 100644
index 11552f8..0000000
--- a/java/dagger/example/gradle/simple/SimpleApplication.java
+++ /dev/null
@@ -1,48 +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.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/gradle/wrapper/gradle-wrapper.properties b/java/dagger/example/gradle/simple/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index 4d9ca16..0000000
--- a/java/dagger/example/gradle/simple/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,5 +0,0 @@
-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
deleted file mode 100755
index b0d6d0a..0000000
--- a/java/dagger/example/gradle/simple/gradlew
+++ /dev/null
@@ -1,188 +0,0 @@
-#!/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 bd889c3..2cbb84a 100644
--- a/java/dagger/example/spi/BUILD
+++ b/java/dagger/example/spi/BUILD
@@ -23,12 +23,12 @@
     name = "binding-graph-visualizer",
     srcs = glob(["*.java"]),
     deps = [
-        "//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/javapoet",
+        "//third_party/java/auto:service",
+        "//third_party/java/error_prone:annotations",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/graph",
+        "//third_party/java/javapoet",
     ],
 )
diff --git a/java/dagger/grpc/server/BUILD b/java/dagger/grpc/server/BUILD
index d9a2f97..8aa82be 100644
--- a/java/dagger/grpc/server/BUILD
+++ b/java/dagger/grpc/server/BUILD
@@ -24,7 +24,7 @@
     javacopts = DOCLINT_HTML_AND_SYNTAX,
     tags = ["maven_coordinates=com.google.dagger:dagger-grpc-server-annotations:" + POM_VERSION],
     deps = [
-        "@google_bazel_common//third_party/java/jsr330_inject",
+        "//third_party/java/jsr330_inject",
     ],
 )
 
@@ -41,15 +41,15 @@
     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/jsr330_inject",
-        "@google_bazel_common//third_party/java/protobuf",
+        "//third_party/java/auto:value",
+        "//third_party/java/grpc:context",
+        "//third_party/java/grpc:core",
+        "//third_party/java/grpc:netty",
+        "//third_party/java/grpc:protobuf",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/protobuf",
     ],
 )
 
diff --git a/java/dagger/grpc/server/processor/BUILD b/java/dagger/grpc/server/processor/BUILD
index dd33651..ed28836 100644
--- a/java/dagger/grpc/server/processor/BUILD
+++ b/java/dagger/grpc/server/processor/BUILD
@@ -17,14 +17,14 @@
     deps = [
         "//:dagger_with_compiler",
         "//java/dagger/grpc/server:annotations",
-        "//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/javapoet",
-        "@google_bazel_common//third_party/java/jsr250_annotations",
-        "@maven//:com_google_auto_auto_common",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:service",
+        "//third_party/java/google_java_format",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/io",
+        "//third_party/java/javapoet",
+        "//third_party/java/jsr250_annotations",
     ],
 )
 
diff --git a/java/dagger/hilt/BUILD b/java/dagger/hilt/BUILD
index 3d12c91..13990a2 100644
--- a/java/dagger/hilt/BUILD
+++ b/java/dagger/hilt/BUILD
@@ -54,7 +54,7 @@
         "//java/dagger/hilt/internal:generated_component",
         "//java/dagger/hilt/internal:preconditions",
         "//java/dagger/hilt/internal:test_singleton_component",
-        "@google_bazel_common//third_party/java/jsr305_annotations",
+        "//third_party/java/jsr305_annotations",
     ],
 )
 
@@ -96,7 +96,7 @@
     name = "package_info",
     srcs = ["package-info.java"],
     deps = [
-        "@google_bazel_common//third_party/java/jsr305_annotations",
+        "//third_party/java/jsr305_annotations",
     ],
 )
 
@@ -135,6 +135,7 @@
     srcs = [
         "//java/dagger/hilt/android:srcs_filegroup",
         "//java/dagger/hilt/android/components:srcs_filegroup",
+        "//java/dagger/hilt/android/flags: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",
@@ -175,6 +176,7 @@
         "//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/dagger/hilt/processor/internal/root/ir:srcs_filegroup",
         "//java/dagger/hilt/processor/internal/uninstallmodules:srcs_filegroup",
     ],
 )
diff --git a/java/dagger/hilt/android/BUILD b/java/dagger/hilt/android/BUILD
index d8833e7..1cce5ea 100644
--- a/java/dagger/hilt/android/BUILD
+++ b/java/dagger/hilt/android/BUILD
@@ -16,6 +16,7 @@
 #   A library based on Hilt that provides standard components and automated injection for Android.
 load("//:build_defs.bzl", "POM_VERSION")
 load("//tools:maven.bzl", "gen_maven_artifact")
+load("//tools:bazel_compat.bzl", "compat_kt_android_library")
 
 package(default_visibility = ["//:src"])
 
@@ -31,6 +32,8 @@
     exports = [
         "//java/dagger/hilt:install_in",
         "//java/dagger/hilt/android/components",
+        "//java/dagger/hilt/android/flags:fragment_get_context_fix",
+        "//java/dagger/hilt/android/internal",
         "//java/dagger/hilt/android/internal/builders",
         "//java/dagger/hilt/android/internal/managers",
         "//java/dagger/hilt/android/internal/managers:component_supplier",
@@ -60,7 +63,8 @@
     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",
+        "//java/dagger/hilt/processor/internal/root:component_tree_deps_plugin",
+        "//java/dagger/hilt/processor/internal/root:root_plugin",
     ],
     exports = [
         ":activity_retained_lifecycle",
@@ -68,6 +72,7 @@
         "//java/dagger/hilt:install_in",
         "//java/dagger/hilt/android/components",
         "//java/dagger/hilt/android/internal/builders",
+        "//java/dagger/hilt/android/internal/legacy:aggregated_element_proxy",
         "//java/dagger/hilt/android/internal/managers",
         "//java/dagger/hilt/android/internal/managers:component_supplier",
         "//java/dagger/hilt/android/internal/modules",
@@ -77,6 +82,7 @@
         "//java/dagger/hilt/internal:generated_component",
         "//java/dagger/hilt/internal:generated_entry_point",
         "//java/dagger/hilt/internal/aggregatedroot",
+        "//java/dagger/hilt/internal/componenttreedeps",
         "//java/dagger/hilt/internal/processedrootsentinel",
         "//java/dagger/hilt/migration:disable_install_in_check",
         "@maven//:androidx_activity_activity",
@@ -93,21 +99,6 @@
 )
 
 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_activity_activity",
-        "@maven//:androidx_fragment_fragment",
-        "@maven//:androidx_lifecycle_lifecycle_common",
-        "@maven//:androidx_lifecycle_lifecycle_viewmodel",
-        "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate",
-    ],
-)
-
-android_library(
     name = "activity_retained_lifecycle",
     srcs = ["ActivityRetainedLifecycle.java"],
     deps = [
@@ -134,10 +125,11 @@
         ":package_info",
         "//:dagger_with_compiler",
         "//java/dagger/hilt:entry_point",
+        "//java/dagger/hilt/android/internal",
         "//java/dagger/hilt/internal:component_manager",
         "//java/dagger/hilt/internal:preconditions",
         "//java/dagger/hilt/internal:test_singleton_component_manager",
-        "@google_bazel_common//third_party/java/jsr305_annotations",
+        "//third_party/java/jsr305_annotations",
     ],
 )
 
@@ -145,7 +137,7 @@
     name = "package_info",
     srcs = ["package-info.java"],
     deps = [
-        "@google_bazel_common//third_party/java/jsr305_annotations",
+        "//third_party/java/jsr305_annotations",
     ],
 )
 
@@ -159,6 +151,7 @@
         ":hilt_android_app",
         ":package_info",
         "//java/dagger/hilt:artifact-core-lib",
+        "//java/dagger/hilt/android/migration:custom_inject",
         "//java/dagger/hilt/android/migration:optional_inject",
         "//java/dagger/lint:lint-android-artifact-lib",
     ],
@@ -178,15 +171,19 @@
         "//java/dagger/hilt/android:package_info",
         "//java/dagger/hilt/android/components",
         "//java/dagger/hilt/android/components:package_info",
+        "//java/dagger/hilt/android/flags:fragment_get_context_fix",
         "//java/dagger/hilt/android/internal",
         "//java/dagger/hilt/android/internal/builders",
         "//java/dagger/hilt/android/internal/earlyentrypoint",
+        "//java/dagger/hilt/android/internal/legacy:aggregated_element_proxy",
         "//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:has_custom_inject",
         "//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:custom_inject",
         "//java/dagger/hilt/android/migration:optional_inject",
         "//java/dagger/hilt/android/migration:package_info",
         "//java/dagger/hilt/android/qualifiers",
@@ -198,6 +195,7 @@
         "//java/dagger/hilt/internal:test_singleton_component_manager",
         "//java/dagger/hilt/internal/aggregatedroot:aggregatedroot",
         "//java/dagger/hilt/internal/processedrootsentinel:processedrootsentinel",
+        "//java/dagger/hilt/internal/componenttreedeps:componenttreedeps",
     ],
     artifact_target_maven_deps = [
         "androidx.activity:activity",
@@ -212,6 +210,7 @@
         "com.google.dagger:dagger",
         "com.google.dagger:hilt-core",
         "javax.inject:javax.inject",
+        "org.jetbrains.kotlin:kotlin-stdlib",
     ],
     artifact_target_maven_deps_banned = [
         "com.google.guava:guava",
@@ -237,6 +236,20 @@
     ],
 )
 
+compat_kt_android_library(
+    name = "entry_point_accessors",
+    srcs = ["EntryPointAccessors.kt"],
+    deps = [
+        "//java/dagger/hilt:entry_point",
+        "//java/dagger/hilt/android/internal",
+        "@maven//:androidx_activity_activity",
+        "@maven//:androidx_fragment_fragment",
+        "@maven//:androidx_lifecycle_lifecycle_common",
+        "@maven//:androidx_lifecycle_lifecycle_viewmodel",
+        "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate",
+    ],
+)
+
 filegroup(
     name = "srcs_filegroup",
     srcs = glob(["*"]),
diff --git a/java/dagger/hilt/android/EarlyEntryPoints.java b/java/dagger/hilt/android/EarlyEntryPoints.java
index e714019..d6ebdd6 100644
--- a/java/dagger/hilt/android/EarlyEntryPoints.java
+++ b/java/dagger/hilt/android/EarlyEntryPoints.java
@@ -16,8 +16,10 @@
 
 package dagger.hilt.android;
 
+import android.app.Application;
 import android.content.Context;
 import dagger.hilt.EntryPoints;
+import dagger.hilt.android.internal.Contexts;
 import dagger.hilt.internal.GeneratedComponentManagerHolder;
 import dagger.hilt.internal.Preconditions;
 import dagger.hilt.internal.TestSingletonComponentManager;
@@ -43,13 +45,13 @@
   // this method easier to use, since most code will use this with an Application or Context type.
   @Nonnull
   public static <T> T get(Context applicationContext, Class<T> entryPoint) {
-    applicationContext = applicationContext.getApplicationContext();
+    Application application = Contexts.getApplication(applicationContext.getApplicationContext());
     Preconditions.checkState(
-        applicationContext instanceof GeneratedComponentManagerHolder,
+        application instanceof GeneratedComponentManagerHolder,
         "Expected application context to implement GeneratedComponentManagerHolder. "
             + "Check that you're passing in an application context that uses Hilt.");
     Object componentManager =
-        ((GeneratedComponentManagerHolder) applicationContext).componentManager();
+        ((GeneratedComponentManagerHolder) application).componentManager();
     if (componentManager instanceof TestSingletonComponentManager) {
       Preconditions.checkState(
           hasAnnotationReflection(entryPoint, EarlyEntryPoint.class),
@@ -62,7 +64,7 @@
 
     // @EarlyEntryPoint only has an effect in test environment, so if this is not a test we
     // delegate to EntryPoints.
-    return EntryPoints.get(applicationContext, entryPoint);
+    return EntryPoints.get(application, entryPoint);
   }
 
   // Note: This method uses reflection but it should only be called in test environments.
diff --git a/java/dagger/hilt/android/EntryPointAccessors.java b/java/dagger/hilt/android/EntryPointAccessors.java
deleted file mode 100644
index 6145af1..0000000
--- a/java/dagger/hilt/android/EntryPointAccessors.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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/EntryPointAccessors.kt b/java/dagger/hilt/android/EntryPointAccessors.kt
new file mode 100644
index 0000000..8970c1e
--- /dev/null
+++ b/java/dagger/hilt/android/EntryPointAccessors.kt
@@ -0,0 +1,88 @@
+/*
+ * 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
+
+import android.app.Activity
+import android.content.Context
+import androidx.fragment.app.Fragment
+import android.view.View
+import dagger.hilt.EntryPoints
+import dagger.hilt.android.internal.Contexts
+
+/** Utility functions for dealing with entry points for standard Android components. */
+object 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
+   * SingletonComponent.
+   */
+  @JvmStatic
+  fun <T> fromApplication(context: Context, entryPoint: Class<T>): T =
+    EntryPoints.get(Contexts.getApplication(context.applicationContext), entryPoint)
+
+  /**
+   * 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
+   * SingletonComponent.
+   */
+  inline fun <reified T> fromApplication(context: Context): T =
+    fromApplication(context, T::class.java)
+
+  /**
+   * Returns the entry point interface from an activity. May only be used with entry point
+   * interfaces installed in the ActivityComponent.
+   */
+  @JvmStatic
+  fun <T> fromActivity(activity: Activity, entryPoint: Class<T>): T =
+    EntryPoints.get(activity, entryPoint)
+
+  /**
+   * Returns the entry point interface from an activity. May only be used with entry point
+   * interfaces installed in the ActivityComponent.
+   */
+  inline fun <reified T> fromActivity(activity: Activity): T =
+    fromActivity(activity, T::class.java)
+
+  /**
+   * Returns the entry point interface from a fragment. May only be used with entry point interfaces
+   * installed in the FragmentComponent.
+   */
+  @JvmStatic
+  fun <T> fromFragment(fragment: Fragment, entryPoint: Class<T>): T =
+    EntryPoints.get(fragment, entryPoint)
+
+  /**
+   * Returns the entry point interface from a fragment. May only be used with entry point interfaces
+   * installed in the FragmentComponent.
+   */
+  inline fun <reified T> fromFragment(fragment: Fragment): T =
+    fromFragment(fragment, T::class.java)
+
+  /**
+   * Returns the entry point interface from a view. May only be used with entry point interfaces
+   * installed in the ViewComponent or ViewNoFragmentComponent.
+   */
+  @JvmStatic
+  fun <T> fromView(view: View, entryPoint: Class<T>): T = EntryPoints.get(view, entryPoint)
+
+  /**
+   * Returns the entry point interface from a view. May only be used with entry point interfaces
+   * installed in the ViewComponent or ViewNoFragmentComponent.
+   */
+  inline fun <reified T> fromView(view: View): T =
+    fromView(view, T::class.java)
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/build.gradle b/java/dagger/hilt/android/example/gradle/simple/app/build.gradle
deleted file mode 100644
index b3687ee..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/build.gradle
+++ /dev/null
@@ -1,76 +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.
- */
-
-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
deleted file mode 100644
index 28a7223..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/androidTest/java/dagger/hilt/android/example/gradle/simple/SettingsActivityEmulatorTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index cbf8f7f..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/androidTest/java/dagger/hilt/android/example/gradle/simple/SimpleActivityEmulatorTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index daf046b..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/androidTest/java/dagger/hilt/android/example/gradle/simple/SimpleEmulatorTestRunner.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index 86460d8..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/debug/AndroidManifest.xml
+++ /dev/null
@@ -1,33 +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.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
deleted file mode 100644
index 662c5b4..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,35 +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.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
deleted file mode 100644
index b4aec95..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/Model.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index d933c81..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/ModelModule.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index f508e48..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SettingsActivity.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index 4f8ab14..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SettingsGreeter.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index 70e2e57..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleActivity.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index a163d8c..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleApplication.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index 6c35600..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleGreeter.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index 173b163..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/UserName.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index 3969b73..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/UserNameModule.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index 8a23af5..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,46 +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="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
deleted file mode 100644
index 466856c..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/main/res/layout/activity_settings.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?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
deleted file mode 100644
index f1b550c..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?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
deleted file mode 100644
index 7f1fcfc..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/ActivityScenarioRuleTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index 7abe3c2..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/BindValueTest.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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/Injection1Test.java b/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/Injection1Test.java
deleted file mode 100644
index 0ebd150..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/Injection1Test.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index 51d60b2..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/Injection2Test.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index 73daf8f..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/ModuleTest.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index f1e5f27..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/SettingsActivityTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index 29b5f70..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/SimpleActivityTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index 0234ffe..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/test/resources/dagger/hilt/android/example/gradle/simple/robolectric.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-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
deleted file mode 100644
index c0916a4..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/build.gradle
+++ /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.
- */
-
-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
deleted file mode 100644
index 462aefc..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/feature/build.gradle
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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
deleted file mode 100644
index f7919a9..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<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
deleted file mode 100644
index 03b9ae9..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureActivity.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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/FeatureModule.kt b/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureModule.kt
deleted file mode 100644
index 84ca99a..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureModule.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index bf52431..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/res/layout/activity_feature.xml
+++ /dev/null
@@ -1,38 +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/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
deleted file mode 100644
index 646c51b..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/gradle.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-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
deleted file mode 100644
index 5c2d1cf..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/gradle/wrapper/gradle-wrapper.jar
+++ /dev/null
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
deleted file mode 100644
index 4d9ca16..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,5 +0,0 @@
-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
deleted file mode 100755
index b0d6d0a..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/gradlew
+++ /dev/null
@@ -1,188 +0,0 @@
-#!/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
deleted file mode 100644
index 2ae0b0a..0000000
--- a/java/dagger/hilt/android/example/gradle/simple/settings.gradle
+++ /dev/null
@@ -1,3 +0,0 @@
-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
deleted file mode 100644
index bff4797..0000000
--- a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/build.gradle
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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/main/AndroidManifest.xml b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/AndroidManifest.xml
deleted file mode 100644
index 2e274ab..0000000
--- a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?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
deleted file mode 100644
index 8a85040..0000000
--- a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/ActivityModule.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index 9ee904d..0000000
--- a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/ApplicationModule.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index 765fb36..0000000
--- a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/KotlinApplication.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index 564f05a..0000000
--- a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/MainActivity.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index 4fe168d..0000000
--- a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/Model.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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/res/layout/activity_main.xml b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/layout/activity_main.xml
deleted file mode 100644
index 0d7637d..0000000
--- a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?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
deleted file mode 100644
index c1e4f27..0000000
--- a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<!--
-  ~ Copyright (C) 2020 The Dagger Authors.
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~ http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT 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
deleted file mode 100644
index d52ff71..0000000
--- a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/test/java/dagger/hilt/android/example/gradle/simpleKotlin/SimpleTest.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index 7a02482..0000000
--- a/java/dagger/hilt/android/example/gradle/simpleKotlin/build.gradle
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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
deleted file mode 100644
index 646c51b..0000000
--- a/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-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
deleted file mode 100644
index 5c2d1cf..0000000
--- a/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle/wrapper/gradle-wrapper.jar
+++ /dev/null
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
deleted file mode 100644
index 4d9ca16..0000000
--- a/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,5 +0,0 @@
-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
deleted file mode 100755
index b0d6d0a..0000000
--- a/java/dagger/hilt/android/example/gradle/simpleKotlin/gradlew
+++ /dev/null
@@ -1,188 +0,0 @@
-#!/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
deleted file mode 100644
index e42f9d1..0000000
--- a/java/dagger/hilt/android/example/gradle/simpleKotlin/settings.gradle
+++ /dev/null
@@ -1,2 +0,0 @@
-rootProject.name='Simple Kotlin Hilt Android'
-include ':app'
diff --git a/java/dagger/hilt/android/flags/BUILD b/java/dagger/hilt/android/flags/BUILD
new file mode 100644
index 0000000..7bd9010
--- /dev/null
+++ b/java/dagger/hilt/android/flags/BUILD
@@ -0,0 +1,40 @@
+# 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:
+#   Runtime flags to control Hilt behavior for rollout of changes. These flags are usually
+#   meant to be temporary and so defaults may change with releases and then these flags
+#   may eventually be removed, just like compiler options with similar purposes.
+
+package(default_visibility = ["//:src"])
+
+android_library(
+    name = "fragment_get_context_fix",
+    srcs = [
+        "FragmentGetContextFix.java",
+    ],
+    deps = [
+        "//:dagger_with_compiler",
+        "//java/dagger/hilt:entry_point",
+        "//java/dagger/hilt:install_in",
+        "//java/dagger/hilt/android:entry_point_accessors",
+        "//java/dagger/hilt/android/components",
+        "//java/dagger/hilt/internal:preconditions",
+    ],
+)
+
+filegroup(
+    name = "srcs_filegroup",
+    srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/android/flags/FragmentGetContextFix.java b/java/dagger/hilt/android/flags/FragmentGetContextFix.java
new file mode 100644
index 0000000..d85d39b
--- /dev/null
+++ b/java/dagger/hilt/android/flags/FragmentGetContextFix.java
@@ -0,0 +1,99 @@
+/*
+ * 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.flags;
+
+import android.content.Context;
+import dagger.Module;
+import dagger.hilt.EntryPoint;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.EntryPointAccessors;
+import dagger.hilt.components.SingletonComponent;
+import dagger.hilt.internal.Preconditions;
+import dagger.multibindings.Multibinds;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+import java.util.Set;
+import javax.inject.Qualifier;
+
+/**
+ * Runtime flag for the Fragment.getContext() fix. See https://github.com/google/dagger/pull/2620
+ * for this change. Controls if fragment code should use the fixed getContext() behavior where it
+ * correctly returns null after a fragment is removed. This fixed behavior matches the behavior of a
+ * regular, non-Hilt fragment and can help catch issues where a removed or leaked fragment is
+ * incorrectly used.
+ *
+ * <p>In order to set the flag, bind a boolean value qualified with
+ * {@link DisableFragmentGetContextFix} into a set in the {@code SingletonComponent}. A set is used
+ * instead of an optional binding to avoid a dependency on Guava. Only one value may be bound into
+ * the set within a given app. If this is not set, the default is to not use the fix. Example for
+ * binding the value:
+ *
+ * <pre><code>
+ * {@literal @}Module
+ * {@literal @}InstallIn(SingletonComponent.class)
+ * public final class DisableFragmentGetContextFixModule {
+ *   {@literal @}Provides
+ *   {@literal @}IntoSet
+ *   {@literal @}FragmentGetContextFix.DisableFragmentGetContextFix
+ *   static Boolean provideDisableFragmentGetContextFix() {
+ *     return // true or false depending on some rollout logic for your app
+ *   }
+ * }
+ * </code></pre>
+ */
+public final class FragmentGetContextFix {
+
+  /** Qualifier annotation to bind disable the Fragment.getContext() fix at runtime. */
+  @Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
+  @Qualifier
+  public @interface DisableFragmentGetContextFix {}
+
+  public static boolean isFragmentGetContextFixDisabled(Context context) {
+    // Use a set here instead of an optional to avoid the Guava dependency
+    Set<Boolean> flagSet = EntryPointAccessors.fromApplication(
+        context, FragmentGetContextFixEntryPoint.class).getDisableFragmentGetContextFix();
+
+    // TODO(b/199927963): Consider adding a plugin to check this at compile time
+    Preconditions.checkState(flagSet.size() <= 1,
+        "Cannot bind the flag @DisableFragmentGetContextFix more than once.");
+
+    if (flagSet.isEmpty()) {
+      return true;
+    } else {
+      return flagSet.iterator().next();
+    }
+  }
+
+  /** Entry point for getting the flag. */
+  @EntryPoint
+  @InstallIn(SingletonComponent.class)
+  public interface FragmentGetContextFixEntryPoint {
+    @DisableFragmentGetContextFix Set<Boolean> getDisableFragmentGetContextFix();
+  }
+
+  /** Declare the empty flag set. */
+  @Module
+  @InstallIn(SingletonComponent.class)
+  abstract static class FragmentGetContextFixModule {
+    @Multibinds
+    @DisableFragmentGetContextFix
+    abstract Set<Boolean> disableFragmentGetContextFix();
+  }
+
+  private FragmentGetContextFix() {
+  }
+}
diff --git a/java/dagger/hilt/android/flags/package-info.java b/java/dagger/hilt/android/flags/package-info.java
new file mode 100644
index 0000000..1a39fc2
--- /dev/null
+++ b/java/dagger/hilt/android/flags/package-info.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+/**
+ * Runtime flags to control Hilt behavior for rollout of changes. These flags are usually meant to
+ * be temporary and so defaults may change with releases and then these flags may eventually be
+ * removed, just like compiler options with similar purposes.
+ *
+ * @see <a href="https://dagger.dev/hilt">Hilt Developer Docs</a>
+ */
+@ParametersAreNonnullByDefault
+package dagger.hilt.android.flags;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/java/dagger/hilt/android/internal/BUILD b/java/dagger/hilt/android/internal/BUILD
index 57c84e3..ae8a066 100644
--- a/java/dagger/hilt/android/internal/BUILD
+++ b/java/dagger/hilt/android/internal/BUILD
@@ -19,7 +19,10 @@
 
 android_library(
     name = "internal",
-    srcs = ["ThreadUtil.java"],
+    srcs = [
+        "Contexts.java",
+        "ThreadUtil.java",
+    ],
 )
 
 filegroup(
diff --git a/java/dagger/hilt/android/internal/Contexts.java b/java/dagger/hilt/android/internal/Contexts.java
new file mode 100644
index 0000000..b0a6cf8
--- /dev/null
+++ b/java/dagger/hilt/android/internal/Contexts.java
@@ -0,0 +1,47 @@
+/*
+ * 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.internal;
+
+import android.app.Application;
+import android.content.Context;
+import android.content.ContextWrapper;
+
+/**
+ * Utility methods for dealing with contexts.
+ */
+public final class Contexts {
+
+  /** Finds the android Application from a context. */
+  public static Application getApplication(Context context) {
+    if (context instanceof Application) {
+      return (Application) context;
+    }
+
+    Context unwrapContext = context;
+    while (unwrapContext instanceof ContextWrapper) {
+      unwrapContext = ((ContextWrapper) unwrapContext).getBaseContext();
+      if (unwrapContext instanceof Application) {
+        return (Application) unwrapContext;
+      }
+    }
+
+    throw new IllegalStateException(
+        "Could not find an Application in the given context: " + context);
+  }
+
+  private Contexts() {}
+}
diff --git a/java/dagger/hilt/android/internal/legacy/AggregatedElementProxy.java b/java/dagger/hilt/android/internal/legacy/AggregatedElementProxy.java
new file mode 100644
index 0000000..e88de29
--- /dev/null
+++ b/java/dagger/hilt/android/internal/legacy/AggregatedElementProxy.java
@@ -0,0 +1,34 @@
+/*
+ * 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.internal.legacy;
+
+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 public proxies that reference package-private aggregating elements from
+ * pre-stable versions of Hilt (version < 2.35).
+ */
+@Target(ElementType.TYPE)
+@Retention(CLASS)
+public @interface AggregatedElementProxy {
+  /** A reference to the legacy package-private aggregating class. */
+  Class<?> value();
+}
diff --git a/java/dagger/hilt/android/example/BUILD b/java/dagger/hilt/android/internal/legacy/BUILD
similarity index 64%
copy from java/dagger/hilt/android/example/BUILD
copy to java/dagger/hilt/android/internal/legacy/BUILD
index 2a4c194..0814af0 100644
--- a/java/dagger/hilt/android/example/BUILD
+++ b/java/dagger/hilt/android/internal/legacy/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Dagger Authors.
+# 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.
@@ -11,17 +11,13 @@
 # WITHOUT 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.
+#   Internal Hilt libraries for legacy code.
 
 package(default_visibility = ["//:src"])
 
-filegroup(
-    name = "srcs_filegroup",
-    srcs = glob(
-        ["**/*"],
-        # Exclude Gradle build folder to enable working along side Bazel
-        exclude = ["**/build/**"],
-    ),
+java_library(
+    name = "aggregated_element_proxy",
+    srcs = ["AggregatedElementProxy.java"],
 )
diff --git a/java/dagger/hilt/android/internal/lifecycle/DefaultViewModelFactories.java b/java/dagger/hilt/android/internal/lifecycle/DefaultViewModelFactories.java
index 448847c..3fb4f19 100644
--- a/java/dagger/hilt/android/internal/lifecycle/DefaultViewModelFactories.java
+++ b/java/dagger/hilt/android/internal/lifecycle/DefaultViewModelFactories.java
@@ -17,12 +17,12 @@
 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.lifecycle.SavedStateViewModelFactory;
+import androidx.lifecycle.ViewModelProvider;
 import androidx.savedstate.SavedStateRegistryOwner;
 import dagger.Module;
 import dagger.hilt.EntryPoint;
diff --git a/java/dagger/hilt/android/internal/lifecycle/HiltViewModelFactory.java b/java/dagger/hilt/android/internal/lifecycle/HiltViewModelFactory.java
index 4438941..4e2e0fa 100644
--- a/java/dagger/hilt/android/internal/lifecycle/HiltViewModelFactory.java
+++ b/java/dagger/hilt/android/internal/lifecycle/HiltViewModelFactory.java
@@ -17,13 +17,13 @@
 package dagger.hilt.android.internal.lifecycle;
 
 import android.app.Activity;
+import android.os.Bundle;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 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;
diff --git a/java/dagger/hilt/android/internal/managers/ActivityRetainedComponentManager.java b/java/dagger/hilt/android/internal/managers/ActivityRetainedComponentManager.java
index 0af3dcd..dc8d7e0 100644
--- a/java/dagger/hilt/android/internal/managers/ActivityRetainedComponentManager.java
+++ b/java/dagger/hilt/android/internal/managers/ActivityRetainedComponentManager.java
@@ -16,19 +16,20 @@
 
 package dagger.hilt.android.internal.managers;
 
+import android.content.Context;
+import androidx.annotation.Nullable;
+import androidx.activity.ComponentActivity;
+import androidx.annotation.NonNull;
 import androidx.lifecycle.ViewModel;
 import androidx.lifecycle.ViewModelProvider;
 import androidx.lifecycle.ViewModelStoreOwner;
-import android.content.Context;
-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.EntryPointAccessors;
 import dagger.hilt.android.components.ActivityRetainedComponent;
 import dagger.hilt.android.internal.ThreadUtil;
 import dagger.hilt.android.internal.builders.ActivityRetainedComponentBuilder;
@@ -84,11 +85,11 @@
   private final Object componentLock = new Object();
 
   ActivityRetainedComponentManager(ComponentActivity activity) {
-    this.viewModelProvider = getViewModelProvider(activity, activity.getApplication());
+    this.viewModelProvider = getViewModelProvider(activity, activity);
   }
 
   private ViewModelProvider getViewModelProvider(
-      ViewModelStoreOwner owner, Context applicationContext) {
+      ViewModelStoreOwner owner, Context context) {
     return new ViewModelProvider(
         owner,
         new ViewModelProvider.Factory() {
@@ -97,8 +98,8 @@
           @SuppressWarnings("unchecked")
           public <T extends ViewModel> T create(@NonNull Class<T> aClass) {
             ActivityRetainedComponent component =
-                EntryPoints.get(
-                        applicationContext, ActivityRetainedComponentBuilderEntryPoint.class)
+                EntryPointAccessors.fromApplication(
+                    context, ActivityRetainedComponentBuilderEntryPoint.class)
                     .retainedComponentBuilder()
                     .build();
             return (T) new ActivityRetainedComponentViewModel(component);
diff --git a/java/dagger/hilt/android/internal/managers/BUILD b/java/dagger/hilt/android/internal/managers/BUILD
index 7661310..e9831a2 100644
--- a/java/dagger/hilt/android/internal/managers/BUILD
+++ b/java/dagger/hilt/android/internal/managers/BUILD
@@ -39,6 +39,7 @@
         "//java/dagger/hilt:entry_point",
         "//java/dagger/hilt:install_in",
         "//java/dagger/hilt/android:activity_retained_lifecycle",
+        "//java/dagger/hilt/android:entry_point_accessors",
         "//java/dagger/hilt/android/components",
         "//java/dagger/hilt/android/internal",
         "//java/dagger/hilt/android/internal/builders",
diff --git a/java/dagger/hilt/android/internal/managers/BroadcastReceiverComponentManager.java b/java/dagger/hilt/android/internal/managers/BroadcastReceiverComponentManager.java
index 471c31c..bc9f9ad 100644
--- a/java/dagger/hilt/android/internal/managers/BroadcastReceiverComponentManager.java
+++ b/java/dagger/hilt/android/internal/managers/BroadcastReceiverComponentManager.java
@@ -18,6 +18,7 @@
 
 import android.app.Application;
 import android.content.Context;
+import dagger.hilt.android.internal.Contexts;
 import dagger.hilt.internal.GeneratedComponentManager;
 import dagger.hilt.internal.Preconditions;
 
@@ -27,9 +28,9 @@
  * <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();
+    Application application = Contexts.getApplication(context.getApplicationContext());
 
     Preconditions.checkArgument(
         application instanceof GeneratedComponentManager,
diff --git a/java/dagger/hilt/android/internal/managers/ViewComponentManager.java b/java/dagger/hilt/android/internal/managers/ViewComponentManager.java
index 7861495..c209f55 100644
--- a/java/dagger/hilt/android/internal/managers/ViewComponentManager.java
+++ b/java/dagger/hilt/android/internal/managers/ViewComponentManager.java
@@ -16,19 +16,20 @@
 
 package dagger.hilt.android.internal.managers;
 
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleEventObserver;
-import androidx.lifecycle.LifecycleOwner;
 import android.content.Context;
 import android.content.ContextWrapper;
 import androidx.fragment.app.Fragment;
 import android.view.LayoutInflater;
 import android.view.View;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleEventObserver;
+import androidx.lifecycle.LifecycleOwner;
 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.Contexts;
 import dagger.hilt.android.internal.builders.ViewComponentBuilder;
 import dagger.hilt.android.internal.builders.ViewWithFragmentComponentBuilder;
 import dagger.hilt.internal.GeneratedComponentManager;
@@ -143,8 +144,8 @@
 
   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
+    if (context == Contexts.getApplication(context.getApplicationContext())) {
+      // If we searched for a type but ended up on the application, just return null
       // as this is never what we are looking for
       Preconditions.checkState(
           allowMissing,
diff --git a/java/dagger/hilt/android/internal/migration/BUILD b/java/dagger/hilt/android/internal/migration/BUILD
index b877f6f..c2549fa 100644
--- a/java/dagger/hilt/android/internal/migration/BUILD
+++ b/java/dagger/hilt/android/internal/migration/BUILD
@@ -18,6 +18,11 @@
 package(default_visibility = ["//:src"])
 
 android_library(
+    name = "has_custom_inject",
+    srcs = ["HasCustomInject.java"],
+)
+
+android_library(
     name = "injected_by_hilt",
     srcs = ["InjectedByHilt.java"],
 )
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/internal/migration/HasCustomInject.java
similarity index 71%
rename from java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/UserName.kt
rename to java/dagger/hilt/android/internal/migration/HasCustomInject.java
index a061ab5..ebcac93 100644
--- 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/internal/migration/HasCustomInject.java
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-package dagger.hilt.android.example.gradle.simpleKotlin
+package dagger.hilt.android.internal.migration;
 
-import javax.inject.Qualifier
-
-/** Qualifies bindings relating to the user name.  */
-@Qualifier
-@Retention(AnnotationRetention.RUNTIME)
-internal annotation class UserName
+/**
+ * Do not use except in Hilt generated code. Internal interface for application's using
+ * {@code CustomInject}.
+ */
+public interface HasCustomInject {
+  void customInject();
+}
diff --git a/java/dagger/hilt/android/internal/modules/ApplicationContextModule.java b/java/dagger/hilt/android/internal/modules/ApplicationContextModule.java
index af4ebc2..00f6e24 100644
--- a/java/dagger/hilt/android/internal/modules/ApplicationContextModule.java
+++ b/java/dagger/hilt/android/internal/modules/ApplicationContextModule.java
@@ -21,6 +21,7 @@
 import dagger.Module;
 import dagger.Provides;
 import dagger.hilt.InstallIn;
+import dagger.hilt.android.internal.Contexts;
 import dagger.hilt.android.qualifiers.ApplicationContext;
 import dagger.hilt.components.SingletonComponent;
 
@@ -42,6 +43,6 @@
 
   @Provides
   Application provideApplication() {
-    return (Application) applicationContext.getApplicationContext();
+    return Contexts.getApplication(applicationContext);
   }
 }
diff --git a/java/dagger/hilt/android/internal/modules/BUILD b/java/dagger/hilt/android/internal/modules/BUILD
index c1b639d..5967743 100644
--- a/java/dagger/hilt/android/internal/modules/BUILD
+++ b/java/dagger/hilt/android/internal/modules/BUILD
@@ -24,6 +24,7 @@
         "//:dagger_with_compiler",
         "//java/dagger/hilt:install_in",
         "//java/dagger/hilt/android/components",
+        "//java/dagger/hilt/android/internal",
         "//java/dagger/hilt/android/qualifiers",
         "@maven//:androidx_activity_activity",
         "@maven//:androidx_annotation_annotation",
diff --git a/java/dagger/hilt/android/internal/testing/BUILD b/java/dagger/hilt/android/internal/testing/BUILD
index 98a273d..899ab94 100644
--- a/java/dagger/hilt/android/internal/testing/BUILD
+++ b/java/dagger/hilt/android/internal/testing/BUILD
@@ -86,6 +86,7 @@
     deps = [
         ":test_application_component_manager",
         ":test_application_component_manager_holder",
+        "//java/dagger/hilt/android/internal",
         "//java/dagger/hilt/internal:component_manager",
         "//java/dagger/hilt/internal:preconditions",
         "@maven//:androidx_test_core",
diff --git a/java/dagger/hilt/android/internal/testing/EarlySingletonComponentCreator.java b/java/dagger/hilt/android/internal/testing/EarlySingletonComponentCreator.java
index 8e61e4a..1dbb73a 100644
--- a/java/dagger/hilt/android/internal/testing/EarlySingletonComponentCreator.java
+++ b/java/dagger/hilt/android/internal/testing/EarlySingletonComponentCreator.java
@@ -23,6 +23,11 @@
   private static final String EARLY_SINGLETON_COMPONENT_CREATOR_IMPL =
       "dagger.hilt.android.internal.testing.EarlySingletonComponentCreatorImpl";
 
+  private static final String ERROR_MSG =
+      "The EarlyComponent was requested but does not exist. Check that you have annotated "
+          + "your test class with @HiltAndroidTest and that the processor is running over your "
+          + "test.";
+
   static Object createComponent() {
     try {
       return Class.forName(EARLY_SINGLETON_COMPONENT_CREATOR_IMPL)
@@ -30,16 +35,19 @@
           .getDeclaredConstructor()
           .newInstance()
           .create();
-    } catch (ClassNotFoundException
-        | NoSuchMethodException
-        | IllegalAccessException
-        | InstantiationException
-        | InvocationTargetException e) {
-      throw new RuntimeException(
-          "The EarlyComponent was requested but does not exist. Check that you have annotated "
-              + "your test class with @HiltAndroidTest and that the processor is running over your "
-              + "test.",
-          e);
+    // We catch each individual exception rather than using a multicatch because multi-catch will
+    // get compiled to the common but new super type ReflectiveOperationException, which is not
+    // allowed on API < 19. See b/187826710.
+    } catch (ClassNotFoundException e) {
+      throw new RuntimeException(ERROR_MSG, e);
+    } catch (NoSuchMethodException e) {
+      throw new RuntimeException(ERROR_MSG, e);
+    } catch (IllegalAccessException e) {
+      throw new RuntimeException(ERROR_MSG, e);
+    } catch (InstantiationException e) {
+      throw new RuntimeException(ERROR_MSG, e);
+    } catch (InvocationTargetException e) {
+      throw new RuntimeException(ERROR_MSG, e);
     }
   }
 
diff --git a/java/dagger/hilt/android/internal/testing/MarkThatRulesRanRule.java b/java/dagger/hilt/android/internal/testing/MarkThatRulesRanRule.java
index 17c40b3..192d30a 100644
--- a/java/dagger/hilt/android/internal/testing/MarkThatRulesRanRule.java
+++ b/java/dagger/hilt/android/internal/testing/MarkThatRulesRanRule.java
@@ -19,8 +19,9 @@
 import static dagger.hilt.internal.Preconditions.checkNotNull;
 import static dagger.hilt.internal.Preconditions.checkState;
 
-import android.content.Context;
+import android.app.Application;
 import androidx.test.core.app.ApplicationProvider;
+import dagger.hilt.android.internal.Contexts;
 import dagger.hilt.internal.GeneratedComponentManager;
 import java.lang.annotation.Annotation;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -38,7 +39,8 @@
   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 Application application = Contexts.getApplication(
+      ApplicationProvider.getApplicationContext());
   private final Object testInstance;
   private final boolean autoAddModule;
 
@@ -52,19 +54,19 @@
         "Expected %s to be annotated with @HiltAndroidTest.",
         testInstance.getClass().getName());
     checkState(
-        context instanceof GeneratedComponentManager,
+        application 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());
+        application.getClass().getName());
     checkState(
-        !hasAnnotation(context, HILT_ANDROID_APP),
+        !hasAnnotation(application, 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());
+        application.getClass().getName());
   }
 
   public void delayComponentReady() {
@@ -114,10 +116,11 @@
 
   private TestApplicationComponentManager getTestApplicationComponentManager() {
     checkState(
-        context instanceof TestApplicationComponentManagerHolder,
-        "The context is not an instance of TestApplicationComponentManagerHolder: %s",
-        context);
-    Object componentManager = ((TestApplicationComponentManagerHolder) context).componentManager();
+        application instanceof TestApplicationComponentManagerHolder,
+        "The application is not an instance of TestApplicationComponentManagerHolder: %s",
+        application);
+    Object componentManager =
+        ((TestApplicationComponentManagerHolder) application).componentManager();
     checkState(
         componentManager instanceof TestApplicationComponentManager,
         "Expected TestApplicationComponentManagerHolder to return an instance of"
diff --git a/java/dagger/hilt/android/internal/testing/TestApplicationComponentManager.java b/java/dagger/hilt/android/internal/testing/TestApplicationComponentManager.java
index c205117..9d08db7 100644
--- a/java/dagger/hilt/android/internal/testing/TestApplicationComponentManager.java
+++ b/java/dagger/hilt/android/internal/testing/TestApplicationComponentManager.java
@@ -95,7 +95,7 @@
     if (component.get() == null) {
       Preconditions.checkState(
           hasHiltTestRule(),
-      "The component was not created. Check that you have added the HiltAndroidRule.");
+          "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());
@@ -284,8 +284,8 @@
 
   void setAutoAddModule(boolean autoAddModule) {
     Preconditions.checkState(
-        autoAddModuleEnabled.get() == null, "autoAddModuleEnabled is already set!");
-    autoAddModuleEnabled.set(autoAddModule);
+        autoAddModuleEnabled.compareAndSet(null, autoAddModule),
+        "autoAddModuleEnabled is already set!");
   }
 
   private Set<Class<?>> requiredModules() {
@@ -318,9 +318,7 @@
   }
 
   private Class<?> testClass() {
-    Preconditions.checkState(
-        hasHiltTestRule(),
-    "Test must have an HiltAndroidRule.");
+    Preconditions.checkState(hasHiltTestRule(), "Test must have a HiltAndroidRule.");
     return hasHiltTestRule.get().getTestClass();
   }
 
diff --git a/java/dagger/hilt/android/internal/testing/TestComponentDataSupplier.java b/java/dagger/hilt/android/internal/testing/TestComponentDataSupplier.java
index b6f8b0b..b1234dd 100644
--- a/java/dagger/hilt/android/internal/testing/TestComponentDataSupplier.java
+++ b/java/dagger/hilt/android/internal/testing/TestComponentDataSupplier.java
@@ -32,22 +32,31 @@
           .getDeclaredConstructor()
           .newInstance()
           .get();
-    } catch (ClassNotFoundException
-        | NoSuchMethodException
-        | IllegalAccessException
-        | InstantiationException
-        | InvocationTargetException e) {
-      throw new RuntimeException(
-          String.format(
-              "Hilt test, %s, is missing generated file: %s. Check that the test class is "
-                  + " annotated with @HiltAndroidTest and that the processor is running over your"
-                  + " test.",
-              testClass.getSimpleName(),
-              generatedClassName),
-          e);
+    // We catch each individual exception rather than using a multicatch because multi-catch will
+    // get compiled to the common but new super type ReflectiveOperationException, which is not
+    // allowed on API < 19. See b/187826710.
+    } catch (ClassNotFoundException e) {
+      throw new RuntimeException(errorMessage(testClass, generatedClassName), e);
+    } catch (NoSuchMethodException e) {
+      throw new RuntimeException(errorMessage(testClass, generatedClassName), e);
+    } catch (IllegalAccessException e) {
+      throw new RuntimeException(errorMessage(testClass, generatedClassName), e);
+    } catch (InstantiationException e) {
+      throw new RuntimeException(errorMessage(testClass, generatedClassName), e);
+    } catch (InvocationTargetException e) {
+      throw new RuntimeException(errorMessage(testClass, generatedClassName), e);
     }
   }
 
+  private static String errorMessage(Class<?> testClass, String generatedClassName) {
+    return String.format(
+        "Hilt test, %s, is missing generated file: %s. Check that the test class is "
+            + " annotated with @HiltAndroidTest and that the processor is running over your"
+            + " test.",
+        testClass.getSimpleName(),
+        generatedClassName);
+  }
+
   private static String getEnclosedClassName(Class<?> testClass) {
     StringBuilder sb = new StringBuilder();
     Class<?> currClass = testClass;
diff --git a/java/dagger/hilt/android/lifecycle/proguard-rules.pro b/java/dagger/hilt/android/lifecycle/proguard-rules.pro
index edd3d3d..6c647f1 100644
--- a/java/dagger/hilt/android/lifecycle/proguard-rules.pro
+++ b/java/dagger/hilt/android/lifecycle/proguard-rules.pro
@@ -1,2 +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
+-keepnames @dagger.hilt.android.lifecycle.HiltViewModel class * extends androidx.lifecycle.ViewModel
\ No newline at end of file
diff --git a/java/dagger/hilt/android/migration/BUILD b/java/dagger/hilt/android/migration/BUILD
index a42f0ac..3694560 100644
--- a/java/dagger/hilt/android/migration/BUILD
+++ b/java/dagger/hilt/android/migration/BUILD
@@ -39,11 +39,28 @@
     ],
 )
 
+android_library(
+    name = "custom_inject",
+    srcs = [
+        "CustomInject.java",
+        "CustomInjection.java",
+    ],
+    exports = [
+        "//java/dagger/hilt/android/internal/migration:has_custom_inject",
+    ],
+    deps = [
+        ":package_info",
+        "//java/dagger/hilt/android/internal/migration:has_custom_inject",
+        "//java/dagger/hilt/internal:preconditions",
+        "@maven//:androidx_annotation_annotation",
+    ],
+)
+
 java_library(
     name = "package_info",
     srcs = ["package-info.java"],
     deps = [
-        "@google_bazel_common//third_party/java/jsr305_annotations",
+        "//third_party/java/jsr305_annotations",
     ],
 )
 
diff --git a/java/dagger/hilt/android/migration/CustomInject.java b/java/dagger/hilt/android/migration/CustomInject.java
new file mode 100644
index 0000000..e142609
--- /dev/null
+++ b/java/dagger/hilt/android/migration/CustomInject.java
@@ -0,0 +1,55 @@
+/*
+ * 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.migration;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * When used on a {@link dagger.hilt.android.HiltAndroidApp}-annotated application, this causes the
+ * application to no longer inject itself in onCreate and instead allows it to be injected at some
+ * other time.
+ *
+ * <p>When using this annotation, you can use {@link CustomInjection#inject} to inject the
+ * application class. Additionally, this annotation will also cause a method, {@code customInject}
+ * to be generated in the Hilt base class as well, that behaves the same as
+ * {@link CustomInjection#inject}. 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 @}CustomInject
+ * {@literal @}HiltAndroidApp(Application.class)
+ * public final class MyApplication extends Hilt_MyApplication {
+ *
+ *   {@literal @}Inject Foo foo;
+ *
+ *   {@literal @}Override
+ *   public void onCreate() {
+ *     // Injection would normally happen in this super.onCreate() call, but won't now because this
+ *     // is using CustomInject.
+ *     super.onCreate();
+ *     doSomethingBeforeInjection();
+ *     // This call now injects the fields in the Application, like the foo field above.
+ *     CustomInject.inject(this);
+ *   }
+ * }
+ * </code></pre>
+ */
+@Target(ElementType.TYPE)
+public @interface CustomInject {}
diff --git a/java/dagger/hilt/android/migration/CustomInjection.java b/java/dagger/hilt/android/migration/CustomInjection.java
new file mode 100644
index 0000000..e3d7a0c
--- /dev/null
+++ b/java/dagger/hilt/android/migration/CustomInjection.java
@@ -0,0 +1,43 @@
+/*
+ * 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.migration;
+
+import android.app.Application;
+import androidx.annotation.NonNull;
+import dagger.hilt.android.internal.migration.HasCustomInject;
+import dagger.hilt.internal.Preconditions;
+
+/**
+ * Utility methods for injecting the application when using {@link CustomInject}.
+ *
+ * @see OptionalInject
+ */
+public final class CustomInjection {
+
+  /** Injects the passed in application. */
+  public static void inject(@NonNull Application app) {
+    Preconditions.checkNotNull(app);
+    Preconditions.checkArgument(
+        app instanceof HasCustomInject,
+        "'%s' is not a custom inject application. Check that you have annotated"
+            + " the application with both @HiltAndroidApp and @CustomInject.",
+        app.getClass());
+    ((HasCustomInject) app).customInject();
+  }
+
+  private CustomInjection() {}
+}
diff --git a/java/dagger/hilt/android/migration/OptionalInjectCheck.java b/java/dagger/hilt/android/migration/OptionalInjectCheck.java
index f9d0ad8..6c6d30b 100644
--- a/java/dagger/hilt/android/migration/OptionalInjectCheck.java
+++ b/java/dagger/hilt/android/migration/OptionalInjectCheck.java
@@ -18,10 +18,10 @@
 
 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 androidx.annotation.NonNull;
 import dagger.hilt.android.internal.migration.InjectedByHilt;
 import dagger.hilt.internal.Preconditions;
 
diff --git a/java/dagger/hilt/android/plugin/BUILD b/java/dagger/hilt/android/plugin/BUILD
index 7fd2c83..ef68b63 100644
--- a/java/dagger/hilt/android/plugin/BUILD
+++ b/java/dagger/hilt/android/plugin/BUILD
@@ -17,6 +17,14 @@
 
 package(default_visibility = ["//:src"])
 
+genrule(
+    name = "import-shared-lib",
+    srcs = ["//java/dagger/hilt/processor/internal/root/ir"],
+    outs = ["processor-shared-lib.jar"],
+    cmd = "find $(SRCS)  -type f -name '*.jar' -exec cp {} $@ \\;",
+    local = True,
+)
+
 filegroup(
     name = "srcs_filegroup",
     srcs = glob(
diff --git a/java/dagger/hilt/android/plugin/build.gradle b/java/dagger/hilt/android/plugin/build.gradle
index 480c6f0..c481c41 100644
--- a/java/dagger/hilt/android/plugin/build.gradle
+++ b/java/dagger/hilt/android/plugin/build.gradle
@@ -15,45 +15,70 @@
  */
 
 buildscript {
+  ext {
+    kotlin_version = "1.5.32"
+    agp_version = System.getenv('AGP_VERSION') ?: "7.2.0-alpha06"
+    pluginArtifactId = 'hilt-android-gradle-plugin'
+    pluginId = 'com.google.dagger.hilt.android'
+  }
   repositories {
     google()
     mavenCentral()
-    jcenter()
   }
 }
 
 plugins {
-  id 'org.jetbrains.kotlin.jvm' version '1.4.20'
+  id 'org.jetbrains.kotlin.jvm' version "$kotlin_version"
   id 'java-gradle-plugin'
   id 'maven-publish'
+  // Use shadow version >= 7.1.1 to get log4j vulnerability patches:
+  //   https://github.com/johnrengelman/shadow/releases/tag/7.1.1
+  id 'com.github.johnrengelman.shadow' version '7.1.1'
 }
 
 repositories {
   google()
   mavenCentral()
-  jcenter()
 }
 
 configurations {
-  additionalTestPlugin {
+  // Config for dependencies that will be shadowed / jarjared
+  shadowed
+  // Make all shadowed dependencies be compileOnly dependencies to not affect
+  // main compilation / configuration
+  compileOnly.extendsFrom(shadowed)
+  // Make all shadowed dependencies be included in the plugin test classpath
+  // since they are compileOnly in the main configuration
+  testPluginCompile.extendsFrom(shadowed)
+  // Config for plugin classpath to be used during tests
+  testPluginCompile {
     canBeConsumed = false
     canBeResolved = true
-    extendsFrom implementation
   }
 }
 
+// Renames default jar to avoid using it in publications.
+jar {
+  archiveClassifier = "before-jarjar"
+}
+shadowJar {
+  archiveClassifier = ""
+  configurations = [project.configurations.shadowed]
+}
+
 dependencies {
+  shadowed fileTree(dir: 'libs', include: '*.jar')
   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'
+  compileOnly "com.android.tools.build:gradle:$agp_version"
+  compileOnly "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
   implementation 'org.javassist:javassist:3.26.0-GA'
   implementation 'org.ow2.asm:asm:9.0'
+  implementation "com.squareup:javapoet:1.13.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'
+  testPluginCompile 'com.android.tools.build:gradle:4.2.0'
 }
 
 // Configure the generating task of plugin-under-test-metadata.properties to
@@ -61,21 +86,91 @@
 // 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)
+  it.pluginClasspath.from(configurations.testPluginCompile)
 }
 
 compileKotlin {
   kotlinOptions {
     jvmTarget = "1.8"
+    allWarningsAsErrors = true
   }
 }
 
+java {
+  sourceCompatibility = "1.8"
+  targetCompatibility = "1.8"
+}
+
+// Imports a shared library from the main project. The library and its classes
+// will be shadowed in the plugin's artifact.
+tasks.register("importSharedLib").configure {
+  def outputDir = file("${project.projectDir}/libs")
+  outputs.dir(outputDir)
+  doLast {
+    def buildCmd = 'bazel'
+    def buildDir = 'bazel-bin'
+    def findGenFilesParent
+    findGenFilesParent = { File dir ->
+      if (dir == null || !dir.isDirectory()) {
+        return null
+      }
+      if (new File(dir, buildDir).exists()) {
+        return dir
+      } else {
+        return findGenFilesParent(dir.parentFile)
+      }
+    }
+    // Build shared lib
+    def bazelOutput = new ByteArrayOutputStream()
+    def buildResult = exec {
+      commandLine buildCmd, 'build', 'import-shared-lib'
+      standardOutput = bazelOutput
+      errorOutput = bazelOutput
+    }
+    buildResult.assertNormalExitValue()
+    // Find shared lib Jar in build directory.
+    def genFilesDir = findGenFilesParent(project.buildFile.parentFile)
+    if (genFilesDir == null) {
+      throw new GradleException("Couldn't find build folder '$buildDir'")
+    }
+    def libPath = bazelOutput.toString().split('\n')
+      .find { line -> line.contains("$buildDir/") }.trim()
+    def inputFile = file("$genFilesDir/$libPath")
+    def outputFile = file("$outputDir/${inputFile.name}")
+    outputFile << inputFile.newInputStream()
+  }
+}
+tasks.getByName('compileKotlin').dependsOn('importSharedLib')
+
+// Task that generates a top-level property containing the version of the
+// project so that it can be used in code and at runtime.
+def pluginVersionOutDir = file("$buildDir/generated/source/plugin-version/")
+tasks.register("generatePluginVersionSource").configure {
+  def version = getPublishVersion()
+  inputs.property('version', version)
+  outputs.dir(pluginVersionOutDir)
+  doLast {
+    def versionFile =
+      file("$pluginVersionOutDir/dagger/hilt/android/plugin/Version.kt")
+    versionFile.parentFile.mkdirs()
+    versionFile.text = """
+      // Generated file. Do not edit!
+      package dagger.hilt.android.plugin
+
+      val HILT_VERSION = "${version}"
+    """.stripIndent()
+  }
+}
+sourceSets.main.java.srcDir pluginVersionOutDir
+tasks.getByName('compileKotlin').dependsOn('generatePluginVersionSource')
+
 // Create sources Jar from main kotlin sources
 tasks.register("sourcesJar", Jar).configure {
   group = JavaBasePlugin.DOCUMENTATION_GROUP
   description = "Assembles sources JAR"
-  classifier = "sources"
+  archiveClassifier.set("sources")
   from(sourceSets["main"].allSource)
+  dependsOn('generatePluginVersionSource')
 }
 
 // Create javadoc Jar. The jar is empty since we don't really have docs
@@ -84,7 +179,7 @@
 tasks.register("javadocJar", Jar).configure {
   group = JavaBasePlugin.DOCUMENTATION_GROUP
   description = "Assembles javadoc JAR"
-  classifier = "javadoc"
+  archiveClassifier.set("javadoc")
 }
 
 // Disable Gradle metadata publication.
@@ -96,53 +191,29 @@
 publishing {
   publications {
     plugin(MavenPublication) {
-      artifactId = 'hilt-android-gradle-plugin'
-      def publishVersion = findProperty("PublishVersion")
-      version = (publishVersion != null) ? publishVersion : "LOCAL-SNAPSHOT"
+      artifactId = pluginArtifactId
+      version = getPublishVersion()
       from components.kotlin
+      artifact(shadowJar)
       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'
-        }
+        addPomTemplate(owner)
+      }
+    }
+    // https://docs.gradle.org/current/userguide/plugins.html#sec:plugin_markers
+    pluginMarker(MavenPublication) {
+      groupId = pluginId
+      artifactId = "${pluginId}.gradle.plugin"
+      version = getPublishVersion()
+      pom {
+        addPomTemplate(owner)
         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')
+          def dependencyNode =
+                  asNode().appendNode("dependencies").appendNode("dependency")
+          dependencyNode.appendNode("groupId", group)
+          dependencyNode.appendNode("artifactId", pluginArtifactId)
+          dependencyNode.appendNode("version", getPublishVersion())
         }
       }
     }
@@ -155,4 +226,54 @@
   }
 }
 
-group='com.google.dagger'
+group = 'com.google.dagger'
+
+// TODO(danysantiago): Use POM template in tools/ to avoid duplicating lines.
+def addPomTemplate(pom) {
+  pom.name = 'Hilt Android Gradle Plugin'
+  pom.description = 'A fast dependency injector for Android and Java.'
+  pom.url = 'https://github.com/google/dagger'
+  pom.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'
+  }
+  pom.issueManagement {
+    system = 'GitHub Issues'
+    url = 'https://github.com/google/dagger/issues'
+  }
+  pom.licenses {
+    license {
+      name = 'Apache 2.0'
+      url = 'https://www.apache.org/licenses/LICENSE-2.0.txt'
+    }
+  }
+  pom.organization {
+    name = 'Google, Inc.'
+    url = 'https://www.google.com'
+  }
+  pom.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')
+  }
+}
+
+def getPublishVersion() {
+  def publishVersion = findProperty("PublishVersion")
+  return (publishVersion != null) ? publishVersion : "LOCAL-SNAPSHOT"
+}
diff --git a/java/dagger/hilt/android/plugin/gradle/wrapper/gradle-wrapper.properties b/java/dagger/hilt/android/plugin/gradle/wrapper/gradle-wrapper.properties
index 4d9ca16..ffed3a2 100644
--- a/java/dagger/hilt/android/plugin/gradle/wrapper/gradle-wrapper.properties
+++ b/java/dagger/hilt/android/plugin/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
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
index 3bbf1e1..e9f667f 100644
--- 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
@@ -189,6 +189,12 @@
           if (currentClassRef != oldSuperclassName) {
             return@forEachInstruction
           }
+          // If the method reference of the instruction is a constructor, then we should not
+          // rewrite it since its an instantiation and not a `super()` call.
+          val methodRefName = constantPool.getMethodrefName(methodRef)
+          if (methodRefName == "<init>") {
+            return@forEachInstruction
+          }
           val nameAndTypeRef = constantPool.getMethodrefNameAndType(methodRef)
           val newSuperclassRef = constantPool.addClassInfo(newSuperclassName)
           val newMethodRef = constantPool.addMethodrefInfo(newSuperclassRef, nameAndTypeRef)
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
index 015bb76..fec3318 100644
--- 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
@@ -6,7 +6,7 @@
 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.gradle.api.tasks.Internal
 import org.objectweb.asm.ClassReader
 import org.objectweb.asm.ClassVisitor
 import org.objectweb.asm.FieldVisitor
@@ -17,18 +17,19 @@
  * 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) {
 
+  @Suppress("UnstableApiUsage") // ASM Pipeline APIs
   interface AndroidEntryPointParams : InstrumentationParameters {
-    @get:Input
+    @get:Internal
     val additionalClassesDir: Property<File>
   }
 
+  @Suppress("UnstableApiUsage") // ASM Pipeline APIs
   abstract class Factory : AsmClassVisitorFactory<AndroidEntryPointParams> {
     override fun createClassVisitor(
       classContext: ClassContext,
@@ -69,7 +70,8 @@
     newSuperclassName =
       packageName + "/Hilt_" + className.replace("$", "_")
     oldSuperclassName = superName ?: error { "Superclass of $name is null!" }
-    super.visit(version, access, name, signature, newSuperclassName, interfaces)
+    val newSignature = signature?.replaceFirst(oldSuperclassName, newSuperclassName)
+    super.visit(version, access, name, newSignature, newSuperclassName, interfaces)
   }
 
   override fun visitMethod(
@@ -80,7 +82,11 @@
     exceptions: Array<out String>?
   ): MethodVisitor {
     val nextMethodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions)
-    val invokeSpecialVisitor = InvokeSpecialAdapter(apiVersion, nextMethodVisitor)
+    val invokeSpecialVisitor = InvokeSpecialAdapter(
+      apiVersion = apiVersion,
+      nextClassVisitor = nextMethodVisitor,
+      isConstructor = name == "<init>"
+    )
     if (name == ON_RECEIVE_METHOD_NAME &&
       descriptor == ON_RECEIVE_METHOD_DESCRIPTOR &&
       hasOnReceiveBytecodeInjectionMarker()
@@ -104,16 +110,22 @@
    * 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.
+   * invocations to use a method reference whose class in the pool is the new superclass.
    *
    * @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
+    nextClassVisitor: MethodVisitor,
+    private val isConstructor: Boolean
   ) : MethodVisitor(apiVersion, nextClassVisitor) {
+
+    // Flag to know that we have visited the first invokespecial instruction in a constructor call
+    // which corresponds to the `super()` constructor call required as the first statement of an
+    // overridden constructor body.
+    private var visitedSuperConstructorInvokeSpecial = false
+
     override fun visitMethodInsn(
       opcode: Int,
       owner: String,
@@ -122,13 +134,29 @@
       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)
+        // Update the owner of INVOKESPECIAL instructions, including those found in constructors.
+        super.visitMethodInsn(opcode, getAdaptedOwner(name) ?: owner, name, descriptor, isInterface)
       } else {
         super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
       }
     }
+
+    // Gets the updated owner of an INVOKESPECIAL found in the method being visited.
+    private fun getAdaptedOwner(methodRefName: String): String? {
+      // If the method reference is a constructor and we are visiting a constructor then only the
+      // first INVOKESPECIAL instruction found should be transformed since that correponds to the
+      // super constructor call.
+      if (methodRefName == "<init>" && isConstructor && !visitedSuperConstructorInvokeSpecial) {
+        visitedSuperConstructorInvokeSpecial = true
+        return newSuperclassName
+      }
+      // If the method reference is not a constructor then the instruction for a super call that
+      // should be transformed.
+      if (methodRefName != "<init>") {
+        return newSuperclassName
+      }
+      return null
+    }
   }
 
   /**
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
index 9bb5160..c9c6708 100644
--- 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
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+@file:Suppress("DEPRECATION") // com.android.build.api.transform is deprecated
+
 package dagger.hilt.android.plugin
 
 import com.android.build.api.transform.DirectoryInput
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
index 7a33836..bbfe292 100644
--- 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
@@ -15,9 +15,7 @@
  */
 package dagger.hilt.android.plugin
 
-/**
- * Configuration options for the Hilt Gradle Plugin
- */
+/** Configuration options for the Hilt Gradle Plugin */
 interface HiltExtension {
 
   /**
@@ -42,9 +40,28 @@
    * deprecated in a future version.
    */
   var enableTransformForLocalTests: Boolean
+
+  /**
+   * If set to `true`, Hilt will perform module and entry points aggregation in a task instead of an
+   * aggregating annotation processor. Enabling this flag improves incremental build times.
+   *
+   * When this flag is enabled, 'enableExperimentalClasspathAggregation' has no effect since
+   * classpath aggregation will be done by default.
+   */
+  var enableAggregatingTask: Boolean
+
+  /**
+   * If set to `true`, Hilt will disable cross compilation root validation.
+   *
+   * See [documentation](https://dagger.dev/hilt/flags#disable-cross-compilation-root-validation)
+   * for more information.
+   */
+  var disableCrossCompilationRootValidation: Boolean
 }
 
 internal open class HiltExtensionImpl : HiltExtension {
   override var enableExperimentalClasspathAggregation: Boolean = false
   override var enableTransformForLocalTests: Boolean = false
+  override var enableAggregatingTask: Boolean = true
+  override var disableCrossCompilationRootValidation: 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
index a82826e..d872d66 100644
--- 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
@@ -16,8 +16,8 @@
 
 package dagger.hilt.android.plugin
 
-import com.android.build.api.component.Component
-import com.android.build.api.extension.AndroidComponentsExtension
+import com.android.build.api.attributes.BuildTypeAttr
+import com.android.build.api.attributes.ProductFlavorAttr
 import com.android.build.api.instrumentation.FramesComputationMode
 import com.android.build.api.instrumentation.InstrumentationScope
 import com.android.build.gradle.AppExtension
@@ -26,18 +26,26 @@
 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.task.AggregateDepsTask
+import dagger.hilt.android.plugin.task.HiltTransformTestClassesTask
+import dagger.hilt.android.plugin.util.AggregatedPackagesTransform
+import dagger.hilt.android.plugin.util.AndroidComponentsExtensionCompat.Companion.getAndroidComponentsExtension
+import dagger.hilt.android.plugin.util.ComponentCompat
 import dagger.hilt.android.plugin.util.CopyTransform
 import dagger.hilt.android.plugin.util.SimpleAGPVersion
+import dagger.hilt.android.plugin.util.capitalize
+import dagger.hilt.android.plugin.util.getSdkPath
 import java.io.File
 import javax.inject.Inject
+import org.gradle.api.JavaVersion
 import org.gradle.api.Plugin
 import org.gradle.api.Project
 import org.gradle.api.artifacts.component.ProjectComponentIdentifier
 import org.gradle.api.attributes.Attribute
+import org.gradle.api.attributes.Usage
 import org.gradle.api.provider.ProviderFactory
+import org.gradle.api.tasks.compile.JavaCompile
+import org.gradle.process.CommandLineArgumentProvider
 
 /**
  * A Gradle plugin that checks if the project is an Android project and if so, registers a
@@ -70,82 +78,92 @@
     val hiltExtension = project.extensions.create(
       HiltExtension::class.java, "hilt", HiltExtensionImpl::class.java
     )
+    configureDependencyTransforms(project)
     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)
+      configureBytecodeTransform(project, hiltExtension)
     } else {
       // Configures bytecode transform using AGP 4.2 ASM pipeline.
-      configureTransformASM(project, hiltExtension)
+      configureBytecodeTransformASM(project, hiltExtension)
     }
-    configureProcessorFlags(project)
+    configureAggregatingTask(project, hiltExtension)
+    configureProcessorFlags(project, hiltExtension)
+  }
+
+  // Configures Gradle dependency transforms.
+  private fun configureDependencyTransforms(project: Project) = 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)
+    }
+    registerTransform(CopyTransform::class.java) { spec ->
+      // File Collection dependencies might be an artifact of type 'directory', e.g. when
+      // adding as a dep the destination directory of the JavaCompile task.
+      spec.from.attribute(ARTIFACT_TYPE_ATTRIBUTE, "directory")
+      spec.to.attribute(ARTIFACT_TYPE_ATTRIBUTE, DAGGER_ARTIFACT_TYPE_VALUE)
+    }
+    registerTransform(AggregatedPackagesTransform::class.java) { spec ->
+      spec.from.attribute(ARTIFACT_TYPE_ATTRIBUTE, DAGGER_ARTIFACT_TYPE_VALUE)
+      spec.to.attribute(ARTIFACT_TYPE_ATTRIBUTE, AGGREGATED_HILT_ARTIFACT_TYPE_VALUE)
+    }
   }
 
   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)
-      }
+    androidExtension.forEachRootVariant { variant ->
+      configureVariantCompileClasspath(project, hiltExtension, androidExtension, variant)
     }
   }
 
-  @Suppress("UnstableApiUsage")
+  // Invokes the [block] function for each Android variant that is considered a Hilt root, where
+  // dependencies are aggregated and components are generated.
+  private fun BaseExtension.forEachRootVariant(
+    @Suppress("DEPRECATION") block: (variant: com.android.build.gradle.api.BaseVariant) -> Unit
+  ) {
+    when (this) {
+      is AppExtension -> {
+        // For an app project we configure the app variant and both androidTest and unitTest
+        // variants, Hilt components are generated in all of them.
+        applicationVariants.all { block(it) }
+        testVariants.all { block(it) }
+        unitTestVariants.all { block(it) }
+      }
+      is LibraryExtension -> {
+        // For a library project, only the androidTest and unitTest variant are configured since
+        // Hilt components are not generated in a library.
+        testVariants.all { block(it) }
+        unitTestVariants.all { block(it) }
+      }
+      is TestExtension -> {
+        applicationVariants.all { block(it) }
+      }
+      else -> error("Hilt plugin does not know how to configure '$this'")
+    }
+  }
+
   private fun configureVariantCompileClasspath(
     project: Project,
     hiltExtension: HiltExtension,
     androidExtension: BaseExtension,
-    variant: BaseVariant
+    @Suppress("DEPRECATION") variant: com.android.build.gradle.api.BaseVariant
   ) {
-    if (!hiltExtension.enableExperimentalClasspathAggregation) {
+    if (
+      !hiltExtension.enableExperimentalClasspathAggregation || hiltExtension.enableAggregatingTask
+    ) {
       // 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 &&
+    if (
+      androidExtension.lintOptions.isCheckReleaseBuilds &&
       SimpleAGPVersion.ANDROID_GRADLE_PLUGIN_VERSION < SimpleAGPVersion(7, 0)
     ) {
       // Sadly we have to ask users to disable lint when enableExperimentalClasspathAggregation is
@@ -160,19 +178,22 @@
 
     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 { providers.gradleProperty(it).forUseAtConfigurationTime().isPresent }
+        "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 {
+        providers.gradleProperty(it).forUseAtConfigurationTime().isPresent
+      }
     ) {
       // 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) {
+    @Suppress("DEPRECATION") // Older variant API is deprecated
+    val runtimeConfiguration = if (variant is com.android.build.gradle.api.TestVariant) {
       // For Android test variants, the tested runtime classpath is used since the test app has
       // tested dependencies removed.
       variant.testedVariant.runtimeConfiguration
@@ -199,10 +220,11 @@
     // debugUnitTest    -> testDebugCompileOnly
     // release          -> releaseCompileOnly
     // releaseUnitTest  -> testReleaseCompileOnly
+    @Suppress("DEPRECATION") // Older variant API is deprecated
     val compileOnlyConfigName = when (variant) {
-      is TestVariant ->
+      is com.android.build.gradle.api.TestVariant ->
         "androidTest${variant.name.substringBeforeLast("AndroidTest").capitalize()}CompileOnly"
-      is UnitTestVariant ->
+      is com.android.build.gradle.api.UnitTestVariant ->
         "test${variant.name.substringBeforeLast("UnitTest").capitalize()}CompileOnly"
       else ->
         "${variant.name}CompileOnly"
@@ -210,10 +232,10 @@
     project.dependencies.add(compileOnlyConfigName, artifactView.files)
   }
 
-  @Suppress("UnstableApiUsage")
-  private fun configureTransformASM(project: Project, hiltExtension: HiltExtension) {
+  @Suppress("UnstableApiUsage") // ASM Pipeline APIs
+  private fun configureBytecodeTransformASM(project: Project, hiltExtension: HiltExtension) {
     var warnAboutLocalTestsFlag = false
-    fun registerTransform(androidComponent: Component) {
+    fun registerTransform(androidComponent: ComponentCompat) {
       if (hiltExtension.enableTransformForLocalTests && !warnAboutLocalTestsFlag) {
         project.logger.warn(
           "The Hilt configuration option 'enableTransformForLocalTests' is no longer necessary " +
@@ -233,14 +255,10 @@
         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) }
+    getAndroidComponentsExtension(project).onAllVariants { registerTransform(it) }
   }
 
-  private fun configureTransform(project: Project, hiltExtension: HiltExtension) {
+  private fun configureBytecodeTransform(project: Project, hiltExtension: HiltExtension) {
     val androidExtension = project.extensions.findByType(BaseExtension::class.java)
       ?: throw error("Android BaseExtension not found.")
     androidExtension.registerTransform(AndroidEntryPointTransform())
@@ -256,17 +274,189 @@
     }
   }
 
-  private fun configureProcessorFlags(project: Project) {
+  private fun configureAggregatingTask(project: Project, hiltExtension: HiltExtension) {
     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) }
+    androidExtension.forEachRootVariant { variant ->
+      configureVariantAggregatingTask(project, hiltExtension, androidExtension, variant)
+    }
+  }
+
+  private fun configureVariantAggregatingTask(
+    project: Project,
+    hiltExtension: HiltExtension,
+    androidExtension: BaseExtension,
+    @Suppress("DEPRECATION") variant: com.android.build.gradle.api.BaseVariant
+  ) {
+    if (!hiltExtension.enableAggregatingTask) {
+      // Option is not enabled, don't configure aggregating task.
+      return
+    }
+
+    val hiltCompileConfiguration = project.configurations.create(
+      "hiltCompileOnly${variant.name.capitalize()}"
+    ).apply {
+      // The runtime config of the test APK differs from the tested one.
+      @Suppress("DEPRECATION") // Older variant API is deprecated
+      if (variant is com.android.build.gradle.api.TestVariant) {
+        extendsFrom(variant.testedVariant.runtimeConfiguration)
+      }
+      extendsFrom(variant.runtimeConfiguration)
+      isCanBeConsumed = false
+      isCanBeResolved = true
+      attributes { attrContainer ->
+        attrContainer.attribute(
+          Usage.USAGE_ATTRIBUTE,
+          project.objects.named(Usage::class.java, Usage.JAVA_RUNTIME)
+        )
+        attrContainer.attribute(
+          BuildTypeAttr.ATTRIBUTE,
+          project.objects.named(BuildTypeAttr::class.java, variant.buildType.name)
+        )
+        variant.productFlavors.forEach { flavor ->
+          attrContainer.attribute(
+            Attribute.of(flavor.dimension!!, ProductFlavorAttr::class.java),
+            project.objects.named(ProductFlavorAttr::class.java, flavor.name)
+          )
         }
       }
     }
+    // Add the JavaCompile task classpath and output dir to the config, the task's classpath
+    // will contain:
+    //  * compileOnly dependencies
+    //  * KAPT and Kotlinc generated bytecode
+    //  * R.jar
+    //  * Tested classes if the variant is androidTest
+    project.dependencies.add(
+      hiltCompileConfiguration.name,
+      project.files(variant.javaCompileProvider.map { it.classpath })
+    )
+    project.dependencies.add(
+      hiltCompileConfiguration.name,
+      project.files(variant.javaCompileProvider.map {it.destinationDirectory.get() })
+    )
+
+    fun getInputClasspath(artifactAttributeValue: String) =
+      hiltCompileConfiguration.incoming.artifactView { view ->
+        view.attributes.attribute(ARTIFACT_TYPE_ATTRIBUTE, artifactAttributeValue)
+      }.files
+
+    val aggregatingTask = project.tasks.register(
+      "hiltAggregateDeps${variant.name.capitalize()}",
+      AggregateDepsTask::class.java
+    ) {
+      it.compileClasspath.setFrom(getInputClasspath(AGGREGATED_HILT_ARTIFACT_TYPE_VALUE))
+      it.outputDir.set(
+        project.file(project.buildDir.resolve("generated/hilt/component_trees/${variant.name}/"))
+      )
+      @Suppress("DEPRECATION") // Older variant API is deprecated
+      it.testEnvironment.set(
+        variant is com.android.build.gradle.api.TestVariant ||
+          variant is com.android.build.gradle.api.UnitTestVariant
+      )
+      it.crossCompilationRootValidationDisabled.set(
+        hiltExtension.disableCrossCompilationRootValidation
+      )
+    }
+
+    val componentClasses = project.files(
+      project.buildDir.resolve("intermediates/hilt/component_classes/${variant.name}/")
+    )
+    val componentsJavaCompileTask = project.tasks.register(
+      "hiltJavaCompile${variant.name.capitalize()}",
+      JavaCompile::class.java
+    ) { compileTask ->
+      compileTask.source = aggregatingTask.map { it.outputDir.asFileTree }.get()
+      // Configure the input classpath based on Java 9 compatibility, specifically for Java 9 the
+      // android.jar is now included in the input classpath instead of the bootstrapClasspath.
+      // See: com/android/build/gradle/tasks/JavaCompileUtils.kt
+      val mainBootstrapClasspath =
+        variant.javaCompileProvider.map { it.options.bootstrapClasspath ?: project.files() }.get()
+      if (
+        JavaVersion.current().isJava9Compatible &&
+        androidExtension.compileOptions.targetCompatibility.isJava9Compatible
+      ) {
+        compileTask.classpath =
+          getInputClasspath(DAGGER_ARTIFACT_TYPE_VALUE).plus(mainBootstrapClasspath)
+        //  Copies argument providers from original task, which should contain the JdkImageInput
+        variant.javaCompileProvider.get().let { originalCompileTask ->
+          originalCompileTask.options.compilerArgumentProviders.forEach {
+            compileTask.options.compilerArgumentProviders.add(it)
+          }
+        }
+        compileTask.options.compilerArgs.add("-XDstringConcat=inline")
+      } else {
+        compileTask.classpath = getInputClasspath(DAGGER_ARTIFACT_TYPE_VALUE)
+        compileTask.options.bootstrapClasspath = mainBootstrapClasspath
+      }
+      compileTask.destinationDirectory.set(componentClasses.singleFile)
+      compileTask.options.apply {
+        annotationProcessorPath = project.configurations.create(
+          "hiltAnnotationProcessor${variant.name.capitalize()}"
+        ).also { config ->
+          // TODO: Consider finding the hilt-compiler dep from the user config and using it here.
+          project.dependencies.add(config.name, "com.google.dagger:hilt-compiler:$HILT_VERSION")
+        }
+        generatedSourceOutputDirectory.set(
+          project.file(
+            project.buildDir.resolve("generated/hilt/component_sources/${variant.name}/")
+          )
+        )
+        if (
+          JavaVersion.current().isJava8Compatible &&
+          androidExtension.compileOptions.targetCompatibility.isJava8Compatible
+        ) {
+          compilerArgs.add("-parameters")
+        }
+        compilerArgs.add("-Adagger.fastInit=enabled")
+        compilerArgs.add("-Adagger.hilt.internal.useAggregatingRootProcessor=false")
+        compilerArgs.add("-Adagger.hilt.android.internal.disableAndroidSuperclassValidation=true")
+        encoding = androidExtension.compileOptions.encoding
+      }
+      compileTask.sourceCompatibility =
+        androidExtension.compileOptions.sourceCompatibility.toString()
+      compileTask.targetCompatibility =
+        androidExtension.compileOptions.targetCompatibility.toString()
+    }
+    componentClasses.builtBy(componentsJavaCompileTask)
+
+    variant.registerPostJavacGeneratedBytecode(componentClasses)
+  }
+
+  private fun getAndroidJar(project: Project, compileSdkVersion: String) =
+    project.files(File(project.getSdkPath(), "platforms/$compileSdkVersion/android.jar"))
+
+  private fun configureProcessorFlags(project: Project, hiltExtension: HiltExtension) {
+    val androidExtension = project.extensions.findByType(BaseExtension::class.java)
+      ?: throw error("Android BaseExtension not found.")
+    androidExtension.defaultConfig.javaCompileOptions.annotationProcessorOptions.apply {
+      // Pass annotation processor flag to enable Dagger's fast-init, the best mode for Hilt.
+      argument("dagger.fastInit", "enabled")
+      // Pass annotation processor flag to disable @AndroidEntryPoint superclass validation.
+      argument("dagger.hilt.android.internal.disableAndroidSuperclassValidation", "true")
+      // Pass certain annotation processor flags via a CommandLineArgumentProvider so that plugin
+      // options defined in the extension are populated from the user's build file. Checking the
+      // option too early would make it seem like it is never set.
+      compilerArgumentProvider(
+        // Suppress due to https://docs.gradle.org/7.2/userguide/validation_problems.html#implementation_unknown
+        @Suppress("ObjectLiteralToLambda")
+        object : CommandLineArgumentProvider {
+          override fun asArguments() = mutableListOf<String>().apply {
+            // Pass annotation processor flag to disable the aggregating processor if aggregating
+            // task is enabled.
+            if (hiltExtension.enableAggregatingTask) {
+              add("-Adagger.hilt.internal.useAggregatingRootProcessor=false")
+            }
+            // Pass annotation processor flag to disable cross compilation root validation.
+            // The plugin option duplicates the processor flag because it is an input of the
+            // aggregating task.
+            if (hiltExtension.disableCrossCompilationRootValidation) {
+              add("-Adagger.hilt.disableCrossCompilationRootValidation=true")
+            }
+          }
+        }
+      )
+    }
   }
 
   private fun verifyDependencies(project: Project) {
@@ -280,7 +470,8 @@
     if (!dependencies.contains(LIBRARY_GROUP to "hilt-android")) {
       error(missingDepError("$LIBRARY_GROUP:hilt-android"))
     }
-    if (!dependencies.contains(LIBRARY_GROUP to "hilt-android-compiler") &&
+    if (
+      !dependencies.contains(LIBRARY_GROUP to "hilt-android-compiler") &&
       !dependencies.contains(LIBRARY_GROUP to "hilt-compiler")
     ) {
       error(missingDepError("$LIBRARY_GROUP:hilt-compiler"))
@@ -290,12 +481,10 @@
   companion object {
     val ARTIFACT_TYPE_ATTRIBUTE = Attribute.of("artifactType", String::class.java)
     const val DAGGER_ARTIFACT_TYPE_VALUE = "jar-for-dagger"
+    const val AGGREGATED_HILT_ARTIFACT_TYPE_VALUE = "aggregated-jar-for-hilt"
 
     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/root/AggregatedAnnotation.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/root/AggregatedAnnotation.kt
new file mode 100644
index 0000000..e08b6f1
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/root/AggregatedAnnotation.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.plugin.root
+
+// Annotations used for aggregating dependencies by the annotation processors.
+internal enum class AggregatedAnnotation(
+  private val descriptor: String,
+  private val aggregatedPackage: String
+) {
+  AGGREGATED_ROOT(
+    "Ldagger/hilt/internal/aggregatedroot/AggregatedRoot;",
+    "dagger/hilt/internal/aggregatedroot/codegen"
+  ),
+  PROCESSED_ROOT_SENTINEL(
+    "Ldagger/hilt/internal/processedrootsentinel/ProcessedRootSentinel;",
+    "dagger/hilt/internal/processedrootsentinel/codegen"
+  ),
+  DEFINE_COMPONENT(
+    "Ldagger/hilt/internal/definecomponent/DefineComponentClasses;",
+    "dagger/hilt/processor/internal/definecomponent/codegen"
+  ),
+  ALIAS_OF(
+    "Ldagger/hilt/internal/aliasof/AliasOfPropagatedData;",
+    "dagger/hilt/processor/internal/aliasof/codegen"
+  ),
+  AGGREGATED_DEP(
+    "Ldagger/hilt/processor/internal/aggregateddeps/AggregatedDeps;",
+    "hilt_aggregated_deps"
+  ),
+  AGGREGATED_DEP_PROXY(
+    "Ldagger/hilt/android/internal/legacy/AggregatedElementProxy;",
+    "", // Proxies share the same package name as the elements they are proxying.
+  ),
+  AGGREGATED_UNINSTALL_MODULES(
+    "Ldagger/hilt/android/internal/uninstallmodules/AggregatedUninstallModules;",
+    "dagger/hilt/android/internal/uninstallmodules/codegen"
+  ),
+  AGGREGATED_EARLY_ENTRY_POINT(
+    "Ldagger/hilt/android/internal/earlyentrypoint/AggregatedEarlyEntryPoint;",
+    "dagger/hilt/android/internal/earlyentrypoint/codegen"
+  ),
+  NONE("", "");
+
+  companion object {
+    fun fromString(str: String) = values().firstOrNull { it.descriptor == str } ?: NONE
+
+    val AGGREGATED_PACKAGES = values().map { it.aggregatedPackage }.filter { it.isNotEmpty() }
+  }
+}
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/root/AggregatedElementProxyGenerator.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/root/AggregatedElementProxyGenerator.kt
new file mode 100644
index 0000000..248976e
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/root/AggregatedElementProxyGenerator.kt
@@ -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.
+ */
+
+package dagger.hilt.android.plugin.root
+
+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.root.ir.AggregatedElementProxyIr
+import java.io.File
+import javax.lang.model.element.Modifier
+
+internal class AggregatedElementProxyGenerator(
+  private val outputDir: File,
+) {
+
+  fun generate(aggregatedElementProxy: AggregatedElementProxyIr) {
+    val typeSpec = TypeSpec.classBuilder(aggregatedElementProxy.fqName)
+      .addAnnotation(
+        AnnotationSpec.builder(AGGREGATED_ELEMENT_PROXY_ANNOTATION)
+          .addMember("value", "\$T.class", aggregatedElementProxy.value)
+          .build()
+      )
+      .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+      .build()
+    JavaFile.builder(aggregatedElementProxy.fqName.packageName(), typeSpec)
+      .build()
+      .writeTo(outputDir)
+  }
+
+  companion object {
+    val AGGREGATED_ELEMENT_PROXY_ANNOTATION =
+      ClassName.get("dagger.hilt.android.internal.legacy", "AggregatedElementProxy")
+  }
+}
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/root/Aggregator.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/root/Aggregator.kt
new file mode 100644
index 0000000..b3cb737
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/root/Aggregator.kt
@@ -0,0 +1,419 @@
+/*
+ * 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.plugin.task
+
+import com.squareup.javapoet.ClassName
+import dagger.hilt.android.plugin.root.AggregatedAnnotation
+import dagger.hilt.android.plugin.util.forEachZipEntry
+import dagger.hilt.android.plugin.util.isClassFile
+import dagger.hilt.android.plugin.util.isJarFile
+import dagger.hilt.processor.internal.root.ir.AggregatedDepsIr
+import dagger.hilt.processor.internal.root.ir.AggregatedEarlyEntryPointIr
+import dagger.hilt.processor.internal.root.ir.AggregatedElementProxyIr
+import dagger.hilt.processor.internal.root.ir.AggregatedRootIr
+import dagger.hilt.processor.internal.root.ir.AggregatedUninstallModulesIr
+import dagger.hilt.processor.internal.root.ir.AliasOfPropagatedDataIr
+import dagger.hilt.processor.internal.root.ir.DefineComponentClassesIr
+import dagger.hilt.processor.internal.root.ir.ProcessedRootSentinelIr
+import java.io.File
+import java.io.InputStream
+import java.util.zip.ZipInputStream
+import org.objectweb.asm.AnnotationVisitor
+import org.objectweb.asm.ClassReader
+import org.objectweb.asm.ClassVisitor
+import org.objectweb.asm.Opcodes
+import org.objectweb.asm.Type
+import org.slf4j.Logger
+
+/** Aggregates Hilt dependencies. */
+internal class Aggregator private constructor(
+  private val logger: Logger,
+  private val asmApiVersion: Int,
+) {
+  private val classVisitor = AggregatedDepClassVisitor(logger, asmApiVersion)
+
+  val aggregatedRoots: Set<AggregatedRootIr>
+    get() = classVisitor.aggregatedRoots
+
+  val processedRoots: Set<ProcessedRootSentinelIr>
+    get() = classVisitor.processedRoots
+
+  val defineComponentDeps: Set<DefineComponentClassesIr>
+    get() = classVisitor.defineComponentDeps
+
+  val aliasOfDeps: Set<AliasOfPropagatedDataIr>
+    get() = classVisitor.aliasOfDeps
+
+  val aggregatedDeps: Set<AggregatedDepsIr>
+    get() = classVisitor.aggregatedDeps
+
+  val aggregatedDepProxies: Set<AggregatedElementProxyIr>
+    get() = classVisitor.aggregatedDepProxies
+
+  val allAggregatedDepProxies: Set<AggregatedElementProxyIr>
+    get() = classVisitor.allAggregatedDepProxies
+
+  val uninstallModulesDeps: Set<AggregatedUninstallModulesIr>
+    get() = classVisitor.uninstallModulesDeps
+
+  val earlyEntryPointDeps: Set<AggregatedEarlyEntryPointIr>
+    get() = classVisitor.earlyEntryPointDeps
+
+  private class AggregatedDepClassVisitor(
+    private val logger: Logger,
+    private val asmApiVersion: Int,
+  ) : ClassVisitor(asmApiVersion) {
+
+    val aggregatedRoots = mutableSetOf<AggregatedRootIr>()
+    val processedRoots = mutableSetOf<ProcessedRootSentinelIr>()
+    val defineComponentDeps = mutableSetOf<DefineComponentClassesIr>()
+    val aliasOfDeps = mutableSetOf<AliasOfPropagatedDataIr>()
+    val aggregatedDeps = mutableSetOf<AggregatedDepsIr>()
+    val aggregatedDepProxies = mutableSetOf<AggregatedElementProxyIr>()
+    val allAggregatedDepProxies = mutableSetOf<AggregatedElementProxyIr>()
+    val uninstallModulesDeps = mutableSetOf<AggregatedUninstallModulesIr>()
+    val earlyEntryPointDeps = mutableSetOf<AggregatedEarlyEntryPointIr>()
+
+    var accessCode: Int = Opcodes.ACC_PUBLIC
+    lateinit var annotatedClassName: ClassName
+
+    override fun visit(
+      version: Int,
+      access: Int,
+      name: String,
+      signature: String?,
+      superName: String?,
+      interfaces: Array<out String>?
+    ) {
+      accessCode = access
+      annotatedClassName = Type.getObjectType(name).toClassName()
+      super.visit(version, access, name, signature, superName, interfaces)
+    }
+
+    override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor? {
+      val nextAnnotationVisitor = super.visitAnnotation(descriptor, visible)
+      val aggregatedAnnotation = AggregatedAnnotation.fromString(descriptor)
+      val isHiltAnnotated = aggregatedAnnotation != AggregatedAnnotation.NONE
+      // For non-public deps, a proxy might be needed, make a note of it.
+      if (isHiltAnnotated && (accessCode and Opcodes.ACC_PUBLIC) != Opcodes.ACC_PUBLIC) {
+        allAggregatedDepProxies.add(
+          AggregatedElementProxyIr(
+            fqName = annotatedClassName.peerClass("_" + annotatedClassName.simpleName()),
+            value = annotatedClassName
+          )
+        )
+      }
+      when (aggregatedAnnotation) {
+        AggregatedAnnotation.AGGREGATED_ROOT -> {
+          return object : AnnotationVisitor(asmApiVersion, nextAnnotationVisitor) {
+            lateinit var rootClass: String
+            lateinit var originatingRootClass: String
+            lateinit var rootAnnotationClassName: Type
+
+            override fun visit(name: String, value: Any?) {
+              when (name) {
+                "root" -> rootClass = value as String
+                "originatingRoot" -> originatingRootClass = value as String
+                "rootAnnotation" -> rootAnnotationClassName = (value as Type)
+              }
+              super.visit(name, value)
+            }
+
+            override fun visitEnd() {
+              aggregatedRoots.add(
+                AggregatedRootIr(
+                  fqName = annotatedClassName,
+                  root = rootClass.toClassName(),
+                  originatingRoot = originatingRootClass.toClassName(),
+                  rootAnnotation = rootAnnotationClassName.toClassName()
+                )
+              )
+              super.visitEnd()
+            }
+          }
+        }
+        AggregatedAnnotation.PROCESSED_ROOT_SENTINEL -> {
+          return object : AnnotationVisitor(asmApiVersion, nextAnnotationVisitor) {
+            val rootClasses = mutableListOf<String>()
+
+            override fun visitArray(name: String): AnnotationVisitor? {
+              return when (name) {
+                "roots" -> visitValue { value -> rootClasses.add(value as String) }
+                else -> super.visitArray(name)
+              }
+            }
+
+            override fun visitEnd() {
+              processedRoots.add(
+                ProcessedRootSentinelIr(
+                  fqName = annotatedClassName,
+                  roots = rootClasses.map { it.toClassName() }
+                )
+              )
+              super.visitEnd()
+            }
+          }
+        }
+        AggregatedAnnotation.DEFINE_COMPONENT -> {
+          return object : AnnotationVisitor(asmApiVersion, nextAnnotationVisitor) {
+            lateinit var componentClass: String
+
+            override fun visit(name: String, value: Any?) {
+              when (name) {
+                "component", "builder" -> componentClass = value as String
+              }
+              super.visit(name, value)
+            }
+
+            override fun visitEnd() {
+              defineComponentDeps.add(
+                DefineComponentClassesIr(
+                  fqName = annotatedClassName,
+                  component = componentClass.toClassName()
+                )
+              )
+              super.visitEnd()
+            }
+          }
+        }
+        AggregatedAnnotation.ALIAS_OF -> {
+          return object : AnnotationVisitor(asmApiVersion, nextAnnotationVisitor) {
+            val defineComponentScopeClassNames = mutableSetOf<Type>()
+            lateinit var aliasClassName: Type
+
+            // visit() handles both array and non-array values.
+            // For array values, each value in the array will be visited individually.
+            override fun visit(name: String, value: Any?) {
+              when (name) {
+                // Older versions of AliasOfPropagatedData only passed a single defineComponentScope
+                // class value. Fall back on reading the single value if we get old propagated data.
+                "defineComponentScope",
+                "defineComponentScopes" -> defineComponentScopeClassNames.add(value as Type)
+                "alias" -> aliasClassName = (value as Type)
+              }
+              super.visit(name, value)
+            }
+
+            override fun visitEnd() {
+              aliasOfDeps.add(
+                AliasOfPropagatedDataIr(
+                  fqName = annotatedClassName,
+                  defineComponentScopes =
+                    defineComponentScopeClassNames.map({ it.toClassName() }).toList(),
+                  alias = aliasClassName.toClassName(),
+                )
+              )
+              super.visitEnd()
+            }
+          }
+        }
+        AggregatedAnnotation.AGGREGATED_DEP -> {
+          return object : AnnotationVisitor(asmApiVersion, nextAnnotationVisitor) {
+            val componentClasses = mutableListOf<String>()
+            var testClass: String? = null
+            val replacesClasses = mutableListOf<String>()
+            var moduleClass: String? = null
+            var entryPoint: String? = null
+            var componentEntryPoint: String? = null
+
+            override fun visit(name: String, value: Any?) {
+              when (name) {
+                "test" -> testClass = value as String
+              }
+              super.visit(name, value)
+            }
+
+            override fun visitArray(name: String): AnnotationVisitor? {
+              return when (name) {
+                "components" ->
+                  visitValue { value -> componentClasses.add(value as String) }
+                "replaces" ->
+                  visitValue { value -> replacesClasses.add(value as String) }
+                "modules" ->
+                  visitValue { value -> moduleClass = value as String }
+                "entryPoints" ->
+                  visitValue { value -> entryPoint = value as String }
+                "componentEntryPoints" ->
+                  visitValue { value -> componentEntryPoint = value as String }
+                else -> super.visitArray(name)
+              }
+            }
+
+            override fun visitEnd() {
+              aggregatedDeps.add(
+                AggregatedDepsIr(
+                  fqName = annotatedClassName,
+                  components = componentClasses.map { it.toClassName() },
+                  test = testClass?.toClassName(),
+                  replaces = replacesClasses.map { it.toClassName() },
+                  module = moduleClass?.toClassName(),
+                  entryPoint = entryPoint?.toClassName(),
+                  componentEntryPoint = componentEntryPoint?.toClassName()
+                )
+              )
+              super.visitEnd()
+            }
+          }
+        }
+        AggregatedAnnotation.AGGREGATED_DEP_PROXY -> {
+          return object : AnnotationVisitor(asmApiVersion, nextAnnotationVisitor) {
+            lateinit var valueClassName: Type
+
+            override fun visit(name: String, value: Any?) {
+              when (name) {
+                "value" -> valueClassName = (value as Type)
+              }
+              super.visit(name, value)
+            }
+
+            override fun visitEnd() {
+              aggregatedDepProxies.add(
+                AggregatedElementProxyIr(
+                  fqName = annotatedClassName,
+                  value = valueClassName.toClassName(),
+                )
+              )
+              super.visitEnd()
+            }
+          }
+        }
+        AggregatedAnnotation.AGGREGATED_UNINSTALL_MODULES -> {
+          return object : AnnotationVisitor(asmApiVersion, nextAnnotationVisitor) {
+            lateinit var testClass: String
+            val uninstallModulesClasses = mutableListOf<String>()
+
+            override fun visit(name: String, value: Any?) {
+              when (name) {
+                "test" -> testClass = value as String
+              }
+              super.visit(name, value)
+            }
+
+            override fun visitArray(name: String): AnnotationVisitor? {
+              return when (name) {
+                "uninstallModules" ->
+                  visitValue { value -> uninstallModulesClasses.add(value as String) }
+                else -> super.visitArray(name)
+              }
+            }
+
+            override fun visitEnd() {
+              uninstallModulesDeps.add(
+                AggregatedUninstallModulesIr(
+                  fqName = annotatedClassName,
+                  test = testClass.toClassName(),
+                  uninstallModules = uninstallModulesClasses.map { it.toClassName() }
+                )
+              )
+              super.visitEnd()
+            }
+          }
+        }
+        AggregatedAnnotation.AGGREGATED_EARLY_ENTRY_POINT -> {
+          return object : AnnotationVisitor(asmApiVersion, nextAnnotationVisitor) {
+            lateinit var earlyEntryPointClass: String
+
+            override fun visit(name: String, value: Any?) {
+              when (name) {
+                "earlyEntryPoint" -> earlyEntryPointClass = value as String
+              }
+              super.visit(name, value)
+            }
+
+            override fun visitEnd() {
+              earlyEntryPointDeps.add(
+                AggregatedEarlyEntryPointIr(
+                  fqName = annotatedClassName,
+                  earlyEntryPoint = earlyEntryPointClass.toClassName()
+                )
+              )
+              super.visitEnd()
+            }
+          }
+        }
+        else -> {
+          logger.warn("Found an unknown annotation in Hilt aggregated packages: $descriptor")
+        }
+      }
+      return nextAnnotationVisitor
+    }
+
+    fun visitValue(block: (value: Any) -> Unit) =
+      object : AnnotationVisitor(asmApiVersion) {
+        override fun visit(nullName: String?, value: Any) {
+          block(value)
+        }
+      }
+  }
+
+  private fun process(files: Iterable<File>) {
+    files.forEach { file ->
+      when {
+        file.isFile -> visitFile(file)
+        file.isDirectory -> file.walkTopDown().filter { it.isFile }.forEach { visitFile(it) }
+        else -> logger.warn("Can't process file/directory that doesn't exist: $file")
+      }
+    }
+  }
+
+  private fun visitFile(file: File) {
+    when {
+      file.isJarFile() -> ZipInputStream(file.inputStream()).forEachZipEntry { inputStream, entry ->
+        if (entry.isClassFile()) {
+          visitClass(inputStream)
+        }
+      }
+      file.isClassFile() -> file.inputStream().use { visitClass(it) }
+      else -> logger.debug("Don't know how to process file: $file")
+    }
+  }
+
+  private fun visitClass(classFileInputStream: InputStream) {
+    ClassReader(classFileInputStream).accept(
+      classVisitor,
+      ClassReader.SKIP_CODE and ClassReader.SKIP_DEBUG and ClassReader.SKIP_FRAMES
+    )
+  }
+
+  companion object {
+    fun from(
+      logger: Logger,
+      asmApiVersion: Int,
+      input: Iterable<File>
+    ) = Aggregator(logger, asmApiVersion).apply { process(input) }
+
+    // Converts this Type to a ClassName, used instead of ClassName.bestGuess() because ASM class
+    // names are based off descriptors and uses 'reflection' naming, i.e. inner classes are split
+    // by '$' instead of '.'
+    fun Type.toClassName(): ClassName {
+      val binaryName = this.className
+      val packageNameEndIndex = binaryName.lastIndexOf('.')
+      val packageName = if (packageNameEndIndex != -1) {
+        binaryName.substring(0, packageNameEndIndex)
+      } else {
+        ""
+      }
+      val shortNames = binaryName.substring(packageNameEndIndex + 1).split('$')
+      return ClassName.get(packageName, shortNames.first(), *shortNames.drop(1).toTypedArray())
+    }
+
+    // Converts this String representing the canonical name of a class to a ClassName.
+    fun String.toClassName(): ClassName {
+      return ClassName.bestGuess(this)
+    }
+  }
+}
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/root/ComponentTreeDepsGenerator.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/root/ComponentTreeDepsGenerator.kt
new file mode 100644
index 0000000..705528a
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/root/ComponentTreeDepsGenerator.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.plugin.root
+
+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.root.ir.ComponentTreeDepsIr
+import java.io.File
+import javax.lang.model.element.Modifier
+
+/** Generates @ComponentTreeDeps annotated sources. */
+internal class ComponentTreeDepsGenerator(
+  private val proxies: Map<ClassName, ClassName>,
+  private val outputDir: File,
+) {
+  fun generate(componentTree: ComponentTreeDepsIr) {
+    val typeSpec = TypeSpec.classBuilder(componentTree.name)
+      .addAnnotation(
+        AnnotationSpec.builder(COMPONENT_TREE_DEPS_ANNOTATION).apply {
+          componentTree.rootDeps.toMaybeProxies().forEach {
+            addMember("rootDeps", "\$T.class", it)
+          }
+          componentTree.defineComponentDeps.toMaybeProxies().forEach {
+            addMember("defineComponentDeps", "\$T.class", it)
+          }
+          componentTree.aliasOfDeps.toMaybeProxies().forEach {
+            addMember("aliasOfDeps", "\$T.class", it)
+          }
+          componentTree.aggregatedDeps.toMaybeProxies().forEach {
+            addMember("aggregatedDeps", "\$T.class", it)
+          }
+          componentTree.uninstallModulesDeps.toMaybeProxies().forEach {
+            addMember("uninstallModulesDeps", "\$T.class", it)
+          }
+          componentTree.earlyEntryPointDeps.toMaybeProxies().forEach {
+            addMember("earlyEntryPointDeps", "\$T.class", it)
+          }
+        }.build()
+      )
+      .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+      .build()
+    JavaFile.builder(componentTree.name.packageName(), typeSpec)
+      .build()
+      .writeTo(outputDir)
+  }
+
+  private fun Collection<ClassName>.toMaybeProxies() =
+    sorted().map { fqName -> proxies[fqName] ?: fqName }
+
+  companion object {
+    val COMPONENT_TREE_DEPS_ANNOTATION: ClassName =
+      ClassName.get("dagger.hilt.internal.componenttreedeps", "ComponentTreeDeps")
+  }
+}
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/root/ProcessedRootSentinelGenerator.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/root/ProcessedRootSentinelGenerator.kt
new file mode 100644
index 0000000..931b424
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/root/ProcessedRootSentinelGenerator.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.plugin.root
+
+import com.squareup.javapoet.AnnotationSpec
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.TypeSpec
+import java.io.File
+import javax.lang.model.element.Modifier
+
+internal class ProcessedRootSentinelGenerator(
+  private val outputDir: File,
+) {
+
+  fun generate(processedRootName: ClassName) {
+    val className = ClassName.get(
+      PROCESSED_ROOT_SENTINEL_GEN_PACKAGE,
+      "_" + processedRootName.toString().replace('.', '_')
+    )
+    val typeSpec = TypeSpec.classBuilder(className)
+      .addAnnotation(
+        AnnotationSpec.builder(PROCESSED_ROOT_SENTINEL_ANNOTATION)
+          .addMember("roots", "\$S", processedRootName)
+          .build()
+      )
+      .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+      .build()
+    JavaFile.builder(PROCESSED_ROOT_SENTINEL_GEN_PACKAGE, typeSpec)
+      .build()
+      .writeTo(outputDir)
+  }
+
+  companion object {
+    val PROCESSED_ROOT_SENTINEL_GEN_PACKAGE = "dagger.hilt.internal.processedrootsentinel.codegen"
+    val PROCESSED_ROOT_SENTINEL_ANNOTATION =
+      ClassName.get("dagger.hilt.internal.processedrootsentinel", "ProcessedRootSentinel")
+  }
+}
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/task/AggregateDepsTask.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/task/AggregateDepsTask.kt
new file mode 100644
index 0000000..f3939fc
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/task/AggregateDepsTask.kt
@@ -0,0 +1,134 @@
+/*
+ * 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.plugin.task
+
+import dagger.hilt.android.plugin.root.AggregatedElementProxyGenerator
+import dagger.hilt.android.plugin.root.ComponentTreeDepsGenerator
+import dagger.hilt.android.plugin.root.ProcessedRootSentinelGenerator
+import dagger.hilt.processor.internal.root.ir.AggregatedRootIrValidator
+import dagger.hilt.processor.internal.root.ir.ComponentTreeDepsIrCreator
+import javax.inject.Inject
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.ConfigurableFileCollection
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.CacheableTask
+import org.gradle.api.tasks.Classpath
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.Optional
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.TaskAction
+import org.gradle.work.InputChanges
+import org.gradle.workers.WorkAction
+import org.gradle.workers.WorkParameters
+import org.gradle.workers.WorkerExecutor
+import org.objectweb.asm.Opcodes
+import org.slf4j.LoggerFactory
+
+/**
+ * Aggregates Hilt component dependencies from the compile classpath and outputs Java sources
+ * with shareable component trees.
+ *
+ * The [compileClasspath] input is expected to contain jars or classes transformed by
+ * [dagger.hilt.android.plugin.util.AggregatedPackagesTransform].
+ */
+@CacheableTask
+abstract class AggregateDepsTask @Inject constructor(
+  private val workerExecutor: WorkerExecutor
+) : DefaultTask() {
+
+  // TODO(danysantiago): Make @Incremental and try to use @CompileClasspath
+  @get:Classpath
+  abstract val compileClasspath: ConfigurableFileCollection
+
+  @get:Input
+  @get:Optional
+  abstract val asmApiVersion: Property<Int>
+
+  @get:OutputDirectory
+  abstract val outputDir: DirectoryProperty
+
+  @get:Input
+  abstract val testEnvironment: Property<Boolean>
+
+  @get:Input
+  abstract val crossCompilationRootValidationDisabled: Property<Boolean>
+
+  @TaskAction
+  internal fun taskAction(@Suppress("UNUSED_PARAMETER") inputs: InputChanges) {
+    workerExecutor.noIsolation().submit(WorkerAction::class.java) {
+      it.compileClasspath.from(compileClasspath)
+      it.asmApiVersion.set(asmApiVersion)
+      it.outputDir.set(outputDir)
+      it.testEnvironment.set(testEnvironment)
+      it.crossCompilationRootValidationDisabled.set(crossCompilationRootValidationDisabled)
+    }
+  }
+
+  internal interface Parameters : WorkParameters {
+    val compileClasspath: ConfigurableFileCollection
+    val asmApiVersion: Property<Int>
+    val outputDir: DirectoryProperty
+    val testEnvironment: Property<Boolean>
+    val crossCompilationRootValidationDisabled: Property<Boolean>
+  }
+
+  abstract class WorkerAction : WorkAction<Parameters> {
+    override fun execute() {
+      // Logger is not an injectable service yet: https://github.com/gradle/gradle/issues/16991
+      val logger = LoggerFactory.getLogger(AggregateDepsTask::class.java)
+      val aggregator = Aggregator.from(
+        logger = logger,
+        asmApiVersion = parameters.asmApiVersion.getOrNull() ?: Opcodes.ASM7,
+        input = parameters.compileClasspath
+      )
+      val rootsToProcess = AggregatedRootIrValidator.rootsToProcess(
+        isCrossCompilationRootValidationDisabled =
+          parameters.crossCompilationRootValidationDisabled.get(),
+        processedRoots = aggregator.processedRoots,
+        aggregatedRoots = aggregator.aggregatedRoots
+      )
+      if (rootsToProcess.isEmpty()) {
+        return
+      }
+      val componentTrees = ComponentTreeDepsIrCreator.components(
+        isTest = parameters.testEnvironment.get(),
+        isSharedTestComponentsEnabled = true,
+        aggregatedRoots = rootsToProcess,
+        defineComponentDeps = aggregator.defineComponentDeps,
+        aliasOfDeps = aggregator.aliasOfDeps,
+        aggregatedDeps = aggregator.aggregatedDeps,
+        aggregatedUninstallModulesDeps = aggregator.uninstallModulesDeps,
+        aggregatedEarlyEntryPointDeps = aggregator.earlyEntryPointDeps,
+      )
+      ComponentTreeDepsGenerator(
+        proxies = aggregator.allAggregatedDepProxies.associate { it.value to it.fqName },
+        outputDir = parameters.outputDir.get().asFile
+      ).let { generator ->
+        componentTrees.forEach { generator.generate(it) }
+      }
+      AggregatedElementProxyGenerator(parameters.outputDir.get().asFile).let { generator ->
+        (aggregator.allAggregatedDepProxies - aggregator.aggregatedDepProxies).forEach {
+          generator.generate(it)
+        }
+      }
+      ProcessedRootSentinelGenerator(parameters.outputDir.get().asFile).let { generator ->
+        rootsToProcess.map { it.root }.forEach { generator.generate(it) }
+      }
+    }
+  }
+}
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/task/HiltTransformTestClassesTask.kt
similarity index 87%
rename from java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltTransformTestClassesTask.kt
rename to java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/task/HiltTransformTestClassesTask.kt
index 84b35b1..e2e4e7c 100644
--- 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/task/HiltTransformTestClassesTask.kt
@@ -13,9 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package dagger.hilt.android.plugin
+package dagger.hilt.android.plugin.task
 
-import com.android.build.gradle.api.UnitTestVariant
+import dagger.hilt.android.plugin.AndroidEntryPointClassTransformer
+import dagger.hilt.android.plugin.HiltExtension
+import dagger.hilt.android.plugin.util.capitalize
+import dagger.hilt.android.plugin.util.getCompileKotlin
 import dagger.hilt.android.plugin.util.isClassFile
 import dagger.hilt.android.plugin.util.isJarFile
 import java.io.File
@@ -31,18 +34,15 @@
 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() {
@@ -113,7 +113,7 @@
 
     fun create(
       project: Project,
-      unitTestVariant: UnitTestVariant,
+      @Suppress("DEPRECATION") unitTestVariant: com.android.build.gradle.api.UnitTestVariant,
       extension: HiltExtension
     ) {
       if (!extension.enableTransformForLocalTests) {
@@ -126,24 +126,18 @@
       // 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))
+        project.files(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>
+      val testCompileTaskProvider = unitTestVariant.javaCompileProvider
       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>
+        val kotlinCompileTaskProvider = getCompileKotlin(unitTestVariant, project)
         inputClasspath.from(kotlinCompileTaskProvider.map { it.destinationDirectory })
       }
 
@@ -157,11 +151,12 @@
       )
       // Map the transform task's output to a file collection.
       val outputFileCollection =
-        project.objects.fileCollection().from(hiltTransformProvider.map { it.outputDir })
+        project.files(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()}"
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/AggregatedPackagesTransform.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/AggregatedPackagesTransform.kt
new file mode 100644
index 0000000..6dfa842
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/AggregatedPackagesTransform.kt
@@ -0,0 +1,98 @@
+/*
+ * 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.plugin.util
+
+import dagger.hilt.android.plugin.root.AggregatedAnnotation
+import java.io.ByteArrayOutputStream
+import java.io.File
+import java.util.zip.ZipEntry
+import java.util.zip.ZipInputStream
+import java.util.zip.ZipOutputStream
+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 outputs classes and jars containing only classes in key aggregating Hilt
+ * packages that are used to pass dependencies between compilation units.
+ */
+@CacheableTransform
+abstract class AggregatedPackagesTransform : TransformAction<TransformParameters.None> {
+  // TODO(danysantiago): Make incremental by using InputChanges and try to use @CompileClasspath
+  @get:Classpath
+  @get:InputArtifact
+  abstract val inputArtifactProvider: Provider<FileSystemLocation>
+
+  override fun transform(outputs: TransformOutputs) {
+    val input = inputArtifactProvider.get().asFile
+    when {
+      input.isFile -> transformFile(outputs, input)
+      input.isDirectory -> input.walkTopDown().filter { it.isFile }.forEach {
+        transformFile(outputs, it)
+      }
+      else -> error("File/directory does not exist: ${input.absolutePath}")
+    }
+  }
+
+  private fun transformFile(outputs: TransformOutputs, file: File) {
+    if (file.isJarFile()) {
+      var atLeastOneEntry = false
+      // TODO(danysantiago): This is an in-memory buffer stream, consider using a temp file.
+      val tmpOutputStream = ByteArrayOutputStream()
+      ZipOutputStream(tmpOutputStream).use { outputStream ->
+        ZipInputStream(file.inputStream()).forEachZipEntry { inputStream, inputEntry ->
+          if (inputEntry.isClassFile()) {
+            val parentDirectory = inputEntry.name.substringBeforeLast('/')
+            val match = AggregatedAnnotation.AGGREGATED_PACKAGES.any { aggregatedPackage ->
+              parentDirectory.endsWith(aggregatedPackage)
+            }
+            if (match) {
+              outputStream.putNextEntry(ZipEntry(inputEntry.name))
+              inputStream.copyTo(outputStream)
+              outputStream.closeEntry()
+              atLeastOneEntry = true
+            }
+          }
+        }
+      }
+      if (atLeastOneEntry) {
+        outputs.file(JAR_NAME).outputStream().use { tmpOutputStream.writeTo(it) }
+      }
+    } else if (file.isClassFile()) {
+      // If transforming a file, check if the parent directory matches one of the known aggregated
+      // packages structure. File and Path APIs are used to avoid OS-specific issues when comparing
+      // paths.
+      val parentDirectory: File = file.parentFile
+      val match = AggregatedAnnotation.AGGREGATED_PACKAGES.any { aggregatedPackage ->
+        parentDirectory.endsWith(aggregatedPackage)
+      }
+      if (match) {
+        outputs.file(file)
+      }
+    }
+  }
+
+  companion object {
+    // The output file name containing classes in the aggregated packages.
+    val JAR_NAME = "hiltAggregated.jar"
+  }
+}
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/AndroidGradleCompat.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/AndroidGradleCompat.kt
new file mode 100644
index 0000000..209005a
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/AndroidGradleCompat.kt
@@ -0,0 +1,225 @@
+/*
+ * 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.plugin.util
+
+import com.android.build.api.AndroidPluginVersion
+import com.android.build.api.instrumentation.AsmClassVisitorFactory
+import com.android.build.api.instrumentation.FramesComputationMode
+import com.android.build.api.instrumentation.InstrumentationParameters
+import com.android.build.api.instrumentation.InstrumentationScope
+import com.android.build.api.variant.AndroidComponentsExtension
+import com.android.build.api.variant.ApplicationVariant
+import com.android.build.api.variant.Component
+import com.android.build.api.variant.LibraryVariant
+import com.android.build.api.variant.Variant
+import org.gradle.api.Project
+
+/**
+ * Compatibility version of [com.android.build.api.variant.AndroidComponentsExtension]
+ * - In AGP 4.2 its package is 'com.android.build.api.extension'
+ * - In AGP 7.0 its packages is 'com.android.build.api.variant'
+ */
+sealed class AndroidComponentsExtensionCompat {
+
+  /**
+   * A combined compatibility function of
+   * [com.android.build.api.variant.AndroidComponentsExtension.onVariants] that includes also
+   * [AndroidTest] and [UnitTest] variants.
+   */
+  abstract fun onAllVariants(block: (ComponentCompat) -> Unit)
+
+  class Api70Impl(
+    private val actual: AndroidComponentsExtension<*, *, *>
+  ) : AndroidComponentsExtensionCompat() {
+
+    private val componentInit: (component: Component) -> ComponentCompat = {
+      if (actual.pluginVersion < AndroidPluginVersion(7, 2)) {
+        ComponentCompat.Api70Impl(it)
+      } else {
+        ComponentCompat.Api72Impl(it)
+      }
+    }
+
+    override fun onAllVariants(block: (ComponentCompat) -> Unit) {
+      actual.onVariants { variant ->
+        // Use reflection to get the AndroidTest component out of the variant because a binary
+        // incompatible change was introduced in AGP 7.0-beta05 that changed the return type of the
+        // method.
+        fun ApplicationVariant.getAndroidTest() =
+          this::class.java.getDeclaredMethod("getAndroidTest").invoke(this) as? Component
+        fun LibraryVariant.getAndroidTest() =
+          this::class.java.getDeclaredMethod("getAndroidTest").invoke(this) as? Component
+        block.invoke(componentInit(variant))
+        when (variant) {
+          is ApplicationVariant -> variant.getAndroidTest()
+          is LibraryVariant -> variant.getAndroidTest()
+          else -> null
+        }?.let { block.invoke(componentInit(it)) }
+        // Use reflection too to get the UnitTest component since in 7.2
+        // com.android.build.api.component.UnitTest was removed and replaced by
+        // com.android.build.api.variant.UnitTest causing the return type of Variant#getUnitTest()
+        // to change and break ABI.
+        fun Variant.getUnitTest() =
+          this::class.java.getDeclaredMethod("getUnitTest").invoke(this) as? Component
+        variant.getUnitTest()?.let { block.invoke(componentInit(it)) }
+      }
+    }
+  }
+
+  class Api42Impl(private val actual: Any) : AndroidComponentsExtensionCompat() {
+
+    private val extensionClazz =
+      Class.forName("com.android.build.api.extension.AndroidComponentsExtension")
+
+    private val variantSelectorClazz =
+      Class.forName("com.android.build.api.extension.VariantSelector")
+
+    override fun onAllVariants(block: (ComponentCompat) -> Unit) {
+      val selector = extensionClazz.getDeclaredMethod("selector").invoke(actual)
+      val allSelector = variantSelectorClazz.getDeclaredMethod("all").invoke(selector)
+      val wrapFunction: (Any) -> Unit = {
+        block.invoke(ComponentCompat.Api42Impl(it))
+      }
+      listOf("onVariants", "androidTests", "unitTests").forEach { methodName ->
+        extensionClazz.getDeclaredMethod(
+          methodName, variantSelectorClazz, Function1::class.java
+        ).invoke(actual, allSelector, wrapFunction)
+      }
+    }
+  }
+
+  companion object {
+    fun getAndroidComponentsExtension(project: Project): AndroidComponentsExtensionCompat {
+      return if (
+        findClass("com.android.build.api.variant.AndroidComponentsExtension") != null
+      ) {
+        val actualExtension = project.extensions.getByType(AndroidComponentsExtension::class.java)
+        Api70Impl(actualExtension)
+      } else {
+        val actualExtension = project.extensions.getByType(
+          Class.forName("com.android.build.api.extension.AndroidComponentsExtension")
+        )
+        Api42Impl(actualExtension)
+      }
+    }
+  }
+}
+
+/**
+ * Compatibility version of [com.android.build.api.variant.Component]
+ * - In AGP 4.2 its package is 'com.android.build.api.component'
+ * - In AGP 7.0 its packages is 'com.android.build.api.variant'
+ */
+@Suppress("UnstableApiUsage") // ASM Pipeline APIs
+sealed class ComponentCompat {
+
+  /**
+   * Redeclaration of [com.android.build.api.variant.ComponentIdentity.name]
+   */
+  abstract val name: String
+
+  /**
+   * Redeclaration of [com.android.build.api.variant.Component.transformClassesWith]
+   */
+  abstract fun <ParamT : InstrumentationParameters> transformClassesWith(
+    classVisitorFactoryImplClass: Class<out AsmClassVisitorFactory<ParamT>>,
+    scope: InstrumentationScope,
+    instrumentationParamsConfig: (ParamT) -> Unit
+  )
+
+  /**
+   * Redeclaration of [com.android.build.api.variant.Component.setAsmFramesComputationMode]
+   */
+  abstract fun setAsmFramesComputationMode(mode: FramesComputationMode)
+
+  class Api72Impl(private val component: Component) : ComponentCompat() {
+
+    override val name: String
+      get() = component.name
+
+    override fun <ParamT : InstrumentationParameters> transformClassesWith(
+      classVisitorFactoryImplClass: Class<out AsmClassVisitorFactory<ParamT>>,
+      scope: InstrumentationScope,
+      instrumentationParamsConfig: (ParamT) -> Unit
+    ) {
+      component.instrumentation.transformClassesWith(
+        classVisitorFactoryImplClass, scope, instrumentationParamsConfig
+      )
+    }
+
+    override fun setAsmFramesComputationMode(mode: FramesComputationMode) {
+      component.instrumentation.setAsmFramesComputationMode(mode)
+    }
+  }
+
+  class Api70Impl(private val component: Component) : ComponentCompat() {
+
+    override val name: String
+      get() = component.name
+
+    override fun <ParamT : InstrumentationParameters> transformClassesWith(
+      classVisitorFactoryImplClass: Class<out AsmClassVisitorFactory<ParamT>>,
+      scope: InstrumentationScope,
+      instrumentationParamsConfig: (ParamT) -> Unit
+    ) {
+      Component::class.java.getDeclaredMethod(
+        "transformClassesWith",
+        Class::class.java,
+        InstrumentationScope::class.java,
+        Function1::class.java
+      ).invoke(component, classVisitorFactoryImplClass, scope, instrumentationParamsConfig)
+    }
+
+    override fun setAsmFramesComputationMode(mode: FramesComputationMode) {
+      Component::class.java.getDeclaredMethod(
+        "setAsmFramesComputationMode",
+        FramesComputationMode::class.java
+      ).invoke(component, mode)
+    }
+  }
+
+  class Api42Impl(private val actual: Any) : ComponentCompat() {
+
+    private val componentClazz = Class.forName("com.android.build.api.component.Component")
+
+    override val name: String
+      get() = componentClazz.getMethod("getName").invoke(actual) as String
+
+    override fun <ParamT : InstrumentationParameters> transformClassesWith(
+      classVisitorFactoryImplClass: Class<out AsmClassVisitorFactory<ParamT>>,
+      scope: InstrumentationScope,
+      instrumentationParamsConfig: (ParamT) -> Unit
+    ) {
+      componentClazz.getDeclaredMethod(
+        "transformClassesWith",
+        Class::class.java, InstrumentationScope::class.java, Function1::class.java
+      ).invoke(actual, classVisitorFactoryImplClass, scope, instrumentationParamsConfig)
+    }
+
+    override fun setAsmFramesComputationMode(mode: FramesComputationMode) {
+      componentClazz.getDeclaredMethod(
+        "setAsmFramesComputationMode", FramesComputationMode::class.java
+      ).invoke(actual, mode)
+    }
+  }
+}
+
+fun findClass(fqName: String) = try {
+  Class.forName(fqName)
+} catch (ex: ClassNotFoundException) {
+  null
+}
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
index 39a2d3a..1b1b583 100644
--- 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
@@ -9,9 +9,10 @@
 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.
+/**
+ * A transform that registers the input file (usually a jar or a class) as an output and thus
+ * changing from one artifact type to another.
+ */
 @CacheableTransform
 abstract class CopyTransform : TransformAction<TransformParameters.None> {
   @get:Classpath
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
index e5a101e..c83d635 100644
--- 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
@@ -1,14 +1,59 @@
 package dagger.hilt.android.plugin.util
 
-import com.android.SdkConstants
 import java.io.File
+import java.io.InputStream
+import java.util.Properties
 import java.util.zip.ZipEntry
+import java.util.zip.ZipInputStream
+import org.gradle.api.Project
 
 /* Checks if a file is a .class file. */
-fun File.isClassFile() = this.isFile && this.extension == SdkConstants.EXT_CLASS
+fun File.isClassFile() = this.isFile && this.extension == "class"
 
 /* Checks if a Zip entry is a .class file. */
-fun ZipEntry.isClassFile() = !this.isDirectory && this.name.endsWith(SdkConstants.DOT_CLASS)
+fun ZipEntry.isClassFile() = !this.isDirectory && this.name.endsWith(".class")
 
-/* CHecks if a file is a .jar file. */
-fun File.isJarFile() = this.isFile && this.extension == SdkConstants.EXT_JAR
+/* Checks if a file is a .jar file. */
+fun File.isJarFile() = this.isFile && this.extension == "jar"
+
+/* Executes the given [block] function over each [ZipEntry] in this [ZipInputStream]. */
+fun ZipInputStream.forEachZipEntry(block: (InputStream, ZipEntry) -> Unit) = use {
+  var inputEntry = nextEntry
+  while (inputEntry != null) {
+    block(this, inputEntry)
+    inputEntry = nextEntry
+  }
+}
+
+/* Gets the Android Sdk Path. */
+fun Project.getSdkPath(): File {
+  val localPropsFile = rootProject.projectDir.resolve("local.properties")
+  if (localPropsFile.exists()) {
+    val localProps = Properties()
+    localPropsFile.inputStream().use { localProps.load(it) }
+    val localSdkDir = localProps["sdk.dir"]?.toString()
+    if (localSdkDir != null) {
+      val sdkDirectory = File(localSdkDir)
+      if (sdkDirectory.isDirectory) {
+        return sdkDirectory
+      }
+    }
+  }
+  return getSdkPathFromEnvironmentVariable()
+}
+
+private fun getSdkPathFromEnvironmentVariable(): File {
+  // Check for environment variables, in the order AGP checks.
+  listOf("ANDROID_HOME", "ANDROID_SDK_ROOT").forEach {
+    val envValue = System.getenv(it)
+    if (envValue != null) {
+      val sdkDirectory = File(envValue)
+      if (sdkDirectory.isDirectory) {
+        return sdkDirectory
+      }
+    }
+  }
+  // Only print the error for SDK ROOT since ANDROID_HOME is deprecated but we first check
+  // it because it is prioritized according to the documentation.
+  error("ANDROID_SDK_ROOT environment variable is not set")
+}
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
index 339c83e..cb442b2 100644
--- 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
@@ -46,11 +46,5 @@
 
       return SimpleAGPVersion(parts[0].toInt(), parts[1].toInt())
     }
-
-    private fun findClass(fqName: String) = try {
-      Class.forName(fqName)
-    } catch (ex: ClassNotFoundException) {
-      null
-    }
   }
 }
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/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/Strings.kt
similarity index 70%
copy from java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/UserName.kt
copy to java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/Strings.kt
index a061ab5..82acc26 100644
--- 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/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/Strings.kt
@@ -14,11 +14,14 @@
  * limitations under the License.
  */
 
-package dagger.hilt.android.example.gradle.simpleKotlin
+package dagger.hilt.android.plugin.util
 
-import javax.inject.Qualifier
+import java.util.Locale
 
-/** Qualifies bindings relating to the user name.  */
-@Qualifier
-@Retention(AnnotationRetention.RUNTIME)
-internal annotation class UserName
+fun String.capitalize(
+  locale: Locale = Locale.getDefault()
+): String = if (isNotEmpty() && this[0].isLowerCase()) {
+  substring(0, 1).uppercase(locale) + substring(1)
+} else {
+  this
+}
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/Tasks.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/Tasks.kt
new file mode 100644
index 0000000..b46c3d6
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/Tasks.kt
@@ -0,0 +1,16 @@
+package dagger.hilt.android.plugin.util
+
+import org.gradle.api.Project
+import org.gradle.api.tasks.TaskProvider
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+/**
+ * Gets [KotlinCompile] task of an Android variant.
+ */
+@Suppress("UNCHECKED_CAST")
+internal fun getCompileKotlin(
+  @Suppress("DEPRECATION") variant: com.android.build.gradle.api.BaseVariant,
+  project: Project
+) = project.tasks.named(
+  "compile${variant.name.capitalize()}Kotlin"
+) as TaskProvider<KotlinCompile>
diff --git a/java/dagger/hilt/android/plugin/src/main/resources/META-INF/gradle-plugins/com.google.dagger.hilt.android.properties b/java/dagger/hilt/android/plugin/src/main/resources/META-INF/gradle-plugins/com.google.dagger.hilt.android.properties
new file mode 100644
index 0000000..5d2b9df
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/resources/META-INF/gradle-plugins/com.google.dagger.hilt.android.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/flavored-project/app/build.gradle b/java/dagger/hilt/android/plugin/src/test/data/flavored-project/app/build.gradle
new file mode 100644
index 0000000..c42caf9
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/flavored-project/app/build.gradle
@@ -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.
+ */
+
+plugins {
+    id 'com.android.application'
+    id 'dagger.hilt.android.plugin'
+}
+
+android {
+    compileSdkVersion 30
+    buildToolsVersion "30.0.2"
+
+    flavorDimensions 'api', 'version'
+    productFlavors {
+        demo {
+            dimension 'version'
+        }
+        full {
+            dimension 'version'
+        }
+        minApi24 {
+            dimension 'api'
+            minSdkVersion '24'
+            versionNameSuffix "-minApi24"
+        }
+        minApi21 {
+            dimension "api"
+            minSdkVersion '21'
+            versionNameSuffix "-minApi21"
+        }
+    }
+
+    defaultConfig {
+        applicationId "simple.app"
+        minSdkVersion 21
+        targetSdkVersion 30
+    }
+
+    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(':feature')
+}
+
+hilt {
+    enableAggregatingTask = true
+}
\ No newline at end of file
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/debug/AndroidManifest.xml b/java/dagger/hilt/android/plugin/src/test/data/flavored-project/app/src/main/AndroidManifest.xml
similarity index 73%
copy from java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/debug/AndroidManifest.xml
copy to java/dagger/hilt/android/plugin/src/test/data/flavored-project/app/src/main/AndroidManifest.xml
index 081c7ad..0ce2315 100644
--- a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/debug/AndroidManifest.xml
+++ b/java/dagger/hilt/android/plugin/src/test/data/flavored-project/app/src/main/AndroidManifest.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2020 The Dagger Authors.
+  ~ 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.
@@ -14,11 +13,8 @@
   ~ 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"/>
+    package="simple.app">
+  <application android:name=".SimpleApp" android:label="Flavored App">
   </application>
-</manifest>
+</manifest>
\ No newline at end of file
diff --git a/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/java/dagger/hilt/android/gradleConfigCache/App.kt b/java/dagger/hilt/android/plugin/src/test/data/flavored-project/app/src/main/java/simple/app/SimpleApp.java
similarity index 72%
rename from javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/java/dagger/hilt/android/gradleConfigCache/App.kt
rename to java/dagger/hilt/android/plugin/src/test/data/flavored-project/app/src/main/java/simple/app/SimpleApp.java
index 1cbd4b0..39cf48e 100644
--- a/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/java/dagger/hilt/android/gradleConfigCache/App.kt
+++ b/java/dagger/hilt/android/plugin/src/test/data/flavored-project/app/src/main/java/simple/app/SimpleApp.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Dagger Authors.
+ * 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.
@@ -14,10 +14,12 @@
  * limitations under the License.
  */
 
-package dagger.hilt.android.gradleConfigCache
+package simple.app;
 
-import androidx.multidex.MultiDexApplication
-import dagger.hilt.android.HiltAndroidApp
+import android.app.Application;
+import dagger.hilt.android.HiltAndroidApp;
 
+/** Just an application. */
 @HiltAndroidApp
-class App : MultiDexApplication()
+public class SimpleApp extends Application {
+}
\ No newline at end of file
diff --git a/java/dagger/example/gradle/android/simple/build.gradle b/java/dagger/hilt/android/plugin/src/test/data/flavored-project/build.gradle
similarity index 75%
rename from java/dagger/example/gradle/android/simple/build.gradle
rename to java/dagger/hilt/android/plugin/src/test/data/flavored-project/build.gradle
index 50f8f0b..ec95fa9 100644
--- a/java/dagger/example/gradle/android/simple/build.gradle
+++ b/java/dagger/hilt/android/plugin/src/test/data/flavored-project/build.gradle
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Dagger Authors.
+ * 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.
@@ -17,19 +17,14 @@
 buildscript {
     repositories {
         google()
-        jcenter()
-    }
-    dependencies {
-        classpath 'com.android.tools.build:gradle:4.1.1'
+        mavenCentral()
     }
 }
 
 allprojects {
     repositories {
-      google()
-      jcenter()
-      mavenCentral()
-      mavenLocal()
+        mavenLocal()
+        google()
+        mavenCentral()
     }
-}
-
+}
\ No newline at end of file
diff --git a/java/dagger/hilt/android/plugin/src/test/data/flavored-project/feature/build.gradle b/java/dagger/hilt/android/plugin/src/test/data/flavored-project/feature/build.gradle
new file mode 100644
index 0000000..218a224
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/flavored-project/feature/build.gradle
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+plugins {
+    id 'com.android.library'
+    id 'dagger.hilt.android.plugin'
+}
+
+android {
+    compileSdkVersion 30
+    buildToolsVersion "30.0.2"
+
+    flavorDimensions 'api', 'version'
+    productFlavors {
+        demo {
+            dimension 'version'
+        }
+        full {
+            dimension 'version'
+        }
+        minApi24 {
+            dimension 'api'
+            minSdkVersion '24'
+            versionNameSuffix "-minApi24"
+        }
+        minApi21 {
+            dimension "api"
+            minSdkVersion '21'
+            versionNameSuffix "-minApi21"
+        }
+    }
+
+    defaultConfig {
+        minSdkVersion 16
+        targetSdkVersion 30
+    }
+
+    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/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/res/values/strings.xml b/java/dagger/hilt/android/plugin/src/test/data/flavored-project/feature/src/main/AndroidManifest.xml
similarity index 75%
rename from javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/res/values/strings.xml
rename to java/dagger/hilt/android/plugin/src/test/data/flavored-project/feature/src/main/AndroidManifest.xml
index 05afdbf..fc219c8 100644
--- a/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/res/values/strings.xml
+++ b/java/dagger/hilt/android/plugin/src/test/data/flavored-project/feature/src/main/AndroidManifest.xml
@@ -1,5 +1,5 @@
 <!--
-  ~ Copyright (C) 2020 The Dagger Authors.
+  ~ 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.
@@ -13,8 +13,6 @@
   ~ 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>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="simple.library">
+</manifest>
\ No newline at end of file
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/plugin/src/test/data/flavored-project/feature/src/main/java/simple/library/LibraryCode.java
similarity index 79%
rename from java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
rename to java/dagger/hilt/android/plugin/src/test/data/flavored-project/feature/src/main/java/simple/library/LibraryCode.java
index 0fd3db3..e3bb9dd 100644
--- 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/plugin/src/test/data/flavored-project/feature/src/main/java/simple/library/LibraryCode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Dagger Authors.
+ * 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.
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
-package dagger.hilt.android.example.gradle.simple.feature
+package simple.library;
 
-class FeatureCounter(var count: Int)
+/** Just some 'Library' code */
+public class LibraryCode {
+}
\ No newline at end of file
diff --git a/java/dagger/example/gradle/android/simple/gradle.properties b/java/dagger/hilt/android/plugin/src/test/data/flavored-project/gradle.properties
similarity index 95%
rename from java/dagger/example/gradle/android/simple/gradle.properties
rename to java/dagger/hilt/android/plugin/src/test/data/flavored-project/gradle.properties
index 2d8d1e4..5bac8ac 100644
--- a/java/dagger/example/gradle/android/simple/gradle.properties
+++ b/java/dagger/hilt/android/plugin/src/test/data/flavored-project/gradle.properties
@@ -1 +1 @@
-android.useAndroidX=true
\ No newline at end of file
+android.useAndroidX=true
diff --git a/java/dagger/hilt/android/plugin/src/test/data/flavored-project/settings.gradle b/java/dagger/hilt/android/plugin/src/test/data/flavored-project/settings.gradle
new file mode 100644
index 0000000..d3cc2b0
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/flavored-project/settings.gradle
@@ -0,0 +1,3 @@
+rootProject.name='Flavored Project'
+include ':app'
+include ':feature'
\ 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
index a6e5d93..4c7ba88 100644
--- 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
@@ -21,8 +21,8 @@
 import javax.inject.Inject;
 
 /** Just an activity. */
-@AndroidEntryPoint(AppCompatActivity.class)
-public class Activity1 extends Hilt_Activity1 {
+@AndroidEntryPoint
+public class Activity1 extends AppCompatActivity {
   @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
index f7793be..9c271c6 100644
--- 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
@@ -21,8 +21,8 @@
 import javax.inject.Inject;
 
 /** Just an activity. */
-@AndroidEntryPoint(AppCompatActivity.class)
-public class Activity2 extends Hilt_Activity2 {
+@AndroidEntryPoint
+public class Activity2 extends AppCompatActivity {
   @Inject String data;
 
   // 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
index 5a92241..ded0b7d 100644
--- 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
@@ -20,7 +20,7 @@
 import dagger.hilt.android.HiltAndroidApp;
 
 /** Just an application. */
-@HiltAndroidApp(Application.class)
-public class SimpleApp extends Hilt_SimpleApp {
+@HiltAndroidApp
+public class SimpleApp extends Application {
   // Insert-change
 }
diff --git a/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/test/java/simple/Test1.java b/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/test/java/simple/Test1.java
new file mode 100644
index 0000000..0ed15c7
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/test/java/simple/Test1.java
@@ -0,0 +1,58 @@
+/*
+ * 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 simple;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+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.android.testing.HiltTestApplication;
+import dagger.hilt.components.SingletonComponent;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+/** Just a test. */
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+@Config(application = HiltTestApplication.class)
+public class Test1 {
+
+  @Rule
+  public HiltAndroidRule hiltRule = new HiltAndroidRule(this);
+
+  @Test
+  public void emptyTest() {
+
+  }
+
+  // Insert-change
+
+  /** An inner test module. */
+  @Module
+  @InstallIn(SingletonComponent.class)
+  public static final class TestModule {
+    @Provides
+    public static double provideDouble() {
+      return 0.0;
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/test/java/simple/Test2.java b/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/test/java/simple/Test2.java
new file mode 100644
index 0000000..a812ed6
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/simple-project/src/test/java/simple/Test2.java
@@ -0,0 +1,55 @@
+/*
+ * 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 simple;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+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.android.testing.HiltTestApplication;
+import dagger.hilt.components.SingletonComponent;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+/** Just a test. */
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+@Config(application = HiltTestApplication.class)
+public class Test2 {
+
+  @Rule
+  public HiltAndroidRule hiltRule = new HiltAndroidRule(this);
+
+  @Test
+  public void emptyTest() {
+
+  }
+
+  /** An inner test module. */
+  @Module
+  @InstallIn(SingletonComponent.class)
+  public static final class TestModule {
+    @Provides
+    public static double provideDouble() {
+      return 0.0;
+    }
+  }
+}
\ No newline at end of file
diff --git a/java/dagger/hilt/android/plugin/src/test/kotlin/BuildCacheTest.kt b/java/dagger/hilt/android/plugin/src/test/kotlin/BuildCacheTest.kt
new file mode 100644
index 0000000..517032d
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/kotlin/BuildCacheTest.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.util.UUID
+import org.gradle.testkit.runner.TaskOutcome.FROM_CACHE
+import org.gradle.testkit.runner.TaskOutcome.SUCCESS
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+// Test that verifies the hilt class transform does not break the Gradle's remote build cache.
+@RunWith(Parameterized::class)
+class BuildCacheTest(private val enableAggregatingTask: Boolean) {
+  @get:Rule val gradleHomeFolder = TemporaryFolder()
+
+  @get:Rule val firstProjectDir = TemporaryFolder()
+  lateinit var firstGradleRunner: GradleTestRunner
+
+  @get:Rule val secondProjectDir = TemporaryFolder()
+  lateinit var secondGradleRunner: GradleTestRunner
+
+  private val testId = UUID.randomUUID().toString()
+
+  @Before
+  fun setup() {
+    firstGradleRunner = createGradleRunner(firstProjectDir)
+    secondGradleRunner = createGradleRunner(secondProjectDir)
+  }
+
+  private fun createGradleRunner(folder: TemporaryFolder): GradleTestRunner {
+    val gradleRunner = GradleTestRunner(folder)
+    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.runAdditionalTasks("--build-cache")
+    gradleRunner.addSrc(
+      srcPath = "minimal/MyApp.java",
+      srcContent =
+        """
+        package minimal;
+
+        import android.app.Application;
+
+        @dagger.hilt.android.HiltAndroidApp
+        public class MyApp extends Application {
+          // random id inserted into the code to ensure that the first is never a cache hit and the
+          // second run always is
+          public static String RANDOM_ID = "$testId";
+        }
+        """.trimIndent()
+    )
+    gradleRunner.setAppClassName(".MyApp")
+    gradleRunner.addHiltOption("enableAggregatingTask = $enableAggregatingTask")
+    return gradleRunner
+  }
+
+  // Verifies that library B and library C injected classes are available in the root classpath.
+  @Test
+  fun test_buildCacheHitOnRelocatedProject() {
+    val firstResult = firstGradleRunner.build()
+    assertEquals(firstResult.getTask(":transformDebugClassesWithAsm").outcome, SUCCESS)
+
+    val secondResult = secondGradleRunner.build()
+    val cacheableTasks: List<String> = mutableListOf<String>().apply {
+      add(":checkDebugAarMetadata")
+      add(":checkDebugDuplicateClasses")
+      add(":compileDebugJavaWithJavac")
+      add(":compressDebugAssets")
+      add(":extractDeepLinksDebug")
+      add(":generateDebugBuildConfig")
+      add(":generateDebugResValues")
+      // When aggregating task is enabled, the plugin adds two more tasks that should be
+      // cacheable.
+      if (enableAggregatingTask) {
+        add(":hiltAggregateDepsDebug")
+        add(":hiltJavaCompileDebug")
+      }
+      add(":javaPreCompileDebug")
+      add(":mergeDebugAssets")
+      add(":mergeDebugJavaResource")
+      add(":mergeDebugJniLibFolders")
+      add(":mergeDebugNativeLibs")
+      add(":mergeDebugShaders")
+      add(":mergeExtDexDebug")
+      add(":mergeLibDexDebug")
+      add(":mergeProjectDexDebug")
+      add(":processDebugManifestForPackage")
+      add(":transformDebugClassesWithAsm")
+      add(":validateSigningDebug")
+      add(":writeDebugAppMetadata")
+      add(":writeDebugSigningConfigVersions")
+    }
+
+    val tasksFromCache =
+      secondResult.tasks.filter { it.outcome == FROM_CACHE }.map { it.path }.sorted()
+    assertEquals(cacheableTasks, tasksFromCache)
+  }
+
+  companion object {
+    @JvmStatic
+    @Parameterized.Parameters(name = "enableAggregatingTask = {0}")
+    fun parameters() = listOf(false, true)
+  }
+}
diff --git a/java/dagger/hilt/android/plugin/src/test/kotlin/CompileClasspathTest.kt b/java/dagger/hilt/android/plugin/src/test/kotlin/CompileClasspathTest.kt
index 6141250..d61b370 100644
--- a/java/dagger/hilt/android/plugin/src/test/kotlin/CompileClasspathTest.kt
+++ b/java/dagger/hilt/android/plugin/src/test/kotlin/CompileClasspathTest.kt
@@ -21,8 +21,12 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
 
-class CompileClasspathTest {
+// Test that verifies compile classpath aggregation done by the plugin.
+@RunWith(Parameterized::class)
+class CompileClasspathTest(private val pluginFlagName: String) {
   @get:Rule
   val testProjectDir = TemporaryFolder()
 
@@ -31,11 +35,13 @@
   @Before
   fun setup() {
     gradleRunner = GradleTestRunner(testProjectDir)
-    gradleRunner.addAndroidOption(
-      "lintOptions.checkReleaseBuilds = false"
-    )
+    if (pluginFlagName == "enableExperimentalClasspathAggregation") {
+      gradleRunner.addAndroidOption(
+        "lintOptions.checkReleaseBuilds = false"
+      )
+    }
     gradleRunner.addHiltOption(
-      "enableExperimentalClasspathAggregation = true"
+      "$pluginFlagName = true"
     )
     gradleRunner.addDependencies(
       "implementation 'androidx.appcompat:appcompat:1.1.0'",
@@ -108,4 +114,13 @@
     val assembleTask = result.getTask(":assembleDebug")
     assertEquals(TaskOutcome.SUCCESS, assembleTask.outcome)
   }
+
+  companion object {
+    @JvmStatic
+    @Parameterized.Parameters(name = "{0}")
+    fun parameters() = listOf(
+      "enableExperimentalClasspathAggregation",
+      "enableAggregatingTask"
+    )
+  }
 }
diff --git a/java/dagger/hilt/android/plugin/src/test/kotlin/CrossCompilationRootValidationTest.kt b/java/dagger/hilt/android/plugin/src/test/kotlin/CrossCompilationRootValidationTest.kt
new file mode 100644
index 0000000..e0848dd
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/kotlin/CrossCompilationRootValidationTest.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+
+class CrossCompilationRootValidationTest {
+  @get:Rule
+  val testProjectDir = TemporaryFolder()
+
+  lateinit var gradleRunner: GradleTestRunner
+
+  @Before
+  fun setup() {
+    gradleRunner = GradleTestRunner(testProjectDir)
+    gradleRunner.addHiltOption(
+      "enableAggregatingTask = 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'",
+      "testImplementation 'com.google.dagger:hilt-android-testing:LOCAL-SNAPSHOT'",
+      "testAnnotationProcessor 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'",
+    )
+    gradleRunner.addSrc(
+      srcPath = "minimal/MyApp.java",
+      srcContent =
+      """
+        package minimal;
+
+        import android.app.Application;
+
+        @dagger.hilt.android.HiltAndroidApp
+        public class MyApp extends Application { }
+        """.trimIndent()
+    )
+    gradleRunner.setAppClassName(".MyApp")
+  }
+
+  @Test
+  fun multipleAppRootsFailure() {
+    gradleRunner.addSrc(
+      srcPath = "minimal/MyApp2.java",
+      srcContent =
+      """
+        package minimal;
+
+        import android.app.Application;
+
+        @dagger.hilt.android.HiltAndroidApp
+        public class MyApp2 extends Application { }
+        """.trimIndent()
+    )
+
+    val result = gradleRunner.buildAndFail()
+    assertThat(result.getOutput()).contains(
+      "Cannot process multiple app roots in the same compilation unit: " +
+        "minimal.MyApp, minimal.MyApp2"
+    )
+  }
+
+  @Test
+  fun testRootsAndAppRootsFailure() {
+    gradleRunner.addTestSrc(
+      srcPath = "minimal/MyTest.java",
+      srcContent =
+      """
+        package minimal;
+
+        @dagger.hilt.android.testing.HiltAndroidTest
+        public class MyTest { }
+        """.trimIndent()
+    )
+    gradleRunner.addTestSrc(
+      srcPath = "minimal/BadApp.java",
+      srcContent =
+      """
+        package minimal;
+
+        import android.app.Application;
+
+        @dagger.hilt.android.HiltAndroidApp
+        public class BadApp extends Application { }
+        """.trimIndent()
+    )
+
+    gradleRunner.runAdditionalTasks("testDebug")
+    val result = gradleRunner.buildAndFail()
+    assertThat(result.getOutput()).contains(
+      "Cannot process test roots and app roots in the same compilation unit:"
+    )
+    assertThat(result.getOutput()).contains(
+      "App root in this compilation unit: minimal.BadApp"
+    )
+    assertThat(result.getOutput()).contains(
+      "Test roots in this compilation unit: minimal.MyTest"
+    )
+  }
+}
diff --git a/java/dagger/hilt/android/plugin/src/test/kotlin/GradleTestRunner.kt b/java/dagger/hilt/android/plugin/src/test/kotlin/GradleTestRunner.kt
index 2d58766..d32b40e 100644
--- a/java/dagger/hilt/android/plugin/src/test/kotlin/GradleTestRunner.kt
+++ b/java/dagger/hilt/android/plugin/src/test/kotlin/GradleTestRunner.kt
@@ -16,6 +16,7 @@
 
 import java.io.File
 import org.gradle.testkit.runner.BuildResult
+import org.gradle.testkit.runner.BuildTask
 import org.gradle.testkit.runner.GradleRunner
 import org.junit.rules.TemporaryFolder
 
@@ -31,9 +32,11 @@
   private var buildFile: File? = null
   private var gradlePropertiesFile: File? = null
   private var manifestFile: File? = null
+  private var additionalTasks = mutableListOf<String>()
 
   init {
     tempFolder.newFolder("src", "main", "java", "minimal")
+    tempFolder.newFolder("src", "test", "java", "minimal")
     tempFolder.newFolder("src", "main", "res")
   }
 
@@ -68,6 +71,12 @@
     return tempFolder.newFile("/src/main/java/$srcPath").apply { writeText(srcContent) }
   }
 
+  // Adds a test source file to the project. The source path is relative to 'src/test/java'.
+  fun addTestSrc(srcPath: String, srcContent: String): File {
+    File(tempFolder.root, "src/test/java/${srcPath.substringBeforeLast(File.separator)}").mkdirs()
+    return tempFolder.newFile("/src/test/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()
@@ -78,6 +87,10 @@
     appClassName = name
   }
 
+  fun runAdditionalTasks(taskName: String) {
+    additionalTasks.add(taskName)
+  }
+
   // Executes a Gradle builds and expects it to succeed.
   fun build(): Result {
     setupFiles()
@@ -104,10 +117,10 @@
         buildscript {
           repositories {
             google()
-            jcenter()
+            mavenCentral()
           }
           dependencies {
-            classpath 'com.android.tools.build:gradle:4.2.0-beta04'
+            classpath 'com.android.tools.build:gradle:4.2.0'
           }
         }
 
@@ -137,7 +150,7 @@
           repositories {
             mavenLocal()
             google()
-            jcenter()
+            mavenCentral()
           }
         }
 
@@ -184,7 +197,7 @@
 
   private fun createRunner() = GradleRunner.create()
     .withProjectDir(tempFolder.root)
-    .withArguments("assembleDebug", "--stacktrace")
+    .withArguments(listOf("--stacktrace", "assembleDebug") + additionalTasks)
     .withPluginClasspath()
 //    .withDebug(true) // Add this line to enable attaching a debugger to the gradle test invocation
     .forwardOutput()
@@ -194,6 +207,9 @@
     private val projectRoot: File,
     private val buildResult: BuildResult
   ) {
+
+    val tasks: List<BuildTask> get() = buildResult.tasks
+
     // Finds a task by name.
     fun getTask(name: String) = buildResult.task(name) ?: error("Task '$name' not 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
index cab50aa..0b60bfe 100644
--- a/java/dagger/hilt/android/plugin/src/test/kotlin/IncrementalProcessorTest.kt
+++ b/java/dagger/hilt/android/plugin/src/test/kotlin/IncrementalProcessorTest.kt
@@ -23,13 +23,16 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
 
 /**
  * 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 {
+@RunWith(Parameterized::class)
+class IncrementalProcessorTest(private val incapMode: String) {
 
   @get:Rule
   val testProjectDir = TemporaryFolder()
@@ -43,6 +46,8 @@
   private lateinit var srcActivity2: File
   private lateinit var srcModule1: File
   private lateinit var srcModule2: File
+  private lateinit var srcTest1: File
+  private lateinit var srcTest2: File
 
   // Generated source files
   private lateinit var genHiltApp: File
@@ -56,8 +61,15 @@
   private lateinit var genActivityInjectorDeps2: File
   private lateinit var genModuleDeps1: File
   private lateinit var genModuleDeps2: File
+  private lateinit var genComponentTreeDeps: File
   private lateinit var genHiltComponents: File
   private lateinit var genDaggerHiltApplicationComponent: File
+  private lateinit var genTest1ComponentTreeDeps: File
+  private lateinit var genTest2ComponentTreeDeps: File
+  private lateinit var genTest1HiltComponents: File
+  private lateinit var genTest2HiltComponents: File
+  private lateinit var genTest1DaggerHiltApplicationComponent: File
+  private lateinit var genTest2DaggerHiltApplicationComponent: File
 
   // Compiled classes
   private lateinit var classSrcApp: File
@@ -65,6 +77,8 @@
   private lateinit var classSrcActivity2: File
   private lateinit var classSrcModule1: File
   private lateinit var classSrcModule2: File
+  private lateinit var classSrcTest1: File
+  private lateinit var classSrcTest2: File
   private lateinit var classGenHiltApp: File
   private lateinit var classGenHiltActivity1: File
   private lateinit var classGenHiltActivity2: File
@@ -76,8 +90,15 @@
   private lateinit var classGenActivityInjectorDeps2: File
   private lateinit var classGenModuleDeps1: File
   private lateinit var classGenModuleDeps2: File
+  private lateinit var classGenComponentTreeDeps: File
   private lateinit var classGenHiltComponents: File
   private lateinit var classGenDaggerHiltApplicationComponent: File
+  private lateinit var classGenTest1ComponentTreeDeps: File
+  private lateinit var classGenTest2ComponentTreeDeps: File
+  private lateinit var classGenTest1HiltComponents: File
+  private lateinit var classGenTest2HiltComponents: File
+  private lateinit var classGenTest1DaggerHiltApplicationComponent: File
+  private lateinit var classGenTest2DaggerHiltApplicationComponent: File
 
   // Timestamps of files
   private lateinit var fileToTimestampMap: Map<File, Long>
@@ -87,6 +108,19 @@
   private lateinit var unchangedFiles: Set<File>
   private lateinit var deletedFiles: Set<File>
 
+  private val compileTaskName = if (incapMode == ISOLATING_MODE) {
+    ":hiltJavaCompileDebug"
+  } else {
+    ":compileDebugJavaWithJavac"
+  }
+  private val testCompileTaskName = if (incapMode == ISOLATING_MODE) {
+    ":hiltJavaCompileDebugUnitTest"
+  } else {
+    ":compileDebugUnitTestJavaWithJavac"
+  }
+  private val aggregatingTaskName = ":hiltAggregateDepsDebug"
+  private val testAggregatingTaskName = ":hiltAggregateDepsDebugUnitTest"
+
   @Before
   fun setup() {
     val projectRoot = testProjectDir.root
@@ -99,15 +133,16 @@
       buildscript {
         repositories {
           google()
-          jcenter()
+          mavenCentral()
         }
         dependencies {
-          classpath 'com.android.tools.build:gradle:3.5.3'
+          classpath 'com.android.tools.build:gradle:4.2.0'
         }
       }
 
       plugins {
         id 'com.android.application'
+        id 'dagger.hilt.android.plugin'
       }
 
       android {
@@ -118,6 +153,11 @@
           applicationId "hilt.simple"
           minSdkVersion 21
           targetSdkVersion 30
+          javaCompileOptions {
+            annotationProcessorOptions {
+                arguments += ["dagger.hilt.shareTestComponents" : "true"]
+            }
+          }
         }
 
         compileOptions {
@@ -129,7 +169,7 @@
       repositories {
         mavenLocal()
         google()
-        jcenter()
+        mavenCentral()
       }
 
       dependencies {
@@ -138,84 +178,194 @@
         annotationProcessor 'com.google.dagger:dagger-compiler:LOCAL-SNAPSHOT'
         implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'
         annotationProcessor 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'
+
+        testImplementation 'junit:junit:4.12'
+        testImplementation 'androidx.test.ext:junit:1.1.2'
+        testImplementation 'androidx.test:runner:1.3.0'
+        testImplementation 'org.robolectric:robolectric:4.4'
+        testImplementation 'com.google.dagger:hilt-android-testing:LOCAL-SNAPSHOT'
+        testAnnotationProcessor 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'
+      }
+
+      hilt {
+        enableAggregatingTask = ${if (incapMode == ISOLATING_MODE) "true" else "false"}
       }
       """.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")
+    // Compute directory paths
+    val defaultGenSrcDir = "build/generated/ap_generated_sources/debug/out/"
+    val testDefaultGenSrcDir = "build/generated/ap_generated_sources/debugUnitTest/out/"
+    fun getComponentTreeDepsGenSrcDir(variant: String) = if (incapMode == ISOLATING_MODE) {
+      "build/generated/hilt/component_trees/$variant/"
+    } else {
+      "build/generated/ap_generated_sources/$variant/out/"
+    }
+    val componentTreeDepsGenSrcDir = getComponentTreeDepsGenSrcDir("debug")
+    val testComponentTreeDepsGenSrcDir = getComponentTreeDepsGenSrcDir("debugUnitTest")
+    fun getRootGenSrcDir(variant: String) = if (incapMode == ISOLATING_MODE) {
+      "build/generated/hilt/component_sources/$variant/"
+    } else {
+      "build/generated/ap_generated_sources/$variant/out/"
+    }
+    val rootGenSrcDir = getRootGenSrcDir("debug")
+    val testRootGenSrcDir = getRootGenSrcDir("debugUnitTest")
+    val defaultClassesDir = "build/intermediates/javac/debug/classes"
+    val testDefaultClassesDir = "build/intermediates/javac/debugUnitTest/classes"
+    fun getRootClassesDir(variant: String) = if (incapMode == ISOLATING_MODE) {
+      "build/intermediates/hilt/component_classes/$variant/"
+    } else {
+      "build/intermediates/javac/$variant/classes"
+    }
+    val rootClassesDir = getRootClassesDir("debug")
+    val testRootClassesDir = getRootClassesDir("debugUnitTest")
 
-    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")
+    // Compute file paths
+    srcApp = File(projectRoot, "$MAIN_SRC_DIR/simple/SimpleApp.java")
+    srcActivity1 = File(projectRoot, "$MAIN_SRC_DIR/simple/Activity1.java")
+    srcActivity2 = File(projectRoot, "$MAIN_SRC_DIR/simple/Activity2.java")
+    srcModule1 = File(projectRoot, "$MAIN_SRC_DIR/simple/Module1.java")
+    srcModule2 = File(projectRoot, "$MAIN_SRC_DIR/simple/Module2.java")
+    srcTest1 = File(projectRoot, "$TEST_SRC_DIR/simple/Test1.java")
+    srcTest2 = File(projectRoot, "$TEST_SRC_DIR/simple/Test2.java")
+
+    genHiltApp = File(projectRoot, "$rootGenSrcDir/simple/Hilt_SimpleApp.java")
+    genHiltActivity1 = File(projectRoot, "$defaultGenSrcDir/simple/Hilt_Activity1.java")
+    genHiltActivity2 = File(projectRoot, "$defaultGenSrcDir/simple/Hilt_Activity2.java")
+    genAppInjector = File(projectRoot, "$defaultGenSrcDir/simple/SimpleApp_GeneratedInjector.java")
+    genActivityInjector1 =
+      File(projectRoot, "$defaultGenSrcDir/simple/Activity1_GeneratedInjector.java")
+    genActivityInjector2 =
+      File(projectRoot, "$defaultGenSrcDir/simple/Activity2_GeneratedInjector.java")
     genAppInjectorDeps = File(
       projectRoot,
-      "$GEN_SRC_DIR/hilt_aggregated_deps/_simple_SimpleApp_GeneratedInjector.java"
+      "$defaultGenSrcDir/hilt_aggregated_deps/_simple_SimpleApp_GeneratedInjector.java"
     )
     genActivityInjectorDeps1 = File(
       projectRoot,
-      "$GEN_SRC_DIR/hilt_aggregated_deps/_simple_Activity1_GeneratedInjector.java"
+      "$defaultGenSrcDir/hilt_aggregated_deps/_simple_Activity1_GeneratedInjector.java"
     )
     genActivityInjectorDeps2 = File(
       projectRoot,
-      "$GEN_SRC_DIR/hilt_aggregated_deps/_simple_Activity2_GeneratedInjector.java"
+      "$defaultGenSrcDir/hilt_aggregated_deps/_simple_Activity2_GeneratedInjector.java"
     )
     genModuleDeps1 = File(
       projectRoot,
-      "$GEN_SRC_DIR/hilt_aggregated_deps/_simple_Module1.java"
+      "$defaultGenSrcDir/hilt_aggregated_deps/_simple_Module1.java"
     )
-    genModuleDeps2 = File(projectRoot, "$GEN_SRC_DIR/hilt_aggregated_deps/_simple_Module2.java")
-    genHiltComponents = File(projectRoot, "$GEN_SRC_DIR/simple/SimpleApp_HiltComponents.java")
+    genModuleDeps2 =
+      File(projectRoot, "$defaultGenSrcDir/hilt_aggregated_deps/_simple_Module2.java")
+    genComponentTreeDeps =
+      File(projectRoot, "$componentTreeDepsGenSrcDir/simple/SimpleApp_ComponentTreeDeps.java")
+    genHiltComponents = File(projectRoot, "$rootGenSrcDir/simple/SimpleApp_HiltComponents.java")
     genDaggerHiltApplicationComponent = File(
       projectRoot,
-      "$GEN_SRC_DIR/simple/DaggerSimpleApp_HiltComponents_SingletonC.java"
+      "$rootGenSrcDir/simple/DaggerSimpleApp_HiltComponents_SingletonC.java"
+    )
+    genTest1ComponentTreeDeps = File(
+      projectRoot,
+      testComponentTreeDepsGenSrcDir +
+        "/dagger/hilt/android/internal/testing/root/Test1_ComponentTreeDeps.java"
+    )
+    genTest2ComponentTreeDeps = File(
+      projectRoot,
+      testComponentTreeDepsGenSrcDir +
+        "/dagger/hilt/android/internal/testing/root/Test2_ComponentTreeDeps.java"
+    )
+    genTest1HiltComponents = File(
+      projectRoot,
+      "$testRootGenSrcDir/dagger/hilt/android/internal/testing/root/Test1_HiltComponents.java"
+    )
+    genTest2HiltComponents = File(
+      projectRoot,
+      "$testRootGenSrcDir/dagger/hilt/android/internal/testing/root/Test2_HiltComponents.java"
+    )
+    genTest1DaggerHiltApplicationComponent = File(
+      projectRoot,
+      testRootGenSrcDir +
+        "/dagger/hilt/android/internal/testing/root/DaggerTest1_HiltComponents_SingletonC.java"
+    )
+    genTest2DaggerHiltApplicationComponent = File(
+      projectRoot,
+      testRootGenSrcDir +
+        "/dagger/hilt/android/internal/testing/root/DaggerTest2_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")
+    classSrcApp = File(projectRoot, "$defaultClassesDir/simple/SimpleApp.class")
+    classSrcActivity1 = File(projectRoot, "$defaultClassesDir/simple/Activity1.class")
+    classSrcActivity2 = File(projectRoot, "$defaultClassesDir/simple/Activity2.class")
+    classSrcModule1 = File(projectRoot, "$defaultClassesDir/simple/Module1.class")
+    classSrcModule2 = File(projectRoot, "$defaultClassesDir/simple/Module2.class")
+    classSrcTest1 = File(projectRoot, "$testDefaultClassesDir/simple/Test1.class")
+    classSrcTest2 = File(projectRoot, "$testDefaultClassesDir/simple/Test2.class")
+    classGenHiltApp = File(projectRoot, "$rootClassesDir/simple/Hilt_SimpleApp.class")
+    classGenHiltActivity1 = File(projectRoot, "$defaultClassesDir/simple/Hilt_Activity1.class")
+    classGenHiltActivity2 = File(projectRoot, "$defaultClassesDir/simple/Hilt_Activity2.class")
+    classGenAppInjector =
+      File(projectRoot, "$defaultClassesDir/simple/SimpleApp_GeneratedInjector.class")
     classGenActivityInjector1 = File(
       projectRoot,
-      "$CLASS_DIR/simple/Activity1_GeneratedInjector.class"
+      "$defaultClassesDir/simple/Activity1_GeneratedInjector.class"
     )
     classGenActivityInjector2 = File(
       projectRoot,
-      "$CLASS_DIR/simple/Activity2_GeneratedInjector.class"
+      "$defaultClassesDir/simple/Activity2_GeneratedInjector.class"
     )
     classGenAppInjectorDeps = File(
       projectRoot,
-      "$CLASS_DIR/hilt_aggregated_deps/_simple_SimpleApp_GeneratedInjector.class"
+      "$defaultClassesDir/hilt_aggregated_deps/_simple_SimpleApp_GeneratedInjector.class"
     )
     classGenActivityInjectorDeps1 = File(
       projectRoot,
-      "$CLASS_DIR/hilt_aggregated_deps/_simple_Activity1_GeneratedInjector.class"
+      "$defaultClassesDir/hilt_aggregated_deps/_simple_Activity1_GeneratedInjector.class"
     )
     classGenActivityInjectorDeps2 = File(
       projectRoot,
-      "$CLASS_DIR/hilt_aggregated_deps/_simple_Activity2_GeneratedInjector.class"
+      "$defaultClassesDir/hilt_aggregated_deps/_simple_Activity2_GeneratedInjector.class"
     )
-    classGenModuleDeps1 = File(projectRoot, "$CLASS_DIR/hilt_aggregated_deps/_simple_Module1.class")
-    classGenModuleDeps2 = File(projectRoot, "$CLASS_DIR/hilt_aggregated_deps/_simple_Module2.class")
+    classGenModuleDeps1 =
+      File(projectRoot, "$defaultClassesDir/hilt_aggregated_deps/_simple_Module1.class")
+    classGenModuleDeps2 =
+      File(projectRoot, "$defaultClassesDir/hilt_aggregated_deps/_simple_Module2.class")
+    classGenComponentTreeDeps = File(
+      projectRoot,
+      "$rootClassesDir/simple/SimpleApp_ComponentTreeDeps.class"
+    )
     classGenHiltComponents = File(
       projectRoot,
-      "$CLASS_DIR/simple/SimpleApp_HiltComponents.class"
+      "$rootClassesDir/simple/SimpleApp_HiltComponents.class"
     )
     classGenDaggerHiltApplicationComponent = File(
       projectRoot,
-      "$CLASS_DIR/simple/DaggerSimpleApp_HiltComponents_SingletonC.class"
+      "$rootClassesDir/simple/DaggerSimpleApp_HiltComponents_SingletonC.class"
+    )
+    classGenTest1ComponentTreeDeps = File(
+      projectRoot,
+      testRootClassesDir +
+        "/dagger/hilt/android/internal/testing/root/Test1_ComponentTreeDeps.class"
+    )
+    classGenTest2ComponentTreeDeps = File(
+      projectRoot,
+      testRootClassesDir +
+        "/dagger/hilt/android/internal/testing/root/Test2_ComponentTreeDeps.class"
+    )
+    classGenTest1HiltComponents = File(
+      projectRoot,
+      "$testRootClassesDir/dagger/hilt/android/internal/testing/root/Test1_HiltComponents.class"
+    )
+    classGenTest2HiltComponents = File(
+      projectRoot,
+      "$testRootClassesDir/dagger/hilt/android/internal/testing/root/Test2_HiltComponents.class"
+    )
+    classGenTest1DaggerHiltApplicationComponent = File(
+      projectRoot,
+      testRootClassesDir +
+        "/dagger/hilt/android/internal/testing/root/DaggerTest1_HiltComponents_SingletonC.class"
+    )
+    classGenTest2DaggerHiltApplicationComponent = File(
+      projectRoot,
+      testRootClassesDir +
+        "/dagger/hilt/android/internal/testing/root/DaggerTest2_HiltComponents_SingletonC.class"
     )
   }
 
@@ -224,51 +374,58 @@
     // 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)
+    expect.that(result.task(compileTaskName)!!.outcome).isEqualTo(TaskOutcome.SUCCESS)
 
     // Check annotation processing outputs
     assertFilesExist(
-      genHiltApp,
-      genHiltActivity1,
-      genHiltActivity2,
-      genAppInjector,
-      genActivityInjector1,
-      genActivityInjector2,
-      genAppInjectorDeps,
-      genActivityInjectorDeps1,
-      genActivityInjectorDeps2,
-      genModuleDeps1,
-      genModuleDeps2,
-      genHiltComponents,
-      genDaggerHiltApplicationComponent
+      listOf(
+        genHiltApp,
+        genHiltActivity1,
+        genHiltActivity2,
+        genAppInjector,
+        genActivityInjector1,
+        genActivityInjector2,
+        genAppInjectorDeps,
+        genActivityInjectorDeps1,
+        genActivityInjectorDeps2,
+        genModuleDeps1,
+        genModuleDeps2,
+        genComponentTreeDeps,
+        genHiltComponents,
+        genDaggerHiltApplicationComponent
+      )
     )
 
     // Check compilation outputs
     assertFilesExist(
-      classSrcApp,
-      classSrcActivity1,
-      classSrcActivity2,
-      classSrcModule1,
-      classSrcModule2,
-      classGenHiltApp,
-      classGenHiltActivity1,
-      classGenHiltActivity2,
-      classGenAppInjector,
-      classGenActivityInjector1,
-      classGenActivityInjector2,
-      classGenAppInjectorDeps,
-      classGenActivityInjectorDeps1,
-      classGenActivityInjectorDeps2,
-      classGenModuleDeps1,
-      classGenModuleDeps2,
-      classGenHiltComponents,
-      classGenDaggerHiltApplicationComponent
+      listOf(
+        classSrcApp,
+        classSrcActivity1,
+        classSrcActivity2,
+        classSrcModule1,
+        classSrcModule2,
+        classGenHiltApp,
+        classGenHiltActivity1,
+        classGenHiltActivity2,
+        classGenAppInjector,
+        classGenActivityInjector1,
+        classGenActivityInjector2,
+        classGenAppInjectorDeps,
+        classGenActivityInjectorDeps1,
+        classGenActivityInjectorDeps2,
+        classGenModuleDeps1,
+        classGenModuleDeps2,
+        classGenComponentTreeDeps,
+        classGenHiltComponents,
+        classGenDaggerHiltApplicationComponent
+      )
     )
   }
 
   @Test
-  fun changeActivitySource() {
+  fun changeActivitySource_addPublicMethod() {
     runFullBuild()
+    val componentTreeDepsFullBuild = genComponentTreeDeps.readText(Charsets.UTF_8)
 
     // Change Activity 1 source
     searchAndReplace(
@@ -282,43 +439,158 @@
     )
 
     val result = runIncrementalBuild()
-    expect.that(result.task(COMPILE_TASK)!!.outcome).isEqualTo(TaskOutcome.SUCCESS)
+    expect.that(result.task(compileTaskName)!!.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
-    )
+    val regeneratedSourceFiles = if (incapMode == ISOLATING_MODE) {
+      // * Aggregating task did not run, no change in deps
+      expect.that(result.task(aggregatingTaskName)!!.outcome).isEqualTo(TaskOutcome.UP_TO_DATE)
+      // * Components are re-generated due to a recompilation of a dep
+      listOf(
+        genHiltApp, // Re-gen because components got re-gen
+        genHiltActivity1,
+        genActivityInjector1,
+        genActivityInjectorDeps1,
+        genHiltComponents,
+        genDaggerHiltApplicationComponent
+      )
+    } else {
+      // * Root classes along with components are always re-generated (aggregated processor)
+      listOf(
+        genHiltApp,
+        genHiltActivity1,
+        genAppInjector,
+        genActivityInjector1,
+        genAppInjectorDeps,
+        genActivityInjectorDeps1,
+        genComponentTreeDeps,
+        genHiltComponents,
+        genDaggerHiltApplicationComponent
+      )
+    }
+    assertChangedFiles(FileType.JAVA, regeneratedSourceFiles)
+
+    val componentTreeDepsIncrementalBuild = genComponentTreeDeps.readText(Charsets.UTF_8)
+    expect.withMessage("Full build")
+        .that(componentTreeDepsFullBuild)
+        .isEqualTo(componentTreeDepsIncrementalBuild)
 
     // 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
+    val recompiledClassFiles = if (incapMode == ISOLATING_MODE) {
+      listOf(
+        classSrcActivity1,
+        classGenHiltApp,
+        classGenHiltActivity1,
+        classGenActivityInjector1,
+        classGenActivityInjectorDeps1,
+        classGenComponentTreeDeps, // Re-compiled because reference to activity injector
+        classGenHiltComponents,
+        classGenDaggerHiltApplicationComponent,
+      )
+    } else {
+      // * All aggregating processor gen sources are re-compiled
+      listOf(
+        classSrcActivity1,
+        classGenHiltApp,
+        classGenHiltActivity1,
+        classGenAppInjector,
+        classGenActivityInjector1,
+        classGenAppInjectorDeps,
+        classGenActivityInjectorDeps1,
+        classGenComponentTreeDeps,
+        classGenHiltComponents,
+        classGenDaggerHiltApplicationComponent
+      )
+    }
+    assertChangedFiles(FileType.CLASS, recompiledClassFiles)
+  }
+
+  @Test
+  fun changeActivitySource_addPrivateMethod() {
+    runFullBuild()
+    val componentTreeDepsFullBuild = genComponentTreeDeps.readText(Charsets.UTF_8)
+
+    // Change Activity 1 source
+    searchAndReplace(
+      srcActivity1, "// Insert-change",
+      """
+      private void foo() { }
+      """.trimIndent()
     )
+
+    val result = runIncrementalBuild()
+    val expectedOutcome = if (incapMode == ISOLATING_MODE) {
+      // In isolating mode, changes that do not affect ABI will not cause re-compilation.
+      TaskOutcome.UP_TO_DATE
+    } else {
+      TaskOutcome.SUCCESS
+    }
+    expect.that(result.task(compileTaskName)!!.outcome).isEqualTo(expectedOutcome)
+
+    // Check annotation processing outputs
+    // * Only activity 1 sources are re-generated, isolation in modules and from other activities
+    val regeneratedSourceFiles = if (incapMode == ISOLATING_MODE) {
+      // * Aggregating task did not run, no change in deps
+      expect.that(result.task(aggregatingTaskName)!!.outcome).isEqualTo(TaskOutcome.UP_TO_DATE)
+      listOf(
+        genHiltActivity1,
+        genActivityInjector1,
+        genActivityInjectorDeps1,
+      )
+    } else {
+      // * Root classes along with components are always re-generated (aggregated processor)
+      listOf(
+        genHiltApp,
+        genHiltActivity1,
+        genAppInjector,
+        genActivityInjector1,
+        genAppInjectorDeps,
+        genActivityInjectorDeps1,
+        genComponentTreeDeps,
+        genHiltComponents,
+        genDaggerHiltApplicationComponent
+      )
+    }
+    assertChangedFiles(FileType.JAVA, regeneratedSourceFiles)
+
+    val componentTreeDepsIncrementalBuild = genComponentTreeDeps.readText(Charsets.UTF_8)
+    expect.withMessage("Full build")
+        .that(componentTreeDepsFullBuild)
+        .isEqualTo(componentTreeDepsIncrementalBuild)
+
+    // Check compilation outputs
+    // * Gen sources from activity 1 are re-compiled
+    val recompiledClassFiles = if (incapMode == ISOLATING_MODE) {
+      listOf(
+        classSrcActivity1,
+        classGenHiltActivity1,
+        classGenActivityInjector1,
+        classGenActivityInjectorDeps1
+      )
+    } else {
+      // * All aggregating processor gen sources are re-compiled
+      listOf(
+        classSrcActivity1,
+        classGenHiltApp,
+        classGenHiltActivity1,
+        classGenAppInjector,
+        classGenActivityInjector1,
+        classGenAppInjectorDeps,
+        classGenActivityInjectorDeps1,
+        classGenComponentTreeDeps,
+        classGenHiltComponents,
+        classGenDaggerHiltApplicationComponent
+      )
+    }
+    assertChangedFiles(FileType.CLASS, recompiledClassFiles)
   }
 
   @Test
   fun changeModuleSource() {
     runFullBuild()
+    val componentTreeDepsFullBuild = genComponentTreeDeps.readText(Charsets.UTF_8)
 
     // Change Module 1 source
     searchAndReplace(
@@ -332,39 +604,70 @@
     )
 
     val result = runIncrementalBuild()
-    expect.that(result.task(COMPILE_TASK)!!.outcome).isEqualTo(TaskOutcome.SUCCESS)
+    expect.that(result.task(compileTaskName)!!.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
-    )
+    val regeneratedSourceFiles = if (incapMode == ISOLATING_MODE) {
+      // * Aggregating task did not run, no change in deps
+      expect.that(result.task(aggregatingTaskName)!!.outcome).isEqualTo(TaskOutcome.UP_TO_DATE)
+      // * Components are re-generated due to a recompilation of a dep
+      listOf(
+        genHiltApp, // Re-generated because components got re-generated
+        genModuleDeps1,
+        genHiltComponents,
+        genDaggerHiltApplicationComponent
+      )
+    } else {
+      // * Root classes along with components are always re-generated (aggregated processor)
+      listOf(
+        genHiltApp,
+        genAppInjector,
+        genAppInjectorDeps,
+        genModuleDeps1,
+        genComponentTreeDeps,
+        genHiltComponents,
+        genDaggerHiltApplicationComponent
+      )
+    }
+    assertChangedFiles(FileType.JAVA, regeneratedSourceFiles)
+
+    val componentTreeDepsIncrementalBuild = genComponentTreeDeps.readText(Charsets.UTF_8)
+    expect.withMessage("Full build")
+        .that(componentTreeDepsFullBuild)
+        .isEqualTo(componentTreeDepsIncrementalBuild)
 
     // 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
-    )
+    val recompiledClassFiles = if (incapMode == ISOLATING_MODE) {
+      listOf(
+        classSrcModule1,
+        classGenHiltApp,
+        classGenModuleDeps1,
+        classGenComponentTreeDeps,
+        classGenHiltComponents,
+        classGenDaggerHiltApplicationComponent
+      )
+    } else {
+      // * All aggregating processor gen sources are re-compiled
+      listOf(
+        classSrcModule1,
+        classGenHiltApp,
+        classGenAppInjector,
+        classGenAppInjectorDeps,
+        classGenModuleDeps1,
+        classGenComponentTreeDeps,
+        classGenHiltComponents,
+        classGenDaggerHiltApplicationComponent
+      )
+    }
+    assertChangedFiles(FileType.CLASS, recompiledClassFiles)
   }
 
   @Test
   fun changeAppSource() {
     runFullBuild()
+    val componentTreeDepsFullBuild = genComponentTreeDeps.readText(Charsets.UTF_8)
 
     // Change Application source
     searchAndReplace(
@@ -378,31 +681,63 @@
     )
 
     val result = runIncrementalBuild()
-    expect.that(result.task(COMPILE_TASK)!!.outcome).isEqualTo(TaskOutcome.SUCCESS)
+    expect.that(result.task(compileTaskName)!!.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
-    )
+    val regeneratedSourceFiles = if (incapMode == ISOLATING_MODE) {
+      // * Aggregating task did not run, no change in deps
+      expect.that(result.task(aggregatingTaskName)!!.outcome).isEqualTo(TaskOutcome.UP_TO_DATE)
+      // * Components are re-generated due to a recompilation of a dep
+      listOf(
+        genHiltApp, // Re-generated because components got re-generated
+        genAppInjector,
+        genAppInjectorDeps,
+        genHiltComponents,
+        genDaggerHiltApplicationComponent
+      )
+    } else {
+      // * Root classes along with components are always re-generated (aggregated processor)
+      listOf(
+        genHiltApp,
+        genAppInjector,
+        genAppInjectorDeps,
+        genComponentTreeDeps,
+        genHiltComponents,
+        genDaggerHiltApplicationComponent
+      )
+    }
+    assertChangedFiles(FileType.JAVA, regeneratedSourceFiles)
+
+    val componentTreeDepsIncrementalBuild = genComponentTreeDeps.readText(Charsets.UTF_8)
+    expect.withMessage("Full build")
+        .that(componentTreeDepsFullBuild)
+        .isEqualTo(componentTreeDepsIncrementalBuild)
 
     // 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
-    )
+    val recompiledClassFiles = if (incapMode == ISOLATING_MODE) {
+      listOf(
+        classSrcApp,
+        classGenHiltApp,
+        classGenAppInjector,
+        classGenAppInjectorDeps,
+        classGenComponentTreeDeps,
+        classGenHiltComponents,
+        classGenDaggerHiltApplicationComponent
+      )
+    } else {
+      // * All aggregating processor gen sources are re-compiled
+      listOf(
+        classSrcApp,
+        classGenHiltApp,
+        classGenAppInjector,
+        classGenAppInjectorDeps,
+        classGenComponentTreeDeps,
+        classGenHiltComponents,
+        classGenDaggerHiltApplicationComponent
+      )
+    }
+    assertChangedFiles(FileType.CLASS, recompiledClassFiles)
   }
 
   @Test
@@ -412,43 +747,70 @@
     srcActivity2.delete()
 
     val result = runIncrementalBuild()
-    expect.that(result.task(COMPILE_TASK)!!.outcome).isEqualTo(TaskOutcome.SUCCESS)
+    expect.that(result.task(compileTaskName)!!.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
+      listOf(
+        genHiltActivity2,
+        genActivityInjector2,
+        genActivityInjectorDeps2
+      )
     )
-    assertChangedFiles(
-      FileType.JAVA,
-      genHiltApp,
-      genAppInjector,
-      genAppInjectorDeps,
-      genHiltComponents,
-      genDaggerHiltApplicationComponent
-    )
+    val regeneratedSourceFiles = if (incapMode == ISOLATING_MODE) {
+      // * Aggregating task ran due to a change in dep
+      expect.that(result.task(aggregatingTaskName)!!.outcome).isEqualTo(TaskOutcome.SUCCESS)
+      // * Components are re-generated since there was a change in dep
+      listOf(
+        genHiltApp, // Re-generated because components got re-generated
+        genComponentTreeDeps,
+        genHiltComponents,
+        genDaggerHiltApplicationComponent
+      )
+    } else {
+      listOf(
+        genHiltApp,
+        genAppInjector,
+        genAppInjectorDeps,
+        genComponentTreeDeps,
+        genHiltComponents,
+        genDaggerHiltApplicationComponent
+      )
+    }
+    assertChangedFiles(FileType.JAVA, regeneratedSourceFiles)
 
     // 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
+      listOf(
+        classSrcActivity2,
+        classGenHiltActivity2,
+        classGenActivityInjector2,
+        classGenActivityInjectorDeps2
+      )
     )
-    assertChangedFiles(
-      FileType.CLASS,
-      classGenHiltApp,
-      classGenAppInjector,
-      classGenAppInjectorDeps,
-      classGenHiltComponents,
-      classGenDaggerHiltApplicationComponent
-    )
+    val recompiledClassFiles = if (incapMode == ISOLATING_MODE) {
+      listOf(
+        classGenHiltApp,
+        classGenComponentTreeDeps,
+        classGenHiltComponents,
+        classGenDaggerHiltApplicationComponent
+      )
+    } else {
+      listOf(
+        classGenHiltApp,
+        classGenAppInjector,
+        classGenAppInjectorDeps,
+        classGenComponentTreeDeps,
+        classGenHiltComponents,
+        classGenDaggerHiltApplicationComponent
+      )
+    }
+    assertChangedFiles(FileType.CLASS, recompiledClassFiles)
   }
 
   @Test
@@ -458,39 +820,277 @@
     srcModule2.delete()
 
     val result = runIncrementalBuild()
-    expect.that(result.task(COMPILE_TASK)!!.outcome).isEqualTo(TaskOutcome.SUCCESS)
+    expect.that(result.task(compileTaskName)!!.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
+      listOf(genModuleDeps2)
     )
-    assertChangedFiles(
-      FileType.JAVA,
-      genHiltApp,
-      genAppInjector,
-      genAppInjectorDeps,
-      genHiltComponents,
-      genDaggerHiltApplicationComponent
-    )
+    val regeneratedSourceFiles = if (incapMode == ISOLATING_MODE) {
+      // * Aggregating task ran due to a change in dep
+      expect.that(result.task(aggregatingTaskName)!!.outcome).isEqualTo(TaskOutcome.SUCCESS)
+      // * Components are re-generated since there was a change in dep
+      listOf(
+        genHiltApp, // Re-generated because components got re-generated
+        genComponentTreeDeps,
+        genHiltComponents,
+        genDaggerHiltApplicationComponent
+      )
+    } else {
+      // * Root classes along with components are always re-generated (aggregated processor)
+      listOf(
+        genHiltApp,
+        genAppInjector,
+        genAppInjectorDeps,
+        genComponentTreeDeps,
+        genHiltComponents,
+        genDaggerHiltApplicationComponent
+      )
+    }
+    assertChangedFiles(FileType.JAVA, regeneratedSourceFiles)
 
     // 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
+      listOf(
+        classSrcModule2,
+        classGenModuleDeps2
+      )
     )
-    assertChangedFiles(
-      FileType.CLASS,
-      classGenHiltApp,
-      classGenAppInjector,
-      classGenAppInjectorDeps,
-      classGenHiltComponents,
-      classGenDaggerHiltApplicationComponent
+    val recompiledClassFiles = if (incapMode == ISOLATING_MODE) {
+      listOf(
+        classGenHiltApp,
+        classGenComponentTreeDeps,
+        classGenHiltComponents,
+        classGenDaggerHiltApplicationComponent
+      )
+    } else {
+      listOf(
+        classGenHiltApp,
+        classGenAppInjector,
+        classGenAppInjectorDeps,
+        classGenComponentTreeDeps,
+        classGenHiltComponents,
+        classGenDaggerHiltApplicationComponent
+      )
+    }
+    assertChangedFiles(FileType.CLASS, recompiledClassFiles)
+  }
+
+  @Test
+  fun addNewSource() {
+    runFullBuild()
+
+    val newSource = File(testProjectDir.root, "$MAIN_SRC_DIR/simple/Foo.java")
+    newSource.writeText(
+      """
+        package simple;
+
+        public class Foo { }
+      """.trimIndent()
     )
+
+    val result = runIncrementalBuild()
+    val expectedOutcome = if (incapMode == ISOLATING_MODE) {
+      // In isolating mode, component compile task does not re-compile.
+      TaskOutcome.UP_TO_DATE
+    } else {
+      TaskOutcome.SUCCESS
+    }
+    expect.that(result.task(compileTaskName)!!.outcome).isEqualTo(expectedOutcome)
+
+    val regeneratedSourceFiles = if (incapMode == ISOLATING_MODE) {
+      // * Aggregating task did not run, no change in deps
+      expect.that(result.task(aggregatingTaskName)!!.outcome).isEqualTo(TaskOutcome.UP_TO_DATE)
+      // * Non-DI related source causes no files to be generated
+      emptyList()
+    } else {
+      // * Root classes are always re-generated (aggregated processor)
+      listOf(
+        genHiltApp,
+        genAppInjector,
+        genAppInjectorDeps,
+      )
+    }
+    assertChangedFiles(FileType.JAVA, regeneratedSourceFiles)
+
+    val recompiledClassFiles = if (incapMode == ISOLATING_MODE) {
+      emptyList()
+    } else {
+      listOf(
+        classGenHiltApp,
+        classGenAppInjector,
+        classGenAppInjectorDeps,
+      )
+    }
+    assertChangedFiles(FileType.CLASS, recompiledClassFiles)
+  }
+
+  @Test
+  fun firstTestFullBuild() {
+    val result = runFullTestBuild()
+    expect.that(result.task(testCompileTaskName)!!.outcome).isEqualTo(TaskOutcome.SUCCESS)
+
+    assertFilesExist(
+      listOf(
+        genTest1ComponentTreeDeps,
+        genTest2ComponentTreeDeps,
+        genTest1HiltComponents,
+        genTest2HiltComponents,
+        genTest1DaggerHiltApplicationComponent,
+        genTest2DaggerHiltApplicationComponent,
+      )
+    )
+
+    assertFilesExist(
+      listOf(
+        classSrcTest1,
+        classSrcTest2,
+        classGenTest1ComponentTreeDeps,
+        classGenTest2ComponentTreeDeps,
+        classGenTest1HiltComponents,
+        classGenTest2HiltComponents,
+        classGenTest1DaggerHiltApplicationComponent,
+        classGenTest2DaggerHiltApplicationComponent,
+      )
+    )
+  }
+
+  @Test
+  fun changeTestSource_addPublicMethod() {
+    runFullTestBuild()
+    val test1ComponentTreeDepsFullBuild = genTest1ComponentTreeDeps.readText(Charsets.UTF_8)
+    val test2ComponentTreeDepsFullBuild = genTest2ComponentTreeDeps.readText(Charsets.UTF_8)
+
+    // Change Test 1 source
+    searchAndReplace(
+      srcTest1, "// Insert-change",
+      """
+      @Test
+      public void newTest() { }
+      """.trimIndent()
+    )
+
+    val result = runIncrementalTestBuild()
+    expect.that(result.task(testCompileTaskName)!!.outcome).isEqualTo(TaskOutcome.SUCCESS)
+
+    // Check annotation processing outputs
+    // * Unrelated test components should be unchanged
+
+    val regeneratedSourceFiles = if (incapMode == ISOLATING_MODE) {
+      listOf(
+        genTest1HiltComponents,
+        genTest1DaggerHiltApplicationComponent,
+      )
+    } else {
+      listOf(
+        genTest1ComponentTreeDeps,
+        genTest2ComponentTreeDeps,
+        genTest1HiltComponents,
+        genTest2HiltComponents,
+        genTest1DaggerHiltApplicationComponent,
+        genTest2DaggerHiltApplicationComponent,
+      )
+    }
+    assertChangedFiles(FileType.JAVA, regeneratedSourceFiles)
+
+    val test1ComponentTreeDepsIncrementalBuild = genTest1ComponentTreeDeps.readText(Charsets.UTF_8)
+    val test2ComponentTreeDepsIncrementalBuild = genTest2ComponentTreeDeps.readText(Charsets.UTF_8)
+    expect.withMessage("Full build")
+        .that(test1ComponentTreeDepsFullBuild)
+        .isEqualTo(test1ComponentTreeDepsIncrementalBuild)
+    expect.withMessage("Full build")
+        .that(test2ComponentTreeDepsFullBuild)
+        .isEqualTo(test2ComponentTreeDepsIncrementalBuild)
+
+    val recompiledClassFiles = if (incapMode == ISOLATING_MODE) {
+      listOf(
+        classSrcTest1,
+        classGenTest1ComponentTreeDeps,
+        classGenTest1HiltComponents,
+        classGenTest1DaggerHiltApplicationComponent,
+      )
+    } else {
+      listOf(
+        classSrcTest1,
+        classGenTest1ComponentTreeDeps,
+        classGenTest2ComponentTreeDeps,
+        classGenTest1HiltComponents,
+        classGenTest2HiltComponents,
+        classGenTest1DaggerHiltApplicationComponent,
+        classGenTest2DaggerHiltApplicationComponent,
+      )
+    }
+    assertChangedFiles(FileType.CLASS, recompiledClassFiles)
+  }
+
+  @Test
+  fun changeTestSource_addPrivateMethod() {
+    runFullTestBuild()
+    val test1ComponentTreeDepsFullBuild = genTest1ComponentTreeDeps.readText(Charsets.UTF_8)
+    val test2ComponentTreeDepsFullBuild = genTest2ComponentTreeDeps.readText(Charsets.UTF_8)
+
+    // Change Test 1 source
+    searchAndReplace(
+      srcTest1, "// Insert-change",
+      """
+      private void newMethod() { }
+      """.trimIndent()
+    )
+
+    val result = runIncrementalTestBuild()
+    val expectedOutcome = if (incapMode == ISOLATING_MODE) {
+      // In isolating mode, changes that do not affect ABI will not cause re-compilation.
+      TaskOutcome.UP_TO_DATE
+    } else {
+      TaskOutcome.SUCCESS
+    }
+    expect.that(result.task(testCompileTaskName)!!.outcome).isEqualTo(expectedOutcome)
+
+    // Check annotation processing outputs
+    // * Unrelated test components should be unchanged
+
+    val regeneratedSourceFiles = if (incapMode == ISOLATING_MODE) {
+      emptyList()
+    } else {
+      listOf(
+        genTest1ComponentTreeDeps,
+        genTest2ComponentTreeDeps,
+        genTest1HiltComponents,
+        genTest2HiltComponents,
+        genTest1DaggerHiltApplicationComponent,
+        genTest2DaggerHiltApplicationComponent,
+      )
+    }
+    assertChangedFiles(FileType.JAVA, regeneratedSourceFiles)
+
+    val test1ComponentTreeDepsIncrementalBuild = genTest1ComponentTreeDeps.readText(Charsets.UTF_8)
+    val test2ComponentTreeDepsIncrementalBuild = genTest2ComponentTreeDeps.readText(Charsets.UTF_8)
+    expect.withMessage("Full build")
+        .that(test1ComponentTreeDepsFullBuild)
+        .isEqualTo(test1ComponentTreeDepsIncrementalBuild)
+    expect.withMessage("Full build")
+        .that(test2ComponentTreeDepsFullBuild)
+        .isEqualTo(test2ComponentTreeDepsIncrementalBuild)
+
+    val recompiledClassFiles = if (incapMode == ISOLATING_MODE) {
+      listOf(classSrcTest1)
+    } else {
+      listOf(
+        classSrcTest1,
+        classGenTest1ComponentTreeDeps,
+        classGenTest2ComponentTreeDeps,
+        classGenTest1HiltComponents,
+        classGenTest2HiltComponents,
+        classGenTest1DaggerHiltApplicationComponent,
+        classGenTest2DaggerHiltApplicationComponent,
+      )
+    }
+    assertChangedFiles(FileType.CLASS, recompiledClassFiles)
   }
 
   private fun runGradleTasks(vararg args: String): BuildResult {
@@ -503,16 +1103,30 @@
   }
 
   private fun runFullBuild(): BuildResult {
-    val result = runGradleTasks(CLEAN_TASK, COMPILE_TASK)
+    val result = runGradleTasks(CLEAN_TASK, compileTaskName)
+    recordTimestamps()
+    return result
+  }
+
+  private fun runFullTestBuild(): BuildResult {
+    runFullBuild()
+    val result = runIncrementalTestBuild()
     recordTimestamps()
     return result
   }
 
   private fun runIncrementalBuild(): BuildResult {
-    val result = runGradleTasks(COMPILE_TASK)
+    val result = runGradleTasks(compileTaskName)
     recordFileChanges()
     return result
   }
+
+  private fun runIncrementalTestBuild(): BuildResult {
+    val result = runGradleTasks(testCompileTaskName)
+    recordFileChanges()
+    return result
+  }
+
   private fun recordTimestamps() {
     val files = listOf(
       genHiltApp,
@@ -526,13 +1140,22 @@
       genActivityInjectorDeps2,
       genModuleDeps1,
       genModuleDeps2,
+      genComponentTreeDeps,
       genHiltComponents,
       genDaggerHiltApplicationComponent,
+      genTest1ComponentTreeDeps,
+      genTest2ComponentTreeDeps,
+      genTest1HiltComponents,
+      genTest2HiltComponents,
+      genTest1DaggerHiltApplicationComponent,
+      genTest2DaggerHiltApplicationComponent,
       classSrcApp,
       classSrcActivity1,
       classSrcActivity2,
       classSrcModule1,
       classSrcModule2,
+      classSrcTest1,
+      classSrcTest2,
       classGenHiltApp,
       classGenHiltActivity1,
       classGenHiltActivity2,
@@ -544,8 +1167,15 @@
       classGenActivityInjectorDeps2,
       classGenModuleDeps1,
       classGenModuleDeps2,
+      classGenComponentTreeDeps,
       classGenHiltComponents,
-      classGenDaggerHiltApplicationComponent
+      classGenDaggerHiltApplicationComponent,
+      classGenTest1ComponentTreeDeps,
+      classGenTest2ComponentTreeDeps,
+      classGenTest1HiltComponents,
+      classGenTest2HiltComponents,
+      classGenTest1DaggerHiltApplicationComponent,
+      classGenTest2DaggerHiltApplicationComponent,
     )
 
     fileToTimestampMap = mutableMapOf<File, Long>().apply {
@@ -567,19 +1197,19 @@
     deletedFiles = fileToTimestampMap.filter { (file, _) -> !file.exists() }.keys
   }
 
-  private fun assertFilesExist(vararg files: File) {
+  private fun assertFilesExist(files: Collection<File>) {
     expect.withMessage("Existing files")
       .that(files.filter { it.exists() })
       .containsExactlyElementsIn(files)
   }
 
-  private fun assertChangedFiles(type: FileType, vararg files: File) {
+  private fun assertChangedFiles(type: FileType, files: Collection<File>) {
     expect.withMessage("Changed files")
       .that(changedFiles.filter { it.name.endsWith(type.extension) })
       .containsExactlyElementsIn(files)
   }
 
-  private fun assertDeletedFiles(vararg files: File) {
+  private fun assertDeletedFiles(files: Collection<File>) {
     expect.withMessage("Deleted files").that(deletedFiles).containsAtLeastElementsIn(files)
   }
 
@@ -593,11 +1223,19 @@
   }
 
   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"
 
+    @JvmStatic
+    @Parameterized.Parameters(name = "{0}")
+    fun parameters() = listOf(
+      ISOLATING_MODE,
+      AGGREGATING_MODE
+    )
+
+    private const val ISOLATING_MODE = "isolating"
+    private const val AGGREGATING_MODE = "aggregating"
+
+    private const val MAIN_SRC_DIR = "src/main/java"
+    private const val TEST_SRC_DIR = "src/test/java"
     private const val CLEAN_TASK = ":clean"
-    private const val COMPILE_TASK = ":compileDebugJavaWithJavac"
   }
 }
diff --git a/java/dagger/hilt/android/plugin/src/test/kotlin/NoTransformTest.kt b/java/dagger/hilt/android/plugin/src/test/kotlin/NoTransformTest.kt
new file mode 100644
index 0000000..cb67c2c
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/kotlin/NoTransformTest.kt
@@ -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.
+ */
+
+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 NoTransformTest {
+
+  @get:Rule
+  val testProjectDir = TemporaryFolder()
+
+  lateinit var gradleRunner: GradleTestRunner
+
+  @Before
+  fun setup() {
+    gradleRunner = GradleTestRunner(testProjectDir)
+  }
+
+  // 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.addAndroidOption(
+      "buildFeatures.buildConfig = false"
+    )
+
+    val result = gradleRunner.build()
+    val assembleTask = result.getTask(":assembleDebug")
+    Assert.assertEquals(TaskOutcome.SUCCESS, assembleTask.outcome)
+  }
+}
diff --git a/java/dagger/hilt/android/plugin/src/test/kotlin/TransformTest.kt b/java/dagger/hilt/android/plugin/src/test/kotlin/TransformTest.kt
index 0b84002..d45dc3e 100644
--- a/java/dagger/hilt/android/plugin/src/test/kotlin/TransformTest.kt
+++ b/java/dagger/hilt/android/plugin/src/test/kotlin/TransformTest.kt
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
+import com.google.common.truth.Truth.assertThat
 import java.io.DataInputStream
 import java.io.FileInputStream
 import javassist.bytecode.ByteArray
 import javassist.bytecode.ClassFile
-import junit.framework.Assert.assertEquals
+import javassist.bytecode.SignatureAttribute
 import org.gradle.testkit.runner.TaskOutcome
 import org.junit.Assert
 import org.junit.Before
@@ -278,12 +279,12 @@
 
     gradleRunner.build().let {
       val assembleTask = it.getTask(TRANSFORM_TASK_NAME)
-      assertEquals(TaskOutcome.SUCCESS, assembleTask.outcome)
+      assertThat(assembleTask.outcome).isEqualTo(TaskOutcome.SUCCESS)
     }
 
     gradleRunner.build().let {
       val assembleTask = it.getTask(TRANSFORM_TASK_NAME)
-      assertEquals(TaskOutcome.UP_TO_DATE, assembleTask.outcome)
+      assertThat(assembleTask.outcome).isEqualTo(TaskOutcome.UP_TO_DATE)
     }
 
     gradleRunner.addSrc(
@@ -303,7 +304,94 @@
 
     val result = gradleRunner.build()
     val assembleTask = result.getTask(TRANSFORM_TASK_NAME)
-    assertEquals(TaskOutcome.SUCCESS, assembleTask.outcome)
+    assertThat(assembleTask.outcome).isEqualTo(TaskOutcome.SUCCESS)
+  }
+
+  // Verifies the Signature attribute in the ClassFile is updated when the superclass uses type
+  // variables or parameterized types.
+  // See: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.9
+  @Test
+  fun testTransform_genericSuperclass() {
+    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/BaseActivity.java",
+      srcContent =
+      """
+        package minimal;
+
+        import androidx.appcompat.app.AppCompatActivity;
+
+        public abstract class BaseActivity<T> extends AppCompatActivity {
+        }
+        """.trimIndent()
+    )
+
+    gradleRunner.addSrc(
+      srcPath = "minimal/SimpleActivity.java",
+      srcContent =
+      """
+        package minimal;
+
+        @dagger.hilt.android.AndroidEntryPoint
+        public class SimpleActivity extends BaseActivity<String> {
+        }
+        """.trimIndent()
+    )
+
+    gradleRunner.addSrc(
+      srcPath = "minimal/BasicActivityThing.java",
+      srcContent =
+      """
+        package minimal;
+
+        public class BasicActivityThing {
+        }
+        """.trimIndent()
+    )
+
+    gradleRunner.addSrc(
+      srcPath = "minimal/BasicActivity.java",
+      srcContent =
+      """
+        package minimal;
+
+        @dagger.hilt.android.AndroidEntryPoint
+        public class BasicActivity extends BaseActivity<BasicActivityThing> {
+        }
+        """.trimIndent()
+    )
+
+    val result = gradleRunner.build()
+    val assembleTask = result.getTask(":assembleDebug")
+    Assert.assertEquals(TaskOutcome.SUCCESS, assembleTask.outcome)
+
+    val transformedClass1 = result.getTransformedFile("minimal/SimpleActivity.class")
+    FileInputStream(transformedClass1).use { fileInput ->
+      ClassFile(DataInputStream(fileInput)).let { classFile ->
+        Assert.assertEquals("minimal.Hilt_SimpleActivity", classFile.superclass)
+        val signatureAttr = classFile.getAttribute(SignatureAttribute.tag) as SignatureAttribute
+        Assert.assertEquals(
+          "Lminimal/Hilt_SimpleActivity<Ljava/lang/String;>;",
+          signatureAttr.signature
+        )
+      }
+    }
+
+    val transformedClass2 = result.getTransformedFile("minimal/BasicActivity.class")
+    FileInputStream(transformedClass2).use { fileInput ->
+      ClassFile(DataInputStream(fileInput)).let { classFile ->
+        val signatureAttr = classFile.getAttribute(SignatureAttribute.tag) as SignatureAttribute
+        Assert.assertEquals(
+          "Lminimal/Hilt_BasicActivity<Lminimal/BasicActivityThing;>;",
+          signatureAttr.signature
+        )
+      }
+    }
   }
 
   companion object {
diff --git a/java/dagger/hilt/android/plugin/src/test/kotlin/VariantsConfigurationTest.kt b/java/dagger/hilt/android/plugin/src/test/kotlin/VariantsConfigurationTest.kt
new file mode 100644
index 0000000..fb4c9c3
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/kotlin/VariantsConfigurationTest.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+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
+
+// Verifies the aggregated task is configured correctly in a multi-module flavored project.
+class VariantsConfigurationTest {
+  @get:Rule
+  val testProjectDir = TemporaryFolder()
+
+  @get:Rule
+  val expect: Expect = Expect.create()
+
+  @Before
+  fun setup() {
+    val projectRoot = testProjectDir.root
+    File("src/test/data/flavored-project").copyRecursively(projectRoot)
+  }
+
+  @Test
+  fun verifyFlavorConfiguration_demoDebug() {
+    val result = runGradleTasks(":app:assembleMinApi21DemoDebug")
+    expect.that(result.task(":app:assembleMinApi21DemoDebug")!!.outcome)
+      .isEqualTo(TaskOutcome.SUCCESS)
+  }
+
+  @Test
+  fun verifyFlavorConfiguration_fullRelease() {
+    val result = runGradleTasks(":app:assembleMinApi24FullRelease")
+    expect.that(result.task(":app:assembleMinApi24FullRelease")!!.outcome)
+      .isEqualTo(TaskOutcome.SUCCESS)
+  }
+
+  private fun runGradleTasks(vararg args: String): BuildResult {
+    return GradleRunner.create()
+      .withProjectDir(testProjectDir.root)
+      .withArguments(*args)
+      .withPluginClasspath()
+      .forwardOutput()
+      .build()
+  }
+}
diff --git a/java/dagger/hilt/android/processor/BUILD b/java/dagger/hilt/android/processor/BUILD
index ccf2103..869e3e2 100644
--- a/java/dagger/hilt/android/processor/BUILD
+++ b/java/dagger/hilt/android/processor/BUILD
@@ -66,9 +66,11 @@
         "//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:component_tree_deps_processor_lib",
+        "//java/dagger/hilt/processor/internal/root:root_processor_lib",
         "//java/dagger/hilt/processor/internal/root:root_metadata",
         "//java/dagger/hilt/processor/internal/root:root_type",
+        "//java/dagger/hilt/processor/internal/root/ir:ir",
         "//java/dagger/hilt/processor/internal/uninstallmodules:processor_lib",
         "//java/dagger/hilt/processor/internal/uninstallmodules:aggregated_uninstall_modules_metadata",
     ],
@@ -85,6 +87,7 @@
         "javax.inject:javax.inject",
         "net.ltgt.gradle.incap:incap",
         "org.jetbrains.kotlin:kotlin-stdlib",
+        "org.jetbrains.kotlin:kotlin-stdlib-jdk8",
         "org.jetbrains.kotlinx:kotlinx-metadata-jvm",
     ],
     javadoc_android_api_level = 30,
@@ -95,8 +98,10 @@
     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"],
+    # The shaded deps are added using jarjar, but they won't be shaded until later
+    # due to: https://github.com/google/dagger/issues/2765. For the shaded rules see
+    # util/deploy-hilt.sh
+    shaded_deps = ["//third_party/java/auto:common"],
 )
 
 filegroup(
diff --git a/java/dagger/hilt/android/processor/internal/AndroidClassNames.java b/java/dagger/hilt/android/processor/internal/AndroidClassNames.java
index d370920..2d95448 100644
--- a/java/dagger/hilt/android/processor/internal/AndroidClassNames.java
+++ b/java/dagger/hilt/android/processor/internal/AndroidClassNames.java
@@ -58,6 +58,10 @@
       get("dagger.hilt.android", "WithFragmentBindings");
   public static final ClassName HILT_ANDROID_APP =
       get("dagger.hilt.android", "HiltAndroidApp");
+  public static final ClassName CUSTOM_INJECT =
+      get("dagger.hilt.android.migration", "CustomInject");
+  public static final ClassName CUSTOM_INJECTION =
+      get("dagger.hilt.android.migration", "CustomInjection");
   public static final ClassName OPTIONAL_INJECT =
       get("dagger.hilt.android.migration", "OptionalInject");
 
@@ -78,6 +82,9 @@
   public static final ClassName VIEW_MODEL_COMPONENT =
       get("dagger.hilt.android.components", "ViewModelComponent");
 
+  public static final ClassName FRAGMENT_GET_CONTEXT_FIX =
+      get("dagger.hilt.android.flags", "FragmentGetContextFix");
+
   public static final ClassName ACTIVITY_COMPONENT_MANAGER =
       get("dagger.hilt.android.internal.managers", "ActivityComponentManager");
   public static final ClassName APPLICATION_COMPONENT_MANAGER =
@@ -93,6 +100,8 @@
   public static final ClassName VIEW_COMPONENT_MANAGER =
       get("dagger.hilt.android.internal.managers", "ViewComponentManager");
 
+  public static final ClassName HAS_CUSTOM_INJECT =
+      get("dagger.hilt.android.internal.migration", "HasCustomInject");
   public static final ClassName INJECTED_BY_HILT =
       get("dagger.hilt.android.internal.migration", "InjectedByHilt");
 
diff --git a/java/dagger/hilt/android/processor/internal/BUILD b/java/dagger/hilt/android/processor/internal/BUILD
index aaf8b89..dfd7333 100644
--- a/java/dagger/hilt/android/processor/internal/BUILD
+++ b/java/dagger/hilt/android/processor/internal/BUILD
@@ -23,7 +23,7 @@
         "AndroidClassNames.java",
     ],
     deps = [
-        "@google_bazel_common//third_party/java/javapoet",
+        "//third_party/java/javapoet",
     ],
 )
 
@@ -34,8 +34,8 @@
         "MoreTypes.java",
     ],
     deps = [
-        "//java/dagger/internal/guava:base",
-        "//java/dagger/internal/guava:collect",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
     ],
 )
 
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGenerator.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGenerator.java
index 86fbaa7..f933a90 100644
--- a/java/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGenerator.java
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGenerator.java
@@ -67,6 +67,7 @@
 
     Generators.addComponentOverride(metadata, builder);
     Generators.copyLintAnnotations(metadata.element(), builder);
+    Generators.copySuppressAnnotations(metadata.element(), builder);
 
     Generators.addInjectionMethods(metadata, builder);
 
@@ -112,10 +113,19 @@
   //       this, super.getDefaultViewModelProviderFactory());
   // }
   private MethodSpec getDefaultViewModelProviderFactory() {
-    return MethodSpec.methodBuilder("getDefaultViewModelProviderFactory")
+    MethodSpec.Builder builder = MethodSpec.methodBuilder("getDefaultViewModelProviderFactory")
         .addAnnotation(Override.class)
         .addModifiers(Modifier.PUBLIC)
-        .returns(AndroidClassNames.VIEW_MODEL_PROVIDER_FACTORY)
+        .returns(AndroidClassNames.VIEW_MODEL_PROVIDER_FACTORY);
+
+    if (metadata.allowsOptionalInjection()) {
+      builder
+          .beginControlFlow("if (!optionalInjectParentUsesHilt(optionalInjectGetParent()))")
+          .addStatement("return super.getDefaultViewModelProviderFactory()")
+          .endControlFlow();
+    }
+
+    return builder
         .addStatement(
             "return $T.getActivityFactory(this, super.getDefaultViewModelProviderFactory())",
             AndroidClassNames.DEFAULT_VIEW_MODEL_FACTORIES)
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java
index c94f6a9..44c7ae1 100644
--- a/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java
@@ -158,8 +158,7 @@
   }
 
   private static ClassName generatedClassName(TypeElement element) {
-    String prefix = "Hilt_";
-    return Processors.prepend(Processors.getEnclosedClassName(ClassName.get(element)), prefix);
+    return Processors.prepend(Processors.getEnclosedClassName(ClassName.get(element)), "Hilt_");
   }
 
   private static final ImmutableSet<ClassName> HILT_ANNOTATION_NAMES =
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointProcessor.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointProcessor.java
index cd3290b..c755bbd 100644
--- a/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointProcessor.java
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointProcessor.java
@@ -16,12 +16,14 @@
 
 package dagger.hilt.android.processor.internal.androidentrypoint;
 
+import static dagger.hilt.processor.internal.HiltCompilerOptions.useAggregatingRootProcessor;
 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 dagger.hilt.processor.internal.ProcessorErrors;
 import java.util.Set;
 import javax.annotation.processing.Processor;
 import javax.lang.model.element.Element;
@@ -54,7 +56,29 @@
     new InjectorEntryPointGenerator(getProcessingEnv(), metadata).generate();
     switch (metadata.androidType()) {
       case APPLICATION:
-        new ApplicationGenerator(getProcessingEnv(), metadata).generate();
+        // The generated application references the generated component so they must be generated
+        // in the same build unit. Thus, we only generate the application here if we're using the
+        // aggregating root processor. If we're using the Hilt Gradle plugin's aggregating task, we
+        // need to generate the application within ComponentTreeDepsProcessor instead.
+        if (useAggregatingRootProcessor(getProcessingEnv())) {
+          // While we could always generate the application in ComponentTreeDepsProcessor, even if
+          // we're using the aggregating root processor, it can lead to extraneous errors when
+          // things fail before ComponentTreeDepsProcessor runs so we generate it here to avoid that
+          new ApplicationGenerator(getProcessingEnv(), metadata).generate();
+        } else {
+          // If we're not using the aggregating root processor, then make sure the root application
+          // does not extend the generated application directly, and instead uses bytecode injection
+          ProcessorErrors.checkState(
+              metadata.requiresBytecodeInjection(),
+              metadata.element(),
+              "'enableAggregatingTask=true' cannot be used when the application directly "
+                  + "references the generated Hilt class, %s. Either extend %s directly (relying "
+                  + "on the Gradle plugin described in "
+                  + "https://dagger.dev/hilt/gradle-setup#why-use-the-plugin or set "
+                  + "'enableAggregatingTask=false'.",
+              metadata.generatedClassName(),
+              metadata.baseClassName());
+        }
         break;
       case ACTIVITY:
         new ActivityGenerator(getProcessingEnv(), metadata).generate();
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/ApplicationGenerator.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/ApplicationGenerator.java
index 8d63cdb..8347b0e 100644
--- a/java/dagger/hilt/android/processor/internal/androidentrypoint/ApplicationGenerator.java
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/ApplicationGenerator.java
@@ -16,7 +16,10 @@
 
 package dagger.hilt.android.processor.internal.androidentrypoint;
 
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.PROTECTED;
 
+import com.google.common.collect.ImmutableSet;
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
 import com.squareup.javapoet.FieldSpec;
@@ -28,10 +31,16 @@
 import com.squareup.javapoet.TypeVariableName;
 import dagger.hilt.android.processor.internal.AndroidClassNames;
 import dagger.hilt.processor.internal.ComponentNames;
+import dagger.hilt.processor.internal.ProcessorErrors;
 import dagger.hilt.processor.internal.Processors;
 import java.io.IOException;
+import java.util.Set;
+import java.util.stream.Collectors;
 import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.element.Modifier;
+import javax.lang.model.util.ElementFilter;
 
 /** Generates an Hilt Application for an @AndroidEntryPoint app class. */
 public final class ApplicationGenerator {
@@ -68,15 +77,49 @@
         .forEachOrdered(typeSpecBuilder::addTypeVariable);
 
     Generators.copyLintAnnotations(metadata.element(), typeSpecBuilder);
+    Generators.copySuppressAnnotations(metadata.element(), typeSpecBuilder);
     Generators.addComponentOverride(metadata, typeSpecBuilder);
 
+    if (hasCustomInject()) {
+      typeSpecBuilder.addSuperinterface(AndroidClassNames.HAS_CUSTOM_INJECT);
+      typeSpecBuilder.addMethod(customInjectMethod());
+    } else {
       typeSpecBuilder.addMethod(onCreateMethod());
+    }
 
     JavaFile.builder(metadata.elementClassName().packageName(), typeSpecBuilder.build())
         .build()
         .writeTo(env.getFiler());
   }
 
+  private boolean hasCustomInject() {
+    boolean hasCustomInject =
+        Processors.hasAnnotation(metadata.element(), AndroidClassNames.CUSTOM_INJECT);
+    if (hasCustomInject) {
+      // Check that the Hilt base class does not already define a customInject implementation.
+      Set<ExecutableElement> customInjectMethods =
+          ElementFilter.methodsIn(
+                  ImmutableSet.<Element>builder()
+                      .addAll(metadata.element().getEnclosedElements())
+                      .addAll(env.getElementUtils().getAllMembers(metadata.baseElement()))
+                      .build())
+              .stream()
+              .filter(method -> method.getSimpleName().contentEquals("customInject"))
+              .filter(method -> method.getParameters().isEmpty())
+              .collect(Collectors.toSet());
+
+      for (ExecutableElement customInjectMethod : customInjectMethods) {
+        ProcessorErrors.checkState(
+            customInjectMethod.getModifiers().containsAll(ImmutableSet.of(ABSTRACT, PROTECTED)),
+            customInjectMethod,
+            "%s#%s, must have modifiers `abstract` and `protected` when using @CustomInject.",
+            customInjectMethod.getEnclosingElement(),
+            customInjectMethod);
+      }
+    }
+    return hasCustomInject;
+  }
+
   // private final ApplicationComponentManager<ApplicationComponent> componentManager =
   //     new ApplicationComponentManager(/* creatorType */);
   private FieldSpec componentManagerField() {
@@ -108,9 +151,6 @@
   //   }
   // }
   private TypeSpec creatorType() {
-    ClassName component =
-        componentNames.generatedComponent(
-            metadata.elementClassName(), AndroidClassNames.SINGLETON_COMPONENT);
     return TypeSpec.anonymousClassBuilder("")
         .addSuperinterface(AndroidClassNames.COMPONENT_SUPPLIER)
         .addMethod(
@@ -118,17 +158,27 @@
                 .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)
+                .addCode(componentBuilder())
                 .build())
         .build();
   }
 
+  // return DaggerApplicationComponent.builder()
+  //     .applicationContextModule(new ApplicationContextModule(Hilt_$APP.this))
+  //     .build();
+  private CodeBlock componentBuilder() {
+    ClassName component =
+        componentNames.generatedComponent(
+            metadata.elementClassName(), AndroidClassNames.SINGLETON_COMPONENT);
+    return CodeBlock.builder()
+        .addStatement(
+            "return $T.builder()$Z" + ".applicationContextModule(new $T($T.this))$Z" + ".build()",
+            Processors.prepend(Processors.getEnclosedClassName(component), "Dagger"),
+            AndroidClassNames.APPLICATION_CONTEXT_MODULE,
+            wrapperClassName)
+        .build();
+  }
+
   // @CallSuper
   // @Override
   // public void onCreate() {
@@ -147,7 +197,21 @@
         .build();
   }
 
-  //   // This is a known unsafe cast but should be fine if the only use is
+  // @Override
+  // public final void customInject() {
+  //   // This is a known unsafe cast but is safe in the only correct use case:
+  //   // $APP extends Hilt_$APP
+  //   generatedComponent().inject(($APP) this);
+  // }
+  private MethodSpec customInjectMethod() {
+    return MethodSpec.methodBuilder("customInject")
+        .addAnnotation(Override.class)
+        .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+        .addCode(injectCodeBlock())
+        .build();
+  }
+
+  //   // This is a known unsafe cast but is safe in the only correct use case:
   //   // $APP extends Hilt_$APP
   //   generatedComponent().inject$APP(($APP) this);
   private CodeBlock injectCodeBlock() {
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/BUILD b/java/dagger/hilt/android/processor/internal/androidentrypoint/BUILD
index efbf9e4..46bcdd6 100644
--- a/java/dagger/hilt/android/processor/internal/androidentrypoint/BUILD
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/BUILD
@@ -38,9 +38,12 @@
         ":metadata",
         "//java/dagger/hilt/android/processor/internal:android_classnames",
         "//java/dagger/hilt/processor/internal:base_processor",
-        "//java/dagger/internal/guava:collect",
-        "@google_bazel_common//third_party/java/auto:service",
-        "@google_bazel_common//third_party/java/incap",
+        "//java/dagger/hilt/processor/internal:compiler_options",
+        "//java/dagger/hilt/processor/internal:processor_errors",
+        "//java/dagger/hilt/processor/internal:processors",
+        "//third_party/java/auto:service",
+        "//third_party/java/guava/collect",
+        "//third_party/java/incap",
     ],
 )
 
@@ -61,15 +64,16 @@
         "//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:compiler_options",
         "//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",
+        "//third_party/java/auto:common",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/javapoet",
     ],
 )
 
@@ -88,11 +92,11 @@
         "//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",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:value",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/javapoet",
     ],
 )
 
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/BroadcastReceiverGenerator.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/BroadcastReceiverGenerator.java
index f8e9f60..a8eb7a6 100644
--- a/java/dagger/hilt/android/processor/internal/androidentrypoint/BroadcastReceiverGenerator.java
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/BroadcastReceiverGenerator.java
@@ -78,6 +78,7 @@
 
     Generators.addInjectionMethods(metadata, builder);
     Generators.copyLintAnnotations(metadata.element(), builder);
+    Generators.copySuppressAnnotations(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
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/FragmentGenerator.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/FragmentGenerator.java
index 81b2b61..e7a7026 100644
--- a/java/dagger/hilt/android/processor/internal/androidentrypoint/FragmentGenerator.java
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/FragmentGenerator.java
@@ -16,10 +16,12 @@
 
 package dagger.hilt.android.processor.internal.androidentrypoint;
 
+import com.squareup.javapoet.AnnotationSpec;
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.FieldSpec;
 import com.squareup.javapoet.JavaFile;
 import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
 import com.squareup.javapoet.TypeSpec;
 import com.squareup.javapoet.TypeVariableName;
 import dagger.hilt.android.processor.internal.AndroidClassNames;
@@ -36,6 +38,11 @@
           .addModifiers(Modifier.PRIVATE)
           .build();
 
+  private static final FieldSpec DISABLE_GET_CONTEXT_FIX_FIELD =
+      FieldSpec.builder(TypeName.BOOLEAN, "disableGetContextFix")
+          .addModifiers(Modifier.PRIVATE)
+          .build();
+
   private final ProcessingEnvironment env;
   private final AndroidEntryPointMetadata metadata;
   private final ClassName generatedClassName;
@@ -69,12 +76,13 @@
             .addMethod(onAttachActivityMethod())
             .addMethod(initializeComponentContextMethod())
             .addMethod(getContextMethod())
-            .addMethod(inflatorMethod());
+            .addMethod(inflatorMethod())
+            .addField(DISABLE_GET_CONTEXT_FIX_FIELD);
 
     Generators.addGeneratedBaseClassJavadoc(builder, AndroidClassNames.ANDROID_ENTRY_POINT);
     Processors.addGeneratedAnnotation(builder, env, getClass());
     Generators.copyLintAnnotations(metadata.element(), builder);
-    Generators.addSuppressAnnotation(builder, "deprecation");
+    Generators.copySuppressAnnotations(metadata.element(), builder);
     Generators.copyConstructors(metadata.baseElement(), builder);
 
     metadata.baseElement().getTypeParameters().stream()
@@ -94,9 +102,10 @@
 
   // @CallSuper
   // @Override
-  // public void onAttach(Activity activity) {
-  //   super.onAttach(activity);
+  // public void onAttach(Context context) {
+  //   super.onAttach(context);
   //   initializeComponentContext();
+  //   inject();
   // }
   private static MethodSpec onAttachContextMethod() {
     return MethodSpec.methodBuilder("onAttach")
@@ -106,21 +115,29 @@
         .addParameter(AndroidClassNames.CONTEXT, "context")
         .addStatement("super.onAttach(context)")
         .addStatement("initializeComponentContext()")
+        // The inject method will internally check if injected already
+        .addStatement("inject()")
         .build();
   }
 
   // @CallSuper
   // @Override
+  // @SuppressWarnings("deprecation")
   // public void onAttach(Activity activity) {
   //   super.onAttach(activity);
   //   Preconditions.checkState(
   //       componentContext == null || FragmentComponentManager.findActivity(
   //           componentContext) == activity, "...");
   //   initializeComponentContext();
+  //   inject();
   // }
   private static MethodSpec onAttachActivityMethod() {
     return MethodSpec.methodBuilder("onAttach")
         .addAnnotation(Override.class)
+        .addAnnotation(
+            AnnotationSpec.builder(ClassNames.SUPPRESS_WARNINGS)
+                .addMember("value", "\"deprecation\"")
+                .build())
         .addAnnotation(AndroidClassNames.CALL_SUPER)
         .addAnnotation(AndroidClassNames.MAIN_THREAD)
         .addModifiers(Modifier.PUBLIC)
@@ -135,23 +152,24 @@
             "onAttach called multiple times with different Context! "
         + "Hilt Fragments should not be retained.")
         .addStatement("initializeComponentContext()")
+        // The inject method will internally check if injected already
+        .addStatement("inject()")
         .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();
+  //     disableGetContextFix = FragmentGetContextFix.isFragmentGetContextFixDisabled(
+  //         super.getContext());
   //   }
   // }
   private MethodSpec initializeComponentContextMethod() {
-    return MethodSpec.methodBuilder("initializeComponentContext")
+    MethodSpec.Builder builder = 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"
@@ -160,21 +178,54 @@
         .addStatement(
             "$N = $T.createContextWrapper(super.getContext(), this)",
             COMPONENT_CONTEXT_FIELD,
-            metadata.componentManager())
-        .addStatement("inject()")
+            metadata.componentManager());
+    if (metadata.allowsOptionalInjection()) {
+      // When optionally injected, since the runtime flag is only available in Hilt, we need to
+      // check that the parent uses Hilt first.
+      builder.beginControlFlow("if (optionalInjectParentUsesHilt(optionalInjectGetParent()))");
+    }
+
+    builder
+        .addStatement("$N = $T.isFragmentGetContextFixDisabled(super.getContext())",
+            DISABLE_GET_CONTEXT_FIX_FIELD,
+            AndroidClassNames.FRAGMENT_GET_CONTEXT_FIX);
+
+    if (metadata.allowsOptionalInjection()) {
+      // If not attached to a Hilt parent, just disable the fix for now since this is the current
+      // default. There's not a good way to flip this at runtime without Hilt, so after we flip
+      // the default we may just have to flip this and hope that the Hilt usage is already enough
+      // coverage as this should be a fairly rare case.
+      builder.nextControlFlow("else")
+          .addStatement("$N = true", DISABLE_GET_CONTEXT_FIX_FIELD)
+          .endControlFlow();
+    }
+
+    return builder
         .endControlFlow()
         .build();
   }
 
   // @Override
   // public Context getContext() {
+  //   if (super.getContext() == null && !disableGetContextFix) {
+  //     return null;
+  //   }
+  //   initializeComponentContext();
   //   return componentContext;
   // }
-  private static MethodSpec getContextMethod() {
+  private MethodSpec getContextMethod() {
     return MethodSpec.methodBuilder("getContext")
         .returns(AndroidClassNames.CONTEXT)
         .addAnnotation(Override.class)
         .addModifiers(Modifier.PUBLIC)
+        // Note that disableGetContext can only be true if componentContext is set, so if it is
+        // true we don't need to check whether componentContext is set or not.
+        .beginControlFlow(
+            "if (super.getContext() == null && !$N)",
+            DISABLE_GET_CONTEXT_FIX_FIELD)
+        .addStatement("return null")
+        .endControlFlow()
+        .addStatement("initializeComponentContext()")
         .addStatement("return $N", COMPONENT_CONTEXT_FIELD)
         .build();
   }
@@ -206,10 +257,19 @@
   //       this, super.getDefaultViewModelProviderFactory());
   // }
   private MethodSpec getDefaultViewModelProviderFactory() {
-    return MethodSpec.methodBuilder("getDefaultViewModelProviderFactory")
+    MethodSpec.Builder builder = MethodSpec.methodBuilder("getDefaultViewModelProviderFactory")
         .addAnnotation(Override.class)
         .addModifiers(Modifier.PUBLIC)
-        .returns(AndroidClassNames.VIEW_MODEL_PROVIDER_FACTORY)
+        .returns(AndroidClassNames.VIEW_MODEL_PROVIDER_FACTORY);
+
+    if (metadata.allowsOptionalInjection()) {
+      builder
+          .beginControlFlow("if (!optionalInjectParentUsesHilt(optionalInjectGetParent()))")
+          .addStatement("return super.getDefaultViewModelProviderFactory()")
+          .endControlFlow();
+    }
+
+    return builder
         .addStatement(
             "return $T.getFragmentFactory(this, super.getDefaultViewModelProviderFactory())",
             AndroidClassNames.DEFAULT_VIEW_MODEL_FACTORIES)
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/Generators.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/Generators.java
index 91df537..e2892aa 100644
--- a/java/dagger/hilt/android/processor/internal/androidentrypoint/Generators.java
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/Generators.java
@@ -18,9 +18,14 @@
 
 import static com.google.common.collect.Iterables.getOnlyElement;
 import static dagger.internal.codegen.extension.DaggerCollectors.toOptional;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static java.util.stream.Collectors.joining;
 import static javax.lang.model.element.Modifier.PRIVATE;
 
+import com.google.auto.common.AnnotationMirrors;
 import com.google.common.base.Preconditions;
+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;
@@ -30,6 +35,7 @@
 import com.squareup.javapoet.TypeName;
 import com.squareup.javapoet.TypeSpec;
 import dagger.hilt.android.processor.internal.AndroidClassNames;
+import dagger.hilt.android.processor.internal.androidentrypoint.AndroidEntryPointMetadata.AndroidType;
 import dagger.hilt.processor.internal.ClassNames;
 import dagger.hilt.processor.internal.Processors;
 import java.util.List;
@@ -45,6 +51,11 @@
 
 /** Helper class for writing Hilt generators. */
 final class Generators {
+  private static final ImmutableMap<ClassName, String> SUPPRESS_ANNOTATION_PROPERTY_NAME =
+      ImmutableMap.<ClassName, String>builder()
+          .put(ClassNames.SUPPRESS_WARNINGS, "value")
+          .put(ClassNames.KOTLIN_SUPPRESS, "names")
+          .build();
 
   static void addGeneratedBaseClassJavadoc(TypeSpec.Builder builder, ClassName annotation) {
     builder.addJavadoc("A generated base class to be extended by the @$T annotated class. If using"
@@ -123,15 +134,14 @@
   private static MethodSpec copyConstructor(ExecutableElement constructor, CodeBlock body) {
     List<ParameterSpec> params =
         constructor.getParameters().stream()
-            .map(parameter -> getParameterSpecWithNullable(parameter))
+            .map(Generators::getParameterSpecWithNullable)
             .collect(Collectors.toList());
 
     final MethodSpec.Builder builder =
         MethodSpec.constructorBuilder()
             .addParameters(params)
             .addStatement(
-                "super($L)",
-                params.stream().map(param -> param.name).collect(Collectors.joining(", ")))
+                "super($L)", params.stream().map(param -> param.name).collect(joining(", ")))
             .addCode(body);
 
     constructor.getAnnotationMirrors().stream()
@@ -143,6 +153,27 @@
     return builder.build();
   }
 
+  /** Copies SuppressWarnings annotations from the annotated element to the generated element. */
+  static void copySuppressAnnotations(Element element, TypeSpec.Builder builder) {
+    ImmutableSet<String> suppressValues =
+        SUPPRESS_ANNOTATION_PROPERTY_NAME.keySet().stream()
+            .filter(annotation -> Processors.hasAnnotation(element, annotation))
+            .map(
+                annotation ->
+                    AnnotationMirrors.getAnnotationValue(
+                        Processors.getAnnotationMirror(element, annotation),
+                        SUPPRESS_ANNOTATION_PROPERTY_NAME.get(annotation)))
+            .flatMap(value -> Processors.getStringArrayAnnotationValue(value).stream())
+            .collect(toImmutableSet());
+
+    if (!suppressValues.isEmpty()) {
+      // Replace kotlin Suppress with java SuppressWarnings, as the generated file is java.
+      AnnotationSpec.Builder annotation = AnnotationSpec.builder(ClassNames.SUPPRESS_WARNINGS);
+      suppressValues.forEach(value -> annotation.addMember("value", "$S", value));
+      builder.addAnnotation(annotation.build());
+    }
+  }
+
   /**
    * Copies the Android lint annotations from the annotated element to the generated element.
    *
@@ -186,7 +217,7 @@
         addComponentManagerMethods(metadata, builder);
         // fall through
       case BROADCAST_RECEIVER:
-        addInjectMethod(metadata, builder);
+        addInjectAndMaybeOptionalInjectMethod(metadata, builder);
         break;
       default:
         throw new AssertionError();
@@ -293,7 +324,7 @@
   //     injected = true;
   //   }
   // }
-  private static void addInjectMethod(
+  private static void addInjectAndMaybeOptionalInjectMethod(
       AndroidEntryPointMetadata metadata, TypeSpec.Builder typeSpecBuilder) {
     MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder("inject")
         .addModifiers(Modifier.PROTECTED);
@@ -301,22 +332,44 @@
     // 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())) {
+    // if (!optionalInjectParentUsesHilt()) {
     //   return;
     //
     if (metadata.allowsOptionalInjection()) {
+      CodeBlock parentCodeBlock;
+      if (metadata.androidType() != AndroidType.BROADCAST_RECEIVER) {
+        parentCodeBlock = CodeBlock.of("optionalInjectGetParent()");
+
+        // Also, add the optionalInjectGetParent method we just used. This is a separate method so
+        // other parts of the code when dealing with @OptionalInject. BroadcastReceiver can't have
+        // this method since the context is only accessible as a parameter to receive()/inject().
+        typeSpecBuilder.addMethod(MethodSpec.methodBuilder("optionalInjectGetParent")
+            .addModifiers(Modifier.PRIVATE)
+            .returns(TypeName.OBJECT)
+            .addStatement("return $L", getParentCodeBlock(metadata))
+            .build());
+      } else {
+        // For BroadcastReceiver, use the "context" field that is on the stack.
+        parentCodeBlock = CodeBlock.of(
+            "$T.getApplication(context.getApplicationContext())", ClassNames.CONTEXTS);
+      }
+
       methodSpecBuilder
-          .addStatement("$T parent = $L", ClassNames.OBJECT, getParentCodeBlock(metadata))
-          .beginControlFlow(
-              "if (!(parent instanceof $T) "
-              + "|| ((parent instanceof $T) && !(($T) parent).wasInjectedByHilt()))",
+          .beginControlFlow("if (!optionalInjectParentUsesHilt($L))", parentCodeBlock)
+          .addStatement("return")
+          .endControlFlow();
+
+      // Add the optionalInjectParentUsesHilt used above.
+      typeSpecBuilder.addMethod(MethodSpec.methodBuilder("optionalInjectParentUsesHilt")
+          .addModifiers(Modifier.PRIVATE)
+          .addParameter(TypeName.OBJECT, "parent")
+          .returns(TypeName.BOOLEAN)
+          .addStatement("return (parent instanceof $T) "
+              + "&& (!(parent instanceof $T) || (($T) parent).wasInjectedByHilt())",
               ClassNames.GENERATED_COMPONENT_MANAGER,
               AndroidClassNames.INJECTED_BY_HILT,
               AndroidClassNames.INJECTED_BY_HILT)
-          .addStatement("return")
-          .endControlFlow();
+          .build());
     }
 
     // Only add @Override if an ancestor extends a generated Hilt class.
@@ -393,15 +446,16 @@
     switch (metadata.androidType()) {
       case ACTIVITY:
       case SERVICE:
-        return CodeBlock.of("getApplicationContext()");
+        return CodeBlock.of("$T.getApplication(getApplicationContext())", ClassNames.CONTEXTS);
       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()");
+        // Broadcast receivers receive a "context" parameter that make it so this code block
+        // isn't really usable anywhere
+        throw new AssertionError("BroadcastReceiver types should not get here");
       default:
         throw new AssertionError();
     }
@@ -476,18 +530,5 @@
         .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/InjectorEntryPointGenerator.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/InjectorEntryPointGenerator.java
index e9e217d..88c40e7 100644
--- a/java/dagger/hilt/android/processor/internal/androidentrypoint/InjectorEntryPointGenerator.java
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/InjectorEntryPointGenerator.java
@@ -61,6 +61,7 @@
 
     Processors.addGeneratedAnnotation(builder, env, getClass());
     Generators.copyLintAnnotations(metadata.element(), builder);
+    Generators.copySuppressAnnotations(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
index a80a215..2673363 100644
--- a/java/dagger/hilt/android/processor/internal/androidentrypoint/ServiceGenerator.java
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/ServiceGenerator.java
@@ -61,6 +61,7 @@
     Generators.addGeneratedBaseClassJavadoc(builder, AndroidClassNames.ANDROID_ENTRY_POINT);
     Processors.addGeneratedAnnotation(builder, env, getClass());
     Generators.copyLintAnnotations(metadata.element(), builder);
+    Generators.copySuppressAnnotations(metadata.element(), builder);
 
     metadata.baseElement().getTypeParameters().stream()
         .map(TypeVariableName::get)
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/ViewGenerator.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/ViewGenerator.java
index 412b09a..47029ca 100644
--- a/java/dagger/hilt/android/processor/internal/androidentrypoint/ViewGenerator.java
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/ViewGenerator.java
@@ -31,7 +31,6 @@
 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;
@@ -70,6 +69,7 @@
     Generators.addGeneratedBaseClassJavadoc(builder, AndroidClassNames.ANDROID_ENTRY_POINT);
     Processors.addGeneratedAnnotation(builder, env, getClass());
     Generators.copyLintAnnotations(metadata.element(), builder);
+    Generators.copySuppressAnnotations(metadata.element(), builder);
 
     metadata.baseElement().getTypeParameters().stream()
         .map(TypeVariableName::get)
@@ -177,13 +177,13 @@
   }
 
   private static boolean isSecondRestrictedParameter(Element element) {
-    return element instanceof TypeElement
+    return MoreElements.isType(element)
         && Processors.isAssignableFrom(
             MoreElements.asType(element), AndroidClassNames.ATTRIBUTE_SET);
   }
 
   private static boolean isFirstRestrictedParameter(Element element) {
-    return element instanceof TypeElement
+    return MoreElements.isType(element)
         && Processors.isAssignableFrom(MoreElements.asType(element), AndroidClassNames.CONTEXT);
   }
 
diff --git a/java/dagger/hilt/android/processor/internal/bindvalue/BUILD b/java/dagger/hilt/android/processor/internal/bindvalue/BUILD
index cbc51b3..5a59735 100644
--- a/java/dagger/hilt/android/processor/internal/bindvalue/BUILD
+++ b/java/dagger/hilt/android/processor/internal/bindvalue/BUILD
@@ -42,15 +42,15 @@
         "//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",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:service",
+        "//third_party/java/auto:value",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/incap",
+        "//third_party/java/javapoet",
+        "//third_party/java/jsr250_annotations",
+        "//third_party/java/jsr330_inject",
     ],
 )
 
diff --git a/java/dagger/hilt/android/processor/internal/bindvalue/BindValueMetadata.java b/java/dagger/hilt/android/processor/internal/bindvalue/BindValueMetadata.java
index bf50288..ecd05a8 100644
--- a/java/dagger/hilt/android/processor/internal/bindvalue/BindValueMetadata.java
+++ b/java/dagger/hilt/android/processor/internal/bindvalue/BindValueMetadata.java
@@ -30,7 +30,6 @@
 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;
@@ -125,7 +124,7 @@
       }
 
       ProcessorErrors.checkState(
-          !Processors.hasAnnotation(element, Inject.class),
+          !Processors.hasAnnotation(element, ClassNames.INJECT),
           element,
           "@%s fields cannot be used with @Inject annotation. Found %s",
           annotationClassName.simpleName(),
diff --git a/java/dagger/hilt/android/processor/internal/customtestapplication/BUILD b/java/dagger/hilt/android/processor/internal/customtestapplication/BUILD
index e9721d5..681ac79 100644
--- a/java/dagger/hilt/android/processor/internal/customtestapplication/BUILD
+++ b/java/dagger/hilt/android/processor/internal/customtestapplication/BUILD
@@ -37,13 +37,13 @@
         "//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",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:service",
+        "//third_party/java/auto:value",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/incap",
+        "//third_party/java/javapoet",
     ],
 )
 
diff --git a/java/dagger/hilt/android/processor/internal/viewmodel/BUILD b/java/dagger/hilt/android/processor/internal/viewmodel/BUILD
index b45925f..353729d 100644
--- a/java/dagger/hilt/android/processor/internal/viewmodel/BUILD
+++ b/java/dagger/hilt/android/processor/internal/viewmodel/BUILD
@@ -38,11 +38,11 @@
         "//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",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:service",
+        "//third_party/java/guava/collect",
+        "//third_party/java/incap",
+        "//third_party/java/javapoet",
     ],
 )
 
@@ -60,10 +60,10 @@
         "//: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",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:service",
+        "//third_party/java/guava/graph",
+        "//third_party/java/javapoet",
     ],
 )
 
diff --git a/java/dagger/hilt/android/qualifiers/BUILD b/java/dagger/hilt/android/qualifiers/BUILD
index 26b45ec..51bb842 100644
--- a/java/dagger/hilt/android/qualifiers/BUILD
+++ b/java/dagger/hilt/android/qualifiers/BUILD
@@ -25,7 +25,7 @@
     ],
     deps = [
         ":package_info",
-        "@google_bazel_common//third_party/java/jsr330_inject",
+        "//third_party/java/jsr330_inject",
     ],
 )
 
diff --git a/java/dagger/hilt/android/scopes/BUILD b/java/dagger/hilt/android/scopes/BUILD
index 5abc27e..d89176f 100644
--- a/java/dagger/hilt/android/scopes/BUILD
+++ b/java/dagger/hilt/android/scopes/BUILD
@@ -29,7 +29,7 @@
     ],
     deps = [
         ":package_info",
-        "@google_bazel_common//third_party/java/jsr330_inject",
+        "//third_party/java/jsr330_inject",
     ],
 )
 
diff --git a/java/dagger/hilt/android/testing/BUILD b/java/dagger/hilt/android/testing/BUILD
index 93d4ceb..85dea9d 100644
--- a/java/dagger/hilt/android/testing/BUILD
+++ b/java/dagger/hilt/android/testing/BUILD
@@ -42,7 +42,8 @@
     testonly = 1,
     srcs = ["HiltAndroidTest.java"],
     exported_plugins = [
-        "//java/dagger/hilt/processor/internal/root:plugin",
+        "//java/dagger/hilt/processor/internal/root:component_tree_deps_plugin",
+        "//java/dagger/hilt/processor/internal/root:root_plugin",
         "//java/dagger/hilt/android/processor/internal/androidentrypoint:plugin",
         "//java/dagger/hilt/android/processor/internal/viewmodel:validation_plugin",
     ],
@@ -54,7 +55,9 @@
         "//:dagger_with_compiler",
         "//java/dagger/hilt:install_in",
         "//java/dagger/hilt/android/components",
+        "//java/dagger/hilt/android/internal",
         "//java/dagger/hilt/android/internal/builders",
+        "//java/dagger/hilt/android/internal/legacy:aggregated_element_proxy",
         "//java/dagger/hilt/android/internal/managers",
         "//java/dagger/hilt/android/internal/modules",
         "//java/dagger/hilt/android/internal/testing:early_test_singleton_component_creator",
@@ -71,6 +74,7 @@
         "//java/dagger/hilt/internal:preconditions",
         "//java/dagger/hilt/internal:test_singleton_component",
         "//java/dagger/hilt/internal/aggregatedroot",
+        "//java/dagger/hilt/internal/componenttreedeps",
         "//java/dagger/hilt/internal/processedrootsentinel",
         "//java/dagger/hilt/migration:disable_install_in_check",
         "@maven//:androidx_annotation_annotation",
@@ -118,10 +122,11 @@
         ":package_info",
         "//:dagger_with_compiler",
         "//java/dagger/hilt:entry_point",
+        "//java/dagger/hilt/android/internal",
         "//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",
+        "//third_party/java/auto:value",
     ],
 )
 
@@ -141,7 +146,7 @@
     ],
 )
 
-java_library(
+android_library(
     name = "bind_value",
     testonly = 1,
     srcs = [
@@ -167,7 +172,7 @@
     name = "package_info",
     srcs = ["package-info.java"],
     deps = [
-        "@google_bazel_common//third_party/java/jsr305_annotations",
+        "//third_party/java/jsr305_annotations",
     ],
 )
 
@@ -226,6 +231,7 @@
         "com.google.dagger:hilt-android",
         "javax.inject:javax.inject",
         "junit:junit",
+        "org.jetbrains.kotlin:kotlin-stdlib",
     ],
     artifact_target_maven_deps_banned = [
         "com.google.guava:guava",
diff --git a/java/dagger/hilt/android/testing/OnComponentReadyRunner.java b/java/dagger/hilt/android/testing/OnComponentReadyRunner.java
index 925a19f..dd670d5 100644
--- a/java/dagger/hilt/android/testing/OnComponentReadyRunner.java
+++ b/java/dagger/hilt/android/testing/OnComponentReadyRunner.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import com.google.auto.value.AutoValue;
 import dagger.hilt.EntryPoints;
+import dagger.hilt.android.internal.Contexts;
 import dagger.hilt.android.internal.testing.TestApplicationComponentManagerHolder;
 import dagger.hilt.internal.GeneratedComponentManager;
 import dagger.hilt.internal.Preconditions;
@@ -48,7 +49,7 @@
   /** 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();
+    Application application = Contexts.getApplication(context.getApplicationContext());
     if (application instanceof TestApplicationComponentManagerHolder) {
       TestApplicationComponentManagerHolder managerHolder =
           (TestApplicationComponentManagerHolder) application;
diff --git a/javatests/dagger/hilt/android/processor/BUILD b/java/dagger/hilt/android/testing/compile/BUILD
similarity index 79%
rename from javatests/dagger/hilt/android/processor/BUILD
rename to java/dagger/hilt/android/testing/compile/BUILD
index 4ec4d56..72fb0c2 100644
--- a/javatests/dagger/hilt/android/processor/BUILD
+++ b/java/dagger/hilt/android/testing/compile/BUILD
@@ -17,22 +17,25 @@
 package(default_visibility = ["//:src"])
 
 java_library(
-    name = "android_compilers",
-    srcs = ["AndroidCompilers.java"],
+    name = "compile",
+    testonly = 1,
+    srcs = ["HiltCompilerTests.java"],
     deps = [
+        "//third_party/java/guava/collect",
+        "//third_party/java/compile_testing",
         "//java/dagger/hilt/android/processor/internal/androidentrypoint:processor_lib",
         "//java/dagger/hilt/android/processor/internal/customtestapplication:processor_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/earlyentrypoint: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/hilt/processor/internal/root:component_tree_deps_processor_lib",
+        "//java/dagger/hilt/processor/internal/root:root_processor_lib",
         "//java/dagger/hilt/processor/internal/uninstallmodules: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/AndroidCompilers.java b/java/dagger/hilt/android/testing/compile/HiltCompilerTests.java
similarity index 81%
rename from javatests/dagger/hilt/android/processor/AndroidCompilers.java
rename to java/dagger/hilt/android/testing/compile/HiltCompilerTests.java
index 1fb19ae..68a2add 100644
--- a/javatests/dagger/hilt/android/processor/AndroidCompilers.java
+++ b/java/dagger/hilt/android/testing/compile/HiltCompilerTests.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package dagger.hilt.android.processor;
+package dagger.hilt.android.testing.compile;
 
 import static java.util.stream.Collectors.toMap;
 
@@ -23,30 +23,36 @@
 import dagger.hilt.android.processor.internal.androidentrypoint.AndroidEntryPointProcessor;
 import dagger.hilt.android.processor.internal.customtestapplication.CustomTestApplicationProcessor;
 import dagger.hilt.processor.internal.aggregateddeps.AggregatedDepsProcessor;
+import dagger.hilt.processor.internal.aliasof.AliasOfProcessor;
 import dagger.hilt.processor.internal.definecomponent.DefineComponentProcessor;
 import dagger.hilt.processor.internal.earlyentrypoint.EarlyEntryPointProcessor;
 import dagger.hilt.processor.internal.generatesrootinput.GeneratesRootInputProcessor;
 import dagger.hilt.processor.internal.originatingelement.OriginatingElementProcessor;
+import dagger.hilt.processor.internal.root.ComponentTreeDepsProcessor;
 import dagger.hilt.processor.internal.root.RootProcessor;
 import dagger.hilt.processor.internal.uninstallmodules.UninstallModulesProcessor;
 import dagger.internal.codegen.ComponentProcessor;
 import dagger.testing.compile.CompilerTests;
 import java.util.Arrays;
+import java.util.Collection;
 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 final class HiltCompilerTests {
 
   public static Compiler compiler(Processor... extraProcessors) {
+    return compiler(Arrays.asList(extraProcessors));
+  }
+
+  public static Compiler compiler(Collection<? extends 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));
+    extraProcessors.stream().forEach(processor -> processors.put(processor.getClass(), processor));
 
     return CompilerTests.compiler().withProcessors(processors.values());
   }
@@ -66,16 +72,18 @@
   private static ImmutableList<Processor> defaultProcessors() {
     return ImmutableList.of(
         new AggregatedDepsProcessor(),
+        new AliasOfProcessor(),
         new AndroidEntryPointProcessor(),
         new ComponentProcessor(),
+        new ComponentTreeDepsProcessor(),
+        new CustomTestApplicationProcessor(),
         new DefineComponentProcessor(),
         new EarlyEntryPointProcessor(),
         new GeneratesRootInputProcessor(),
         new OriginatingElementProcessor(),
-        new CustomTestApplicationProcessor(),
-        new UninstallModulesProcessor(),
-        new RootProcessor());
+        new RootProcessor(),
+        new UninstallModulesProcessor());
   }
 
-  private AndroidCompilers() {}
+  private HiltCompilerTests() {}
 }
diff --git a/java/dagger/hilt/components/BUILD b/java/dagger/hilt/components/BUILD
index 48dc8cd..9baf476 100644
--- a/java/dagger/hilt/components/BUILD
+++ b/java/dagger/hilt/components/BUILD
@@ -25,7 +25,7 @@
     deps = [
         ":package_info",
         "//java/dagger/hilt:define_component",
-        "@google_bazel_common//third_party/java/jsr330_inject",
+        "//third_party/java/jsr330_inject",
     ],
 )
 
diff --git a/java/dagger/hilt/internal/aggregatedroot/AggregatedRoot.java b/java/dagger/hilt/internal/aggregatedroot/AggregatedRoot.java
index b53ee72..a6aa5a5 100644
--- a/java/dagger/hilt/internal/aggregatedroot/AggregatedRoot.java
+++ b/java/dagger/hilt/internal/aggregatedroot/AggregatedRoot.java
@@ -30,5 +30,7 @@
 public @interface AggregatedRoot {
   String root();
 
+  String originatingRoot();
+
   Class<?> rootAnnotation();
 }
diff --git a/java/dagger/hilt/internal/aliasof/AliasOfPropagatedData.java b/java/dagger/hilt/internal/aliasof/AliasOfPropagatedData.java
index 25ea704..53c5cae 100644
--- a/java/dagger/hilt/internal/aliasof/AliasOfPropagatedData.java
+++ b/java/dagger/hilt/internal/aliasof/AliasOfPropagatedData.java
@@ -26,7 +26,7 @@
 @Target(ElementType.TYPE)
 @Retention(RetentionPolicy.CLASS)
 public @interface AliasOfPropagatedData {
-  Class<? extends Annotation> defineComponentScope();
+  Class<? extends Annotation>[] defineComponentScopes();
 
   Class<? extends Annotation> alias();
 }
diff --git a/java/dagger/hilt/android/example/BUILD b/java/dagger/hilt/internal/componenttreedeps/BUILD
similarity index 69%
rename from java/dagger/hilt/android/example/BUILD
rename to java/dagger/hilt/internal/componenttreedeps/BUILD
index 2a4c194..1a100d5 100644
--- a/java/dagger/hilt/android/example/BUILD
+++ b/java/dagger/hilt/internal/componenttreedeps/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Dagger Authors.
+# 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.
@@ -11,17 +11,18 @@
 # WITHOUT 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.
+#   The annotation for aggregating information about Hilt roots.
 
 package(default_visibility = ["//:src"])
 
+java_library(
+    name = "componenttreedeps",
+    srcs = ["ComponentTreeDeps.java"],
+)
+
 filegroup(
     name = "srcs_filegroup",
-    srcs = glob(
-        ["**/*"],
-        # Exclude Gradle build folder to enable working along side Bazel
-        exclude = ["**/build/**"],
-    ),
+    srcs = glob(["*"]),
 )
diff --git a/java/dagger/hilt/internal/componenttreedeps/ComponentTreeDeps.java b/java/dagger/hilt/internal/componenttreedeps/ComponentTreeDeps.java
new file mode 100644
index 0000000..52123e5
--- /dev/null
+++ b/java/dagger/hilt/internal/componenttreedeps/ComponentTreeDeps.java
@@ -0,0 +1,55 @@
+/*
+ * 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.internal.componenttreedeps;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** An annotation that kicks off the generation of a component tree. */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.CLASS)
+public @interface ComponentTreeDeps {
+
+  /** Returns the set of {@link dagger.hilt.internal.aggregatedroot.AggregatedRoot} dependencies. */
+  Class<?>[] rootDeps() default {};
+
+  /**
+   * Returns the set of {@link dagger.hilt.internal.definecomponent.DefineComponentClasses}
+   * dependencies.
+   */
+  Class<?>[] defineComponentDeps() default {};
+
+  /** Returns the set of {@link dagger.hilt.internal.aliasof.AliasOfPropagatedData} dependencies. */
+  Class<?>[] aliasOfDeps() default {};
+
+  /** Returns the set of {@link dagger.hilt.internal.aggregateddeps.AggregatedDeps} dependencies. */
+  Class<?>[] aggregatedDeps() default {};
+
+  /**
+   * Returns the set of {@link
+   * dagger.hilt.internal.uninstallmodules.AggregatedUninstallModulesMetadata} dependencies.
+   */
+  Class<?>[] uninstallModulesDeps() default {};
+
+  /**
+   * Returns the set of {@link dagger.hilt.android.earlyentrypoint.AggregatedEarlyEntryPoint}
+   * dependencies.
+   */
+  Class<?>[] earlyEntryPointDeps() default {};
+}
diff --git a/java/dagger/hilt/migration/AliasOf.java b/java/dagger/hilt/migration/AliasOf.java
index b68d4f1..c26197d 100644
--- a/java/dagger/hilt/migration/AliasOf.java
+++ b/java/dagger/hilt/migration/AliasOf.java
@@ -41,6 +41,6 @@
 @Retention(RetentionPolicy.CLASS)
 @GeneratesRootInput
 public @interface AliasOf {
-  /** Returns the existing Hilt scope that the annotated scope is aliasing. */
-  Class<? extends Annotation> value();
+  /** Returns the existing Hilt scope(s) that the annotated scope is aliasing. */
+  Class<? extends Annotation>[] value();
 }
diff --git a/java/dagger/hilt/migration/BUILD b/java/dagger/hilt/migration/BUILD
index a14410d..446e7a2 100644
--- a/java/dagger/hilt/migration/BUILD
+++ b/java/dagger/hilt/migration/BUILD
@@ -16,7 +16,7 @@
 
 package(default_visibility = ["//:src"])
 
-android_library(
+java_library(
     name = "alias_of",
     srcs = [
         "AliasOf.java",
diff --git a/java/dagger/hilt/processor/BUILD b/java/dagger/hilt/processor/BUILD
index 1f75d0a..bf2166c 100644
--- a/java/dagger/hilt/processor/BUILD
+++ b/java/dagger/hilt/processor/BUILD
@@ -35,7 +35,8 @@
         "//java/dagger/hilt/processor/internal/earlyentrypoint: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/hilt/processor/internal/root:component_tree_deps_processor_lib",
+        "//java/dagger/hilt/processor/internal/root:root_processor_lib",
         "//java/dagger/hilt/processor/internal/uninstallmodules:processor_lib",
         "//java/dagger/internal/codegen:processor",
     ],
@@ -87,9 +88,11 @@
         "//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:component_tree_deps_processor_lib",
+        "//java/dagger/hilt/processor/internal/root:root_processor_lib",
         "//java/dagger/hilt/processor/internal/root:root_metadata",
         "//java/dagger/hilt/processor/internal/root:root_type",
+        "//java/dagger/hilt/processor/internal/root/ir:ir",
         "//java/dagger/hilt/processor/internal/uninstallmodules:processor_lib",
         "//java/dagger/hilt/processor/internal/uninstallmodules:aggregated_uninstall_modules_metadata",
     ],
@@ -106,6 +109,7 @@
         "javax.inject:javax.inject",
         "net.ltgt.gradle.incap:incap",
         "org.jetbrains.kotlin:kotlin-stdlib",
+        "org.jetbrains.kotlin:kotlin-stdlib-jdk8",
         "org.jetbrains.kotlinx:kotlinx-metadata-jvm",
     ],
     javadoc_android_api_level = 30,
@@ -116,8 +120,10 @@
     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"],
+    # The shaded deps are added using jarjar, but they won't be shaded until later
+    # due to: https://github.com/google/dagger/issues/2765. For the shaded rules see
+    # util/deploy-hilt.sh
+    shaded_deps = ["//third_party/java/auto:common"],
 )
 
 filegroup(
diff --git a/java/dagger/hilt/processor/internal/AggregatedElements.java b/java/dagger/hilt/processor/internal/AggregatedElements.java
index 8ee820f..be593e9 100644
--- a/java/dagger/hilt/processor/internal/AggregatedElements.java
+++ b/java/dagger/hilt/processor/internal/AggregatedElements.java
@@ -17,10 +17,12 @@
 package dagger.hilt.processor.internal;
 
 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static javax.lang.model.element.Modifier.PUBLIC;
 
 import com.google.auto.common.MoreElements;
 import com.google.common.collect.ImmutableSet;
 import com.squareup.javapoet.ClassName;
+import java.util.Optional;
 import javax.lang.model.element.PackageElement;
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.util.Elements;
@@ -28,6 +30,34 @@
 /** Utility class for aggregating metadata. */
 public final class AggregatedElements {
 
+  /** Returns the class name of the proxy or {@link Optional#empty()} if a proxy is not needed. */
+  public static Optional<ClassName> aggregatedElementProxyName(TypeElement aggregatedElement) {
+    if (aggregatedElement.getModifiers().contains(PUBLIC)) {
+      // Public aggregated elements do not have proxies.
+      return Optional.empty();
+    }
+    ClassName name = ClassName.get(aggregatedElement);
+    // To avoid going over the class name size limit, just prepend a single character.
+    return Optional.of(name.peerClass("_" + name.simpleName()));
+  }
+
+  /** Returns back the set of input {@code aggregatedElements} with all proxies unwrapped. */
+  public static ImmutableSet<TypeElement> unwrapProxies(
+      ImmutableSet<TypeElement> aggregatedElements, Elements elements) {
+    return aggregatedElements.stream()
+        .map(aggregatedElement -> unwrapProxy(aggregatedElement, elements))
+        .collect(toImmutableSet());
+  }
+
+  private static TypeElement unwrapProxy(TypeElement element, Elements elements) {
+    return Processors.hasAnnotation(element, ClassNames.AGGREGATED_ELEMENT_PROXY)
+        ? Processors.getAnnotationClassValue(
+            elements,
+            Processors.getAnnotationMirror(element, ClassNames.AGGREGATED_ELEMENT_PROXY),
+            "value")
+        : element;
+  }
+
   /** Returns all aggregated elements in the aggregating package after validating them. */
   public static ImmutableSet<TypeElement> from(
       String aggregatingPackage, ClassName aggregatingAnnotation, Elements elements) {
@@ -40,6 +70,10 @@
     ImmutableSet<TypeElement> aggregatedElements =
         packageElement.getEnclosedElements().stream()
             .map(MoreElements::asType)
+            // We're only interested in returning the original deps here. Proxies will be generated
+            // (if needed) and swapped just before generating @ComponentTreeDeps.
+            .filter(
+                element -> !Processors.hasAnnotation(element, ClassNames.AGGREGATED_ELEMENT_PROXY))
             .collect(toImmutableSet());
 
     ProcessorErrors.checkState(
diff --git a/java/dagger/hilt/processor/internal/BUILD b/java/dagger/hilt/processor/internal/BUILD
index 978655d..b63a899 100644
--- a/java/dagger/hilt/processor/internal/BUILD
+++ b/java/dagger/hilt/processor/internal/BUILD
@@ -27,11 +27,11 @@
         ":compiler_options",
         ":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",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:value",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/javapoet",
     ],
 )
 
@@ -43,11 +43,11 @@
         "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",
+        "//third_party/java/auto:common",
+        "//third_party/java/error_prone:annotations",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/jsr305_annotations",
     ],
 )
 
@@ -63,11 +63,11 @@
         ":processor_errors",
         "//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/javapoet",
-        "@google_bazel_common//third_party/java/jsr330_inject",
-        "@maven//:com_google_auto_auto_common",
+        "//third_party/java/auto:common",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/javapoet",
+        "//third_party/java/jsr330_inject",
         "@maven//:org_jetbrains_kotlin_kotlin_stdlib",
         "@maven//:org_jetbrains_kotlinx_kotlinx_metadata_jvm",
     ],
@@ -79,7 +79,7 @@
         "ClassNames.java",
     ],
     deps = [
-        "@google_bazel_common//third_party/java/javapoet",
+        "//third_party/java/javapoet",
     ],
 )
 
@@ -89,11 +89,10 @@
         "ComponentNames.java",
     ],
     deps = [
-        ":classnames",
         ":processors",
-        "//java/dagger/internal/guava:base",
-        "//java/dagger/internal/guava:collect",
-        "@google_bazel_common//third_party/java/javapoet",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/javapoet",
     ],
 )
 
@@ -103,12 +102,13 @@
         "AggregatedElements.java",
     ],
     deps = [
+        ":classnames",
         ":processor_errors",
         ":processors",
         "//java/dagger/internal/codegen/extension",
-        "//java/dagger/internal/guava:collect",
-        "@google_bazel_common//third_party/java/javapoet",
-        "@maven//:com_google_auto_auto_common",
+        "//third_party/java/auto:common",
+        "//third_party/java/guava/collect",
+        "//third_party/java/javapoet",
     ],
 )
 
@@ -116,9 +116,9 @@
     name = "component_descriptor",
     srcs = ["ComponentDescriptor.java"],
     deps = [
-        "//java/dagger/internal/guava:collect",
-        "@google_bazel_common//third_party/java/auto:value",
-        "@google_bazel_common//third_party/java/javapoet",
+        "//third_party/java/auto:value",
+        "//third_party/java/guava/collect",
+        "//third_party/java/javapoet",
     ],
 )
 
@@ -135,10 +135,10 @@
         ":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",
+        "//third_party/java/auto:common",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/javapoet",
     ],
 )
 
@@ -156,8 +156,9 @@
     srcs = ["HiltCompilerOptions.java"],
     deps = [
         ":processor_errors",
-        "//java/dagger/internal/guava:collect",
-        "@google_bazel_common//third_party/java/javapoet",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/javapoet",
     ],
 )
 
diff --git a/java/dagger/hilt/processor/internal/BaseProcessor.java b/java/dagger/hilt/processor/internal/BaseProcessor.java
index 1a63f8b..a921075 100644
--- a/java/dagger/hilt/processor/internal/BaseProcessor.java
+++ b/java/dagger/hilt/processor/internal/BaseProcessor.java
@@ -20,6 +20,7 @@
 
 import com.google.auto.common.MoreElements;
 import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.LinkedHashMultimap;
 import com.google.common.collect.SetMultimap;
 import com.squareup.javapoet.ClassName;
@@ -102,7 +103,15 @@
     // warning if any used option is not unsupported. This can happen when there is a module
     // which uses Hilt but lacks any @AndroidEntryPoint annotations.
     // See: https://github.com/google/dagger/issues/2040
-    return HiltCompilerOptions.getProcessorOptions();
+    return ImmutableSet.<String>builder()
+        .addAll(HiltCompilerOptions.getProcessorOptions())
+        .addAll(additionalProcessingOptions())
+        .build();
+  }
+
+  /** Returns additional processing options that should only be applied for a single processor. */
+  protected Set<String> additionalProcessingOptions() {
+    return ImmutableSet.of();
   }
 
   /** Used to perform initialization before each round of processing. */
@@ -150,6 +159,7 @@
     this.elements = processingEnv.getElementUtils();
     this.types = processingEnv.getTypeUtils();
     this.errorHandler = new ProcessorErrorHandler(processingEnvironment);
+    HiltCompilerOptions.checkWrongAndDeprecatedOptions(processingEnvironment);
   }
 
   @Override
diff --git a/java/dagger/hilt/processor/internal/ClassNames.java b/java/dagger/hilt/processor/internal/ClassNames.java
index 093e1b3..dd65202 100644
--- a/java/dagger/hilt/processor/internal/ClassNames.java
+++ b/java/dagger/hilt/processor/internal/ClassNames.java
@@ -22,6 +22,11 @@
 
 /** Holder for commonly used class names. */
 public final class ClassNames {
+  public static final ClassName AGGREGATED_ELEMENT_PROXY =
+      get("dagger.hilt.android.internal.legacy", "AggregatedElementProxy");
+  public static final ClassName COMPONENT_TREE_DEPS =
+      get("dagger.hilt.internal.componenttreedeps", "ComponentTreeDeps");
+
   public static final String AGGREGATED_ROOT_PACKAGE =
       "dagger.hilt.internal.aggregatedroot.codegen";
   public static final ClassName AGGREGATED_ROOT =
@@ -31,6 +36,8 @@
   public static final ClassName PROCESSED_ROOT_SENTINEL =
       get("dagger.hilt.internal.processedrootsentinel", "ProcessedRootSentinel");
 
+  public static final ClassName CONTEXTS = get("dagger.hilt.android.internal", "Contexts");
+
   public static final String AGGREGATED_EARLY_ENTRY_POINT_PACKAGE =
       "dagger.hilt.android.internal.earlyentrypoint.codegen";
   public static final ClassName AGGREGATED_EARLY_ENTRY_POINT =
@@ -202,6 +209,9 @@
 
   public static final ClassName OBJECT = get("java.lang", "Object");
 
+  public static final ClassName SUPPRESS_WARNINGS = get("java.lang", "SuppressWarnings");
+  public static final ClassName KOTLIN_SUPPRESS = get("kotlin", "Suppress");
+
   // Kotlin-specific class names
   public static final ClassName KOTLIN_METADATA = get("kotlin", "Metadata");
 
diff --git a/java/dagger/hilt/processor/internal/ComponentNames.java b/java/dagger/hilt/processor/internal/ComponentNames.java
index eeaa5f4..f3f0444 100644
--- a/java/dagger/hilt/processor/internal/ComponentNames.java
+++ b/java/dagger/hilt/processor/internal/ComponentNames.java
@@ -16,25 +16,8 @@
 
 package dagger.hilt.processor.internal;
 
-import static java.lang.Character.isUpperCase;
-import static java.lang.String.format;
-import static java.util.Comparator.comparing;
-
-import com.google.common.base.CharMatcher;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Splitter;
-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.Multimaps;
 import com.squareup.javapoet.ClassName;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-import javax.lang.model.element.Name;
-import javax.lang.model.element.TypeElement;
+import java.util.function.Function;
 
 /**
  * Utility class for getting the generated component name.
@@ -42,41 +25,38 @@
  * <p>This should not be used externally.
  */
 public final class ComponentNames {
-  private static final Splitter QUALIFIED_NAME_SPLITTER = Splitter.on('.');
 
-  private final boolean renameTestComponents;
-  private final String destinationPackage;
-  private final ImmutableMap<ClassName, String> simpleNameByClassName;
-
+  /**
+   * Returns an instance of {@link ComponentNames} that will base all component names off of the
+   * given root.
+   */
   public static ComponentNames withoutRenaming() {
-    return new ComponentNames(
-        /*renameTestComponents=*/ false, /*destinationPackage=*/ null, ImmutableMap.of());
+    return new ComponentNames(Function.identity());
   }
 
-  public static ComponentNames withRenamingIntoPackage(
-      String destinationPackage, ImmutableList<TypeElement> roots) {
-    ImmutableMap.Builder<ClassName, String> builder = ImmutableMap.builder();
-    ImmutableListMultimap<String, TypeElement> rootsBySimpleName =
-        Multimaps.index(roots, typeElement -> typeElement.getSimpleName().toString());
-    rootsBySimpleName.asMap().values().stream()
-        .map(ComponentNames::disambiguateConflictingSimpleNames)
-        .forEach(builder::putAll);
-    return new ComponentNames(/*renameTestComponents=*/ true, destinationPackage, builder.build());
+  /**
+   * Returns an instance of {@link ComponentNames} that will base all component names off of the
+   * given root after mapping it with {@code rootRenamer}.
+   */
+  public static ComponentNames withRenaming(Function<ClassName, ClassName> rootRenamer) {
+    return new ComponentNames(rootRenamer);
   }
 
-  private ComponentNames(
-      boolean renameTestComponents,
-      String destinationPackage,
-      ImmutableMap<ClassName, String> simpleNameByClassName) {
-    this.renameTestComponents = renameTestComponents;
-    this.destinationPackage = destinationPackage;
-    this.simpleNameByClassName = simpleNameByClassName;
+  private final Function<ClassName, ClassName> rootRenamer;
+
+  private ComponentNames(Function<ClassName, ClassName> rootRenamer) {
+    this.rootRenamer = rootRenamer;
+  }
+
+  public ClassName generatedComponentTreeDeps(ClassName root) {
+    return Processors.append(
+        Processors.getEnclosedClassName(rootRenamer.apply(root)), "_ComponentTreeDeps");
   }
 
   /** Returns the name of the generated component wrapper. */
   public ClassName generatedComponentsWrapper(ClassName root) {
     return Processors.append(
-        Processors.getEnclosedClassName(maybeRenameComponent(root)), "_HiltComponents");
+        Processors.getEnclosedClassName(rootRenamer.apply(root)), "_HiltComponents");
   }
 
   /** Returns the name of the generated component. */
@@ -99,81 +79,4 @@
     return Processors.getEnclosedName(component).replaceAll("Component$", "C");
   }
 
-  /**
-   * Rewrites the provided HiltAndroidTest-annotated class name using the shared component
-   * directory.
-   */
-  private ClassName maybeRenameComponent(ClassName className) {
-    return (renameTestComponents && !className.equals(ClassNames.DEFAULT_ROOT))
-        ? ClassName.get(destinationPackage, dedupeSimpleName(className))
-        : className;
-  }
-
-  /**
-   * Derives a new generated component base name, should the simple names of two roots have
-   * conflicting simple names.
-   *
-   * <p>This is lifted nearly verbatim (albeit with new different struct types) from {@link
-   * dagger.internal.codegen.writing.SubcomponentNames}.
-   */
-  private String dedupeSimpleName(ClassName className) {
-    Preconditions.checkState(
-        simpleNameByClassName.containsKey(className),
-        "Class name %s not found in simple name map",
-        className.canonicalName());
-    return simpleNameByClassName.get(className);
-  }
-
-  private static ImmutableMap<ClassName, String> disambiguateConflictingSimpleNames(
-      Collection<TypeElement> rootsWithConflictingNames) {
-    // If there's only 1 root there's nothing to disambiguate so return the simple name.
-    if (rootsWithConflictingNames.size() == 1) {
-      TypeElement root = Iterables.getOnlyElement(rootsWithConflictingNames);
-      return ImmutableMap.of(ClassName.get(root), root.getSimpleName().toString());
-    }
-
-    // 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.
-    // Sorted in order to guarantee determinism if this is invoked by different processors.
-    ImmutableList<TypeElement> sortedRootsWithConflictingNames =
-        ImmutableList.sortedCopyOf(
-            comparing(typeElement -> typeElement.getQualifiedName().toString()),
-            rootsWithConflictingNames);
-    Set<String> usedNames = new HashSet<>();
-    ImmutableMap.Builder<ClassName, String> uniqueNames = ImmutableMap.builder();
-    for (TypeElement root : sortedRootsWithConflictingNames) {
-      String basePrefix = uniquingPrefix(root);
-      String uniqueName = basePrefix;
-      for (int differentiator = 2; !usedNames.add(uniqueName); differentiator++) {
-        uniqueName = basePrefix + differentiator;
-      }
-      uniqueNames.put(ClassName.get(root), format("%s_%s", uniqueName, root.getSimpleName()));
-    }
-    return uniqueNames.build();
-  }
-
-  /** Returns a prefix that could make the component's simple name more unique. */
-  private static String uniquingPrefix(TypeElement 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/hilt/processor/internal/HiltCompilerOptions.java b/java/dagger/hilt/processor/internal/HiltCompilerOptions.java
index 0d24823..b06faed 100644
--- a/java/dagger/hilt/processor/internal/HiltCompilerOptions.java
+++ b/java/dagger/hilt/processor/internal/HiltCompilerOptions.java
@@ -16,12 +16,14 @@
 
 package dagger.hilt.processor.internal;
 
+import com.google.common.base.Ascii;
 import com.google.common.collect.ImmutableSet;
 import java.util.Arrays;
 import java.util.Set;
 import java.util.stream.Collectors;
 import javax.annotation.processing.ProcessingEnvironment;
 import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic.Kind;
 
 /** Hilt annotation processor options. */
 // TODO(danysantiago): Consider consolidating with Dagger compiler options logic.
@@ -75,16 +77,30 @@
     return BooleanOption.SHARE_TEST_COMPONENTS.get(env);
   }
 
+  /**
+   * Returns {@code true} if the aggregating processor is enabled (default is {@code true}).
+   *
+   * <p>Note:This is for internal use only!
+   */
+  public static boolean useAggregatingRootProcessor(ProcessingEnvironment env) {
+    return BooleanOption.USE_AGGREGATING_ROOT_PROCESSOR.get(env);
+  }
+
   /** Processor options which can have true or false values. */
   private enum BooleanOption {
+    /** Do not use! This is for internal use only. */
     DISABLE_ANDROID_SUPERCLASS_VALIDATION(
         "android.internal.disableAndroidSuperclassValidation", false),
 
+    /** Do not use! This is for internal use only. */
+    USE_AGGREGATING_ROOT_PROCESSOR("internal.useAggregatingRootProcessor", true),
+
     DISABLE_CROSS_COMPILATION_ROOT_VALIDATION("disableCrossCompilationRootValidation", false),
 
     DISABLE_MODULES_HAVE_INSTALL_IN_CHECK("disableModulesHaveInstallInCheck", false),
 
-    SHARE_TEST_COMPONENTS("shareTestComponents", false);
+    SHARE_TEST_COMPONENTS(
+        "shareTestComponents", true);
 
     private final String name;
     private final boolean defaultValue;
@@ -99,8 +115,22 @@
       if (value == null) {
         return defaultValue;
       }
-      // TODO(danysantiago): Strictly verify input, either 'true' or 'false' and nothing else.
-      return Boolean.parseBoolean(value);
+
+      // Using Boolean.parseBoolean will turn any non-"true" value into false. Strictly verify the
+      // inputs to reduce user errors.
+      String lowercaseValue = Ascii.toLowerCase(value);
+      switch (lowercaseValue) {
+        case "true":
+          return true;
+        case "false":
+          return false;
+        default:
+          throw new IllegalStateException(
+              "Expected a value of true/false for the flag \""
+                  + name
+                  + "\". Got instead: "
+                  + value);
+      }
     }
 
     String getQualifiedName() {
@@ -108,6 +138,32 @@
     }
   }
 
+  private static final ImmutableSet<String> DEPRECATED_OPTIONS = ImmutableSet.of(
+      "dagger.hilt.android.useFragmentGetContextFix");
+
+  public static void checkWrongAndDeprecatedOptions(ProcessingEnvironment env) {
+    Set<String> knownOptions = getProcessorOptions();
+    for (String option : env.getOptions().keySet()) {
+      if (knownOptions.contains(option)) {
+        continue;
+      }
+
+      if (DEPRECATED_OPTIONS.contains(option)) {
+        env.getMessager().printMessage(
+            Kind.ERROR,
+            "The compiler option " + option + " is deprecated and no longer does anything. "
+            + "Please do not set this option.");
+        continue;
+      }
+
+      if (option.startsWith("dagger.hilt.")) {
+        env.getMessager().printMessage(
+            Kind.ERROR,
+            "The compiler option " + option + " is not a recognized Hilt option. Is there a typo?");
+      }
+    }
+  }
+
   public static Set<String> getProcessorOptions() {
     return Arrays.stream(BooleanOption.values())
         .map(BooleanOption::getQualifiedName)
diff --git a/java/dagger/hilt/processor/internal/Processors.java b/java/dagger/hilt/processor/internal/Processors.java
index 8740bd6..4e2e256 100644
--- a/java/dagger/hilt/processor/internal/Processors.java
+++ b/java/dagger/hilt/processor/internal/Processors.java
@@ -62,7 +62,6 @@
 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;
@@ -104,7 +103,7 @@
             .addModifiers(PUBLIC)
             .addOriginatingElement(element)
             .addAnnotation(aggregatingAnnotation)
-            .addJavadoc("This class should only be referenced by generated code!")
+            .addJavadoc("This class should only be referenced by generated code! ")
             .addJavadoc("This class aggregates information across multiple compilations.\n");;
 
     addGeneratedAnnotation(builder, env, generatedAnnotationClass);
@@ -767,7 +766,7 @@
   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);
+        AnnotationMirrors.getAnnotatedAnnotations(element, ClassNames.QUALIFIER.canonicalName());
     KotlinMetadataUtil metadataUtil = KotlinMetadataUtils.getMetadataUtil();
     if (element.getKind() == ElementKind.FIELD
         // static fields are generally not supported, no need to get qualifier from kotlin metadata
@@ -779,7 +778,7 @@
               metadataUtil.isMissingSyntheticPropertyForAnnotations(fieldElement)
                   ? Stream.empty()
                   : metadataUtil
-                      .getSyntheticPropertyAnnotations(fieldElement, Qualifier.class)
+                      .getSyntheticPropertyAnnotations(fieldElement, ClassNames.QUALIFIER)
                       .stream())
           .map(AnnotationMirrors.equivalence()::wrap)
           .distinct()
@@ -931,6 +930,17 @@
         && !KotlinMetadataUtils.getMetadataUtil().isObjectOrCompanionObjectClass(module);
   }
 
+  public 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(Modifier.PRIVATE)
+                        );
+  }
+
   private static boolean isBindingMethod(ExecutableElement method) {
     return hasAnnotation(method, ClassNames.PROVIDES)
         || hasAnnotation(method, ClassNames.BINDS)
diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsMetadata.java b/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsMetadata.java
index 09e1651..9217a57 100644
--- a/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsMetadata.java
+++ b/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsMetadata.java
@@ -24,11 +24,14 @@
 import com.google.auto.value.AutoValue;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
 import dagger.hilt.processor.internal.AggregatedElements;
 import dagger.hilt.processor.internal.AnnotationValues;
 import dagger.hilt.processor.internal.ClassNames;
 import dagger.hilt.processor.internal.Processors;
+import dagger.hilt.processor.internal.root.ir.AggregatedDepsIr;
 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.TypeElement;
@@ -39,7 +42,7 @@
  * dagger.hilt.processor.internal.aggregateddeps.AggregatedDeps} annotation.
  */
 @AutoValue
-abstract class AggregatedDepsMetadata {
+public abstract class AggregatedDepsMetadata {
   private static final String AGGREGATED_DEPS_PACKAGE = "hilt_aggregated_deps";
 
   enum DependencyType {
@@ -48,24 +51,61 @@
     COMPONENT_ENTRY_POINT
   }
 
-  abstract Optional<TypeElement> testElement();
+  /** Returns the aggregating element */
+  public abstract TypeElement aggregatingElement();
 
-  abstract ImmutableSet<TypeElement> componentElements();
+  public abstract Optional<TypeElement> testElement();
+
+  public abstract ImmutableSet<TypeElement> componentElements();
 
   abstract DependencyType dependencyType();
 
   abstract TypeElement dependency();
 
-  abstract ImmutableSet<TypeElement> replacedDependencies();
+  public abstract ImmutableSet<TypeElement> replacedDependencies();
 
-  /** Returns all aggregated deps in the aggregating package. */
+  public boolean isModule() {
+    return dependencyType() == DependencyType.MODULE;
+  }
+
+  /** Returns metadata for all aggregated elements in the aggregating package. */
   public static ImmutableSet<AggregatedDepsMetadata> from(Elements elements) {
-    return AggregatedElements.from(AGGREGATED_DEPS_PACKAGE, ClassNames.AGGREGATED_DEPS, elements)
-        .stream()
+    return from(
+        AggregatedElements.from(AGGREGATED_DEPS_PACKAGE, ClassNames.AGGREGATED_DEPS, elements),
+        elements);
+  }
+
+  /** Returns metadata for each aggregated element. */
+  public static ImmutableSet<AggregatedDepsMetadata> from(
+      ImmutableSet<TypeElement> aggregatedElements, Elements elements) {
+    return aggregatedElements.stream()
         .map(aggregatedElement -> create(aggregatedElement, elements))
         .collect(toImmutableSet());
   }
 
+  public static AggregatedDepsIr toIr(AggregatedDepsMetadata metadata) {
+    return new AggregatedDepsIr(
+        ClassName.get(metadata.aggregatingElement()),
+        metadata.componentElements().stream()
+            .map(ClassName::get)
+            .collect(Collectors.toList()),
+        metadata.testElement()
+            .map(ClassName::get)
+            .orElse(null),
+        metadata.replacedDependencies().stream()
+            .map(ClassName::get)
+            .collect(Collectors.toList()),
+        metadata.dependencyType() == DependencyType.MODULE
+            ? ClassName.get(metadata.dependency())
+            : null,
+        metadata.dependencyType() == DependencyType.ENTRY_POINT
+            ? ClassName.get(metadata.dependency())
+            : null,
+        metadata.dependencyType() == DependencyType.COMPONENT_ENTRY_POINT
+            ? ClassName.get(metadata.dependency())
+            : null);
+  }
+
   private static AggregatedDepsMetadata create(TypeElement element, Elements elements) {
     AnnotationMirror annotationMirror =
         Processors.getAnnotationMirror(element, ClassNames.AGGREGATED_DEPS);
@@ -74,12 +114,11 @@
         Processors.getAnnotationValues(elements, annotationMirror);
 
     return new AutoValue_AggregatedDepsMetadata(
+        element,
         getTestElement(values.get("test"), elements),
         getComponents(values.get("components"), elements),
         getDependencyType(
-            values.get("modules"),
-            values.get("entryPoints"),
-            values.get("componentEntryPoints")),
+            values.get("modules"), values.get("entryPoints"), values.get("componentEntryPoints")),
         getDependency(
             values.get("modules"),
             values.get("entryPoints"),
diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsProcessor.java b/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsProcessor.java
index 151401e..184894a 100644
--- a/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsProcessor.java
+++ b/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsProcessor.java
@@ -26,7 +26,6 @@
 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;
 
@@ -135,7 +134,7 @@
             || 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"
+            + " https://dagger.dev/hilt/flags#disable-install-in-check for how to disable this"
             + " check.",
         element);
 
@@ -167,8 +166,8 @@
     ProcessorErrors.checkState(
         // Skip ApplicationContextModule, since Hilt manages this module internally.
         ClassNames.APPLICATION_CONTEXT_MODULE.equals(ClassName.get(module))
-        || !Processors.requiresModuleInstance(getElementUtils(), module)
-        || hasVisibleEmptyConstructor(module),
+            || !Processors.requiresModuleInstance(getElementUtils(), module)
+            || Processors.hasVisibleEmptyConstructor(module),
         module,
         "Modules that need to be instantiated by Hilt must have a visible, empty constructor.");
 
@@ -444,15 +443,4 @@
     return name.contentEquals("javax.annotation.Generated")
         || name.contentEquals("javax.annotation.processing.Generated");
   }
-
-  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
index 5da2241..6af6692 100644
--- a/java/dagger/hilt/processor/internal/aggregateddeps/BUILD
+++ b/java/dagger/hilt/processor/internal/aggregateddeps/BUILD
@@ -52,11 +52,11 @@
         "//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/incap",
-        "@google_bazel_common//third_party/java/javapoet",
-        "@maven//:com_google_auto_auto_common",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:service",
+        "//third_party/java/guava/collect",
+        "//third_party/java/incap",
+        "//third_party/java/javapoet",
     ],
 )
 
@@ -67,9 +67,9 @@
         "//java/dagger/hilt/processor/internal:classnames",
         "//java/dagger/hilt/processor/internal:kotlin",
         "//java/dagger/hilt/processor/internal:processors",
-        "@google_bazel_common//third_party/java/auto:value",
-        "@google_bazel_common//third_party/java/javapoet",
-        "@maven//:com_google_auto_auto_common",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:value",
+        "//third_party/java/javapoet",
     ],
 )
 
@@ -86,12 +86,13 @@
         "//java/dagger/hilt/processor/internal:component_descriptor",
         "//java/dagger/hilt/processor/internal:processors",
         "//java/dagger/hilt/processor/internal/earlyentrypoint:aggregated_early_entry_point_metadata",
+        "//java/dagger/hilt/processor/internal/root/ir",
         "//java/dagger/hilt/processor/internal/uninstallmodules:aggregated_uninstall_modules_metadata",
         "//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",
+        "//third_party/java/auto:value",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/javapoet",
     ],
 )
 
diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/ComponentDependencies.java b/java/dagger/hilt/processor/internal/aggregateddeps/ComponentDependencies.java
index 897ffd1..ed71c98 100644
--- a/java/dagger/hilt/processor/internal/aggregateddeps/ComponentDependencies.java
+++ b/java/dagger/hilt/processor/internal/aggregateddeps/ComponentDependencies.java
@@ -38,204 +38,85 @@
   }
 
   /** Returns the modules for a component, without any filtering. */
-  public abstract Dependencies modules();
+  public abstract ImmutableSetMultimap<ClassName, TypeElement> modules();
 
   /** Returns the entry points associated with the given a component. */
-  public abstract Dependencies entryPoints();
+  public abstract ImmutableSetMultimap<ClassName, TypeElement> entryPoints();
 
   /** Returns the component entry point associated with the given a component. */
-  public abstract Dependencies componentEntryPoints();
-
-  /** Returns the set of early entry points */
-  public abstract ImmutableSet<ClassName> earlyEntryPoints();
-
-  /** Returns {@code true} if any entry points are annotated with {@code EarlyEntryPoints}. */
-  public boolean hasEarlyEntryPoints() {
-    return !earlyEntryPoints().isEmpty();
-  }
-
-  /**
-   * Returns {@code true} if the test binds or uninstalls test-specific bindings that would prevent
-   * it from sharing components with other test roots.
-   */
-  public final boolean includesTestDeps(ClassName root) {
-    return modules().testDeps().keySet().stream().anyMatch((key) -> key.test().equals(root))
-        || modules().uninstalledTestDeps().containsKey(root);
-  }
+  public abstract ImmutableSetMultimap<ClassName, TypeElement> componentEntryPoints();
 
   @AutoValue.Builder
   abstract static class Builder {
-    abstract Dependencies.Builder modulesBuilder();
+    abstract ImmutableSetMultimap.Builder<ClassName, TypeElement> modulesBuilder();
 
-    abstract Dependencies.Builder entryPointsBuilder();
+    abstract ImmutableSetMultimap.Builder<ClassName, TypeElement> entryPointsBuilder();
 
-    abstract Dependencies.Builder componentEntryPointsBuilder();
-
-    abstract ImmutableSet.Builder<ClassName> earlyEntryPointsBuilder();
+    abstract ImmutableSetMultimap.Builder<ClassName, TypeElement> componentEntryPointsBuilder();
 
     abstract ComponentDependencies build();
   }
 
-  /** 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 global singleton component. */
-    ImmutableSet<TypeElement> getGlobalSingletonDeps() {
-      return ImmutableSet.<TypeElement>builder()
-          .addAll(
-              globalDeps().get(ClassNames.SINGLETON_COMPONENT).stream()
-                  .filter(dep -> !globalUninstalledTestDeps().contains(dep))
-                  .collect(toImmutableSet()))
-          .addAll(globalTestDeps().get(ClassNames.SINGLETON_COMPONENT))
-          .build();
-    }
-
-    /** 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>
-   */
+  /** Returns the component dependencies for the given metadata. */
   public static ComponentDependencies from(
-      ImmutableSet<ComponentDescriptor> descriptors, Elements elements) {
+      ImmutableSet<ComponentDescriptor> descriptors,
+      ImmutableSet<AggregatedDepsMetadata> aggregatedDepsMetadata,
+      ImmutableSet<AggregatedUninstallModulesMetadata> aggregatedUninstallModulesMetadata,
+      ImmutableSet<AggregatedEarlyEntryPointMetadata> aggregatedEarlyEntryPointMetadata,
+      Elements elements) {
+    ImmutableSet<TypeElement> uninstalledModules =
+        ImmutableSet.<TypeElement>builder()
+            .addAll(
+                aggregatedUninstallModulesMetadata.stream()
+                    .flatMap(metadata -> metadata.uninstallModuleElements().stream())
+                    // @AggregatedUninstallModules always references the user module, so convert to
+                    // the generated public wrapper if needed.
+                    // TODO(bcorso): Consider converting this to the public module in the processor.
+                    .map(module -> PkgPrivateMetadata.publicModule(module, elements))
+                    .collect(toImmutableSet()))
+            .addAll(
+                aggregatedDepsMetadata.stream()
+                    .flatMap(metadata -> metadata.replacedDependencies().stream())
+                    .collect(toImmutableSet()))
+            .build();
+
+    ComponentDependencies.Builder componentDependencies = ComponentDependencies.builder();
     ImmutableSet<ClassName> componentNames =
         descriptors.stream().map(ComponentDescriptor::component).collect(toImmutableSet());
-    ComponentDependencies.Builder componentDependencies = ComponentDependencies.builder();
-    for (AggregatedDepsMetadata metadata : AggregatedDepsMetadata.from(elements)) {
-      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 (AggregatedDepsMetadata metadata : aggregatedDepsMetadata) {
       for (TypeElement componentElement : metadata.componentElements()) {
         ClassName componentName = ClassName.get(componentElement);
         checkState(
             componentNames.contains(componentName), "%s is not a valid Component.", componentName);
-        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(componentName, 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(componentName, metadata.dependency());
-            builder.globalUninstalledTestDepsBuilder().addAll(metadata.replacedDependencies());
-          } else {
-            builder.globalDepsBuilder().put(componentName, metadata.dependency());
-          }
+        switch (metadata.dependencyType()) {
+          case MODULE:
+            if (!uninstalledModules.contains(metadata.dependency())) {
+              componentDependencies.modulesBuilder().put(componentName, metadata.dependency());
+            }
+            break;
+          case ENTRY_POINT:
+            componentDependencies.entryPointsBuilder().put(componentName, metadata.dependency());
+            break;
+          case COMPONENT_ENTRY_POINT:
+            componentDependencies
+                .componentEntryPointsBuilder()
+                .put(componentName, metadata.dependency());
+            break;
         }
       }
     }
 
-    AggregatedUninstallModulesMetadata.from(elements)
-        .forEach(
-            metadata ->
-                componentDependencies
-                    .modulesBuilder()
-                    .uninstalledTestDepsBuilder()
-                    .putAll(
-                        ClassName.get(metadata.testElement()),
-                        metadata.uninstallModuleElements().stream()
-                            .map(module -> PkgPrivateMetadata.publicModule(module, elements))
-                            .collect(toImmutableSet())));
-
-    AggregatedEarlyEntryPointMetadata.from(elements).stream()
-        .map(AggregatedEarlyEntryPointMetadata::earlyEntryPoint)
-        .map(entryPoint -> PkgPrivateMetadata.publicEarlyEntryPoint(entryPoint, elements))
-        .map(ClassName::get)
-        .forEach(componentDependencies.earlyEntryPointsBuilder()::add);
+    componentDependencies
+        .entryPointsBuilder()
+        .putAll(
+            ClassNames.SINGLETON_COMPONENT,
+            aggregatedEarlyEntryPointMetadata.stream()
+                .map(AggregatedEarlyEntryPointMetadata::earlyEntryPoint)
+                // @AggregatedEarlyEntryPointMetadata always references the user module, so convert
+                // to the generated public wrapper if needed.
+                // TODO(bcorso): Consider converting this to the public module in the processor.
+                .map(entryPoint -> PkgPrivateMetadata.publicEarlyEntryPoint(entryPoint, elements))
+                .collect(toImmutableSet()));
 
     return componentDependencies.build();
   }
diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateMetadata.java b/java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateMetadata.java
index ff344a6..41c6b7c 100644
--- a/java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateMetadata.java
+++ b/java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateMetadata.java
@@ -44,6 +44,11 @@
     return publicDep(element, elements, ClassNames.EARLY_ENTRY_POINT);
   }
 
+  /** Returns the public Hilt wrapped type or the type itself if it is already public. */
+  public static TypeElement publicEntryPoint(TypeElement element, Elements elements) {
+    return publicDep(element, elements, ClassNames.ENTRY_POINT);
+  }
+
   private static TypeElement publicDep(
       TypeElement element, Elements elements, ClassName annotation) {
     return of(elements, element, annotation)
@@ -102,8 +107,11 @@
 
     if (annotation.equals(ClassNames.MODULE)
         ) {
-      // Skip modules that require a module instance. Required by
-      // dagger (b/31489617)
+      // Skip modules that require a module instance. Otherwise Dagger validation will (correctly)
+      // fail on the wrapper saying a public module can't include a private one, which makes the
+      // error more confusing for users since they probably aren't aware of the wrapper. When
+      // skipped, if the root is in a different package, the error will instead just be on the
+      // generated Hilt component.
       if (Processors.requiresModuleInstance(elements, MoreElements.asType(element))) {
         return Optional.empty();
       }
diff --git a/java/dagger/hilt/processor/internal/aliasof/AliasOfProcessor.java b/java/dagger/hilt/processor/internal/aliasof/AliasOfProcessor.java
index 02e7f5d..6c6a734 100644
--- a/java/dagger/hilt/processor/internal/aliasof/AliasOfProcessor.java
+++ b/java/dagger/hilt/processor/internal/aliasof/AliasOfProcessor.java
@@ -20,6 +20,7 @@
 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 dagger.hilt.processor.internal.BaseProcessor;
 import dagger.hilt.processor.internal.ClassNames;
@@ -53,10 +54,16 @@
     AnnotationMirror annotationMirror =
         Processors.getAnnotationMirror(element, ClassNames.ALIAS_OF);
 
-    TypeElement defineComponentScope =
-        Processors.getAnnotationClassValue(getElementUtils(), annotationMirror, "value");
+    ImmutableList<TypeElement> defineComponentScopes =
+        Processors.getAnnotationClassValues(getElementUtils(), annotationMirror, "value");
 
-    new AliasOfPropagatedDataGenerator(getProcessingEnv(), asType(element), defineComponentScope)
+    ProcessorErrors.checkState(
+        defineComponentScopes.size() >= 1,
+        element,
+        "@AliasOf annotation %s must declare at least one scope to alias.",
+        annotationMirror);
+
+    new AliasOfPropagatedDataGenerator(getProcessingEnv(), asType(element), defineComponentScopes)
         .generate();
   }
 }
diff --git a/java/dagger/hilt/processor/internal/aliasof/AliasOfPropagatedDataGenerator.java b/java/dagger/hilt/processor/internal/aliasof/AliasOfPropagatedDataGenerator.java
index 1d7edf2..a2441f9 100644
--- a/java/dagger/hilt/processor/internal/aliasof/AliasOfPropagatedDataGenerator.java
+++ b/java/dagger/hilt/processor/internal/aliasof/AliasOfPropagatedDataGenerator.java
@@ -16,6 +16,7 @@
 
 package dagger.hilt.processor.internal.aliasof;
 
+import com.google.common.collect.ImmutableList;
 import com.squareup.javapoet.AnnotationSpec;
 import dagger.hilt.processor.internal.ClassNames;
 import dagger.hilt.processor.internal.Processors;
@@ -28,26 +29,32 @@
 
   private final ProcessingEnvironment processingEnv;
   private final TypeElement aliasScope;
-  private final TypeElement defineComponentScope;
+  private final ImmutableList<TypeElement> defineComponentScopes;
 
   AliasOfPropagatedDataGenerator(
       ProcessingEnvironment processingEnv,
       TypeElement aliasScope,
-      TypeElement defineComponentScope) {
+      ImmutableList<TypeElement> defineComponentScopes) {
     this.processingEnv = processingEnv;
     this.aliasScope = aliasScope;
-    this.defineComponentScope = defineComponentScope;
+    this.defineComponentScopes = defineComponentScopes;
   }
 
   void generate() throws IOException {
     Processors.generateAggregatingClass(
         ClassNames.ALIAS_OF_PROPAGATED_DATA_PACKAGE,
-        AnnotationSpec.builder(ClassNames.ALIAS_OF_PROPAGATED_DATA)
-                    .addMember("defineComponentScope", "$T.class", defineComponentScope)
-                    .addMember("alias", "$T.class", aliasScope)
-                    .build(),
+        propagatedDataAnnotation(),
         aliasScope,
         getClass(),
         processingEnv);
   }
+
+  private AnnotationSpec propagatedDataAnnotation() {
+    AnnotationSpec.Builder builder = AnnotationSpec.builder(ClassNames.ALIAS_OF_PROPAGATED_DATA);
+    for (TypeElement defineComponentScope : defineComponentScopes) {
+      builder.addMember("defineComponentScopes", "$T.class", defineComponentScope);
+    }
+    builder.addMember("alias", "$T.class", aliasScope);
+    return builder.build();
+  }
 }
diff --git a/java/dagger/hilt/processor/internal/aliasof/AliasOfPropagatedDataMetadata.java b/java/dagger/hilt/processor/internal/aliasof/AliasOfPropagatedDataMetadata.java
index 30a2c70..d0c89db 100644
--- a/java/dagger/hilt/processor/internal/aliasof/AliasOfPropagatedDataMetadata.java
+++ b/java/dagger/hilt/processor/internal/aliasof/AliasOfPropagatedDataMetadata.java
@@ -16,15 +16,20 @@
 
 package dagger.hilt.processor.internal.aliasof;
 
+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.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
 import dagger.hilt.processor.internal.AggregatedElements;
 import dagger.hilt.processor.internal.AnnotationValues;
+import dagger.hilt.processor.internal.BadInputException;
 import dagger.hilt.processor.internal.ClassNames;
 import dagger.hilt.processor.internal.Processors;
+import dagger.hilt.processor.internal.root.ir.AliasOfPropagatedDataIr;
 import javax.lang.model.element.AnnotationMirror;
 import javax.lang.model.element.AnnotationValue;
 import javax.lang.model.element.TypeElement;
@@ -35,22 +40,42 @@
  * dagger.hilt.internal.aliasof.AliasOfPropagatedData} annotation.
  */
 @AutoValue
-abstract class AliasOfPropagatedDataMetadata {
+public abstract class AliasOfPropagatedDataMetadata {
 
-  abstract TypeElement defineComponentScopeElement();
+  /** Returns the aggregating element */
+  public abstract TypeElement aggregatingElement();
+
+  abstract ImmutableList<TypeElement> defineComponentScopeElements();
 
   abstract TypeElement aliasElement();
 
-  static ImmutableSet<AliasOfPropagatedDataMetadata> from(Elements elements) {
-    return AggregatedElements.from(
+  /** Returns metadata for all aggregated elements in the aggregating package. */
+  public static ImmutableSet<AliasOfPropagatedDataMetadata> from(Elements elements) {
+    return from(
+        AggregatedElements.from(
             ClassNames.ALIAS_OF_PROPAGATED_DATA_PACKAGE,
             ClassNames.ALIAS_OF_PROPAGATED_DATA,
-            elements)
-        .stream()
+            elements),
+        elements);
+  }
+
+  /** Returns metadata for each aggregated element. */
+  public static ImmutableSet<AliasOfPropagatedDataMetadata> from(
+      ImmutableSet<TypeElement> aggregatedElements, Elements elements) {
+    return aggregatedElements.stream()
         .map(aggregatedElement -> create(aggregatedElement, elements))
         .collect(toImmutableSet());
   }
 
+  public static AliasOfPropagatedDataIr toIr(AliasOfPropagatedDataMetadata metadata) {
+    return new AliasOfPropagatedDataIr(
+        ClassName.get(metadata.aggregatingElement()),
+        metadata.defineComponentScopeElements().stream()
+            .map(ClassName::get)
+            .collect(toImmutableList()),
+        ClassName.get(metadata.aliasElement()));
+  }
+
   private static AliasOfPropagatedDataMetadata create(TypeElement element, Elements elements) {
     AnnotationMirror annotationMirror =
         Processors.getAnnotationMirror(element, ClassNames.ALIAS_OF_PROPAGATED_DATA);
@@ -58,8 +83,22 @@
     ImmutableMap<String, AnnotationValue> values =
         Processors.getAnnotationValues(elements, annotationMirror);
 
+    ImmutableList<TypeElement> defineComponentScopes;
+    if (values.containsKey("defineComponentScopes")) {
+      defineComponentScopes =
+          ImmutableList.copyOf(
+              AnnotationValues.getTypeElements(values.get("defineComponentScopes")));
+    } else if (values.containsKey("defineComponentScope")) {
+      // Older version of AliasOfPropagatedData only passed a single defineComponentScope class
+      // value. Fall back on reading the single value if we get old propagated data.
+      defineComponentScopes =
+          ImmutableList.of(AnnotationValues.getTypeElement(values.get("defineComponentScope")));
+    } else {
+      throw new BadInputException(
+          "AliasOfPropagatedData is missing defineComponentScopes", element);
+    }
+
     return new AutoValue_AliasOfPropagatedDataMetadata(
-        AnnotationValues.getTypeElement(values.get("defineComponentScope")),
-        AnnotationValues.getTypeElement(values.get("alias")));
+        element, defineComponentScopes, AnnotationValues.getTypeElement(values.get("alias")));
   }
 }
diff --git a/java/dagger/hilt/processor/internal/aliasof/AliasOfs.java b/java/dagger/hilt/processor/internal/aliasof/AliasOfs.java
index 18951bd..1fee2a3 100644
--- a/java/dagger/hilt/processor/internal/aliasof/AliasOfs.java
+++ b/java/dagger/hilt/processor/internal/aliasof/AliasOfs.java
@@ -16,35 +16,46 @@
 
 package dagger.hilt.processor.internal.aliasof;
 
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
 
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSetMultimap;
 import com.squareup.javapoet.ClassName;
+import dagger.hilt.processor.internal.ComponentDescriptor;
 import dagger.hilt.processor.internal.ProcessorErrors;
-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 {
-  public static AliasOfs create(Elements elements, ImmutableSet<ClassName> defineComponentScopes) {
+  public static AliasOfs create(
+      ImmutableSet<AliasOfPropagatedDataMetadata> metadatas,
+      ImmutableSet<ComponentDescriptor> componentDescriptors) {
+    ImmutableSet<ClassName> defineComponentScopes =
+        componentDescriptors.stream()
+            .flatMap(descriptor -> descriptor.scopes().stream())
+            .collect(toImmutableSet());
+
     ImmutableSetMultimap.Builder<ClassName, ClassName> builder = ImmutableSetMultimap.builder();
-    AliasOfPropagatedDataMetadata.from(elements)
-        .forEach(
-            metadata -> {
-              ClassName defineComponentScopeName =
-                  ClassName.get(metadata.defineComponentScopeElement());
-              ClassName aliasScopeName = ClassName.get(metadata.aliasElement());
-              ProcessorErrors.checkState(
-                  defineComponentScopes.contains(defineComponentScopeName),
-                  metadata.aliasElement(),
-                  "The scope %s cannot be an alias for %s. You can only have aliases of a scope"
-                      + " defined directly on a @DefineComponent type.",
-                  aliasScopeName,
-                  defineComponentScopeName);
-              builder.put(defineComponentScopeName, aliasScopeName);
-            });
+    metadatas.forEach(
+        metadata -> {
+          ClassName aliasScopeName = ClassName.get(metadata.aliasElement());
+          metadata
+              .defineComponentScopeElements()
+              .forEach(
+                  defineComponentScope -> {
+                    ClassName defineComponentScopeName = ClassName.get(defineComponentScope);
+                    ProcessorErrors.checkState(
+                        defineComponentScopes.contains(defineComponentScopeName),
+                        metadata.aliasElement(),
+                        "The scope %s cannot be an alias for %s. You can only have aliases of a"
+                            + " scope defined directly on a @DefineComponent type.",
+                        aliasScopeName,
+                        defineComponentScopeName);
+                    builder.put(defineComponentScopeName, aliasScopeName);
+                  });
+        });
     return new AliasOfs(builder.build());
   }
 
diff --git a/java/dagger/hilt/processor/internal/aliasof/BUILD b/java/dagger/hilt/processor/internal/aliasof/BUILD
index ffd0c9a..d589cb2 100644
--- a/java/dagger/hilt/processor/internal/aliasof/BUILD
+++ b/java/dagger/hilt/processor/internal/aliasof/BUILD
@@ -35,11 +35,11 @@
         "//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",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:service",
+        "//third_party/java/guava/collect",
+        "//third_party/java/incap",
+        "//third_party/java/javapoet",
     ],
 )
 
@@ -52,12 +52,14 @@
     deps = [
         "//java/dagger/hilt/processor/internal:aggregated_elements",
         "//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/hilt/processor/internal/root/ir",
         "//java/dagger/internal/codegen/extension",
-        "//java/dagger/internal/guava:collect",
-        "@google_bazel_common//third_party/java/auto:value",
-        "@google_bazel_common//third_party/java/javapoet",
+        "//third_party/java/auto:value",
+        "//third_party/java/guava/collect",
+        "//third_party/java/javapoet",
     ],
 )
 
diff --git a/java/dagger/hilt/processor/internal/definecomponent/BUILD b/java/dagger/hilt/processor/internal/definecomponent/BUILD
index 43f4dfd..9701a06 100644
--- a/java/dagger/hilt/processor/internal/definecomponent/BUILD
+++ b/java/dagger/hilt/processor/internal/definecomponent/BUILD
@@ -21,6 +21,9 @@
     name = "processor",
     generates_api = 1,
     processor_class = "dagger.hilt.processor.internal.definecomponent.DefineComponentProcessor",
+    visibility = [
+        "//java/dagger/hilt:__pkg__",
+    ],
     deps = [":processor_lib"],
 )
 
@@ -34,10 +37,10 @@
         "//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",
+        "//third_party/java/auto:service",
+        "//third_party/java/guava/collect",
+        "//third_party/java/incap",
+        "//third_party/java/javapoet",
     ],
 )
 
@@ -55,11 +58,12 @@
         "//java/dagger/hilt/processor/internal:component_descriptor",
         "//java/dagger/hilt/processor/internal:processor_errors",
         "//java/dagger/hilt/processor/internal:processors",
+        "//java/dagger/hilt/processor/internal/root/ir",
         "//java/dagger/internal/codegen/extension",
-        "//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",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:value",
+        "//third_party/java/guava/collect",
+        "//third_party/java/javapoet",
     ],
 )
 
diff --git a/java/dagger/hilt/processor/internal/definecomponent/DefineComponentClassesMetadata.java b/java/dagger/hilt/processor/internal/definecomponent/DefineComponentClassesMetadata.java
index 36ac28f..a9da094 100644
--- a/java/dagger/hilt/processor/internal/definecomponent/DefineComponentClassesMetadata.java
+++ b/java/dagger/hilt/processor/internal/definecomponent/DefineComponentClassesMetadata.java
@@ -21,11 +21,13 @@
 import com.google.auto.value.AutoValue;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
 import dagger.hilt.processor.internal.AggregatedElements;
 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 dagger.hilt.processor.internal.root.ir.DefineComponentClassesIr;
 import javax.lang.model.element.AnnotationMirror;
 import javax.lang.model.element.AnnotationValue;
 import javax.lang.model.element.TypeElement;
@@ -36,7 +38,10 @@
  * dagger.hilt.internal.definecomponent.DefineComponentClasses} annotation.
  */
 @AutoValue
-abstract class DefineComponentClassesMetadata {
+public abstract class DefineComponentClassesMetadata {
+
+  /** Returns the aggregating element */
+  public abstract TypeElement aggregatingElement();
 
   /**
    * Returns the element annotated with {@code dagger.hilt.internal.definecomponent.DefineComponent}
@@ -52,12 +57,20 @@
     return !isComponent();
   }
 
-  static ImmutableSet<DefineComponentClassesMetadata> from(Elements elements) {
-    return AggregatedElements.from(
+  /** Returns metadata for all aggregated elements in the aggregating package. */
+  public static ImmutableSet<DefineComponentClassesMetadata> from(Elements elements) {
+    return from(
+        AggregatedElements.from(
             ClassNames.DEFINE_COMPONENT_CLASSES_PACKAGE,
             ClassNames.DEFINE_COMPONENT_CLASSES,
-            elements)
-        .stream()
+            elements),
+        elements);
+  }
+
+  /** Returns metadata for each aggregated element. */
+  public static ImmutableSet<DefineComponentClassesMetadata> from(
+      ImmutableSet<TypeElement> aggregatedElements, Elements elements) {
+    return aggregatedElements.stream()
         .map(aggregatedElement -> create(aggregatedElement, elements))
         .collect(toImmutableSet());
   }
@@ -92,6 +105,13 @@
         ClassNames.DEFINE_COMPONENT_CLASSES.simpleName(),
         isComponent ? "component" : "builder",
         componentOrBuilderName);
-    return new AutoValue_DefineComponentClassesMetadata(componentOrBuilderElement, isComponent);
+    return new AutoValue_DefineComponentClassesMetadata(
+        element, componentOrBuilderElement, isComponent);
+  }
+
+  public static DefineComponentClassesIr toIr(DefineComponentClassesMetadata metadata) {
+    return new DefineComponentClassesIr(
+        ClassName.get(metadata.aggregatingElement()),
+        ClassName.get(metadata.element()));
   }
 }
diff --git a/java/dagger/hilt/processor/internal/definecomponent/DefineComponents.java b/java/dagger/hilt/processor/internal/definecomponent/DefineComponents.java
index efa5011..e9200d0 100644
--- a/java/dagger/hilt/processor/internal/definecomponent/DefineComponents.java
+++ b/java/dagger/hilt/processor/internal/definecomponent/DefineComponents.java
@@ -33,7 +33,6 @@
 import java.util.Map;
 import javax.lang.model.element.Element;
 import javax.lang.model.element.TypeElement;
-import javax.lang.model.util.Elements;
 
 /**
  * A utility class for getting {@link DefineComponentMetadata} and {@link
@@ -78,10 +77,8 @@
   }
 
   /** Returns the set of aggregated {@link ComponentDescriptor}s. */
-  public ImmutableSet<ComponentDescriptor> getComponentDescriptors(Elements elements) {
-    ImmutableSet<DefineComponentClassesMetadata> aggregatedMetadatas =
-        DefineComponentClassesMetadata.from(elements);
-
+  public ImmutableSet<ComponentDescriptor> getComponentDescriptors(
+      ImmutableSet<DefineComponentClassesMetadata> aggregatedMetadatas) {
     ImmutableSet<DefineComponentMetadata> components =
         aggregatedMetadatas.stream()
             .filter(DefineComponentClassesMetadata::isComponent)
diff --git a/java/dagger/hilt/processor/internal/disableinstallincheck/BUILD b/java/dagger/hilt/processor/internal/disableinstallincheck/BUILD
index e3865a6..c06f9fe 100644
--- a/java/dagger/hilt/processor/internal/disableinstallincheck/BUILD
+++ b/java/dagger/hilt/processor/internal/disableinstallincheck/BUILD
@@ -37,9 +37,9 @@
         "//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",
+        "//third_party/java/auto:service",
+        "//third_party/java/guava/collect",
+        "//third_party/java/incap",
     ],
 )
 
diff --git a/java/dagger/hilt/processor/internal/earlyentrypoint/AggregatedEarlyEntryPointMetadata.java b/java/dagger/hilt/processor/internal/earlyentrypoint/AggregatedEarlyEntryPointMetadata.java
index ed347bd..1a93cf2 100644
--- a/java/dagger/hilt/processor/internal/earlyentrypoint/AggregatedEarlyEntryPointMetadata.java
+++ b/java/dagger/hilt/processor/internal/earlyentrypoint/AggregatedEarlyEntryPointMetadata.java
@@ -21,10 +21,12 @@
 import com.google.auto.value.AutoValue;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
 import dagger.hilt.processor.internal.AggregatedElements;
 import dagger.hilt.processor.internal.AnnotationValues;
 import dagger.hilt.processor.internal.ClassNames;
 import dagger.hilt.processor.internal.Processors;
+import dagger.hilt.processor.internal.root.ir.AggregatedEarlyEntryPointIr;
 import javax.lang.model.element.AnnotationMirror;
 import javax.lang.model.element.AnnotationValue;
 import javax.lang.model.element.TypeElement;
@@ -32,25 +34,41 @@
 
 /**
  * A class that represents the values stored in an {@link
- * dagger.hilt.android.internal.uninstallmodules.AggregatedUninstallModules} annotation.
+ * dagger.hilt.android.internal.earlyentrypoint.AggregatedEarlyEntryPoint} annotation.
  */
 @AutoValue
 public abstract class AggregatedEarlyEntryPointMetadata {
 
+  /** Returns the aggregating element */
+  public abstract TypeElement aggregatingElement();
+
   /** Returns the element annotated with {@link dagger.hilt.android.EarlyEntryPoint}. */
   public abstract TypeElement earlyEntryPoint();
 
-  /** Returns all aggregated deps in the aggregating package. */
+  /** Returns metadata for all aggregated elements in the aggregating package. */
   public static ImmutableSet<AggregatedEarlyEntryPointMetadata> from(Elements elements) {
-    return AggregatedElements.from(
+    return from(
+        AggregatedElements.from(
             ClassNames.AGGREGATED_EARLY_ENTRY_POINT_PACKAGE,
             ClassNames.AGGREGATED_EARLY_ENTRY_POINT,
-            elements)
-        .stream()
+            elements),
+        elements);
+  }
+
+  /** Returns metadata for each aggregated element. */
+  public static ImmutableSet<AggregatedEarlyEntryPointMetadata> from(
+      ImmutableSet<TypeElement> aggregatedElements, Elements elements) {
+    return aggregatedElements.stream()
         .map(aggregatedElement -> create(aggregatedElement, elements))
         .collect(toImmutableSet());
   }
 
+  public static AggregatedEarlyEntryPointIr toIr(AggregatedEarlyEntryPointMetadata metadata) {
+    return new AggregatedEarlyEntryPointIr(
+        ClassName.get(metadata.aggregatingElement()),
+        ClassName.get(metadata.earlyEntryPoint()));
+  }
+
   private static AggregatedEarlyEntryPointMetadata create(TypeElement element, Elements elements) {
     AnnotationMirror annotationMirror =
         Processors.getAnnotationMirror(element, ClassNames.AGGREGATED_EARLY_ENTRY_POINT);
@@ -59,6 +77,7 @@
         Processors.getAnnotationValues(elements, annotationMirror);
 
     return new AutoValue_AggregatedEarlyEntryPointMetadata(
+        element,
         elements.getTypeElement(AnnotationValues.getString(values.get("earlyEntryPoint"))));
   }
 }
diff --git a/java/dagger/hilt/processor/internal/earlyentrypoint/BUILD b/java/dagger/hilt/processor/internal/earlyentrypoint/BUILD
index 3573e86..7bbc90c 100644
--- a/java/dagger/hilt/processor/internal/earlyentrypoint/BUILD
+++ b/java/dagger/hilt/processor/internal/earlyentrypoint/BUILD
@@ -34,11 +34,11 @@
         "//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",
-        "@maven//:com_google_auto_auto_common",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:service",
+        "//third_party/java/guava/collect",
+        "//third_party/java/incap",
+        "//third_party/java/javapoet",
     ],
 )
 
@@ -51,9 +51,11 @@
         "//java/dagger/hilt/processor/internal:aggregated_elements",
         "//java/dagger/hilt/processor/internal:classnames",
         "//java/dagger/hilt/processor/internal:processors",
+        "//java/dagger/hilt/processor/internal/root/ir",
         "//java/dagger/internal/codegen/extension",
-        "//java/dagger/internal/guava:collect",
-        "@google_bazel_common//third_party/java/auto:value",
+        "//third_party/java/auto:value",
+        "//third_party/java/guava/collect",
+        "//third_party/java/javapoet",
     ],
 )
 
diff --git a/java/dagger/hilt/processor/internal/generatesrootinput/BUILD b/java/dagger/hilt/processor/internal/generatesrootinput/BUILD
index f24ffb7..2b56408 100644
--- a/java/dagger/hilt/processor/internal/generatesrootinput/BUILD
+++ b/java/dagger/hilt/processor/internal/generatesrootinput/BUILD
@@ -36,10 +36,10 @@
         "//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",
+        "//third_party/java/auto:service",
+        "//third_party/java/guava/collect",
+        "//third_party/java/incap",
+        "//third_party/java/javapoet",
     ],
 )
 
@@ -53,9 +53,9 @@
         "//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",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/javapoet",
     ],
 )
 
diff --git a/java/dagger/hilt/processor/internal/originatingelement/BUILD b/java/dagger/hilt/processor/internal/originatingelement/BUILD
index 50dbcf6..8942535 100644
--- a/java/dagger/hilt/processor/internal/originatingelement/BUILD
+++ b/java/dagger/hilt/processor/internal/originatingelement/BUILD
@@ -34,10 +34,10 @@
         "//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",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:service",
+        "//third_party/java/guava/collect",
+        "//third_party/java/incap",
     ],
 )
 
diff --git a/java/dagger/hilt/processor/internal/root/AggregatedRootGenerator.java b/java/dagger/hilt/processor/internal/root/AggregatedRootGenerator.java
index 722b99b..d79c0a7 100644
--- a/java/dagger/hilt/processor/internal/root/AggregatedRootGenerator.java
+++ b/java/dagger/hilt/processor/internal/root/AggregatedRootGenerator.java
@@ -26,12 +26,17 @@
 /** Generates an {@link dagger.hilt.internal.aggregatedroot.AggregatedRoot}. */
 final class AggregatedRootGenerator {
   private final TypeElement rootElement;
+  private final TypeElement originatingRootElement;
   private final TypeElement rootAnnotation;
   private final ProcessingEnvironment processingEnv;
 
   AggregatedRootGenerator(
-      TypeElement rootElement, TypeElement rootAnnotation, ProcessingEnvironment processingEnv) {
+      TypeElement rootElement,
+      TypeElement originatingRootElement,
+      TypeElement rootAnnotation,
+      ProcessingEnvironment processingEnv) {
     this.rootElement = rootElement;
+    this.originatingRootElement = originatingRootElement;
     this.rootAnnotation = rootAnnotation;
     this.processingEnv = processingEnv;
   }
@@ -41,6 +46,7 @@
         ClassNames.AGGREGATED_ROOT_PACKAGE,
         AnnotationSpec.builder(ClassNames.AGGREGATED_ROOT)
             .addMember("root", "$S", rootElement.getQualifiedName())
+            .addMember("originatingRoot", "$S", originatingRootElement.getQualifiedName())
             .addMember("rootAnnotation", "$T.class", rootAnnotation)
             .build(),
         rootElement,
diff --git a/java/dagger/hilt/processor/internal/root/AggregatedRootMetadata.java b/java/dagger/hilt/processor/internal/root/AggregatedRootMetadata.java
index 961689b..70ee63f 100644
--- a/java/dagger/hilt/processor/internal/root/AggregatedRootMetadata.java
+++ b/java/dagger/hilt/processor/internal/root/AggregatedRootMetadata.java
@@ -19,16 +19,19 @@
 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.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
 import dagger.hilt.processor.internal.AggregatedElements;
 import dagger.hilt.processor.internal.AnnotationValues;
 import dagger.hilt.processor.internal.ClassNames;
 import dagger.hilt.processor.internal.Processors;
+import dagger.hilt.processor.internal.root.ir.AggregatedRootIr;
+import javax.annotation.processing.ProcessingEnvironment;
 import javax.lang.model.element.AnnotationMirror;
 import javax.lang.model.element.AnnotationValue;
 import javax.lang.model.element.TypeElement;
-import javax.lang.model.util.Elements;
 
 /**
  * Represents the values stored in an {@link dagger.hilt.internal.aggregatedroot.AggregatedRoot}.
@@ -36,29 +39,69 @@
 @AutoValue
 abstract class AggregatedRootMetadata {
 
+  /** Returns the aggregating element */
+  public abstract TypeElement aggregatingElement();
+
   /** Returns the element that was annotated with the root annotation. */
   abstract TypeElement rootElement();
 
+  /**
+   * Returns the originating root element. In most cases this will be the same as
+   * {@link #rootElement()}.
+   */
+  abstract TypeElement originatingRootElement();
+
   /** Returns the root annotation as an element. */
   abstract TypeElement rootAnnotation();
 
-  static ImmutableSet<AggregatedRootMetadata> from(Elements elements) {
-    return AggregatedElements.from(
-            ClassNames.AGGREGATED_ROOT_PACKAGE, ClassNames.AGGREGATED_ROOT, elements)
-        .stream()
-        .map(aggregatedElement -> create(aggregatedElement, elements))
+  /** Returns whether this root can use a shared component. */
+  abstract boolean allowsSharingComponent();
+
+  @Memoized
+  RootType rootType() {
+    return RootType.of(rootElement());
+  }
+
+  static ImmutableSet<AggregatedRootMetadata> from(ProcessingEnvironment env) {
+    return from(
+        AggregatedElements.from(
+            ClassNames.AGGREGATED_ROOT_PACKAGE, ClassNames.AGGREGATED_ROOT, env.getElementUtils()),
+        env);
+  }
+
+  /** Returns metadata for each aggregated element. */
+  public static ImmutableSet<AggregatedRootMetadata> from(
+      ImmutableSet<TypeElement> aggregatedElements, ProcessingEnvironment env) {
+    return aggregatedElements.stream()
+        .map(aggregatedElement -> create(aggregatedElement, env))
         .collect(toImmutableSet());
   }
 
-  private static AggregatedRootMetadata create(TypeElement element, Elements elements) {
+  public static AggregatedRootIr toIr(AggregatedRootMetadata metadata) {
+    return new AggregatedRootIr(
+        ClassName.get(metadata.aggregatingElement()),
+        ClassName.get(metadata.rootElement()),
+        ClassName.get(metadata.originatingRootElement()),
+        ClassName.get(metadata.rootAnnotation()),
+        metadata.allowsSharingComponent());
+  }
+
+  private static AggregatedRootMetadata create(TypeElement element, ProcessingEnvironment env) {
     AnnotationMirror annotationMirror =
         Processors.getAnnotationMirror(element, ClassNames.AGGREGATED_ROOT);
 
     ImmutableMap<String, AnnotationValue> values =
-        Processors.getAnnotationValues(elements, annotationMirror);
+        Processors.getAnnotationValues(env.getElementUtils(), annotationMirror);
 
+    TypeElement rootElement =
+        env.getElementUtils().getTypeElement(AnnotationValues.getString(values.get("root")));
+    boolean allowSharingComponent = true;
     return new AutoValue_AggregatedRootMetadata(
-        elements.getTypeElement(AnnotationValues.getString(values.get("root"))),
-        AnnotationValues.getTypeElement(values.get("rootAnnotation")));
+        element,
+        rootElement,
+        env.getElementUtils()
+            .getTypeElement(AnnotationValues.getString(values.get("originatingRoot"))),
+        AnnotationValues.getTypeElement(values.get("rootAnnotation")),
+        allowSharingComponent);
   }
 }
diff --git a/java/dagger/hilt/processor/internal/root/BUILD b/java/dagger/hilt/processor/internal/root/BUILD
index 5ce762f..126662c 100644
--- a/java/dagger/hilt/processor/internal/root/BUILD
+++ b/java/dagger/hilt/processor/internal/root/BUILD
@@ -18,30 +18,28 @@
 package(default_visibility = ["//:src"])
 
 java_plugin(
-    name = "plugin",
+    name = "component_tree_deps_plugin",
     generates_api = 1,
-    processor_class = "dagger.hilt.processor.internal.root.RootProcessor",
+    processor_class = "dagger.hilt.processor.internal.root.ComponentTreeDepsProcessor",
     deps = [
-        ":processor_lib",
+        ":component_tree_deps_processor_lib",
     ],
 )
 
 java_library(
-    name = "processor_lib",
+    name = "component_tree_deps_processor_lib",
     srcs = [
-        "AggregatedRootGenerator.java",
         "ComponentGenerator.java",
+        "ComponentTreeDepsProcessor.java",
         "EarlySingletonComponentCreatorGenerator.java",
-        "ProcessedRootSentinelGenerator.java",
         "RootFileFormatter.java",
         "RootGenerator.java",
-        "RootProcessor.java",
         "TestComponentDataGenerator.java",
-        "TestInjectorGenerator.java",
     ],
     deps = [
         ":root_metadata",
-        ":root_type",
+        "//java/dagger/hilt/android/processor/internal/androidentrypoint:android_generators",
+        "//java/dagger/hilt/android/processor/internal/androidentrypoint:metadata",
         "//java/dagger/hilt/processor/internal:base_processor",
         "//java/dagger/hilt/processor/internal:classnames",
         "//java/dagger/hilt/processor/internal:compiler_options",
@@ -50,16 +48,65 @@
         "//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/hilt/processor/internal/definecomponent:define_components",
-        "//java/dagger/hilt/processor/internal/generatesrootinput:generates_root_inputs",
+        "//java/dagger/hilt/processor/internal/earlyentrypoint:aggregated_early_entry_point_metadata",
+        "//java/dagger/hilt/processor/internal/uninstallmodules:aggregated_uninstall_modules_metadata",
         "//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",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:service",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/graph",
+        "//third_party/java/incap",
+        "//third_party/java/javapoet",
+    ],
+)
+
+java_plugin(
+    name = "root_plugin",
+    generates_api = 1,
+    processor_class = "dagger.hilt.processor.internal.root.RootProcessor",
+    deps = [
+        ":root_processor_lib",
+    ],
+)
+
+java_library(
+    name = "root_processor_lib",
+    srcs = [
+        "AggregatedRootGenerator.java",
+        "ComponentTreeDepsGenerator.java",
+        "ProcessedRootSentinelGenerator.java",
+        "RootProcessor.java",
+        "TestInjectorGenerator.java",
+    ],
+    deps = [
+        ":root_metadata",
+        ":root_type",
+        "//java/dagger/hilt/android/processor/internal/androidentrypoint:android_generators",
+        "//java/dagger/hilt/android/processor/internal/androidentrypoint:metadata",
+        "//java/dagger/hilt/processor/internal:aggregated_elements",
+        "//java/dagger/hilt/processor/internal:base_processor",
+        "//java/dagger/hilt/processor/internal:classnames",
+        "//java/dagger/hilt/processor/internal:compiler_options",
+        "//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/aliasof:alias_ofs",
+        "//java/dagger/hilt/processor/internal/definecomponent:define_components",
+        "//java/dagger/hilt/processor/internal/earlyentrypoint:aggregated_early_entry_point_metadata",
+        "//java/dagger/hilt/processor/internal/generatesrootinput:generates_root_inputs",
+        "//java/dagger/hilt/processor/internal/root/ir",
+        "//java/dagger/hilt/processor/internal/uninstallmodules:aggregated_uninstall_modules_metadata",
+        "//java/dagger/internal/codegen/extension",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:service",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/incap",
+        "//third_party/java/javapoet",
     ],
 )
 
@@ -68,6 +115,7 @@
     srcs = [
         "AggregatedRootMetadata.java",
         "ComponentTree.java",
+        "ComponentTreeDepsMetadata.java",
         "ProcessedRootSentinelMetadata.java",
         "Root.java",
         "RootMetadata.java",
@@ -77,21 +125,21 @@
         ":root_type",
         "//java/dagger/hilt/processor/internal:aggregated_elements",
         "//java/dagger/hilt/processor/internal:classnames",
-        "//java/dagger/hilt/processor/internal:compiler_options",
         "//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/hilt/processor/internal/root/ir",
         "//java/dagger/internal/codegen/extension",
         "//java/dagger/internal/codegen/kotlin",
-        "//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",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:value",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/graph",
+        "//third_party/java/javapoet",
     ],
 )
 
@@ -101,7 +149,7 @@
     deps = [
         "//java/dagger/hilt/processor/internal:classnames",
         "//java/dagger/hilt/processor/internal:processors",
-        "@google_bazel_common//third_party/java/javapoet",
+        "//third_party/java/javapoet",
     ],
 )
 
diff --git a/java/dagger/hilt/processor/internal/root/ComponentTreeDepsGenerator.java b/java/dagger/hilt/processor/internal/root/ComponentTreeDepsGenerator.java
new file mode 100644
index 0000000..34c586a
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/root/ComponentTreeDepsGenerator.java
@@ -0,0 +1,117 @@
+/*
+ * 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.processor.internal.root;
+
+import static javax.lang.model.element.Modifier.PUBLIC;
+
+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.AggregatedElements;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.Processors;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.TypeElement;
+
+/** Generates an {@link dagger.hilt.internal.componenttreedeps.ComponentTreeDeps}. */
+final class ComponentTreeDepsGenerator {
+  // Keeps track of already generated proxies. For correctness, this same instance of
+  // ComponentTreeDepsGenerator must be used for a given round.
+  private final Set<ClassName> generatedProxies = new HashSet<>();
+  private final ProcessingEnvironment env;
+
+  ComponentTreeDepsGenerator(ProcessingEnvironment env) {
+    this.env = env;
+  }
+
+  void generate(ComponentTreeDepsMetadata metadata) throws IOException {
+    ClassName name = metadata.name();
+    TypeSpec.Builder builder =
+        TypeSpec.classBuilder(name)
+            // No originating element since this is generated by the aggregating processor.
+            .addAnnotation(componentTreeDepsAnnotation(metadata));
+
+    Processors.addGeneratedAnnotation(builder, env, ClassNames.ROOT_PROCESSOR.toString());
+
+    JavaFile.builder(name.packageName(), builder.build()).build().writeTo(env.getFiler());
+  }
+
+  AnnotationSpec componentTreeDepsAnnotation(ComponentTreeDepsMetadata metadata)
+      throws IOException {
+    AnnotationSpec.Builder builder = AnnotationSpec.builder(ClassNames.COMPONENT_TREE_DEPS);
+    addDeps(builder, metadata.aggregatedRootDeps(), "rootDeps");
+    addDeps(builder, metadata.defineComponentDeps(), "defineComponentDeps");
+    addDeps(builder, metadata.aliasOfDeps(), "aliasOfDeps");
+    addDeps(builder, metadata.aggregatedDeps(), "aggregatedDeps");
+    addDeps(builder, metadata.aggregatedUninstallModulesDeps(), "uninstallModulesDeps");
+    addDeps(builder, metadata.aggregatedEarlyEntryPointDeps(), "earlyEntryPointDeps");
+    return builder.build();
+  }
+
+  private void addDeps(AnnotationSpec.Builder builder, ImmutableSet<TypeElement> deps, String name)
+      throws IOException {
+    for (TypeElement dep : deps) {
+      builder.addMember(name, "$T.class", maybeWrapInPublicProxy(dep));
+    }
+  }
+
+  /**
+   * This method will return the public proxy for {@code dep} if it is not public, otherwise it will
+   * return {@code dep} itself. It will also generate the proxy if it doesn't already exist.
+   *
+   * <p>Note: These proxies are only used for serialization. The proxy will be unwrapped when
+   * converting to {@link ComponentTreeDepsMetadata}.
+   *
+   * <p>Note: The public proxy is needed because Hilt versions < 2.35 generated package-private
+   * aggregating elements, which can't be referenced directly in the {@code @ComponentTreeDeps}.
+   */
+  private ClassName maybeWrapInPublicProxy(TypeElement dep) throws IOException {
+    Optional<ClassName> proxyName = AggregatedElements.aggregatedElementProxyName(dep);
+    if (proxyName.isPresent()) {
+      // Check the set of already generated proxies to ensure we don't regenerate the proxy in
+      // this round. Also check that the element doesn't already exist to ensure we don't regenerate
+      // a proxy generated in a previous round.
+      if (generatedProxies.add(proxyName.get())
+          && env.getElementUtils().getTypeElement(proxyName.get().canonicalName()) == null) {
+        generateProxy(dep, proxyName.get());
+      }
+      return proxyName.get();
+    }
+    return ClassName.get(dep);
+  }
+
+  private void generateProxy(TypeElement dep, ClassName proxyName) throws IOException {
+    TypeSpec.Builder builder =
+        TypeSpec.classBuilder(proxyName)
+            .addModifiers(PUBLIC)
+            // No originating element since this is generated by the aggregating processor.
+            .addAnnotation(
+                AnnotationSpec.builder(ClassNames.AGGREGATED_ELEMENT_PROXY)
+                    .addMember("value", "$T.class", dep)
+                    .build());
+
+    Processors.addGeneratedAnnotation(builder, env, ClassNames.ROOT_PROCESSOR.toString());
+
+    JavaFile.builder(proxyName.packageName(), builder.build()).build().writeTo(env.getFiler());
+  }
+}
diff --git a/java/dagger/hilt/processor/internal/root/ComponentTreeDepsMetadata.java b/java/dagger/hilt/processor/internal/root/ComponentTreeDepsMetadata.java
new file mode 100644
index 0000000..7d1f367
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/root/ComponentTreeDepsMetadata.java
@@ -0,0 +1,127 @@
+/*
+ * 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.processor.internal.root;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static dagger.hilt.processor.internal.AggregatedElements.unwrapProxies;
+import static dagger.hilt.processor.internal.AnnotationValues.getTypeElements;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.Processors;
+import dagger.hilt.processor.internal.root.ir.ComponentTreeDepsIr;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
+
+/**
+ * Represents the values stored in an {@link
+ * dagger.hilt.internal.componenttreedeps.ComponentTreeDeps}.
+ *
+ * <p>This class is used in both writing ({@link ComponentTreeDepsGenerator}) and reading ({@link
+ * ComponentTreeDepsProcessor}) of the {@code @ComponentTreeDeps} annotation.
+ */
+@AutoValue
+abstract class ComponentTreeDepsMetadata {
+  /**
+   * Returns the name of the element annotated with {@link
+   * dagger.hilt.internal.componenttreedeps.ComponentTreeDeps}.
+   */
+  abstract ClassName name();
+
+  /** Returns the {@link dagger.hilt.internal.aggregatedroot.AggregatedRoot} deps. */
+  abstract ImmutableSet<TypeElement> aggregatedRootDeps();
+
+  /** Returns the {@link dagger.hilt.internal.definecomponent.DefineComponentClasses} deps. */
+  abstract ImmutableSet<TypeElement> defineComponentDeps();
+
+  /** Returns the {@link dagger.hilt.internal.aliasof.AliasOfPropagatedData} deps. */
+  abstract ImmutableSet<TypeElement> aliasOfDeps();
+
+  /** Returns the {@link dagger.hilt.internal.aggregateddeps.AggregatedDeps} deps. */
+  abstract ImmutableSet<TypeElement> aggregatedDeps();
+
+  /** Returns the {@link dagger.hilt.android.uninstallmodules.AggregatedUninstallModules} deps. */
+  abstract ImmutableSet<TypeElement> aggregatedUninstallModulesDeps();
+
+  /** Returns the {@link dagger.hilt.android.earlyentrypoint.AggregatedEarlyEntryPoint} deps. */
+  abstract ImmutableSet<TypeElement> aggregatedEarlyEntryPointDeps();
+
+  static ComponentTreeDepsMetadata from(TypeElement element, Elements elements) {
+    checkArgument(Processors.hasAnnotation(element, ClassNames.COMPONENT_TREE_DEPS));
+    AnnotationMirror annotationMirror =
+        Processors.getAnnotationMirror(element, ClassNames.COMPONENT_TREE_DEPS);
+
+    ImmutableMap<String, AnnotationValue> values =
+        Processors.getAnnotationValues(elements, annotationMirror);
+
+    return create(
+        ClassName.get(element),
+        unwrapProxies(getTypeElements(values.get("rootDeps")), elements),
+        unwrapProxies(getTypeElements(values.get("defineComponentDeps")), elements),
+        unwrapProxies(getTypeElements(values.get("aliasOfDeps")), elements),
+        unwrapProxies(getTypeElements(values.get("aggregatedDeps")), elements),
+        unwrapProxies(getTypeElements(values.get("uninstallModulesDeps")), elements),
+        unwrapProxies(getTypeElements(values.get("earlyEntryPointDeps")), elements));
+  }
+
+  static ComponentTreeDepsMetadata from(ComponentTreeDepsIr ir, Elements elements) {
+    return create(
+        ir.getName(),
+        ir.getRootDeps().stream()
+            .map(it -> elements.getTypeElement(it.canonicalName()))
+            .collect(toImmutableSet()),
+        ir.getDefineComponentDeps().stream()
+            .map(it -> elements.getTypeElement(it.canonicalName()))
+            .collect(toImmutableSet()),
+        ir.getAliasOfDeps().stream()
+            .map(it -> elements.getTypeElement(it.canonicalName()))
+            .collect(toImmutableSet()),
+        ir.getAggregatedDeps().stream()
+            .map(it -> elements.getTypeElement(it.canonicalName()))
+            .collect(toImmutableSet()),
+        ir.getUninstallModulesDeps().stream()
+            .map(it -> elements.getTypeElement(it.canonicalName()))
+            .collect(toImmutableSet()),
+        ir.getEarlyEntryPointDeps().stream()
+            .map(it -> elements.getTypeElement(it.canonicalName()))
+            .collect(toImmutableSet()));
+  }
+
+  static ComponentTreeDepsMetadata create(
+      ClassName name,
+      ImmutableSet<TypeElement> aggregatedRootDeps,
+      ImmutableSet<TypeElement> defineComponentDeps,
+      ImmutableSet<TypeElement> aliasOfDeps,
+      ImmutableSet<TypeElement> aggregatedDeps,
+      ImmutableSet<TypeElement> aggregatedUninstallModulesDeps,
+      ImmutableSet<TypeElement> aggregatedEarlyEntryPointDeps) {
+    return new AutoValue_ComponentTreeDepsMetadata(
+        name,
+        aggregatedRootDeps,
+        defineComponentDeps,
+        aliasOfDeps,
+        aggregatedDeps,
+        aggregatedUninstallModulesDeps,
+        aggregatedEarlyEntryPointDeps);
+  }
+}
diff --git a/java/dagger/hilt/processor/internal/root/ComponentTreeDepsProcessor.java b/java/dagger/hilt/processor/internal/root/ComponentTreeDepsProcessor.java
new file mode 100644
index 0000000..85eb59a
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/root/ComponentTreeDepsProcessor.java
@@ -0,0 +1,204 @@
+/*
+ * 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.processor.internal.root;
+
+import static com.google.auto.common.MoreElements.asType;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.hilt.processor.internal.HiltCompilerOptions.useAggregatingRootProcessor;
+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.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.android.processor.internal.androidentrypoint.AndroidEntryPointMetadata;
+import dagger.hilt.android.processor.internal.androidentrypoint.ApplicationGenerator;
+import dagger.hilt.processor.internal.BaseProcessor;
+import dagger.hilt.processor.internal.ClassNames;
+import dagger.hilt.processor.internal.ComponentDescriptor;
+import dagger.hilt.processor.internal.ComponentNames;
+import dagger.hilt.processor.internal.ProcessorErrors;
+import dagger.hilt.processor.internal.Processors;
+import dagger.hilt.processor.internal.aggregateddeps.AggregatedDepsMetadata;
+import dagger.hilt.processor.internal.aggregateddeps.ComponentDependencies;
+import dagger.hilt.processor.internal.aliasof.AliasOfPropagatedDataMetadata;
+import dagger.hilt.processor.internal.aliasof.AliasOfs;
+import dagger.hilt.processor.internal.definecomponent.DefineComponentClassesMetadata;
+import dagger.hilt.processor.internal.definecomponent.DefineComponents;
+import dagger.hilt.processor.internal.earlyentrypoint.AggregatedEarlyEntryPointMetadata;
+import dagger.hilt.processor.internal.uninstallmodules.AggregatedUninstallModulesMetadata;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+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(ISOLATING)
+@AutoService(Processor.class)
+public final class ComponentTreeDepsProcessor extends BaseProcessor {
+  private final Set<ClassName> componentTreeDepNames = new HashSet<>();
+  private final Set<ClassName> processed = new HashSet<>();
+  private final DefineComponents defineComponents = DefineComponents.create();
+
+  @Override
+  public ImmutableSet<String> getSupportedAnnotationTypes() {
+    return ImmutableSet.of(ClassNames.COMPONENT_TREE_DEPS.toString());
+  }
+
+  @Override
+  public void processEach(TypeElement annotation, Element element) {
+    componentTreeDepNames.add(ClassName.get(asType(element)));
+  }
+
+  @Override
+  public void postRoundProcess(RoundEnvironment roundEnv) throws Exception {
+    ImmutableSet<ComponentTreeDepsMetadata> componentTreeDepsToProcess =
+        componentTreeDepNames.stream()
+            .filter(className -> !processed.contains(className))
+            .map(className -> getElementUtils().getTypeElement(className.canonicalName()))
+            .map(element -> ComponentTreeDepsMetadata.from(element, getElementUtils()))
+            .collect(toImmutableSet());
+
+    for (ComponentTreeDepsMetadata metadata : componentTreeDepsToProcess) {
+      processComponentTreeDeps(metadata);
+    }
+  }
+
+  private void processComponentTreeDeps(ComponentTreeDepsMetadata metadata) throws IOException {
+    TypeElement metadataElement = getElementUtils().getTypeElement(metadata.name().canonicalName());
+    try {
+      // We choose a name for the generated components/wrapper based off of the originating element
+      // annotated with @ComponentTreeDeps. This is close to but isn't necessarily a "real" name of
+      // a root, since with shared test components, even for single roots, the component tree deps
+      // will be moved to a shared package with a deduped name.
+      ClassName renamedRoot = Processors.removeNameSuffix(metadataElement, "_ComponentTreeDeps");
+      ComponentNames componentNames = ComponentNames.withRenaming(rootName -> renamedRoot);
+
+      boolean isDefaultRoot = ClassNames.DEFAULT_ROOT.equals(renamedRoot);
+      ImmutableSet<Root> roots =
+          AggregatedRootMetadata.from(metadata.aggregatedRootDeps(), processingEnv).stream()
+              .map(AggregatedRootMetadata::rootElement)
+              .map(rootElement -> Root.create(rootElement, getProcessingEnv()))
+              .collect(toImmutableSet());
+
+      // TODO(bcorso): For legacy reasons, a lot of the generating code requires a "root" as input
+      // since we used to assume 1 root per component tree. Now that each ComponentTreeDeps may
+      // represent multiple roots, we should refactor this logic.
+      Root root =
+          isDefaultRoot
+              ? Root.createDefaultRoot(getProcessingEnv())
+              // Non-default roots should only ever be associated with one root element
+              : getOnlyElement(roots);
+
+      ImmutableSet<ComponentDescriptor> componentDescriptors =
+          defineComponents.getComponentDescriptors(
+              DefineComponentClassesMetadata.from(
+                  metadata.defineComponentDeps(), getElementUtils()));
+      ComponentTree tree = ComponentTree.from(componentDescriptors);
+      ComponentDependencies deps =
+          ComponentDependencies.from(
+              componentDescriptors,
+              AggregatedDepsMetadata.from(metadata.aggregatedDeps(), getElementUtils()),
+              AggregatedUninstallModulesMetadata.from(
+                  metadata.aggregatedUninstallModulesDeps(), getElementUtils()),
+              AggregatedEarlyEntryPointMetadata.from(
+                  metadata.aggregatedEarlyEntryPointDeps(), getElementUtils()),
+              getElementUtils());
+      AliasOfs aliasOfs =
+          AliasOfs.create(
+              AliasOfPropagatedDataMetadata.from(metadata.aliasOfDeps(), getElementUtils()),
+              componentDescriptors);
+      RootMetadata rootMetadata =
+          RootMetadata.create(root, tree, deps, aliasOfs, getProcessingEnv());
+
+      generateComponents(metadata, rootMetadata, componentNames);
+
+        // Generate a creator for the early entry point if there is a default component available
+        // and there are early entry points.
+        if (isDefaultRoot && !metadata.aggregatedEarlyEntryPointDeps().isEmpty()) {
+          EarlySingletonComponentCreatorGenerator.generate(getProcessingEnv());
+        }
+
+        if (root.isTestRoot()) {
+          // Generate test related classes for each test root that uses this component.
+          ImmutableList<RootMetadata> rootMetadatas =
+              roots.stream()
+                  .map(test -> RootMetadata.create(test, tree, deps, aliasOfs, getProcessingEnv()))
+                  .collect(toImmutableList());
+          generateTestComponentData(metadataElement, rootMetadatas, componentNames);
+        } else {
+          generateApplication(root.element());
+        }
+
+      setProcessingState(metadata, root);
+    } catch (Exception e) {
+      processed.add(metadata.name());
+      throw e;
+    }
+  }
+
+  private void setProcessingState(ComponentTreeDepsMetadata metadata, Root root) {
+    processed.add(metadata.name());
+  }
+
+  private void generateComponents(
+      ComponentTreeDepsMetadata metadata, RootMetadata rootMetadata, ComponentNames componentNames)
+      throws IOException {
+    RootGenerator.generate(metadata, rootMetadata, componentNames, getProcessingEnv());
+  }
+
+  private void generateTestComponentData(
+      TypeElement metadataElement,
+      ImmutableList<RootMetadata> rootMetadatas,
+      ComponentNames componentNames)
+      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(), metadataElement, rootMetadata, componentNames)
+          .generate();
+    }
+  }
+
+  private void generateApplication(TypeElement rootElement) throws IOException {
+    // The generated application references the generated component so they must be generated
+    // in the same build unit. Thus, we only generate the application here if we're using the
+    // Hilt Gradle plugin's aggregating task. If we're using the aggregating processor, we need
+    // to generate the application within AndroidEntryPointProcessor instead.
+    if (!useAggregatingRootProcessor(getProcessingEnv())) {
+      AndroidEntryPointMetadata metadata =
+          AndroidEntryPointMetadata.of(getProcessingEnv(), rootElement);
+      new ApplicationGenerator(
+              getProcessingEnv(),
+              metadata)
+          .generate();
+    }
+  }
+}
diff --git a/java/dagger/hilt/processor/internal/root/EarlySingletonComponentCreatorGenerator.java b/java/dagger/hilt/processor/internal/root/EarlySingletonComponentCreatorGenerator.java
index 1dbda39..81a904a 100644
--- a/java/dagger/hilt/processor/internal/root/EarlySingletonComponentCreatorGenerator.java
+++ b/java/dagger/hilt/processor/internal/root/EarlySingletonComponentCreatorGenerator.java
@@ -45,10 +45,12 @@
                     .returns(ClassName.OBJECT)
                     .addStatement(
                         "return $T.builder()\n"
-                            + ".applicationContextModule(new $T($T.getApplicationContext()))\n"
+                            + ".applicationContextModule(\n"
+                            + "    new $T($T.getApplication($T.getApplicationContext())))\n"
                             + ".build()",
                         DEFAULT_COMPONENT_IMPL,
                         ClassNames.APPLICATION_CONTEXT_MODULE,
+                        ClassNames.CONTEXTS,
                         ClassNames.APPLICATION_PROVIDER)
                     .build());
 
diff --git a/java/dagger/hilt/processor/internal/root/ProcessedRootSentinelMetadata.java b/java/dagger/hilt/processor/internal/root/ProcessedRootSentinelMetadata.java
index d600154..a7070b8 100644
--- a/java/dagger/hilt/processor/internal/root/ProcessedRootSentinelMetadata.java
+++ b/java/dagger/hilt/processor/internal/root/ProcessedRootSentinelMetadata.java
@@ -21,10 +21,13 @@
 import com.google.auto.value.AutoValue;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
 import dagger.hilt.processor.internal.AggregatedElements;
 import dagger.hilt.processor.internal.AnnotationValues;
 import dagger.hilt.processor.internal.ClassNames;
 import dagger.hilt.processor.internal.Processors;
+import dagger.hilt.processor.internal.root.ir.ProcessedRootSentinelIr;
+import java.util.stream.Collectors;
 import javax.lang.model.element.AnnotationMirror;
 import javax.lang.model.element.AnnotationValue;
 import javax.lang.model.element.TypeElement;
@@ -37,6 +40,9 @@
 @AutoValue
 abstract class ProcessedRootSentinelMetadata {
 
+  /** Returns the aggregating element */
+  public abstract TypeElement aggregatingElement();
+
   /** Returns the processed root elements. */
   abstract ImmutableSet<TypeElement> rootElements();
 
@@ -50,6 +56,13 @@
         .collect(toImmutableSet());
   }
 
+  static ProcessedRootSentinelIr toIr(ProcessedRootSentinelMetadata metadata) {
+    return new ProcessedRootSentinelIr(
+        ClassName.get(metadata.aggregatingElement()),
+        metadata.rootElements().stream().map(ClassName::get).collect(Collectors.toList())
+    );
+  }
+
   private static ProcessedRootSentinelMetadata create(TypeElement element, Elements elements) {
     AnnotationMirror annotationMirror =
         Processors.getAnnotationMirror(element, ClassNames.PROCESSED_ROOT_SENTINEL);
@@ -58,6 +71,7 @@
         Processors.getAnnotationValues(elements, annotationMirror);
 
     return new AutoValue_ProcessedRootSentinelMetadata(
+        element,
         AnnotationValues.getStrings(values.get("roots")).stream()
             .map(elements::getTypeElement)
             .collect(toImmutableSet()));
diff --git a/java/dagger/hilt/processor/internal/root/Root.java b/java/dagger/hilt/processor/internal/root/Root.java
index 24440b0..00e17ca 100644
--- a/java/dagger/hilt/processor/internal/root/Root.java
+++ b/java/dagger/hilt/processor/internal/root/Root.java
@@ -43,18 +43,27 @@
   static Root createDefaultRoot(ProcessingEnvironment env) {
     TypeElement rootElement =
         env.getElementUtils().getTypeElement(ClassNames.DEFAULT_ROOT.canonicalName());
-    return new AutoValue_Root(rootElement, /*isTestRoot=*/ true);
+    return new AutoValue_Root(rootElement, rootElement, /*isTestRoot=*/ true);
   }
 
   /** 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(rootElement, RootType.of(rootElement).isTestRoot());
+    if (ClassNames.DEFAULT_ROOT.equals(ClassName.get(rootElement))) {
+      return createDefaultRoot(env);
+    }
+    return new AutoValue_Root(rootElement, rootElement, RootType.of(rootElement).isTestRoot());
   }
 
   /** Returns the root element that should be used with processing. */
   abstract TypeElement element();
 
+  /**
+   * Returns the originating root element. In most cases this will be the same as {@link
+   * #element()}.
+   */
+  abstract TypeElement originatingRootElement();
+
   /** Returns {@code true} if this is a test root. */
   abstract boolean isTestRoot();
 
@@ -63,9 +72,14 @@
     return ClassName.get(element());
   }
 
+  /** Returns the class name of the originating root element. */
+  ClassName originatingRootClassname() {
+    return ClassName.get(originatingRootElement());
+  }
+
   @Override
   public final String toString() {
-    return element().toString();
+    return originatingRootElement().toString();
   }
 
   /** Returns {@code true} if this uses the default root. */
diff --git a/java/dagger/hilt/processor/internal/root/RootGenerator.java b/java/dagger/hilt/processor/internal/root/RootGenerator.java
index f87fbd9..732c6ea 100644
--- a/java/dagger/hilt/processor/internal/root/RootGenerator.java
+++ b/java/dagger/hilt/processor/internal/root/RootGenerator.java
@@ -16,6 +16,7 @@
 
 package dagger.hilt.processor.internal.root;
 
+import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
 import static dagger.hilt.processor.internal.Processors.toClassNames;
 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
@@ -44,20 +45,26 @@
 import java.util.Optional;
 import javax.annotation.processing.ProcessingEnvironment;
 import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
 
 /** Generates components and any other classes needed for a root. */
 final class RootGenerator {
 
   static void generate(
-      RootMetadata metadata, ComponentNames componentNames, ProcessingEnvironment env)
+      ComponentTreeDepsMetadata componentTreeDepsMetadata,
+      RootMetadata metadata,
+      ComponentNames componentNames,
+      ProcessingEnvironment env)
       throws IOException {
     new RootGenerator(
+            componentTreeDepsMetadata,
             RootMetadata.copyWithNewTree(metadata, filterDescriptors(metadata.componentTree())),
             componentNames,
             env)
         .generateComponents();
   }
 
+  private final TypeElement originatingElement;
   private final RootMetadata metadata;
   private final ProcessingEnvironment env;
   private final Root root;
@@ -66,7 +73,13 @@
   private final ComponentNames componentNames;
 
   private RootGenerator(
-      RootMetadata metadata, ComponentNames componentNames, ProcessingEnvironment env) {
+      ComponentTreeDepsMetadata componentTreeDepsMetadata,
+      RootMetadata metadata,
+      ComponentNames componentNames,
+      ProcessingEnvironment env) {
+    this.originatingElement =
+        checkNotNull(
+            env.getElementUtils().getTypeElement(componentTreeDepsMetadata.name().toString()));
     this.metadata = metadata;
     this.componentNames = componentNames;
     this.env = env;
@@ -165,7 +178,7 @@
       ClassName componentName, ClassName builderName, ClassName moduleName) {
     TypeSpec.Builder subcomponentBuilderModule =
         TypeSpec.interfaceBuilder(moduleName)
-            .addOriginatingElement(root.element())
+            .addOriginatingElement(originatingElement)
             .addModifiers(ABSTRACT)
             .addAnnotation(
                 AnnotationSpec.builder(ClassNames.MODULE)
@@ -192,7 +205,7 @@
         .map(
             creator ->
                 TypeSpec.interfaceBuilder("Builder")
-                    .addOriginatingElement(root.element())
+                    .addOriginatingElement(originatingElement)
                     .addModifiers(STATIC, ABSTRACT)
                     .addSuperinterface(creator)
                     .addAnnotation(componentBuilderAnnotation(descriptor))
@@ -221,7 +234,7 @@
   }
 
   private ClassName getComponentsWrapperClassName() {
-    return componentNames.generatedComponentsWrapper(root.classname());
+    return componentNames.generatedComponentsWrapper(root.originatingRootClassname());
   }
 
   private ClassName getComponentClassName(ComponentDescriptor componentDescriptor) {
@@ -240,7 +253,8 @@
         componentDescriptor.component());
 
     ClassName generatedComponent =
-        componentNames.generatedComponent(root.classname(), componentDescriptor.component());
+        componentNames.generatedComponent(
+            root.originatingRootClassname(), componentDescriptor.component());
 
     Integer suffix = simpleComponentNamesToDedupeSuffix.get(generatedComponent.simpleName());
     if (suffix != null) {
diff --git a/java/dagger/hilt/processor/internal/root/RootMetadata.java b/java/dagger/hilt/processor/internal/root/RootMetadata.java
index b39b590..5b39003 100644
--- a/java/dagger/hilt/processor/internal/root/RootMetadata.java
+++ b/java/dagger/hilt/processor/internal/root/RootMetadata.java
@@ -18,14 +18,12 @@
 
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.base.Suppliers.memoize;
-import static dagger.hilt.processor.internal.HiltCompilerOptions.isSharedTestComponentsEnabled;
 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.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSetMultimap;
 import com.squareup.javapoet.ClassName;
@@ -54,33 +52,22 @@
       Root root,
       ComponentTree componentTree,
       ComponentDependencies deps,
+      AliasOfs aliasOfs,
       ProcessingEnvironment env) {
-    return createInternal(root, ImmutableList.of(), componentTree, deps, env);
-  }
-
-  static RootMetadata createForDefaultRoot(
-      Root root,
-      ImmutableList<RootMetadata> rootsUsingDefaultComponents,
-      ComponentTree componentTree,
-      ComponentDependencies deps,
-      ProcessingEnvironment env) {
-    checkState(root.isDefaultRoot());
-    return createInternal(root, rootsUsingDefaultComponents, componentTree, deps, env);
+    return createInternal(root, componentTree, deps, aliasOfs, env);
   }
 
   static RootMetadata copyWithNewTree(RootMetadata other, ComponentTree componentTree) {
-    return createInternal(
-        other.root, other.rootsUsingDefaultComponents, componentTree, other.deps, other.env);
+    return createInternal(other.root, componentTree, other.deps, other.aliasOfs, other.env);
   }
 
   private static RootMetadata createInternal(
       Root root,
-      ImmutableList<RootMetadata> rootsUsingDefaultComponents,
       ComponentTree componentTree,
       ComponentDependencies deps,
+      AliasOfs aliasOfs,
       ProcessingEnvironment env) {
-    RootMetadata metadata =
-        new RootMetadata(root, componentTree, deps, rootsUsingDefaultComponents, env);
+    RootMetadata metadata = new RootMetadata(root, componentTree, deps, aliasOfs, env);
     metadata.validate();
     return metadata;
   }
@@ -90,7 +77,7 @@
   private final Elements elements;
   private final ComponentTree componentTree;
   private final ComponentDependencies deps;
-  private final ImmutableList<RootMetadata> rootsUsingDefaultComponents;
+  private final AliasOfs aliasOfs;
   private final Supplier<ImmutableSetMultimap<ClassName, ClassName>> scopesByComponent =
       memoize(this::getScopesByComponentUncached);
   private final Supplier<TestRootMetadata> testRootMetadata =
@@ -100,14 +87,14 @@
       Root root,
       ComponentTree componentTree,
       ComponentDependencies deps,
-      ImmutableList<RootMetadata> rootsUsingDefaultComponents,
+      AliasOfs aliasOfs,
       ProcessingEnvironment env) {
     this.root = root;
     this.env = env;
     this.elements = env.getElementUtils();
     this.componentTree = componentTree;
     this.deps = deps;
-    this.rootsUsingDefaultComponents = rootsUsingDefaultComponents;
+    this.aliasOfs = aliasOfs;
   }
 
   public Root root() {
@@ -123,24 +110,15 @@
   }
 
   public ImmutableSet<TypeElement> modules(ClassName componentName) {
-    return deps.modules().get(componentName, root.classname(), root.isTestRoot());
-  }
-
-  /**
-   * Returns {@code true} if this is a test root that provides no test-specific dependencies or sets
-   * other options that would prevent it from sharing components with other test roots.
-   */
-  // TODO(groakley): Allow more tests to share modules, e.g. tests that uninstall the same module.
-  // In that case, this might instead return which shared dep grouping should be used.
-  public boolean canShareTestComponents() {
-    return isSharedTestComponentsEnabled(env)
-        && root.isTestRoot()
-        && !deps.includesTestDeps(root.classname());
+    return deps.modules().get(componentName);
   }
 
   public ImmutableSet<TypeName> entryPoints(ClassName componentName) {
     return ImmutableSet.<TypeName>builder()
-        .addAll(getUserDefinedEntryPoints(componentName))
+        .addAll(
+            deps.entryPoints().get(componentName).stream()
+                .map(ClassName::get)
+                .collect(toImmutableSet()))
         .add(
             root.isTestRoot() && componentName.equals(ClassNames.SINGLETON_COMPONENT)
                 ? ClassNames.TEST_SINGLETON_COMPONENT
@@ -197,7 +175,7 @@
                   "[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());
+                  root.originatingRootElement());
         } else if (!root.isTestRoot()) {
           env.getMessager()
               .printMessage(
@@ -205,41 +183,14 @@
                   "[Hilt] All modules must be static and use static provision methods or have a "
                       + "visible, no-arg constructor. Found: "
                       + extraModule.getQualifiedName(),
-                  root.element());
+                  root.originatingRootElement());
         }
       }
     }
   }
 
-  private ImmutableSet<TypeName> getUserDefinedEntryPoints(ClassName componentName) {
-    ImmutableSet.Builder<TypeName> entryPointSet = ImmutableSet.builder();
-    if (root.isDefaultRoot() && !rootsUsingDefaultComponents.isEmpty()) {
-      // Add entry points for shared component
-      rootsUsingDefaultComponents.stream()
-          .flatMap(metadata -> metadata.entryPoints(componentName).stream())
-          .forEach(entryPointSet::add);
-    } else if (root.isDefaultRoot() && componentName.equals(ClassNames.SINGLETON_COMPONENT)) {
-      // We only do this for SingletonComponent because EarlyEntryPoints can only be installed
-      // in the SingletonComponent.
-      deps.earlyEntryPoints().forEach(entryPointSet::add);
-    } else {
-      deps.entryPoints().get(componentName, root.classname(), root.isTestRoot()).stream()
-          .map(ClassName::get)
-          .forEach(entryPointSet::add);
-    }
-    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 = AliasOfs.create(env.getElementUtils(), defineComponentScopes);
-
     for (ComponentDescriptor componentDescriptor : componentTree.getComponentDescriptors()) {
       for (ClassName scope : componentDescriptor.scopes()) {
         builder.put(componentDescriptor.component(), scope);
diff --git a/java/dagger/hilt/processor/internal/root/RootProcessor.java b/java/dagger/hilt/processor/internal/root/RootProcessor.java
index 1ee4446..3dd35f5 100644
--- a/java/dagger/hilt/processor/internal/root/RootProcessor.java
+++ b/java/dagger/hilt/processor/internal/root/RootProcessor.java
@@ -19,29 +19,35 @@
 import static com.google.common.base.Preconditions.checkState;
 import static dagger.hilt.processor.internal.HiltCompilerOptions.isCrossCompilationRootValidationDisabled;
 import static dagger.hilt.processor.internal.HiltCompilerOptions.isSharedTestComponentsEnabled;
-import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.hilt.processor.internal.HiltCompilerOptions.useAggregatingRootProcessor;
 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
-import static java.util.Comparator.comparing;
-import static javax.lang.model.element.Modifier.PUBLIC;
 import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.AGGREGATING;
+import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.DYNAMIC;
+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 com.squareup.javapoet.ClassName;
+import dagger.hilt.processor.internal.BadInputException;
 import dagger.hilt.processor.internal.BaseProcessor;
-import dagger.hilt.processor.internal.ClassNames;
-import dagger.hilt.processor.internal.ComponentDescriptor;
-import dagger.hilt.processor.internal.ComponentNames;
-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.aggregateddeps.AggregatedDepsMetadata;
+import dagger.hilt.processor.internal.aliasof.AliasOfPropagatedDataMetadata;
+import dagger.hilt.processor.internal.definecomponent.DefineComponentClassesMetadata;
+import dagger.hilt.processor.internal.earlyentrypoint.AggregatedEarlyEntryPointMetadata;
 import dagger.hilt.processor.internal.generatesrootinput.GeneratesRootInputs;
-import java.io.IOException;
+import dagger.hilt.processor.internal.root.ir.AggregatedDepsIr;
+import dagger.hilt.processor.internal.root.ir.AggregatedEarlyEntryPointIr;
+import dagger.hilt.processor.internal.root.ir.AggregatedRootIr;
+import dagger.hilt.processor.internal.root.ir.AggregatedRootIrValidator;
+import dagger.hilt.processor.internal.root.ir.AggregatedUninstallModulesIr;
+import dagger.hilt.processor.internal.root.ir.AliasOfPropagatedDataIr;
+import dagger.hilt.processor.internal.root.ir.ComponentTreeDepsIr;
+import dagger.hilt.processor.internal.root.ir.ComponentTreeDepsIrCreator;
+import dagger.hilt.processor.internal.root.ir.DefineComponentClassesIr;
+import dagger.hilt.processor.internal.root.ir.InvalidRootsException;
+import dagger.hilt.processor.internal.root.ir.ProcessedRootSentinelIr;
+import dagger.hilt.processor.internal.uninstallmodules.AggregatedUninstallModulesMetadata;
 import java.util.Arrays;
-import java.util.Comparator;
-import java.util.HashSet;
 import java.util.Set;
 import javax.annotation.processing.ProcessingEnvironment;
 import javax.annotation.processing.Processor;
@@ -51,15 +57,11 @@
 import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
 
 /** Processor that outputs dagger components based on transitive build deps. */
-@IncrementalAnnotationProcessor(AGGREGATING)
+@IncrementalAnnotationProcessor(DYNAMIC)
 @AutoService(Processor.class)
 public final class RootProcessor extends BaseProcessor {
-  private static final Comparator<TypeElement> QUALIFIED_NAME_COMPARATOR =
-      comparing(TypeElement::getQualifiedName, (n1, n2) -> n1.toString().compareTo(n2.toString()));
 
-  private final Set<ClassName> processed = new HashSet<>();
-  // TODO(bcorso): Consider using a Dagger component to create/scope these objects
-  private final DefineComponents defineComponents = DefineComponents.create();
+  private boolean processed;
   private GeneratesRootInputs generatesRootInputs;
 
   @Override
@@ -69,6 +71,13 @@
   }
 
   @Override
+  public ImmutableSet<String> additionalProcessingOptions() {
+    return useAggregatingRootProcessor(getProcessingEnv())
+        ? ImmutableSet.of(AGGREGATING.getProcessorOption())
+        : ImmutableSet.of(ISOLATING.getProcessorOption());
+  }
+
+  @Override
   public ImmutableSet<String> getSupportedAnnotationTypes() {
     return ImmutableSet.<String>builder()
         .addAll(
@@ -81,224 +90,118 @@
   @Override
   public void processEach(TypeElement annotation, Element element) throws Exception {
     TypeElement rootElement = MoreElements.asType(element);
+    // TODO(bcorso): Move this logic into a separate isolating processor to avoid regenerating it
+    // for unrelated changes in Gradle.
     RootType rootType = RootType.of(rootElement);
     if (rootType.isTestRoot()) {
       new TestInjectorGenerator(
               getProcessingEnv(), TestRootMetadata.of(getProcessingEnv(), rootElement))
           .generate();
     }
-    new AggregatedRootGenerator(rootElement, annotation, getProcessingEnv()).generate();
+    TypeElement originatingRootElement =
+        Root.create(rootElement, getProcessingEnv()).originatingRootElement();
+    new AggregatedRootGenerator(rootElement, originatingRootElement, annotation, getProcessingEnv())
+        .generate();
   }
 
   @Override
   public void postRoundProcess(RoundEnvironment roundEnv) throws Exception {
+    if (!useAggregatingRootProcessor(getProcessingEnv())) {
+      return;
+    }
     Set<Element> newElements = generatesRootInputs.getElementsToWaitFor(roundEnv);
-    if (!processed.isEmpty() ) {
+    if (processed) {
       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);
+    } else if (newElements.isEmpty()) {
+      processed = true;
+
+      ImmutableSet<AggregatedRootIr> rootsToProcess = rootsToProcess();
+      if (rootsToProcess.isEmpty()) {
+        return;
+      }
+
+      // Generate an @ComponentTreeDeps for each unique component tree.
+      ComponentTreeDepsGenerator componentTreeDepsGenerator =
+          new ComponentTreeDepsGenerator(getProcessingEnv());
+      for (ComponentTreeDepsMetadata metadata : componentTreeDepsMetadatas(rootsToProcess)) {
+        componentTreeDepsGenerator.generate(metadata);
+      }
+
+      // Generate a sentinel for all processed roots.
+      for (AggregatedRootIr ir : rootsToProcess) {
+        TypeElement rootElement = getElementUtils().getTypeElement(ir.getRoot().canonicalName());
+        new ProcessedRootSentinelGenerator(rootElement, getProcessingEnv()).generate();
+      }
     }
+  }
 
-    if (!newElements.isEmpty()) {
-      // Skip further processing since there's new elements that generate root inputs in this round.
-      return;
-    }
-
-    ImmutableSet<Root> allRoots =
-        AggregatedRootMetadata.from(getElementUtils()).stream()
-            .map(metadata -> Root.create(metadata.rootElement(), getProcessingEnv()))
-            .collect(toImmutableSet());
-
-    ImmutableSet<Root> processedRoots =
+  private ImmutableSet<AggregatedRootIr> rootsToProcess() {
+    ImmutableSet<ProcessedRootSentinelIr> processedRoots =
         ProcessedRootSentinelMetadata.from(getElementUtils()).stream()
-            .flatMap(metadata -> metadata.rootElements().stream())
-            .map(rootElement -> Root.create(rootElement, getProcessingEnv()))
+            .map(ProcessedRootSentinelMetadata::toIr)
+            .collect(toImmutableSet());
+    ImmutableSet<AggregatedRootIr> aggregatedRoots =
+        AggregatedRootMetadata.from(processingEnv).stream()
+            .map(AggregatedRootMetadata::toIr)
             .collect(toImmutableSet());
 
-    ImmutableSet<Root> rootsToProcess =
-        allRoots.stream()
-            .filter(root -> !processedRoots.contains(root))
-            .filter(root -> !processed.contains(rootName(root)))
-            .collect(toImmutableSet());
-
-    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.
+    boolean isCrossCompilationRootValidationDisabled =
+        isCrossCompilationRootValidationDisabled(
+            aggregatedRoots.stream()
+                .map(ir -> getElementUtils().getTypeElement(ir.getRoot().canonicalName()))
+                .collect(toImmutableSet()),
+            processingEnv);
     try {
-      validateRoots(allRoots, rootsToProcess);
-
-      boolean isTestEnv = rootsToProcess.stream().anyMatch(Root::isTestRoot);
-      ComponentNames componentNames =
-          isTestEnv && isSharedTestComponentsEnabled(getProcessingEnv())
-              ? ComponentNames.withRenamingIntoPackage(
-                  ClassNames.DEFAULT_ROOT.packageName(),
-                  rootsToProcess.stream().map(Root::element).collect(toImmutableList()))
-              : ComponentNames.withoutRenaming();
-
-      ImmutableSet<ComponentDescriptor> componentDescriptors =
-          defineComponents.getComponentDescriptors(getElementUtils());
-      ComponentTree tree = ComponentTree.from(componentDescriptors);
-      ComponentDependencies deps =
-          ComponentDependencies.from(componentDescriptors, getElementUtils());
-      ImmutableList<RootMetadata> rootMetadatas =
-          rootsToProcess.stream()
-              .map(root -> RootMetadata.create(root, tree, deps, getProcessingEnv()))
-              .collect(toImmutableList());
-
-      for (RootMetadata rootMetadata : rootMetadatas) {
-        if (!rootMetadata.canShareTestComponents()) {
-          generateComponents(rootMetadata, componentNames);
-        }
-      }
-
-      if (isTestEnv) {
-        ImmutableList<RootMetadata> rootsThatCanShareComponents =
-            rootMetadatas.stream()
-                .filter(RootMetadata::canShareTestComponents)
-                .collect(toImmutableList());
-        generateTestComponentData(rootMetadatas, componentNames);
-        if (deps.hasEarlyEntryPoints() || !rootsThatCanShareComponents.isEmpty()) {
-          Root defaultRoot = Root.createDefaultRoot(getProcessingEnv());
-          generateComponents(
-              RootMetadata.createForDefaultRoot(
-                  defaultRoot, rootsThatCanShareComponents, tree, deps, getProcessingEnv()),
-              componentNames);
-          EarlySingletonComponentCreatorGenerator.generate(getProcessingEnv());
-        }
-      }
-    } catch (Exception e) {
-      for (Root root : rootsToProcess) {
-        processed.add(rootName(root));
-      }
-      throw e;
-    } finally {
-      rootsToProcess.forEach(this::setProcessingState);
-      // Calculate the roots processed in this round. We do this in the finally-block rather than in
-      // the try-block because the catch-block can change the processing state.
-      ImmutableSet<Root> rootsProcessedInRound =
-          rootsToProcess.stream()
-              // Only add a sentinel for processed roots. Skip preprocessed roots since those will
-              // will be processed in the next round.
-              .filter(root -> processed.contains(rootName(root)))
-              .collect(toImmutableSet());
-      for (Root root : rootsProcessedInRound) {
-        new ProcessedRootSentinelGenerator(rootElement(root), getProcessingEnv()).generate();
-      }
+      return ImmutableSet.copyOf(
+          AggregatedRootIrValidator.rootsToProcess(
+              isCrossCompilationRootValidationDisabled, processedRoots, aggregatedRoots));
+    } catch (InvalidRootsException ex) {
+      throw new BadInputException(ex.getMessage());
     }
   }
 
-  private void validateRoots(ImmutableSet<Root> allRoots, ImmutableSet<Root> rootsToProcess) {
-
-    ImmutableSet<TypeElement> rootElementsToProcess =
-        rootsToProcess.stream()
-            .map(Root::element)
-            .sorted(QUALIFIED_NAME_COMPARATOR)
+  private ImmutableSet<ComponentTreeDepsMetadata> componentTreeDepsMetadatas(
+      ImmutableSet<AggregatedRootIr> aggregatedRoots) {
+    ImmutableSet<DefineComponentClassesIr> defineComponentDeps =
+        DefineComponentClassesMetadata.from(getElementUtils()).stream()
+            .map(DefineComponentClassesMetadata::toIr)
+            .collect(toImmutableSet());
+    ImmutableSet<AliasOfPropagatedDataIr> aliasOfDeps =
+        AliasOfPropagatedDataMetadata.from(getElementUtils()).stream()
+            .map(AliasOfPropagatedDataMetadata::toIr)
+            .collect(toImmutableSet());
+    ImmutableSet<AggregatedDepsIr> aggregatedDeps =
+        AggregatedDepsMetadata.from(getElementUtils()).stream()
+            .map(AggregatedDepsMetadata::toIr)
+            .collect(toImmutableSet());
+    ImmutableSet<AggregatedUninstallModulesIr> aggregatedUninstallModulesDeps =
+        AggregatedUninstallModulesMetadata.from(getElementUtils()).stream()
+            .map(AggregatedUninstallModulesMetadata::toIr)
+            .collect(toImmutableSet());
+    ImmutableSet<AggregatedEarlyEntryPointIr> aggregatedEarlyEntryPointDeps =
+        AggregatedEarlyEntryPointMetadata.from(getElementUtils()).stream()
+            .map(AggregatedEarlyEntryPointMetadata::toIr)
             .collect(toImmutableSet());
 
-    ImmutableSet<TypeElement> appRootElementsToProcess =
-        rootsToProcess.stream()
-            .filter(root -> !root.isTestRoot())
-            .map(Root::element)
-            .sorted(QUALIFIED_NAME_COMPARATOR)
-            .collect(toImmutableSet());
-
-    // Perform validation between roots in this compilation unit.
-    if (!appRootElementsToProcess.isEmpty()) {
-      ImmutableSet<TypeElement> testRootElementsToProcess =
-          rootsToProcess.stream()
-              .filter(Root::isTestRoot)
-              .map(Root::element)
-              .sorted(QUALIFIED_NAME_COMPARATOR)
-              .collect(toImmutableSet());
-
-      ProcessorErrors.checkState(
-          testRootElementsToProcess.isEmpty(),
-          "Cannot process test roots and app roots in the same compilation unit:"
-              + "\n\tApp root in this compilation unit: %s"
-              + "\n\tTest roots in this compilation unit: %s",
-          appRootElementsToProcess,
-          testRootElementsToProcess);
-
-      ProcessorErrors.checkState(
-          appRootElementsToProcess.size() == 1,
-          "Cannot process multiple app roots in the same compilation unit: %s",
-          appRootElementsToProcess);
-    }
-
-    // Perform validation across roots previous compilation units.
-    if (!isCrossCompilationRootValidationDisabled(rootElementsToProcess, getProcessingEnv())) {
-      ImmutableSet<TypeElement> processedTestRootElements =
-          allRoots.stream()
-              .filter(Root::isTestRoot)
-              .filter(root -> !rootsToProcess.contains(root))
-              .map(Root::element)
-              .sorted(QUALIFIED_NAME_COMPARATOR)
-              .collect(toImmutableSet());
-
-      // TODO(b/185742783): Add an explanation or link to docs to explain why we're forbidding this.
-      ProcessorErrors.checkState(
-          processedTestRootElements.isEmpty(),
-          "Cannot process new roots when there are test roots from a previous compilation unit:"
-              + "\n\tTest roots from previous compilation unit: %s"
-              + "\n\tAll roots from this compilation unit: %s",
-          processedTestRootElements,
-          rootElementsToProcess);
-
-      ImmutableSet<TypeElement> processedAppRootElements =
-          allRoots.stream()
-              .filter(root -> !root.isTestRoot())
-              .filter(root -> !rootsToProcess.contains(root))
-              .map(Root::element)
-              .sorted(QUALIFIED_NAME_COMPARATOR)
-              .collect(toImmutableSet());
-
-      ProcessorErrors.checkState(
-          processedAppRootElements.isEmpty() || appRootElementsToProcess.isEmpty(),
-          "Cannot process app roots in this compilation unit since there are app roots in a "
-              + "previous compilation unit:"
-              + "\n\tApp roots in previous compilation unit: %s"
-              + "\n\tApp roots in this compilation unit: %s",
-          processedAppRootElements,
-          appRootElementsToProcess);
-    }
-  }
-
-  private void setProcessingState(Root root) {
-    processed.add(rootName(root));
-  }
-
-  private ClassName rootName(Root root) {
-    return ClassName.get(rootElement(root));
-  }
-
-  private TypeElement rootElement(Root root) {
-    return root.element();
-  }
-
-  private void generateComponents(RootMetadata rootMetadata, ComponentNames componentNames)
-      throws IOException {
-    RootGenerator.generate(rootMetadata, componentNames, getProcessingEnv());
-  }
-
-  private void generateTestComponentData(
-      ImmutableList<RootMetadata> rootMetadatas, ComponentNames componentNames) 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, componentNames).generate();
-    }
+    // We should be guaranteed that there are no mixed roots, so check if this is prod or test.
+    boolean isTest = aggregatedRoots.stream().anyMatch(AggregatedRootIr::isTestRoot);
+    Set<ComponentTreeDepsIr> componentTreeDeps =
+        ComponentTreeDepsIrCreator.components(
+            isTest,
+            isSharedTestComponentsEnabled(processingEnv),
+            aggregatedRoots,
+            defineComponentDeps,
+            aliasOfDeps,
+            aggregatedDeps,
+            aggregatedUninstallModulesDeps,
+            aggregatedEarlyEntryPointDeps);
+    return componentTreeDeps.stream()
+        .map(it -> ComponentTreeDepsMetadata.from(it, getElementUtils()))
+        .collect(toImmutableSet());
   }
 }
diff --git a/java/dagger/hilt/processor/internal/root/RootType.java b/java/dagger/hilt/processor/internal/root/RootType.java
index 807f71d..a6efc84 100644
--- a/java/dagger/hilt/processor/internal/root/RootType.java
+++ b/java/dagger/hilt/processor/internal/root/RootType.java
@@ -23,13 +23,13 @@
 
 /** 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),
+enum RootType {
+  ROOT(ClassNames.HILT_ANDROID_APP),
 
-    // Placeholder to make sure @HiltAndroidTest usages get processed
-    HILT_ANDROID_TEST_ROOT(ClassNames.HILT_ANDROID_TEST),
+  // Placeholder to make sure @HiltAndroidTest usages get processed
+  HILT_ANDROID_TEST_ROOT(ClassNames.HILT_ANDROID_TEST),
 
-    TEST_ROOT(ClassNames.INTERNAL_TEST_ROOT);
+  TEST_ROOT(ClassNames.INTERNAL_TEST_ROOT);
 
   @SuppressWarnings("ImmutableEnumChecker")
   private final ClassName annotation;
diff --git a/java/dagger/hilt/processor/internal/root/TestComponentDataGenerator.java b/java/dagger/hilt/processor/internal/root/TestComponentDataGenerator.java
index 101c124..19145f4 100644
--- a/java/dagger/hilt/processor/internal/root/TestComponentDataGenerator.java
+++ b/java/dagger/hilt/processor/internal/root/TestComponentDataGenerator.java
@@ -44,15 +44,18 @@
 /** Generates an implementation of {@link dagger.hilt.android.internal.TestComponentData}. */
 public final class TestComponentDataGenerator {
   private final ProcessingEnvironment processingEnv;
+  private final TypeElement originatingElement;
   private final RootMetadata rootMetadata;
   private final ClassName name;
   private final ComponentNames componentNames;
 
   public TestComponentDataGenerator(
       ProcessingEnvironment processingEnv,
+      TypeElement originatingElement,
       RootMetadata rootMetadata,
       ComponentNames componentNames) {
     this.processingEnv = processingEnv;
+    this.originatingElement = originatingElement;
     this.rootMetadata = rootMetadata;
     this.componentNames = componentNames;
     this.name =
@@ -75,7 +78,8 @@
    *         modules ->
    *             DaggerFooTest_ApplicationComponent.builder()
    *                 .applicationContextModule(
-   *                     new ApplicationContextModule(ApplicationProvider.getApplicationContext()))
+   *                     new ApplicationContextModule(
+   *                         Contexts.getApplication(ApplicationProvider.getApplicationContext())))
    *                 .testModule((FooTest.TestModule) modules.get(FooTest.TestModule.class))
    *                 .testModule(modules.containsKey(FooTest.TestModule.class)
    *                   ? (FooTest.TestModule) modules.get(FooTest.TestModule.class)
@@ -88,6 +92,7 @@
   public void generate() throws IOException {
     TypeSpec.Builder generator =
         TypeSpec.classBuilder(name)
+            .addOriginatingElement(originatingElement)
             .superclass(ClassNames.TEST_COMPONENT_DATA_SUPPLIER)
             .addModifiers(PUBLIC, FINAL)
             .addMethod(getMethod())
@@ -105,10 +110,7 @@
     TypeElement testElement = rootMetadata.testRootMetadata().testElement();
     ClassName component =
         componentNames.generatedComponent(
-            rootMetadata.canShareTestComponents()
-                ? ClassNames.DEFAULT_ROOT
-                : ClassName.get(testElement),
-            ClassNames.SINGLETON_COMPONENT);
+            ClassName.get(testElement), ClassNames.SINGLETON_COMPONENT);
     ImmutableSet<TypeElement> daggerRequiredModules =
         rootMetadata.modulesThatDaggerCannotConstruct(ClassNames.SINGLETON_COMPONENT);
     ImmutableSet<TypeElement> hiltRequiredModules =
@@ -128,11 +130,13 @@
             getElementsListed(hiltRequiredModules),
             CodeBlock.of(
                 "(modules, testInstance, autoAddModuleEnabled) -> $T.builder()\n"
-                    + ".applicationContextModule(new $T($T.getApplicationContext()))\n"
+                    + ".applicationContextModule(\n"
+                    + "    new $T($T.getApplication($T.getApplicationContext())))\n"
                     + "$L"
                     + ".build()",
                 Processors.prepend(Processors.getEnclosedClassName(component), "Dagger"),
                 ClassNames.APPLICATION_CONTEXT_MODULE,
+                ClassNames.CONTEXTS,
                 ClassNames.APPLICATION_PROVIDER,
                 daggerRequiredModules.stream()
                     .map(module -> getAddModuleStatement(module, testElement))
@@ -216,9 +220,10 @@
 
   private CodeBlock callInjectTest(TypeElement testElement) {
     return CodeBlock.of(
-        "(($T) (($T) $T.getApplicationContext()).generatedComponent()).injectTest(testInstance)",
+        "(($T) (($T) $T.getApplication($T.getApplicationContext())).generatedComponent()).injectTest(testInstance)",
         rootMetadata.testRootMetadata().testInjectorName(),
         ClassNames.GENERATED_COMPONENT_MANAGER,
+        ClassNames.CONTEXTS,
         ClassNames.APPLICATION_PROVIDER);
   }
 }
diff --git a/java/dagger/hilt/processor/internal/root/ir/AggregatedRootIrValidator.kt b/java/dagger/hilt/processor/internal/root/ir/AggregatedRootIrValidator.kt
new file mode 100644
index 0000000..b24cb14
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/root/ir/AggregatedRootIrValidator.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.processor.internal.root.ir
+
+import kotlin.jvm.Throws
+
+// Validates roots being processed.
+object AggregatedRootIrValidator {
+    @JvmStatic
+    @Throws(InvalidRootsException::class)
+    fun rootsToProcess(
+      isCrossCompilationRootValidationDisabled: Boolean,
+      processedRoots: Set<ProcessedRootSentinelIr>,
+      aggregatedRoots: Set<AggregatedRootIr>
+    ): Set<AggregatedRootIr> {
+      val processedRootNames = processedRoots.flatMap { it.roots }.toSet()
+      val rootsToProcess = aggregatedRoots
+        .filterNot { processedRootNames.contains(it.root) }
+        .sortedBy { it.root.toString() }
+      val testRootsToProcess = rootsToProcess.filter { it.isTestRoot }
+      val appRootsToProcess = rootsToProcess - testRootsToProcess
+      fun Collection<AggregatedRootIr>.rootsToString() = map { it.root }.joinToString()
+      if (appRootsToProcess.size > 1) {
+        throw InvalidRootsException(
+          "Cannot process multiple app roots in the same compilation unit: " +
+            appRootsToProcess.rootsToString()
+        )
+      }
+      if (testRootsToProcess.isNotEmpty() && appRootsToProcess.isNotEmpty()) {
+        throw InvalidRootsException("""
+        Cannot process test roots and app roots in the same compilation unit:
+          App root in this compilation unit: ${appRootsToProcess.rootsToString()}
+          Test roots in this compilation unit: ${testRootsToProcess.rootsToString()}
+        """.trimIndent()
+        )
+      }
+      // Perform validation across roots previous compilation units.
+      if (!isCrossCompilationRootValidationDisabled) {
+        val alreadyProcessedTestRoots = aggregatedRoots.filter {
+          it.isTestRoot && processedRootNames.contains(it.root)
+        }
+        // TODO(b/185742783): Add an explanation or link to docs to explain why we're forbidding this.
+        if (alreadyProcessedTestRoots.isNotEmpty() && rootsToProcess.isNotEmpty()) {
+          throw InvalidRootsException("""
+          Cannot process new roots when there are test roots from a previous compilation unit:
+            Test roots from previous compilation unit: ${alreadyProcessedTestRoots.rootsToString()}
+            All roots from this compilation unit: ${rootsToProcess.rootsToString()}
+          """.trimIndent()
+          )
+        }
+        val alreadyProcessedAppRoots = aggregatedRoots.filter {
+          !it.isTestRoot && processedRootNames.contains(it.root)
+        }
+        if (alreadyProcessedAppRoots.isNotEmpty() && appRootsToProcess.isNotEmpty()) {
+          throw InvalidRootsException("""
+          Cannot process new app roots when there are app roots from a previous compilation unit:
+            App roots in previous compilation unit: ${alreadyProcessedAppRoots.rootsToString()}
+            App roots in this compilation unit: ${appRootsToProcess.rootsToString()}
+          """.trimIndent()
+          )
+        }
+      }
+      return rootsToProcess.toSet()
+    }
+}
+
+// An exception thrown when root validation fails.
+class InvalidRootsException(msg: String) : Exception(msg)
diff --git a/java/dagger/hilt/processor/internal/root/ir/BUILD b/java/dagger/hilt/processor/internal/root/ir/BUILD
new file mode 100644
index 0000000..4bc1a29
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/root/ir/BUILD
@@ -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.
+
+# Description:
+#   A library containing intermediate representations of the various Hilt
+#   aggregating annotations along with logic to process them.
+
+load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_library")
+
+package(default_visibility = ["//:src"])
+
+kt_jvm_library(
+    name = "ir",
+    srcs = glob(["*.kt"]),
+    # Dependencies here should be kept to a minimum since this library is
+    # shadowed into the Hilt Gradle Plugin artifact.
+    deps = [
+        "//third_party/java/javapoet",
+    ],
+)
+
+# 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 = "ir-sources",
+    srcs = glob(["*.kt"]),
+    outs = ["libir-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 .
+    """,
+)
+
+filegroup(
+    name = "srcs_filegroup",
+    srcs = glob(["*"]),
+)
diff --git a/java/dagger/hilt/processor/internal/root/ir/ComponentTreeDepsIrCreator.kt b/java/dagger/hilt/processor/internal/root/ir/ComponentTreeDepsIrCreator.kt
new file mode 100644
index 0000000..4c7b94d
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/root/ir/ComponentTreeDepsIrCreator.kt
@@ -0,0 +1,260 @@
+/*
+ * 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.processor.internal.root.ir
+
+import com.squareup.javapoet.ClassName
+
+// Produces ComponentTreeDepsIr for a set of aggregated deps and roots to process.
+class ComponentTreeDepsIrCreator private constructor(
+  private val isSharedTestComponentsEnabled: Boolean,
+  private val aggregatedRoots: Set<AggregatedRootIr>,
+  private val defineComponentDeps: Set<DefineComponentClassesIr>,
+  private val aliasOfDeps: Set<AliasOfPropagatedDataIr>,
+  private val aggregatedDeps: Set<AggregatedDepsIr>,
+  private val aggregatedUninstallModulesDeps: Set<AggregatedUninstallModulesIr>,
+  private val aggregatedEarlyEntryPointDeps: Set<AggregatedEarlyEntryPointIr>,
+) {
+  private fun prodComponents(): Set<ComponentTreeDepsIr> {
+    // There should only be one prod root in a given build.
+    val aggregatedRoot = aggregatedRoots.single()
+    return setOf(
+      ComponentTreeDepsIr(
+        name = ComponentTreeDepsNameGenerator().generate(aggregatedRoot.root),
+        rootDeps = setOf(aggregatedRoot.fqName),
+        defineComponentDeps = defineComponentDeps.map { it.fqName }.toSet(),
+        aliasOfDeps = aliasOfDeps.map { it.fqName }.toSet(),
+        aggregatedDeps =
+          // @AggregatedDeps with non-empty replaces are from @TestInstallIn and should not be
+          // installed in production components
+          aggregatedDeps.filter { it.replaces.isEmpty() }.map { it.fqName }.toSet(),
+        uninstallModulesDeps = emptySet(),
+        earlyEntryPointDeps = emptySet(),
+      )
+    )
+  }
+
+  private fun testComponents(): Set<ComponentTreeDepsIr> {
+    val rootsUsingSharedComponent = rootsUsingSharedComponent(aggregatedRoots)
+    val aggregatedRootsByRoot = aggregatedRoots.associateBy { it.root }
+    val aggregatedDepsByRoot = aggregatedDepsByRoot(
+      aggregatedRoots = aggregatedRoots,
+      rootsUsingSharedComponent = rootsUsingSharedComponent,
+      hasEarlyEntryPoints = aggregatedEarlyEntryPointDeps.isNotEmpty()
+    )
+    val uninstallModuleDepsByRoot =
+      aggregatedUninstallModulesDeps.associate { it.test to it.fqName }
+    return mutableSetOf<ComponentTreeDepsIr>().apply {
+      aggregatedDepsByRoot.keys.forEach { root ->
+        val isDefaultRoot = root == DEFAULT_ROOT_CLASS_NAME
+        val isEarlyEntryPointRoot = isDefaultRoot && aggregatedEarlyEntryPointDeps.isNotEmpty()
+        // We want to base the generated name on the user written root rather than a generated root.
+        val rootName = if (isDefaultRoot) {
+          DEFAULT_ROOT_CLASS_NAME
+        } else {
+          aggregatedRootsByRoot.getValue(root).originatingRoot
+        }
+        val componentNameGenerator =
+          if (isSharedTestComponentsEnabled) {
+            ComponentTreeDepsNameGenerator(
+              destinationPackage = "dagger.hilt.android.internal.testing.root",
+              otherRootNames = aggregatedDepsByRoot.keys
+            )
+          } else {
+            ComponentTreeDepsNameGenerator()
+          }
+        add(
+          ComponentTreeDepsIr(
+            name = componentNameGenerator.generate(rootName),
+            rootDeps =
+              // Non-default component: the root
+              // Shared component: all roots sharing the component
+              // EarlyEntryPoint component: empty
+              if (isDefaultRoot) {
+                rootsUsingSharedComponent.map { aggregatedRootsByRoot.getValue(it).fqName }.toSet()
+              } else {
+                setOf(aggregatedRootsByRoot.getValue(root).fqName)
+              },
+            defineComponentDeps = defineComponentDeps.map { it.fqName }.toSet(),
+            aliasOfDeps = aliasOfDeps.map { it.fqName }.toSet(),
+            aggregatedDeps = aggregatedDepsByRoot.getOrElse(root) { emptySet() },
+            uninstallModulesDeps = uninstallModuleDepsByRoot[root]?.let { setOf(it) } ?: emptySet(),
+            earlyEntryPointDeps =
+              if (isEarlyEntryPointRoot) {
+                aggregatedEarlyEntryPointDeps.map { it.fqName }.toSet()
+              } else {
+                emptySet()
+              }
+          )
+        )
+      }
+    }
+  }
+
+  private fun rootsUsingSharedComponent(roots: Set<AggregatedRootIr>): Set<ClassName> {
+    if (!isSharedTestComponentsEnabled) {
+      return emptySet()
+    }
+    val hasLocalModuleDependencies: Set<ClassName> = mutableSetOf<ClassName>().apply {
+      addAll(aggregatedDeps.filter { it.module != null }.mapNotNull { it.test })
+      addAll(aggregatedUninstallModulesDeps.map { it.test })
+    }
+    return roots
+      .filter { it.isTestRoot && it.allowsSharingComponent }
+      .map { it.root }
+      .filter { !hasLocalModuleDependencies.contains(it) }
+      .toSet()
+  }
+
+  private fun aggregatedDepsByRoot(
+    aggregatedRoots: Set<AggregatedRootIr>,
+    rootsUsingSharedComponent: Set<ClassName>,
+    hasEarlyEntryPoints: Boolean
+  ): Map<ClassName, Set<ClassName>> {
+    val testDepsByRoot = aggregatedDeps
+      .filter { it.test != null }
+      .groupBy(keySelector = { it.test }, valueTransform = { it.fqName })
+    val globalModules = aggregatedDeps
+      .filter { it.test == null && it.module != null }
+      .map { it.fqName }
+    val globalEntryPointsByComponent = aggregatedDeps
+      .filter { it.test == null && it.module == null }
+      .groupBy(keySelector = { it.test }, valueTransform = { it.fqName })
+    val result = mutableMapOf<ClassName, LinkedHashSet<ClassName>>()
+    aggregatedRoots.forEach { aggregatedRoot ->
+      if (!rootsUsingSharedComponent.contains(aggregatedRoot.root)) {
+        result.getOrPut(aggregatedRoot.root) { linkedSetOf() }.apply {
+          addAll(globalModules)
+          addAll(globalEntryPointsByComponent.values.flatten())
+          addAll(testDepsByRoot.getOrElse(aggregatedRoot.root) { emptyList() })
+        }
+      }
+    }
+    // Add the Default/EarlyEntryPoint root if necessary.
+    if (rootsUsingSharedComponent.isNotEmpty()) {
+      result.getOrPut(DEFAULT_ROOT_CLASS_NAME) { linkedSetOf() }.apply {
+        addAll(globalModules)
+        addAll(globalEntryPointsByComponent.values.flatten())
+        addAll(rootsUsingSharedComponent.flatMap { testDepsByRoot.getOrElse(it) { emptyList() } })
+      }
+    } else if (hasEarlyEntryPoints) {
+      result.getOrPut(DEFAULT_ROOT_CLASS_NAME) { linkedSetOf() }.apply {
+        addAll(globalModules)
+        addAll(
+          globalEntryPointsByComponent.entries
+            .filterNot { (component, _) -> component == SINGLETON_COMPONENT_CLASS_NAME }
+            .flatMap { (_, entryPoints) -> entryPoints }
+        )
+      }
+    }
+    return result
+  }
+
+  /**
+   * Generates a component name for a tree that will be based off the given root after mapping it to
+   * the [destinationPackage] and disambiguating from [otherRootNames].
+   */
+  private class ComponentTreeDepsNameGenerator(
+    private val destinationPackage: String? = null,
+    private val otherRootNames: Collection<ClassName> = emptySet()
+  ) {
+    private val simpleNameMap: Map<ClassName, String> by lazy {
+      mutableMapOf<ClassName, String>().apply {
+        otherRootNames.groupBy { it.enclosedName() }.values.forEach { conflictingRootNames ->
+          if (conflictingRootNames.size == 1) {
+            // If there's only 1 root there's nothing to disambiguate so return the simple name.
+            put(conflictingRootNames.first(), conflictingRootNames.first().enclosedName())
+          } else {
+            // 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.
+            // Sorted in order to guarantee determinism if this is invoked by different processors.
+            val usedNames = mutableSetOf<String>()
+            conflictingRootNames.sorted().forEach { rootClassName ->
+              val basePrefix = rootClassName.let { className ->
+                val containerName = className.enclosingClassName()?.enclosedName() ?: ""
+                if (containerName.isNotEmpty() && containerName[0].isUpperCase()) {
+                  // If parent element looks like a class, use its initials as a prefix.
+                  containerName.filterNot { it.isLowerCase() }
+                } else {
+                  // Not in a normally named class. Prefix with the initials of the elements
+                  // leading here.
+                  className.toString().split('.').dropLast(1)
+                    .joinToString(separator = "") { "${it.first()}" }
+                }
+              }
+              var uniqueName = basePrefix
+              var differentiator = 2
+              while (!usedNames.add(uniqueName)) {
+                uniqueName = basePrefix + differentiator++
+              }
+              put(rootClassName, "${uniqueName}_${rootClassName.enclosedName()}")
+            }
+          }
+        }
+      }
+    }
+
+    fun generate(rootName: ClassName): ClassName =
+      ClassName.get(
+        destinationPackage ?: rootName.packageName(),
+        if (otherRootNames.isEmpty()) {
+          rootName.enclosedName()
+        } else {
+          simpleNameMap.getValue(rootName)
+        }
+      ).append("_ComponentTreeDeps")
+
+    private fun ClassName.enclosedName() = simpleNames().joinToString(separator = "_")
+
+    private fun ClassName.append(suffix: String) = peerClass(simpleName() + suffix)
+  }
+
+  companion object {
+
+    @JvmStatic
+    fun components(
+      isTest: Boolean,
+      isSharedTestComponentsEnabled: Boolean,
+      aggregatedRoots: Set<AggregatedRootIr>,
+      defineComponentDeps: Set<DefineComponentClassesIr>,
+      aliasOfDeps: Set<AliasOfPropagatedDataIr>,
+      aggregatedDeps: Set<AggregatedDepsIr>,
+      aggregatedUninstallModulesDeps: Set<AggregatedUninstallModulesIr>,
+      aggregatedEarlyEntryPointDeps: Set<AggregatedEarlyEntryPointIr>,
+    ) = ComponentTreeDepsIrCreator(
+      isSharedTestComponentsEnabled,
+      // TODO(bcorso): Consider creating a common interface for fqName so that we can sort these
+      // using a shared method rather than repeating the sorting logic.
+      aggregatedRoots.toList().sortedBy { it.fqName.canonicalName() }.toSet(),
+      defineComponentDeps.toList().sortedBy { it.fqName.canonicalName() }.toSet(),
+      aliasOfDeps.toList().sortedBy { it.fqName.canonicalName() }.toSet(),
+      aggregatedDeps.toList().sortedBy { it.fqName.canonicalName() }.toSet(),
+      aggregatedUninstallModulesDeps.toList().sortedBy { it.fqName.canonicalName() }.toSet(),
+      aggregatedEarlyEntryPointDeps.toList().sortedBy { it.fqName.canonicalName() }.toSet()
+    ).let { producer ->
+      if (isTest) {
+        producer.testComponents()
+      } else {
+        producer.prodComponents()
+      }
+    }
+
+    val DEFAULT_ROOT_CLASS_NAME: ClassName =
+      ClassName.get("dagger.hilt.android.internal.testing.root", "Default")
+    val SINGLETON_COMPONENT_CLASS_NAME: ClassName =
+      ClassName.get("dagger.hilt.components", "SingletonComponent")
+  }
+}
diff --git a/java/dagger/hilt/processor/internal/root/ir/MetadataIr.kt b/java/dagger/hilt/processor/internal/root/ir/MetadataIr.kt
new file mode 100644
index 0000000..200c0d4
--- /dev/null
+++ b/java/dagger/hilt/processor/internal/root/ir/MetadataIr.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.processor.internal.root.ir
+
+import com.squareup.javapoet.ClassName
+
+/**
+ * Represents [dagger.hilt.processor.internal.aggregateddeps.AggregatedDeps]
+ *
+ * Even though the annotation uses arrays for modules, entryPoints and componentEntryPoints the
+ * reality is that exactly only one value will be present in one of those arrays.
+ */
+data class AggregatedDepsIr(
+  val fqName: ClassName,
+  val components: List<ClassName>,
+  val test: ClassName?,
+  val replaces: List<ClassName>,
+  val module: ClassName?,
+  val entryPoint: ClassName?,
+  val componentEntryPoint: ClassName?,
+)
+
+/** Represents [dagger.hilt.android.internal.earlyentrypoint.AggregatedEarlyEntryPoint] */
+data class AggregatedEarlyEntryPointIr(
+  val fqName: ClassName,
+  val earlyEntryPoint: ClassName,
+)
+
+/** Represents [dagger.hilt.android.internal.legacy.AggregatedElementProxy] */
+data class AggregatedElementProxyIr(
+  val fqName: ClassName,
+  val value: ClassName,
+)
+
+/** Represents [dagger.hilt.internal.aggregatedroot.AggregatedRoot] */
+data class AggregatedRootIr(
+  val fqName: ClassName,
+  val root: ClassName,
+  val originatingRoot: ClassName,
+  val rootAnnotation: ClassName,
+  // External property from the annotation that indicates if root can use a shared component.
+  val allowsSharingComponent: Boolean = true
+) {
+  // Equivalent to RootType.isTestRoot()
+  val isTestRoot = TEST_ROOT_ANNOTATIONS.contains(rootAnnotation.toString())
+
+  companion object {
+    private val TEST_ROOT_ANNOTATIONS =
+      listOf(
+        "dagger.hilt.android.testing.HiltAndroidTest",
+        "dagger.hilt.android.internal.testing.InternalTestRoot",
+      )
+  }
+}
+
+/** Represents [dagger.hilt.android.internal.uninstallmodules.AggregatedUninstallModules] */
+data class AggregatedUninstallModulesIr(
+  val fqName: ClassName,
+  val test: ClassName,
+  val uninstallModules: List<ClassName>
+)
+
+/** Represents [dagger.hilt.internal.aliasof.AliasOfPropagatedData] */
+data class AliasOfPropagatedDataIr(
+  val fqName: ClassName,
+  val defineComponentScopes: List<ClassName>,
+  val alias: ClassName,
+)
+
+/** Represents [dagger.hilt.internal.componenttreedeps.ComponentTreeDeps] */
+data class ComponentTreeDepsIr(
+  val name: ClassName,
+  val rootDeps: Set<ClassName>,
+  val defineComponentDeps: Set<ClassName>,
+  val aliasOfDeps: Set<ClassName>,
+  val aggregatedDeps: Set<ClassName>,
+  val uninstallModulesDeps: Set<ClassName>,
+  val earlyEntryPointDeps: Set<ClassName>,
+)
+
+/** Represents [dagger.hilt.internal.definecomponent.DefineComponentClasses] */
+data class DefineComponentClassesIr(
+  val fqName: ClassName,
+  val component: ClassName,
+)
+
+/** Represents [dagger.hilt.internal.processedrootsentinel.ProcessedRootSentinel] */
+data class ProcessedRootSentinelIr(val fqName: ClassName, val roots: List<ClassName>)
diff --git a/java/dagger/hilt/processor/internal/uninstallmodules/AggregatedUninstallModulesMetadata.java b/java/dagger/hilt/processor/internal/uninstallmodules/AggregatedUninstallModulesMetadata.java
index eee7849..5cad3c9 100644
--- a/java/dagger/hilt/processor/internal/uninstallmodules/AggregatedUninstallModulesMetadata.java
+++ b/java/dagger/hilt/processor/internal/uninstallmodules/AggregatedUninstallModulesMetadata.java
@@ -23,10 +23,13 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
 import dagger.hilt.processor.internal.AggregatedElements;
 import dagger.hilt.processor.internal.AnnotationValues;
 import dagger.hilt.processor.internal.ClassNames;
 import dagger.hilt.processor.internal.Processors;
+import dagger.hilt.processor.internal.root.ir.AggregatedUninstallModulesIr;
+import java.util.stream.Collectors;
 import javax.lang.model.element.AnnotationMirror;
 import javax.lang.model.element.AnnotationValue;
 import javax.lang.model.element.TypeElement;
@@ -39,6 +42,9 @@
 @AutoValue
 public abstract class AggregatedUninstallModulesMetadata {
 
+  /** Returns the aggregating element */
+  public abstract TypeElement aggregatingElement();
+
   /** Returns the test annotated with {@link dagger.hilt.android.testing.UninstallModules}. */
   public abstract TypeElement testElement();
 
@@ -47,17 +53,33 @@
    */
   public abstract ImmutableList<TypeElement> uninstallModuleElements();
 
-  /** Returns all aggregated deps in the aggregating package mapped by the top-level element. */
+  /** Returns metadata for all aggregated elements in the aggregating package. */
   public static ImmutableSet<AggregatedUninstallModulesMetadata> from(Elements elements) {
-    return AggregatedElements.from(
+    return from(
+        AggregatedElements.from(
             ClassNames.AGGREGATED_UNINSTALL_MODULES_PACKAGE,
             ClassNames.AGGREGATED_UNINSTALL_MODULES,
-            elements)
-        .stream()
+            elements),
+        elements);
+  }
+
+  /** Returns metadata for each aggregated element. */
+  public static ImmutableSet<AggregatedUninstallModulesMetadata> from(
+      ImmutableSet<TypeElement> aggregatedElements, Elements elements) {
+    return aggregatedElements.stream()
         .map(aggregatedElement -> create(aggregatedElement, elements))
         .collect(toImmutableSet());
   }
 
+  public static AggregatedUninstallModulesIr toIr(AggregatedUninstallModulesMetadata metadata) {
+    return new AggregatedUninstallModulesIr(
+        ClassName.get(metadata.aggregatingElement()),
+        ClassName.get(metadata.testElement()),
+        metadata.uninstallModuleElements().stream()
+            .map(ClassName::get)
+            .collect(Collectors.toList()));
+  }
+
   private static AggregatedUninstallModulesMetadata create(TypeElement element, Elements elements) {
     AnnotationMirror annotationMirror =
         Processors.getAnnotationMirror(element, ClassNames.AGGREGATED_UNINSTALL_MODULES);
@@ -66,6 +88,7 @@
         Processors.getAnnotationValues(elements, annotationMirror);
 
     return new AutoValue_AggregatedUninstallModulesMetadata(
+        element,
         elements.getTypeElement(AnnotationValues.getString(values.get("test"))),
         AnnotationValues.getAnnotationValues(values.get("uninstallModules")).stream()
             .map(AnnotationValues::getString)
diff --git a/java/dagger/hilt/processor/internal/uninstallmodules/BUILD b/java/dagger/hilt/processor/internal/uninstallmodules/BUILD
index 4f944be..876c473 100644
--- a/java/dagger/hilt/processor/internal/uninstallmodules/BUILD
+++ b/java/dagger/hilt/processor/internal/uninstallmodules/BUILD
@@ -36,11 +36,11 @@
         "//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",
-        "@google_bazel_common//third_party/java/javapoet",
-        "@maven//:com_google_auto_auto_common",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:service",
+        "//third_party/java/guava/collect",
+        "//third_party/java/incap",
+        "//third_party/java/javapoet",
     ],
 )
 
@@ -53,9 +53,11 @@
         "//java/dagger/hilt/processor/internal:aggregated_elements",
         "//java/dagger/hilt/processor/internal:classnames",
         "//java/dagger/hilt/processor/internal:processors",
+        "//java/dagger/hilt/processor/internal/root/ir",
         "//java/dagger/internal/codegen/extension",
-        "//java/dagger/internal/guava:collect",
-        "@google_bazel_common//third_party/java/auto:value",
+        "//third_party/java/auto:value",
+        "//third_party/java/guava/collect",
+        "//third_party/java/javapoet",
     ],
 )
 
diff --git a/java/dagger/hilt/testing/BUILD b/java/dagger/hilt/testing/BUILD
index 3ea1c5f..59a1e94 100644
--- a/java/dagger/hilt/testing/BUILD
+++ b/java/dagger/hilt/testing/BUILD
@@ -21,7 +21,7 @@
     name = "package_info",
     srcs = ["package-info.java"],
     deps = [
-        "@google_bazel_common//third_party/java/jsr305_annotations",
+        "//third_party/java/jsr305_annotations",
     ],
 )
 
diff --git a/java/dagger/internal/DoubleCheck.java b/java/dagger/internal/DoubleCheck.java
index ea07528..af7d7f6 100644
--- a/java/dagger/internal/DoubleCheck.java
+++ b/java/dagger/internal/DoubleCheck.java
@@ -60,11 +60,8 @@
    * new instance is the same as the current instance, return the instance. However, if the new
    * instance differs from the current instance, an {@link IllegalStateException} is thrown.
    */
-  public static Object reentrantCheck(Object currentInstance, Object newInstance) {
-    boolean isReentrant = !(currentInstance == UNINITIALIZED
-        // This check is needed for fastInit's implementation, which uses MemoizedSentinel types.
-        || currentInstance instanceof MemoizedSentinel);
-
+  private static Object reentrantCheck(Object currentInstance, Object newInstance) {
+    boolean isReentrant = currentInstance != UNINITIALIZED;
     if (isReentrant && currentInstance != newInstance) {
       throw new IllegalStateException("Scoped provider was invoked recursively returning "
           + "different results: " + currentInstance + " & " + newInstance + ". This is likely "
diff --git a/java/dagger/internal/QualifierMetadata.java b/java/dagger/internal/QualifierMetadata.java
new file mode 100644
index 0000000..aa6f0d0
--- /dev/null
+++ b/java/dagger/internal/QualifierMetadata.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.TYPE;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/** Stores the qualifier information about a type after it's been processed. */
+@Retention(CLASS)
+@Target(TYPE)
+public @interface QualifierMetadata {
+  /**
+   * Returns the list of fully qualified qualifier names used in a particular context.
+   *
+   * <p>For example, when annotating Dagger's generated {@code _Factory} class for an inject
+   * constructor, it contains all qualifiers used on parameters within the constructor. When
+   * annotating Dagger's generated {@code _MembersInjector} class for inject fields and methods, it
+   * contains all qualifiers found on the fields and method parameters. When annotating Dagger's
+   * generated {@code _Factory} class for provision methods it includes all qualifiers used on the
+   * provision method and its parameters.
+   */
+  String[] value() default {};
+}
diff --git a/java/dagger/internal/ScopeMetadata.java b/java/dagger/internal/ScopeMetadata.java
new file mode 100644
index 0000000..a313b5e
--- /dev/null
+++ b/java/dagger/internal/ScopeMetadata.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.TYPE;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/** Stores the scope information about a type after it's been processed. */
+@Retention(CLASS)
+@Target(TYPE)
+public @interface ScopeMetadata {
+  /** The qualified name of the scope, if one exists, otherwise an empty string. */
+  String value() default "";
+}
diff --git a/java/dagger/internal/codegen/AssistedFactoryProcessingStep.java b/java/dagger/internal/codegen/AssistedFactoryProcessingStep.java
index dbd2b33..7242e26 100644
--- a/java/dagger/internal/codegen/AssistedFactoryProcessingStep.java
+++ b/java/dagger/internal/codegen/AssistedFactoryProcessingStep.java
@@ -16,24 +16,31 @@
 
 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 androidx.room.compiler.processing.compat.XConverters.toJavac;
 import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.assistedFactoryMethods;
 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 dagger.internal.codegen.xprocessing.XElements.asTypeElement;
+import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
+import static dagger.internal.codegen.xprocessing.XMethodElements.hasTypeParameters;
+import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
 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 androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XFiler;
+import androidx.room.compiler.processing.XMessager;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.squareup.javapoet.ClassName;
@@ -45,7 +52,6 @@
 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;
@@ -53,62 +59,55 @@
 import dagger.internal.codegen.binding.AssistedInjectionAnnotations.AssistedParameter;
 import dagger.internal.codegen.binding.BindingFactory;
 import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.javapoet.TypeNames;
 import dagger.internal.codegen.langmodel.DaggerElements;
 import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.validation.SuperficialValidator;
 import dagger.internal.codegen.validation.TypeCheckingProcessingStep;
 import dagger.internal.codegen.validation.ValidationReport;
-import java.lang.annotation.Annotation;
+import dagger.internal.codegen.xprocessing.MethodSpecs;
 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;
+final class AssistedFactoryProcessingStep extends TypeCheckingProcessingStep<XTypeElement> {
+  private final XMessager messager;
+  private final XFiler filer;
   private final SourceVersion sourceVersion;
   private final DaggerElements elements;
   private final DaggerTypes types;
   private final BindingFactory bindingFactory;
+  private final SuperficialValidator superficialValidator;
 
   @Inject
   AssistedFactoryProcessingStep(
-      Messager messager,
-      Filer filer,
+      XMessager messager,
+      XFiler filer,
       SourceVersion sourceVersion,
       DaggerElements elements,
       DaggerTypes types,
-      BindingFactory bindingFactory) {
-    super(MoreElements::asType);
+      BindingFactory bindingFactory,
+      SuperficialValidator superficialValidator) {
     this.messager = messager;
     this.filer = filer;
     this.sourceVersion = sourceVersion;
     this.elements = elements;
     this.types = types;
     this.bindingFactory = bindingFactory;
+    this.superficialValidator = superficialValidator;
   }
 
   @Override
-  public ImmutableSet<Class<? extends Annotation>> annotations() {
-    return ImmutableSet.of(AssistedFactory.class);
+  public ImmutableSet<ClassName> annotationClassNames() {
+    return ImmutableSet.of(TypeNames.ASSISTED_FACTORY);
   }
 
   @Override
-  protected void process(
-      TypeElement factory, ImmutableSet<Class<? extends Annotation>> annotations) {
-    ValidationReport<TypeElement> report = new AssistedFactoryValidator().validate(factory);
+  protected void process(XTypeElement factory, ImmutableSet<ClassName> annotations) {
+    ValidationReport report = new AssistedFactoryValidator().validate(factory);
     report.printMessagesTo(messager);
     if (report.isClean()) {
       try {
@@ -121,10 +120,10 @@
   }
 
   private final class AssistedFactoryValidator {
-    ValidationReport<TypeElement> validate(TypeElement factory) {
-      ValidationReport.Builder<TypeElement> report = ValidationReport.about(factory);
+    ValidationReport validate(XTypeElement factory) {
+      ValidationReport.Builder report = ValidationReport.about(factory);
 
-      if (!factory.getModifiers().contains(ABSTRACT)) {
+      if (!factory.isAbstract()) {
         return report
             .addError(
                 "The @AssistedFactory-annotated type must be either an abstract class or "
@@ -133,12 +132,11 @@
             .build();
       }
 
-      if (factory.getNestingKind().isNested() && !factory.getModifiers().contains(STATIC)) {
+      if (factory.isNested() && !factory.isStatic()) {
         report.addError("Nested @AssistedFactory-annotated types must be static. ", factory);
       }
 
-      ImmutableSet<ExecutableElement> abstractFactoryMethods =
-          AssistedInjectionAnnotations.assistedFactoryMethods(factory, elements);
+      ImmutableSet<XMethodElement> abstractFactoryMethods = assistedFactoryMethods(factory);
 
       if (abstractFactoryMethods.isEmpty()) {
         report.addError(
@@ -147,17 +145,23 @@
             factory);
       }
 
-      for (ExecutableElement method : abstractFactoryMethods) {
-        ExecutableType methodType = types.resolveExecutableType(method, factory.asType());
-        if (!isAssistedInjectionType(methodType.getReturnType())) {
+      for (XMethodElement method : abstractFactoryMethods) {
+        XType returnType = method.asMemberOf(factory.getType()).getReturnType();
+        // The default superficial validation only applies to the @AssistedFactory-annotated
+        // element, so we have to manually check the superficial validation  of the @AssistedInject
+        // element before using it to ensure it's ready for processing.
+        if (isDeclared(returnType)) {
+          superficialValidator.throwIfNearestEnclosingTypeNotValid(returnType.getTypeElement());
+        }
+        if (!isAssistedInjectionType(returnType)) {
           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()),
+                  returnType),
               method);
         }
-        if (!method.getTypeParameters().isEmpty()) {
+        if (hasTypeParameters(method)) {
           report.addError(
               "@AssistedFactory does not currently support type parameters in the creator "
                   + "method. See https://github.com/google/dagger/issues/2279",
@@ -177,8 +181,7 @@
         return report.build();
       }
 
-      AssistedFactoryMetadata metadata =
-          AssistedFactoryMetadata.create(factory.asType(), elements, types);
+      AssistedFactoryMetadata metadata = AssistedFactoryMetadata.create(factory.getType());
 
       // Note: We check uniqueness of the @AssistedInject constructor parameters in
       // AssistedInjectProcessingStep. We need to check uniqueness for here too because we may
@@ -188,7 +191,7 @@
         if (!uniqueAssistedParameters.add(assistedParameter)) {
           report.addError(
               "@AssistedFactory method has duplicate @Assisted types: " + assistedParameter,
-              assistedParameter.variableElement());
+              assistedParameter.element());
         }
       }
 
@@ -203,7 +206,7 @@
                 metadata.factory().getQualifiedName(),
                 metadata.factoryMethod(),
                 metadata.factory().getQualifiedName(),
-                metadata.factoryMethod().getSimpleName(),
+                getSimpleName(metadata.factoryMethod()),
                 metadata.assistedInjectAssistedParameters().stream()
                     .map(AssistedParameter::type)
                     .map(Object::toString)
@@ -214,9 +217,9 @@
       return report.build();
     }
 
-    private boolean isAssistedInjectionType(TypeMirror type) {
-      return type.getKind() == TypeKind.DECLARED
-          && AssistedInjectionAnnotations.isAssistedInjectionType(asTypeElement(type));
+    private boolean isAssistedInjectionType(XType type) {
+      return isDeclared(type)
+          && AssistedInjectionAnnotations.isAssistedInjectionType(type.getTypeElement());
     }
   }
 
@@ -227,7 +230,7 @@
     }
 
     @Override
-    public Element originatingElement(ProvisionBinding binding) {
+    public XElement originatingElement(ProvisionBinding binding) {
       return binding.bindingElement().get();
     }
 
@@ -265,25 +268,24 @@
     // }
     @Override
     public ImmutableList<TypeSpec.Builder> topLevelTypes(ProvisionBinding binding) {
-      TypeElement factory = asType(binding.bindingElement().get());
+      XTypeElement factory = asTypeElement(binding.bindingElement().get());
 
       ClassName name = generatedClassNameForBinding(binding);
       TypeSpec.Builder builder =
           TypeSpec.classBuilder(name)
               .addModifiers(PUBLIC, FINAL)
               .addTypeVariables(
-                  factory.getTypeParameters().stream()
+                  toJavac(factory).getTypeParameters().stream()
                       .map(TypeVariableName::get)
                       .collect(toImmutableList()));
 
-      if (factory.getKind() == ElementKind.INTERFACE) {
-        builder.addSuperinterface(factory.asType());
+      if (factory.isInterface()) {
+        builder.addSuperinterface(factory.getType().getTypeName());
       } else {
-        builder.superclass(factory.asType());
+        builder.superclass(factory.getType().getTypeName());
       }
 
-      AssistedFactoryMetadata metadata =
-          AssistedFactoryMetadata.create(asDeclared(factory.asType()), elements, types);
+      AssistedFactoryMetadata metadata = AssistedFactoryMetadata.create(factory.getType());
       ParameterSpec delegateFactoryParam =
           ParameterSpec.builder(
                   delegateFactoryTypeName(metadata.assistedInjectType()), "delegateFactory")
@@ -299,7 +301,7 @@
                   .addStatement("this.$1N = $1N", delegateFactoryParam)
                   .build())
           .addMethod(
-              MethodSpec.overriding(metadata.factoryMethod(), metadata.factoryType(), types)
+              MethodSpecs.overriding(metadata.factoryMethod(), metadata.factoryType())
                   .addStatement(
                       "return $N.get($L)",
                       delegateFactoryParam,
@@ -307,7 +309,7 @@
                       // use the parameter names of the @AssistedFactory method.
                       metadata.assistedInjectAssistedParameters().stream()
                           .map(metadata.assistedFactoryAssistedParametersMap()::get)
-                          .map(param -> CodeBlock.of("$L", param.getSimpleName()))
+                          .map(param -> CodeBlock.of("$L", getSimpleName(param)))
                           .collect(toParametersCodeBlock()))
                   .build())
           .addMethod(
@@ -315,10 +317,10 @@
                   .addModifiers(PUBLIC, STATIC)
                   .addParameter(delegateFactoryParam)
                   .addTypeVariables(
-                      metadata.assistedInjectElement().getTypeParameters().stream()
+                      toJavac(metadata.assistedInjectElement()).getTypeParameters().stream()
                           .map(TypeVariableName::get)
                           .collect(toImmutableList()))
-                  .returns(providerOf(TypeName.get(factory.asType())))
+                  .returns(providerOf(factory.getType().getTypeName()))
                   .addStatement(
                       "return $T.$Lcreate(new $T($N))",
                       INSTANCE_FACTORY,
@@ -333,13 +335,13 @@
     }
 
     /** Returns the generated factory {@link TypeName type} for an @AssistedInject constructor. */
-    private TypeName delegateFactoryTypeName(DeclaredType assistedInjectType) {
+    private TypeName delegateFactoryTypeName(XType 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))),
+                  getOnlyElement(assistedInjectedConstructors(assistedInjectType.getTypeElement())),
                   Optional.empty()));
 
       // Return the factory type resolved with the same type parameters as the assisted inject type.
@@ -348,7 +350,7 @@
           : ParameterizedTypeName.get(
               generatedFactoryClassName,
               assistedInjectType.getTypeArguments().stream()
-                  .map(TypeName::get)
+                  .map(XType::getTypeName)
                   .collect(toImmutableList())
                   .toArray(new TypeName[0]));
     }
diff --git a/java/dagger/internal/codegen/AssistedInjectProcessingStep.java b/java/dagger/internal/codegen/AssistedInjectProcessingStep.java
index 167c4ae..df3a7d6 100644
--- a/java/dagger/internal/codegen/AssistedInjectProcessingStep.java
+++ b/java/dagger/internal/codegen/AssistedInjectProcessingStep.java
@@ -16,71 +16,60 @@
 
 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 static dagger.internal.codegen.binding.AssistedInjectionAnnotations.assistedInjectAssistedParameters;
 
-import com.google.auto.common.MoreElements;
+import androidx.room.compiler.processing.XConstructorElement;
+import androidx.room.compiler.processing.XMessager;
+import androidx.room.compiler.processing.XType;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
-import dagger.assisted.AssistedInject;
-import dagger.internal.codegen.binding.AssistedInjectionAnnotations;
+import com.squareup.javapoet.ClassName;
 import dagger.internal.codegen.binding.AssistedInjectionAnnotations.AssistedParameter;
-import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.javapoet.TypeNames;
 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;
+final class AssistedInjectProcessingStep extends TypeCheckingProcessingStep<XConstructorElement> {
+  private final XMessager messager;
 
   @Inject
-  AssistedInjectProcessingStep(DaggerTypes types, Messager messager) {
-    super(MoreElements::asExecutable);
-    this.types = types;
+  AssistedInjectProcessingStep(XMessager messager) {
     this.messager = messager;
   }
 
   @Override
-  public ImmutableSet<Class<? extends Annotation>> annotations() {
-    return ImmutableSet.of(AssistedInject.class);
+  public ImmutableSet<ClassName> annotationClassNames() {
+    return ImmutableSet.of(TypeNames.ASSISTED_INJECT);
   }
 
   @Override
   protected void process(
-      ExecutableElement assistedInjectElement,
-      ImmutableSet<Class<? extends Annotation>> annotations) {
+      XConstructorElement assistedInjectElement, ImmutableSet<ClassName> 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);
+    ValidationReport validate(XConstructorElement constructor) {
+      ValidationReport.Builder report = ValidationReport.about(constructor);
 
-      DeclaredType assistedInjectType =
-          asDeclared(closestEnclosingTypeElement(constructor).asType());
+      XType assistedInjectType = constructor.getEnclosingElement().getType();
       ImmutableList<AssistedParameter> assistedParameters =
-          AssistedInjectionAnnotations.assistedInjectAssistedParameters(assistedInjectType, types);
+          assistedInjectAssistedParameters(assistedInjectType);
 
       Set<AssistedParameter> uniqueAssistedParameters = new HashSet<>();
       for (AssistedParameter assistedParameter : assistedParameters) {
         if (!uniqueAssistedParameters.add(assistedParameter)) {
           report.addError(
-              String.format("@AssistedInject constructor has duplicate @Assisted type: %s. "
-                  + "Consider setting an identifier on the parameter by using "
-                  + "@Assisted(\"identifier\") in both the factory and @AssistedInject constructor",
+              String.format(
+                  "@AssistedInject constructor has duplicate @Assisted type: %s. Consider setting"
+                      + " an identifier on the parameter by using @Assisted(\"identifier\") in both"
+                      + " the factory and @AssistedInject constructor",
                   assistedParameter),
-              assistedParameter.variableElement());
+              assistedParameter.element());
         }
       }
 
diff --git a/java/dagger/internal/codegen/AssistedProcessingStep.java b/java/dagger/internal/codegen/AssistedProcessingStep.java
index 12a0d34..c44d2c5 100644
--- a/java/dagger/internal/codegen/AssistedProcessingStep.java
+++ b/java/dagger/internal/codegen/AssistedProcessingStep.java
@@ -16,67 +16,57 @@
 
 package dagger.internal.codegen;
 
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static dagger.internal.codegen.langmodel.DaggerElements.closestEnclosingTypeElement;
+import static androidx.room.compiler.processing.XElementKt.isConstructor;
+import static androidx.room.compiler.processing.XElementKt.isMethod;
+import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.assistedFactoryMethod;
+import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.isAssistedFactoryType;
+import static dagger.internal.codegen.xprocessing.XElements.asMethod;
+import static dagger.internal.codegen.xprocessing.XElements.closestEnclosingTypeElement;
+import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
 
-import com.google.auto.common.MoreElements;
+import androidx.room.compiler.processing.XExecutableElement;
+import androidx.room.compiler.processing.XExecutableParameterElement;
+import androidx.room.compiler.processing.XMessager;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.common.collect.ImmutableSet;
-import dagger.assisted.Assisted;
-import dagger.assisted.AssistedInject;
-import dagger.internal.codegen.binding.AssistedInjectionAnnotations;
+import com.squareup.javapoet.ClassName;
 import dagger.internal.codegen.binding.InjectionAnnotations;
-import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
-import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.javapoet.TypeNames;
 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;
+final class AssistedProcessingStep extends TypeCheckingProcessingStep<XExecutableParameterElement> {
   private final InjectionAnnotations injectionAnnotations;
-  private final DaggerElements elements;
-  private final Messager messager;
+  private final XMessager messager;
 
   @Inject
-  AssistedProcessingStep(
-      KotlinMetadataUtil kotlinMetadataUtil,
-      InjectionAnnotations injectionAnnotations,
-      DaggerElements elements,
-      Messager messager) {
-    super(MoreElements::asVariable);
-    this.kotlinMetadataUtil = kotlinMetadataUtil;
+  AssistedProcessingStep(InjectionAnnotations injectionAnnotations, XMessager messager) {
     this.injectionAnnotations = injectionAnnotations;
-    this.elements = elements;
     this.messager = messager;
   }
 
   @Override
-  public ImmutableSet<Class<? extends Annotation>> annotations() {
-    return ImmutableSet.of(Assisted.class);
+  public ImmutableSet<ClassName> annotationClassNames() {
+    return ImmutableSet.of(TypeNames.ASSISTED);
   }
 
   @Override
   protected void process(
-      VariableElement assisted, ImmutableSet<Class<? extends Annotation>> annotations) {
+      XExecutableParameterElement assisted, ImmutableSet<ClassName> annotations) {
     new AssistedValidator().validate(assisted).printMessagesTo(messager);
   }
 
   private final class AssistedValidator {
-    ValidationReport<VariableElement> validate(VariableElement assisted) {
-      ValidationReport.Builder<VariableElement> report = ValidationReport.about(assisted);
+    ValidationReport validate(XExecutableParameterElement assisted) {
+      ValidationReport.Builder report = ValidationReport.about(assisted);
 
-      Element enclosingElement = assisted.getEnclosingElement();
+      XExecutableElement enclosingElement = assisted.getEnclosingMethodElement();
       if (!isAssistedInjectConstructor(enclosingElement)
           && !isAssistedFactoryCreateMethod(enclosingElement)
           // The generated java stubs for kotlin data classes contain a "copy" method that has
@@ -99,30 +89,29 @@
     }
   }
 
-  private boolean isAssistedInjectConstructor(Element element) {
-    return element.getKind() == ElementKind.CONSTRUCTOR
-        && isAnnotationPresent(element, AssistedInject.class);
+  private boolean isAssistedInjectConstructor(XExecutableElement executableElement) {
+    return isConstructor(executableElement)
+        && executableElement.hasAnnotation(TypeNames.ASSISTED_INJECT);
   }
 
-  private boolean isAssistedFactoryCreateMethod(Element element) {
-    if (element.getKind() == ElementKind.METHOD) {
-      TypeElement enclosingElement = closestEnclosingTypeElement(element);
-      return AssistedInjectionAnnotations.isAssistedFactoryType(enclosingElement)
+  private boolean isAssistedFactoryCreateMethod(XExecutableElement executableElement) {
+    if (isMethod(executableElement)) {
+      XTypeElement enclosingElement = closestEnclosingTypeElement(executableElement);
+      return isAssistedFactoryType(enclosingElement)
           // This assumes we've already validated AssistedFactory and that a valid method exists.
-          && AssistedInjectionAnnotations.assistedFactoryMethod(enclosingElement, elements)
-              .equals(element);
+          && assistedFactoryMethod(enclosingElement).equals(executableElement);
     }
     return false;
   }
 
-  private boolean isKotlinDataClassCopyMethod(Element element) {
+  private boolean isKotlinDataClassCopyMethod(XExecutableElement executableElement) {
     // 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));
+    return isMethod(executableElement)
+        && getSimpleName(asMethod(executableElement)).contentEquals("copy")
+        && closestEnclosingTypeElement(executableElement.getEnclosingElement()).isDataClass();
   }
 }
diff --git a/java/dagger/internal/codegen/BUILD b/java/dagger/internal/codegen/BUILD
index e2f74e9..d1e01ae 100644
--- a/java/dagger/internal/codegen/BUILD
+++ b/java/dagger/internal/codegen/BUILD
@@ -34,9 +34,6 @@
         "//java/dagger/internal/codegen/bootstrap",
     ],
     tags = ["maven_coordinates=com.google.dagger:dagger-compiler:" + POM_VERSION],
-    exports = [
-        "@google_bazel_common//third_party/java/jsr250_annotations",  # Export for @Generated
-    ],
     deps = [
         ":package_info",
         "//java/dagger:core",
@@ -51,17 +48,19 @@
         "//java/dagger/internal/codegen/langmodel",
         "//java/dagger/internal/codegen/validation",
         "//java/dagger/internal/codegen/writing",
-        "//java/dagger/internal/guava:collect",
-        "//java/dagger/producers",
+        "//java/dagger/internal/codegen/xprocessing",
         "//java/dagger/spi",
-        "@google_bazel_common//third_party/java/auto:service",
-        "@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",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:service",
+        "//third_party/java/auto:value",
+        "//third_party/java/error_prone:annotations",
+        "//third_party/java/google_java_format",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/incap",
+        "//third_party/java/javapoet",
+        "//third_party/java/jsr330_inject",
+        "@bazel_tools//tools/jdk:langtools-neverlink",
     ],
 )
 
@@ -69,7 +68,7 @@
     name = "package_info",
     srcs = ["package-info.java"],
     tags = ["maven:merged"],
-    deps = ["@google_bazel_common//third_party/java/error_prone:annotations"],
+    deps = ["//third_party/java/error_prone:annotations"],
 )
 
 gen_maven_artifact(
@@ -90,7 +89,6 @@
         "//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",
@@ -102,19 +100,18 @@
         "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.kotlin:kotlin-stdlib-jdk8",
         "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"],
+    # The shaded deps are inherited from dagger spi. For the shaded rules see util/deploy-dagger.sh
 )
 
 java_plugin(
diff --git a/java/dagger/internal/codegen/ComponentHjarProcessingStep.java b/java/dagger/internal/codegen/ComponentHjarProcessingStep.java
index 260f65a..a75c40b 100644
--- a/java/dagger/internal/codegen/ComponentHjarProcessingStep.java
+++ b/java/dagger/internal/codegen/ComponentHjarProcessingStep.java
@@ -16,14 +16,15 @@
 
 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.base.ComponentAnnotation.rootComponentAnnotations;
-import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.rootComponentCreatorAnnotations;
+import static dagger.internal.codegen.base.ComponentCreatorAnnotation.rootComponentCreatorAnnotations;
 import static java.util.Collections.disjoint;
 
-import com.google.auto.common.MoreElements;
+import androidx.room.compiler.processing.XMessager;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
 import dagger.internal.codegen.base.SourceFileGenerator;
 import dagger.internal.codegen.binding.BindingGraph;
 import dagger.internal.codegen.binding.ComponentDescriptor;
@@ -32,11 +33,8 @@
 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.Set;
-import javax.annotation.processing.Messager;
 import javax.inject.Inject;
-import javax.lang.model.element.TypeElement;
 
 /**
  * A processing step that emits the API of a generated component, without any actual implementation.
@@ -51,8 +49,8 @@
  * <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 ComponentHjarProcessingStep extends TypeCheckingProcessingStep<TypeElement> {
-  private final Messager messager;
+final class ComponentHjarProcessingStep extends TypeCheckingProcessingStep<XTypeElement> {
+  private final XMessager messager;
   private final ComponentValidator componentValidator;
   private final ComponentCreatorValidator creatorValidator;
   private final ComponentDescriptorFactory componentDescriptorFactory;
@@ -60,12 +58,11 @@
 
   @Inject
   ComponentHjarProcessingStep(
-      Messager messager,
+      XMessager messager,
       ComponentValidator componentValidator,
       ComponentCreatorValidator creatorValidator,
       ComponentDescriptorFactory componentDescriptorFactory,
       SourceFileGenerator<ComponentDescriptor> componentGenerator) {
-    super(MoreElements::asType);
     this.messager = messager;
     this.componentValidator = componentValidator;
     this.creatorValidator = creatorValidator;
@@ -74,7 +71,7 @@
   }
 
   @Override
-  public Set<Class<? extends Annotation>> annotations() {
+  public Set<ClassName> annotationClassNames() {
     return union(rootComponentAnnotations(), rootComponentCreatorAnnotations());
   }
 
@@ -83,8 +80,7 @@
   // clause for any exception that's not TypeNotPresentException and ignore the component entirely
   // in that case.
   @Override
-  protected void process(
-      TypeElement element, ImmutableSet<Class<? extends Annotation>> annotations) {
+  protected void process(XTypeElement element, ImmutableSet<ClassName> annotations) {
     if (!disjoint(annotations, rootComponentAnnotations())) {
       processRootComponent(element);
     }
@@ -93,8 +89,8 @@
     }
   }
 
-  private void processRootComponent(TypeElement element) {
-    ValidationReport<TypeElement> validationReport = componentValidator.validate(element);
+  private void processRootComponent(XTypeElement element) {
+    ValidationReport validationReport = componentValidator.validate(element);
     validationReport.printMessagesTo(messager);
     if (validationReport.isClean()) {
       componentGenerator.generate(
@@ -102,7 +98,7 @@
     }
   }
 
-  private void processRootCreator(TypeElement creator) {
-    creatorValidator.validate(asType(creator)).printMessagesTo(messager);
+  private void processRootCreator(XTypeElement creator) {
+    creatorValidator.validate(creator).printMessagesTo(messager);
   }
 }
diff --git a/java/dagger/internal/codegen/ComponentProcessingStep.java b/java/dagger/internal/codegen/ComponentProcessingStep.java
index 3125ce7..6a1f095 100644
--- a/java/dagger/internal/codegen/ComponentProcessingStep.java
+++ b/java/dagger/internal/codegen/ComponentProcessingStep.java
@@ -16,18 +16,19 @@
 
 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.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 dagger.internal.codegen.base.ComponentCreatorAnnotation.allCreatorAnnotations;
 import static java.util.Collections.disjoint;
 
+import androidx.room.compiler.processing.XMessager;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
-import com.google.auto.common.MoreElements;
 import com.google.common.collect.ImmutableSet;
 import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.squareup.javapoet.ClassName;
 import dagger.internal.codegen.base.SourceFileGenerator;
 import dagger.internal.codegen.binding.BindingGraph;
 import dagger.internal.codegen.binding.BindingGraphFactory;
@@ -39,19 +40,15 @@
 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.Set;
-import javax.annotation.processing.Messager;
 import javax.inject.Inject;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
 
 /**
  * A {@link ProcessingStep} that is responsible for dealing with a component or production component
  * as part of the {@link ComponentProcessor}.
  */
-final class ComponentProcessingStep extends TypeCheckingProcessingStep<TypeElement> {
-  private final Messager messager;
+final class ComponentProcessingStep extends TypeCheckingProcessingStep<XTypeElement> {
+  private final XMessager messager;
   private final ComponentValidator componentValidator;
   private final ComponentCreatorValidator creatorValidator;
   private final ComponentDescriptorValidator componentDescriptorValidator;
@@ -62,7 +59,7 @@
 
   @Inject
   ComponentProcessingStep(
-      Messager messager,
+      XMessager messager,
       ComponentValidator componentValidator,
       ComponentCreatorValidator creatorValidator,
       ComponentDescriptorValidator componentDescriptorValidator,
@@ -70,7 +67,6 @@
       BindingGraphFactory bindingGraphFactory,
       SourceFileGenerator<BindingGraph> componentGenerator,
       BindingGraphValidator bindingGraphValidator) {
-    super(MoreElements::asType);
     this.messager = messager;
     this.componentValidator = componentValidator;
     this.creatorValidator = creatorValidator;
@@ -82,13 +78,12 @@
   }
 
   @Override
-  public Set<Class<? extends Annotation>> annotations() {
+  public Set<ClassName> annotationClassNames() {
     return union(allComponentAnnotations(), allCreatorAnnotations());
   }
 
   @Override
-  protected void process(
-      TypeElement element, ImmutableSet<Class<? extends Annotation>> annotations) {
+  protected void process(XTypeElement element, ImmutableSet<ClassName> annotations) {
     if (!disjoint(annotations, rootComponentAnnotations())) {
       processRootComponent(element);
     }
@@ -100,7 +95,7 @@
     }
   }
 
-  private void processRootComponent(TypeElement component) {
+  private void processRootComponent(XTypeElement component) {
     if (!isComponentValid(component)) {
       return;
     }
@@ -118,7 +113,7 @@
     }
   }
 
-  private void processSubcomponent(TypeElement subcomponent) {
+  private void processSubcomponent(XTypeElement subcomponent) {
     if (!isComponentValid(subcomponent)) {
       return;
     }
@@ -132,20 +127,20 @@
     componentGenerator.generate(bindingGraph, messager);
   }
 
-  private void processCreator(Element creator) {
-    creatorValidator.validate(MoreElements.asType(creator)).printMessagesTo(messager);
+  private void processCreator(XTypeElement creator) {
+    creatorValidator.validate(creator).printMessagesTo(messager);
   }
 
-  private boolean isComponentValid(Element component) {
-    ValidationReport<?> report = componentValidator.validate(asType(component));
+  private boolean isComponentValid(XTypeElement component) {
+    ValidationReport report = componentValidator.validate(component);
     report.printMessagesTo(messager);
     return report.isClean();
   }
 
   @CanIgnoreReturnValue
   private boolean validateFullBindingGraph(ComponentDescriptor componentDescriptor) {
-    TypeElement component = componentDescriptor.typeElement();
-    if (!bindingGraphValidator.shouldDoFullBindingGraphValidation(component)) {
+    if (!bindingGraphValidator.shouldDoFullBindingGraphValidation(
+        componentDescriptor.typeElement())) {
       return true;
     }
     BindingGraph fullBindingGraph = bindingGraphFactory.create(componentDescriptor, true);
@@ -153,7 +148,7 @@
   }
 
   private boolean isValid(ComponentDescriptor componentDescriptor) {
-    ValidationReport<TypeElement> componentDescriptorReport =
+    ValidationReport componentDescriptorReport =
         componentDescriptorValidator.validate(componentDescriptor);
     componentDescriptorReport.printMessagesTo(messager);
     return componentDescriptorReport.isClean();
diff --git a/java/dagger/internal/codegen/ComponentProcessor.java b/java/dagger/internal/codegen/ComponentProcessor.java
index e392252..facb04d 100644
--- a/java/dagger/internal/codegen/ComponentProcessor.java
+++ b/java/dagger/internal/codegen/ComponentProcessor.java
@@ -18,18 +18,21 @@
 
 import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING;
 
-import com.google.auto.common.BasicAnnotationProcessor;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XProcessingEnvConfig;
+import androidx.room.compiler.processing.XProcessingStep;
+import androidx.room.compiler.processing.XRoundEnv;
+import androidx.room.compiler.processing.compat.XConverters;
+import androidx.room.compiler.processing.javac.JavacBasicAnnotationProcessor;
 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.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;
@@ -40,20 +43,20 @@
 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.ExternalBindingGraphPlugins;
 import dagger.internal.codegen.validation.InjectBindingRegistryModule;
 import dagger.internal.codegen.validation.MonitoringModuleProcessingStep;
 import dagger.internal.codegen.validation.MultibindingAnnotationsProcessingStep;
+import dagger.internal.codegen.validation.ValidationBindingGraphPlugins;
 import dagger.spi.BindingGraphPlugin;
 import java.util.Arrays;
+import java.util.Map;
 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;
 import javax.inject.Singleton;
 import javax.lang.model.SourceVersion;
@@ -67,21 +70,28 @@
  */
 @IncrementalAnnotationProcessor(ISOLATING)
 @AutoService(Processor.class)
-public class ComponentProcessor extends BasicAnnotationProcessor {
+public class ComponentProcessor extends JavacBasicAnnotationProcessor {
+  private static XProcessingEnvConfig envConfig(Map<String, String> options)  {
+    return new XProcessingEnvConfig.Builder().disableAnnotatedElementValidation(true).build();
+  }
+
   private final Optional<ImmutableSet<BindingGraphPlugin>> testingPlugins;
 
   @Inject InjectBindingRegistry injectBindingRegistry;
   @Inject SourceFileGenerator<ProvisionBinding> factoryGenerator;
   @Inject SourceFileGenerator<MembersInjectionBinding> membersInjectorGenerator;
-  @Inject ImmutableList<ProcessingStep> processingSteps;
-  @Inject BindingGraphPlugins bindingGraphPlugins;
+  @Inject ImmutableList<XProcessingStep> processingSteps;
+  @Inject ValidationBindingGraphPlugins validationBindingGraphPlugins;
+  @Inject ExternalBindingGraphPlugins externalBindingGraphPlugins;
   @Inject Set<ClearableCache> clearableCaches;
 
   public ComponentProcessor() {
+    super(ComponentProcessor::envConfig);
     this.testingPlugins = Optional.empty();
   }
 
   private ComponentProcessor(Iterable<BindingGraphPlugin> testingPlugins) {
+    super(ComponentProcessor::envConfig);
     this.testingPlugins = Optional.of(ImmutableSet.copyOf(testingPlugins));
   }
 
@@ -104,27 +114,38 @@
   }
 
   @Override
+  public void initialize(XProcessingEnv env) {
+    ProcessorComponent.factory()
+        .create(env, testingPlugins.orElseGet(this::loadExternalPlugins))
+        .inject(this);
+  }
+
+  @Override
   public SourceVersion getSupportedSourceVersion() {
     return SourceVersion.latestSupported();
   }
 
   @Override
-  public Set<String> getSupportedOptions() {
-    return Sets.union(
-            ProcessingEnvironmentCompilerOptions.supportedOptions(),
-            bindingGraphPlugins.allSupportedOptions())
-        .immutableCopy();
+  public ImmutableSet<String> getSupportedOptions() {
+    return ImmutableSet.<String>builder()
+        .addAll(ProcessingEnvironmentCompilerOptions.supportedOptions())
+        .addAll(validationBindingGraphPlugins.allSupportedOptions())
+        .addAll(externalBindingGraphPlugins.allSupportedOptions())
+        .build();
   }
 
   @Override
-  protected Iterable<? extends ProcessingStep> initSteps() {
-    ProcessorComponent.factory().create(processingEnv, testingPlugins).inject(this);
-
-    bindingGraphPlugins.initializePlugins();
+  public Iterable<XProcessingStep> processingSteps() {
+    validationBindingGraphPlugins.initializePlugins();
+    externalBindingGraphPlugins.initializePlugins();
 
     return processingSteps;
   }
 
+  private ImmutableSet<BindingGraphPlugin> loadExternalPlugins() {
+    return ServiceLoaders.load(processingEnv, BindingGraphPlugin.class);
+  }
+
   @Singleton
   @Component(
       modules = {
@@ -136,7 +157,6 @@
         ProcessingRoundCacheModule.class,
         ProcessingStepsModule.class,
         SourceFileGeneratorsModule.class,
-        SpiModule.class
       })
   interface ProcessorComponent {
     void inject(ComponentProcessor processor);
@@ -149,15 +169,15 @@
     interface Factory {
       @CheckReturnValue
       ProcessorComponent create(
-          @BindsInstance ProcessingEnvironment processingEnv,
-          @BindsInstance @TestingPlugins Optional<ImmutableSet<BindingGraphPlugin>> testingPlugins);
+          @BindsInstance XProcessingEnv xProcessingEnv,
+          @BindsInstance ImmutableSet<BindingGraphPlugin> externalPlugins);
     }
   }
 
   @Module
   interface ProcessingStepsModule {
     @Provides
-    static ImmutableList<ProcessingStep> processingSteps(
+    static ImmutableList<XProcessingStep> processingSteps(
         MapKeyProcessingStep mapKeyProcessingStep,
         InjectProcessingStep injectProcessingStep,
         AssistedInjectProcessingStep assistedInjectProcessingStep,
@@ -189,13 +209,14 @@
   }
 
   @Override
-  protected void postRound(RoundEnvironment roundEnv) {
-    if (!roundEnv.processingOver()) {
+  public void postRound(XProcessingEnv env, XRoundEnv roundEnv) {
+    // TODO(bcorso): Add a way to determine if processing is over without converting to Javac here.
+    if (!XConverters.toJavac(roundEnv).processingOver()) {
       try {
         injectBindingRegistry.generateSourcesForRequiredBindings(
             factoryGenerator, membersInjectorGenerator);
       } catch (SourceFileGenerationException e) {
-        e.printMessageTo(processingEnv.getMessager());
+        e.printMessageTo(env.getMessager());
       }
     }
     clearableCaches.forEach(ClearableCache::clearCache);
diff --git a/java/dagger/internal/codegen/InjectProcessingStep.java b/java/dagger/internal/codegen/InjectProcessingStep.java
index 5374592..a201afe 100644
--- a/java/dagger/internal/codegen/InjectProcessingStep.java
+++ b/java/dagger/internal/codegen/InjectProcessingStep.java
@@ -16,73 +16,59 @@
 
 package dagger.internal.codegen;
 
-import com.google.auto.common.MoreElements;
+import static androidx.room.compiler.processing.XElementKt.isConstructor;
+import static androidx.room.compiler.processing.XElementKt.isField;
+import static androidx.room.compiler.processing.XElementKt.isMethod;
+import static dagger.internal.codegen.xprocessing.XElements.asConstructor;
+import static dagger.internal.codegen.xprocessing.XElements.asField;
+import static dagger.internal.codegen.xprocessing.XElements.asMethod;
+
+import androidx.room.compiler.processing.XElement;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
-import dagger.assisted.AssistedInject;
+import com.squareup.javapoet.ClassName;
 import dagger.internal.codegen.binding.InjectBindingRegistry;
+import dagger.internal.codegen.javapoet.TypeNames;
 import dagger.internal.codegen.validation.TypeCheckingProcessingStep;
-import java.lang.annotation.Annotation;
 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.VariableElement;
-import javax.lang.model.util.ElementKindVisitor8;
 
 /**
  * An annotation processor for generating Dagger implementation code based on the {@link Inject}
  * annotation.
  */
 // 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();
+// TODO(bcorso): Add support in TypeCheckingProcessingStep to perform custom validation and use
+// SuperficialInjectValidator rather than SuperficialValidator.
+final class InjectProcessingStep extends TypeCheckingProcessingStep<XElement> {
+  private final InjectBindingRegistry injectBindingRegistry;
+  private final Set<XElement> processedElements = Sets.newHashSet();
 
   @Inject
   InjectProcessingStep(InjectBindingRegistry injectBindingRegistry) {
-    super(e -> e);
-    this.visitor =
-        new ElementKindVisitor8<Void, Void>() {
-          @Override
-          public Void visitExecutableAsConstructor(
-              ExecutableElement constructorElement, Void aVoid) {
-            injectBindingRegistry.tryRegisterConstructor(constructorElement);
-            return null;
-          }
-
-          @Override
-          public Void visitVariableAsField(VariableElement fieldElement, Void aVoid) {
-            injectBindingRegistry.tryRegisterMembersInjectedType(
-                MoreElements.asType(fieldElement.getEnclosingElement()));
-            return null;
-          }
-
-          @Override
-          public Void visitExecutableAsMethod(ExecutableElement methodElement, Void aVoid) {
-            injectBindingRegistry.tryRegisterMembersInjectedType(
-                MoreElements.asType(methodElement.getEnclosingElement()));
-            return null;
-          }
-        };
+    this.injectBindingRegistry = injectBindingRegistry;
   }
 
   @Override
-  public Set<Class<? extends Annotation>> annotations() {
-    return ImmutableSet.of(Inject.class, AssistedInject.class);
+  public ImmutableSet<ClassName> annotationClassNames() {
+    return ImmutableSet.of(TypeNames.INJECT, TypeNames.INJECT_JAVAX, TypeNames.ASSISTED_INJECT);
   }
 
   @Override
-  protected void process(
-      Element injectElement, ImmutableSet<Class<? extends Annotation>> annotations) {
+  protected void process(XElement injectElement, ImmutableSet<ClassName> 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);
+    if (isConstructor(injectElement)) {
+      injectBindingRegistry.tryRegisterInjectConstructor(asConstructor(injectElement));
+    } else if (isField(injectElement)) {
+      injectBindingRegistry.tryRegisterInjectField(asField(injectElement));
+    } else if (isMethod(injectElement)) {
+      injectBindingRegistry.tryRegisterInjectMethod(asMethod(injectElement));
+    }
 
     processedElements.add(injectElement);
   }
diff --git a/java/dagger/internal/codegen/MapKeyProcessingStep.java b/java/dagger/internal/codegen/MapKeyProcessingStep.java
index 50693da..4d1f3b8 100644
--- a/java/dagger/internal/codegen/MapKeyProcessingStep.java
+++ b/java/dagger/internal/codegen/MapKeyProcessingStep.java
@@ -17,77 +17,64 @@
 package dagger.internal.codegen;
 
 import static dagger.internal.codegen.binding.MapKeys.getUnwrappedMapKeyType;
-import static javax.lang.model.element.ElementKind.ANNOTATION_TYPE;
+import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
 
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XMessager;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
 import dagger.MapKey;
-import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.javapoet.TypeNames;
 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;
 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.type.DeclaredType;
 
 /**
  * The annotation processor responsible for validating the mapKey annotation and auto-generate
  * implementation of annotations marked with {@link MapKey @MapKey} where necessary.
  */
-final class MapKeyProcessingStep extends TypeCheckingProcessingStep<TypeElement> {
-  private final Messager messager;
-  private final DaggerTypes types;
+final class MapKeyProcessingStep extends TypeCheckingProcessingStep<XTypeElement> {
+  private final XMessager messager;
   private final MapKeyValidator mapKeyValidator;
   private final AnnotationCreatorGenerator annotationCreatorGenerator;
   private final UnwrappedMapKeyGenerator unwrappedMapKeyGenerator;
 
   @Inject
   MapKeyProcessingStep(
-      Messager messager,
-      DaggerTypes types,
+      XMessager messager,
       MapKeyValidator mapKeyValidator,
       AnnotationCreatorGenerator annotationCreatorGenerator,
       UnwrappedMapKeyGenerator unwrappedMapKeyGenerator) {
-    super(MoreElements::asType);
     this.messager = messager;
-    this.types = types;
     this.mapKeyValidator = mapKeyValidator;
     this.annotationCreatorGenerator = annotationCreatorGenerator;
     this.unwrappedMapKeyGenerator = unwrappedMapKeyGenerator;
   }
 
   @Override
-  public Set<Class<? extends Annotation>> annotations() {
-    return ImmutableSet.<Class<? extends Annotation>>of(MapKey.class);
+  public ImmutableSet<ClassName> annotationClassNames() {
+    return ImmutableSet.of(TypeNames.MAP_KEY);
   }
 
   @Override
-  protected void process(
-      TypeElement mapKeyAnnotationType, ImmutableSet<Class<? extends Annotation>> annotations) {
-    ValidationReport<Element> mapKeyReport = mapKeyValidator.validate(mapKeyAnnotationType);
+  protected void process(XTypeElement mapAnnotation, ImmutableSet<ClassName> annotations) {
+    ValidationReport mapKeyReport = mapKeyValidator.validate(mapAnnotation);
     mapKeyReport.printMessagesTo(messager);
 
     if (mapKeyReport.isClean()) {
-      MapKey mapkey = mapKeyAnnotationType.getAnnotation(MapKey.class);
-      if (!mapkey.unwrapValue()) {
-        annotationCreatorGenerator.generate(mapKeyAnnotationType, messager);
-      } else if (unwrappedValueKind(mapKeyAnnotationType).equals(ANNOTATION_TYPE)) {
-        unwrappedMapKeyGenerator.generate(mapKeyAnnotationType, messager);
+      if (!mapAnnotation.getAnnotation(TypeNames.MAP_KEY).getAsBoolean("unwrapValue")) {
+        annotationCreatorGenerator.generate(mapAnnotation, messager);
+      } else if (isAnnotationType(getUnwrappedMapKeyType(mapAnnotation.getType()))) {
+        unwrappedMapKeyGenerator.generate(mapAnnotation, messager);
       }
     }
   }
 
-  private ElementKind unwrappedValueKind(TypeElement mapKeyAnnotationType) {
-    DeclaredType unwrappedMapKeyType =
-        getUnwrappedMapKeyType(MoreTypes.asDeclared(mapKeyAnnotationType.asType()), types);
-    return unwrappedMapKeyType.asElement().getKind();
+  private boolean isAnnotationType(XType type) {
+    return isDeclared(type) && type.getTypeElement().isAnnotationClass();
   }
 }
diff --git a/java/dagger/internal/codegen/ModuleProcessingStep.java b/java/dagger/internal/codegen/ModuleProcessingStep.java
index 2bf3354..811d35a 100644
--- a/java/dagger/internal/codegen/ModuleProcessingStep.java
+++ b/java/dagger/internal/codegen/ModuleProcessingStep.java
@@ -16,70 +16,59 @@
 
 package dagger.internal.codegen;
 
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static javax.lang.model.util.ElementFilter.methodsIn;
-import static javax.lang.model.util.ElementFilter.typesIn;
+import static dagger.internal.codegen.extension.DaggerCollectors.toOptional;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
 
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XMessager;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
-import com.google.auto.common.MoreElements;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.SetMultimap;
 import com.google.common.collect.Sets;
-import dagger.Binds;
-import dagger.Module;
-import dagger.Provides;
+import com.squareup.javapoet.ClassName;
 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.javapoet.TypeNames;
 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;
-import java.util.List;
+import java.util.Map;
 import java.util.Set;
-import javax.annotation.processing.Messager;
 import javax.inject.Inject;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
 
 /**
  * A {@link ProcessingStep} that validates module classes and generates factories for binding
  * methods.
  */
-final class ModuleProcessingStep extends TypeCheckingProcessingStep<TypeElement> {
-  private final Messager messager;
+final class ModuleProcessingStep extends TypeCheckingProcessingStep<XTypeElement> {
+  private final XMessager messager;
   private final ModuleValidator moduleValidator;
   private final BindingFactory bindingFactory;
   private final SourceFileGenerator<ProvisionBinding> factoryGenerator;
   private final SourceFileGenerator<ProductionBinding> producerFactoryGenerator;
-  private final SourceFileGenerator<TypeElement> moduleConstructorProxyGenerator;
+  private final SourceFileGenerator<XTypeElement> moduleConstructorProxyGenerator;
   private final InaccessibleMapKeyProxyGenerator inaccessibleMapKeyProxyGenerator;
   private final DelegateDeclaration.Factory delegateDeclarationFactory;
-  private final KotlinMetadataUtil metadataUtil;
-  private final Set<TypeElement> processedModuleElements = Sets.newLinkedHashSet();
+  private final Set<XTypeElement> processedModuleElements = Sets.newLinkedHashSet();
 
   @Inject
   ModuleProcessingStep(
-      Messager messager,
+      XMessager messager,
       ModuleValidator moduleValidator,
       BindingFactory bindingFactory,
       SourceFileGenerator<ProvisionBinding> factoryGenerator,
       SourceFileGenerator<ProductionBinding> producerFactoryGenerator,
-      @ModuleGenerator SourceFileGenerator<TypeElement> moduleConstructorProxyGenerator,
+      @ModuleGenerator SourceFileGenerator<XTypeElement> moduleConstructorProxyGenerator,
       InaccessibleMapKeyProxyGenerator inaccessibleMapKeyProxyGenerator,
-      Factory delegateDeclarationFactory,
-      KotlinMetadataUtil metadataUtil) {
-    super(MoreElements::asType);
+      DelegateDeclaration.Factory delegateDeclarationFactory) {
     this.messager = messager;
     this.moduleValidator = moduleValidator;
     this.bindingFactory = bindingFactory;
@@ -88,54 +77,57 @@
     this.moduleConstructorProxyGenerator = moduleConstructorProxyGenerator;
     this.inaccessibleMapKeyProxyGenerator = inaccessibleMapKeyProxyGenerator;
     this.delegateDeclarationFactory = delegateDeclarationFactory;
-    this.metadataUtil = metadataUtil;
   }
 
   @Override
-  public Set<? extends Class<? extends Annotation>> annotations() {
-    return ImmutableSet.of(Module.class, ProducerModule.class);
+  public ImmutableSet<ClassName> annotationClassNames() {
+    return ImmutableSet.of(TypeNames.MODULE, TypeNames.PRODUCER_MODULE);
   }
 
   @Override
-  public ImmutableSet<Element> process(
-      SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
-    List<TypeElement> modules = typesIn(elementsByAnnotation.values());
-    moduleValidator.addKnownModules(modules);
-    return super.process(elementsByAnnotation);
+  public ImmutableSet<XElement> process(
+      XProcessingEnv env, Map<String, ? extends Set<? extends XElement>> elementsByAnnotation) {
+    moduleValidator.addKnownModules(
+        elementsByAnnotation.values().stream()
+            .flatMap(Set::stream)
+            // This cast is safe because @Module has @Target(ElementType.TYPE)
+            .map(XTypeElement.class::cast)
+            .collect(toImmutableSet()));
+    return super.process(env, elementsByAnnotation);
   }
 
   @Override
-  protected void process(
-      TypeElement module, ImmutableSet<Class<? extends Annotation>> annotations) {
+  protected void process(XTypeElement module, ImmutableSet<ClassName> annotations) {
     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)) {
+    if (module.isCompanionObject()) {
       // 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);
+    ValidationReport report = moduleValidator.validate(module);
     report.printMessagesTo(messager);
     if (report.isClean()) {
       generateForMethodsIn(module);
-      if (metadataUtil.hasEnclosedCompanionObject(module)) {
-        generateForMethodsIn(metadataUtil.getEnclosedCompanionObject(module));
-      }
+      module.getEnclosedTypeElements().stream()
+          .filter(XTypeElement::isCompanionObject)
+          .collect(toOptional())
+          .ifPresent(this::generateForMethodsIn);
     }
     processedModuleElements.add(module);
   }
 
-  private void generateForMethodsIn(TypeElement module) {
-    for (ExecutableElement method : methodsIn(module.getEnclosedElements())) {
-      if (isAnnotationPresent(method, Provides.class)) {
+  private void generateForMethodsIn(XTypeElement module) {
+    for (XMethodElement method : module.getDeclaredMethods()) {
+      if (method.hasAnnotation(TypeNames.PROVIDES)) {
         generate(factoryGenerator, bindingFactory.providesMethodBinding(method, module));
-      } else if (isAnnotationPresent(method, Produces.class)) {
+      } else if (method.hasAnnotation(TypeNames.PRODUCES)) {
         generate(producerFactoryGenerator, bindingFactory.producesMethodBinding(method, module));
-      } else if (isAnnotationPresent(method, Binds.class)) {
+      } else if (method.hasAnnotation(TypeNames.BINDS)) {
         inaccessibleMapKeyProxyGenerator.generate(bindsMethodBinding(module, method), messager);
       }
     }
@@ -148,7 +140,7 @@
     inaccessibleMapKeyProxyGenerator.generate(binding, messager);
   }
 
-  private ContributionBinding bindsMethodBinding(TypeElement module, ExecutableElement method) {
+  private ContributionBinding bindsMethodBinding(XTypeElement module, XMethodElement method) {
     return bindingFactory.unresolvedDelegateBinding(
         delegateDeclarationFactory.create(method, module));
   }
diff --git a/java/dagger/internal/codegen/ProcessingEnvironmentModule.java b/java/dagger/internal/codegen/ProcessingEnvironmentModule.java
index 120714b..1561c7c 100644
--- a/java/dagger/internal/codegen/ProcessingEnvironmentModule.java
+++ b/java/dagger/internal/codegen/ProcessingEnvironmentModule.java
@@ -16,28 +16,27 @@
 
 package dagger.internal.codegen;
 
+import androidx.room.compiler.processing.XFiler;
+import androidx.room.compiler.processing.XMessager;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.compat.XConverters;
 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.base.ClearableCache;
 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.internal.codegen.langmodel.DaggerTypes;
 import dagger.multibindings.IntoSet;
-import dagger.spi.BindingGraphPlugin;
 import java.util.Map;
-import javax.annotation.processing.Filer;
-import javax.annotation.processing.Messager;
-import javax.annotation.processing.ProcessingEnvironment;
 import javax.inject.Singleton;
 import javax.lang.model.SourceVersion;
-import javax.lang.model.util.Types;
 
-/** Bindings that depend on the {@link ProcessingEnvironment}. */
+/** Bindings that depend on the {@link XProcessingEnv}. */
 @Module
 interface ProcessingEnvironmentModule {
   @Binds
@@ -47,47 +46,43 @@
 
   @Provides
   @ProcessingOptions
-  static Map<String, String> processingOptions(ProcessingEnvironment processingEnvironment) {
-    return processingEnvironment.getOptions();
+  static Map<String, String> processingOptions(XProcessingEnv xProcessingEnv) {
+    return xProcessingEnv.getOptions();
   }
 
   @Provides
-  static Messager messager(ProcessingEnvironment processingEnvironment) {
-    return processingEnvironment.getMessager();
+  static XMessager messager(XProcessingEnv xProcessingEnv) {
+    return xProcessingEnv.getMessager();
   }
 
   @Provides
-  static Filer filer(CompilerOptions compilerOptions, ProcessingEnvironment processingEnvironment) {
-    if (compilerOptions.headerCompilation() || !compilerOptions.formatGeneratedSource()) {
-      return processingEnvironment.getFiler();
-    } else {
-      return new FormattingFiler(processingEnvironment.getFiler());
-    }
+  static XFiler filer(CompilerOptions compilerOptions, XProcessingEnv xProcessingEnv) {
+    return compilerOptions.headerCompilation() || !compilerOptions.formatGeneratedSource()
+        ? xProcessingEnv.getFiler()
+        : XConverters.toXProcessing(
+            new FormattingFiler(XConverters.toJavac(xProcessingEnv.getFiler())), xProcessingEnv);
   }
 
   @Provides
-  static Types types(ProcessingEnvironment processingEnvironment) {
-    return processingEnvironment.getTypeUtils();
-  }
-
-  @Provides
-  static SourceVersion sourceVersion(ProcessingEnvironment processingEnvironment) {
-    return processingEnvironment.getSourceVersion();
+  static SourceVersion sourceVersion(XProcessingEnv xProcessingEnv) {
+    return XConverters.toJavac(xProcessingEnv).getSourceVersion();
   }
 
   @Provides
   @Singleton
-  static DaggerElements daggerElements(ProcessingEnvironment processingEnvironment) {
-    return new DaggerElements(processingEnvironment);
+  static DaggerElements daggerElements(XProcessingEnv xProcessingEnv) {
+    return new DaggerElements(
+        XConverters.toJavac(xProcessingEnv).getElementUtils(),
+        XConverters.toJavac(xProcessingEnv).getTypeUtils());
+  }
+
+  @Provides
+  @Singleton
+  static DaggerTypes daggerTypes(XProcessingEnv xProcessingEnv, DaggerElements elements) {
+    return new DaggerTypes(XConverters.toJavac(xProcessingEnv).getTypeUtils(), elements);
   }
 
   @Binds
   @IntoSet
   ClearableCache daggerElementAsClearableCache(DaggerElements elements);
-
-  @Provides
-  @ProcessorClassLoader
-  static ClassLoader processorClassloader(ProcessingEnvironment processingEnvironment) {
-    return BindingGraphPlugin.class.getClassLoader();
-  }
 }
diff --git a/java/dagger/internal/codegen/ProcessingRoundCacheModule.java b/java/dagger/internal/codegen/ProcessingRoundCacheModule.java
index 2373cd2..0d9b02a 100644
--- a/java/dagger/internal/codegen/ProcessingRoundCacheModule.java
+++ b/java/dagger/internal/codegen/ProcessingRoundCacheModule.java
@@ -26,6 +26,7 @@
 import dagger.internal.codegen.validation.ComponentCreatorValidator;
 import dagger.internal.codegen.validation.ComponentValidator;
 import dagger.internal.codegen.validation.InjectValidator;
+import dagger.internal.codegen.validation.SuperficialValidator;
 import dagger.multibindings.IntoSet;
 
 /**
@@ -61,4 +62,8 @@
   @Binds
   @IntoSet
   ClearableCache kotlinMetadata(KotlinMetadataFactory cache);
+
+  @Binds
+  @IntoSet
+  ClearableCache superficialValidator(SuperficialValidator cache);
 }
diff --git a/java/dagger/internal/codegen/ServiceLoaders.java b/java/dagger/internal/codegen/ServiceLoaders.java
new file mode 100644
index 0000000..0f9b355
--- /dev/null
+++ b/java/dagger/internal/codegen/ServiceLoaders.java
@@ -0,0 +1,37 @@
+/*
+ * 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 com.google.common.collect.ImmutableSet;
+import java.util.ServiceLoader;
+import javax.annotation.processing.ProcessingEnvironment;
+
+/** A class that loads services for the {@link ComponentProcessor}. */
+final class ServiceLoaders {
+
+  private ServiceLoaders() {}
+
+  static <T> ImmutableSet<T> load(ProcessingEnvironment processingEnvironment, Class<T> clazz) {
+    return ImmutableSet.copyOf(
+        ServiceLoader.load(clazz, classloaderFor(processingEnvironment, clazz)));
+  }
+
+  private static ClassLoader classloaderFor(
+      ProcessingEnvironment processingEnvironment, Class<?> clazz) {
+    return clazz.getClassLoader();
+  }
+}
diff --git a/java/dagger/internal/codegen/SourceFileGeneratorsModule.java b/java/dagger/internal/codegen/SourceFileGeneratorsModule.java
index 426afae..1ba7351 100644
--- a/java/dagger/internal/codegen/SourceFileGeneratorsModule.java
+++ b/java/dagger/internal/codegen/SourceFileGeneratorsModule.java
@@ -16,6 +16,7 @@
 
 package dagger.internal.codegen;
 
+import androidx.room.compiler.processing.XTypeElement;
 import dagger.Module;
 import dagger.Provides;
 import dagger.internal.codegen.base.SourceFileGenerator;
@@ -29,7 +30,6 @@
 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
 abstract class SourceFileGeneratorsModule {
@@ -54,7 +54,7 @@
 
   @Provides
   @ModuleGenerator
-  static SourceFileGenerator<TypeElement> moduleConstructorProxyGenerator(
+  static SourceFileGenerator<XTypeElement> moduleConstructorProxyGenerator(
       ModuleConstructorProxyGenerator generator, CompilerOptions compilerOptions) {
     return hjarWrapper(generator, compilerOptions);
   }
diff --git a/java/dagger/internal/codegen/SpiModule.java b/java/dagger/internal/codegen/SpiModule.java
deleted file mode 100644
index a4f537b..0000000
--- a/java/dagger/internal/codegen/SpiModule.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 static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.PARAMETER;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import com.google.common.collect.ImmutableSet;
-import dagger.Module;
-import dagger.Provides;
-import dagger.internal.codegen.validation.BindingGraphValidator;
-import dagger.spi.BindingGraphPlugin;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-import java.util.Optional;
-import java.util.ServiceLoader;
-import javax.inject.Qualifier;
-import javax.inject.Singleton;
-
-/** Contains the bindings for {@link BindingGraphValidator} from external SPI providers. */
-@Module
-abstract class SpiModule {
-  private SpiModule() {}
-
-  @Provides
-  @Singleton
-  static ImmutableSet<BindingGraphPlugin> externalPlugins(
-      @TestingPlugins Optional<ImmutableSet<BindingGraphPlugin>> testingPlugins,
-      @ProcessorClassLoader ClassLoader processorClassLoader) {
-    return testingPlugins.orElseGet(
-        () ->
-            ImmutableSet.copyOf(
-                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/base/BUILD b/java/dagger/internal/codegen/base/BUILD
index 0bcbb12..69e84d0 100644
--- a/java/dagger/internal/codegen/base/BUILD
+++ b/java/dagger/internal/codegen/base/BUILD
@@ -35,20 +35,20 @@
     tags = ["maven:merged"],
     exports = [":shared"],
     deps = [
-        ":shared",
         "//java/dagger:core",
+        "//java/dagger/internal/codegen/compileroption",
         "//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/internal/codegen/xprocessing",
         "//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",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:value",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/graph",
+        "//third_party/java/javapoet",
+        "//third_party/java/jsr330_inject",
     ],
 )
 
@@ -59,8 +59,8 @@
     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",
+        "//third_party/java/auto:common",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
     ],
 )
diff --git a/java/dagger/internal/codegen/base/ComponentAnnotation.java b/java/dagger/internal/codegen/base/ComponentAnnotation.java
index b70ef54..e0871eb 100644
--- a/java/dagger/internal/codegen/base/ComponentAnnotation.java
+++ b/java/dagger/internal/codegen/base/ComponentAnnotation.java
@@ -16,31 +16,22 @@
 
 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 static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.xprocessing.XAnnotations.getClassName;
+import static dagger.internal.codegen.xprocessing.XElements.getAnyAnnotation;
 
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 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 dagger.internal.codegen.javapoet.TypeNames;
 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
@@ -48,74 +39,82 @@
  * annotation that is being treated as a component annotation when validating full binding graphs
  * for modules.
  */
+@AutoValue
 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);
+  private static final ImmutableSet<ClassName> ROOT_COMPONENT_ANNOTATIONS =
+      ImmutableSet.of(TypeNames.COMPONENT, TypeNames.PRODUCTION_COMPONENT);
 
   /** The subcomponent annotation types. */
-  private static final ImmutableSet<Class<? extends Annotation>> SUBCOMPONENT_ANNOTATIONS =
-     ImmutableSet.of(Subcomponent.class, ProductionSubcomponent.class);
-
-  // TODO(erichang): Move ComponentCreatorAnnotation into /base and use that here?
-  /** The component/subcomponent creator annotation types. */
-  private static final ImmutableSet<Class<? extends Annotation>> CREATOR_ANNOTATIONS =
-      ImmutableSet.of(
-          Component.Builder.class,
-          Component.Factory.class,
-          ProductionComponent.Builder.class,
-          ProductionComponent.Factory.class,
-          Subcomponent.Builder.class,
-          Subcomponent.Factory.class,
-          ProductionSubcomponent.Builder.class,
-          ProductionSubcomponent.Factory.class);
+  private static final ImmutableSet<ClassName> SUBCOMPONENT_ANNOTATIONS =
+      ImmutableSet.of(TypeNames.SUBCOMPONENT, TypeNames.PRODUCTION_SUBCOMPONENT);
 
   /** 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();
+  private static final ImmutableSet<ClassName> ALL_COMPONENT_ANNOTATIONS =
+      ImmutableSet.<ClassName>builder()
+          .addAll(ROOT_COMPONENT_ANNOTATIONS)
+          .addAll(SUBCOMPONENT_ANNOTATIONS)
+          .build();
 
   /** All component and creator annotation types. */
-  private static final ImmutableSet<Class<? extends Annotation>>
-      ALL_COMPONENT_AND_CREATOR_ANNOTATIONS = ImmutableSet.<Class<? extends Annotation>>builder()
-         .addAll(ALL_COMPONENT_ANNOTATIONS)
-         .addAll(CREATOR_ANNOTATIONS)
-         .build();
+  private static final ImmutableSet<ClassName> ALL_COMPONENT_AND_CREATOR_ANNOTATIONS =
+      ImmutableSet.<ClassName>builder()
+          .addAll(ALL_COMPONENT_ANNOTATIONS)
+          .addAll(ComponentCreatorAnnotation.allCreatorAnnotations())
+          .build();
+
+  /** All production annotation types. */
+  private static final ImmutableSet<ClassName> PRODUCTION_ANNOTATIONS =
+      ImmutableSet.of(
+          TypeNames.PRODUCTION_COMPONENT,
+          TypeNames.PRODUCTION_SUBCOMPONENT,
+          TypeNames.PRODUCER_MODULE);
+
+  private XAnnotation annotation;
 
   /** The annotation itself. */
-  public abstract AnnotationMirror annotation();
+  public final XAnnotation annotation() {
+    return annotation;
+  }
+
+  /** Returns the {@link ClassName} name of the annotation. */
+  public abstract ClassName className();
 
   /** The simple name of the annotation type. */
-  public String simpleName() {
-    return MoreAnnotationMirrors.simpleName(annotation()).toString();
+  public final String simpleName() {
+    return className().simpleName();
   }
 
   /**
    * Returns {@code true} if the annotation is a {@code @Subcomponent} or
    * {@code @ProductionSubcomponent}.
    */
-  public abstract boolean isSubcomponent();
+  public final boolean isSubcomponent() {
+    return SUBCOMPONENT_ANNOTATIONS.contains(className());
+  }
 
   /**
    * Returns {@code true} if the annotation is a {@code @ProductionComponent},
    * {@code @ProductionSubcomponent}, or {@code @ProducerModule}.
    */
-  public abstract boolean isProduction();
+  public final boolean isProduction() {
+    return PRODUCTION_ANNOTATIONS.contains(className());
+  }
 
   /**
    * 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();
+  public final boolean isRealComponent() {
+    return ALL_COMPONENT_ANNOTATIONS.contains(className());
+  }
 
   /** The types listed as {@code dependencies}. */
-  public ImmutableList<TypeMirror> dependencyTypes() {
-    return dependencyValues().stream().map(MoreAnnotationValues::asType).collect(toImmutableList());
+  @Memoized
+  public ImmutableList<XType> dependencyTypes() {
+    return isRootComponent()
+        ? ImmutableList.copyOf(annotation.getAsTypeList("dependencies"))
+        : ImmutableList.of();
   }
 
   /**
@@ -123,229 +122,100 @@
    *
    * @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());
+  @Memoized
+  public ImmutableSet<XTypeElement> dependencies() {
+    return dependencyTypes().stream().map(XType::getTypeElement).collect(toImmutableSet());
   }
 
   /**
    * The types listed as {@code modules}.
    *
-   * @throws IllegalArgumentException if any of {@link #moduleTypes()} are error types
+   * @throws IllegalArgumentException if any module is an error type.
    */
-  public ImmutableSet<TypeElement> modules() {
-    return asTypeElements(moduleTypes());
+  @Memoized
+  public ImmutableSet<XTypeElement> modules() {
+    return annotation.getAsTypeList(isRealComponent() ? "modules" : "includes").stream()
+        .map(XType::getTypeElement)
+        .collect(toImmutableSet());
   }
 
-  protected final ImmutableList<AnnotationValue> getAnnotationValues(String parameterName) {
-    return asAnnotationValues(getAnnotationValue(annotation(), parameterName));
+  private final boolean isRootComponent() {
+    return ROOT_COMPONENT_ANNOTATIONS.contains(className());
   }
 
   /**
    * 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);
+  public static Optional<ComponentAnnotation> rootComponentAnnotation(
+      XTypeElement typeElement, DaggerSuperficialValidation superficialValidation) {
+    return anyComponentAnnotation(typeElement, ROOT_COMPONENT_ANNOTATIONS, superficialValidation);
   }
 
   /**
    * 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);
+  public static Optional<ComponentAnnotation> subcomponentAnnotation(
+      XTypeElement typeElement, DaggerSuperficialValidation superficialValidation) {
+    return anyComponentAnnotation(typeElement, SUBCOMPONENT_ANNOTATIONS, superficialValidation);
   }
 
   /**
    * 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);
+  public static Optional<ComponentAnnotation> anyComponentAnnotation(
+      XElement element, DaggerSuperficialValidation superficialValidation) {
+    return anyComponentAnnotation(element, ALL_COMPONENT_ANNOTATIONS, superficialValidation);
   }
 
   private static Optional<ComponentAnnotation> anyComponentAnnotation(
-      TypeElement typeElement, Collection<Class<? extends Annotation>> annotations) {
-    return getAnyAnnotation(typeElement, annotations).map(ComponentAnnotation::componentAnnotation);
+      XElement element,
+      Collection<ClassName> annotations,
+      DaggerSuperficialValidation superficialValidation) {
+    return getAnyAnnotation(element, annotations)
+        .map(
+            annotation -> {
+              superficialValidation.validateAnnotationOf(element, annotation);
+              return create(annotation);
+            });
   }
 
   /** 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");
+  public static boolean isComponentAnnotation(XAnnotation annotation) {
+    return ALL_COMPONENT_ANNOTATIONS.contains(getClassName(annotation));
   }
 
   /** Creates a fictional component annotation representing a module. */
   public static ComponentAnnotation fromModuleAnnotation(ModuleAnnotation moduleAnnotation) {
-    return new AutoValue_ComponentAnnotation_FictionalComponentAnnotation(moduleAnnotation);
+    return create(moduleAnnotation.annotation());
+  }
+
+  private static ComponentAnnotation create(XAnnotation annotation) {
+    ComponentAnnotation componentAnnotation =
+        new AutoValue_ComponentAnnotation(getClassName(annotation));
+    componentAnnotation.annotation = annotation;
+    return componentAnnotation;
   }
 
   /** The root component annotation types. */
-  public static ImmutableSet<Class<? extends Annotation>> rootComponentAnnotations() {
+  public static ImmutableSet<ClassName> rootComponentAnnotations() {
     return ROOT_COMPONENT_ANNOTATIONS;
   }
 
   /** The subcomponent annotation types. */
-  public static ImmutableSet<Class<? extends Annotation>> subcomponentAnnotations() {
+  public static ImmutableSet<ClassName> subcomponentAnnotations() {
     return SUBCOMPONENT_ANNOTATIONS;
   }
 
   /** All component annotation types. */
-  public static ImmutableSet<Class<? extends Annotation>> allComponentAnnotations() {
+  public static ImmutableSet<ClassName> allComponentAnnotations() {
     return ALL_COMPONENT_ANNOTATIONS;
   }
 
   /** All component and creator annotation types. */
-  public static ImmutableSet<Class<? extends Annotation>> allComponentAndCreatorAnnotations() {
+  public static ImmutableSet<ClassName> allComponentAndCreatorAnnotations() {
     return ALL_COMPONENT_AND_CREATOR_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/binding/ComponentCreatorAnnotation.java b/java/dagger/internal/codegen/base/ComponentCreatorAnnotation.java
similarity index 62%
rename from java/dagger/internal/codegen/binding/ComponentCreatorAnnotation.java
rename to java/dagger/internal/codegen/base/ComponentCreatorAnnotation.java
index 6297188..621d66f 100644
--- a/java/dagger/internal/codegen/binding/ComponentCreatorAnnotation.java
+++ b/java/dagger/internal/codegen/base/ComponentCreatorAnnotation.java
@@ -14,68 +14,62 @@
  * limitations under the License.
  */
 
-package dagger.internal.codegen.binding;
+package dagger.internal.codegen.base;
 
-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 androidx.room.compiler.processing.XTypeElement;
 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 com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.javapoet.TypeNames;
 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),
+  COMPONENT_BUILDER(TypeNames.COMPONENT_BUILDER),
+  COMPONENT_FACTORY(TypeNames.COMPONENT_FACTORY),
+  SUBCOMPONENT_BUILDER(TypeNames.SUBCOMPONENT_BUILDER),
+  SUBCOMPONENT_FACTORY(TypeNames.SUBCOMPONENT_FACTORY),
+  PRODUCTION_COMPONENT_BUILDER(TypeNames.PRODUCTION_COMPONENT_BUILDER),
+  PRODUCTION_COMPONENT_FACTORY(TypeNames.PRODUCTION_COMPONENT_FACTORY),
+  PRODUCTION_SUBCOMPONENT_BUILDER(TypeNames.PRODUCTION_SUBCOMPONENT_BUILDER),
+  PRODUCTION_SUBCOMPONENT_FACTORY(TypeNames.PRODUCTION_SUBCOMPONENT_FACTORY),
   ;
 
-  private final Class<? extends Annotation> annotation;
+  private final ClassName annotation;
   private final ComponentCreatorKind creatorKind;
-  private final Class<? extends Annotation> componentAnnotation;
+  private final ClassName componentAnnotation;
 
-  @SuppressWarnings("unchecked") // Builder/factory annotations live within their parent annotation.
-  ComponentCreatorAnnotation(Class<? extends Annotation> annotation) {
+  ComponentCreatorAnnotation(ClassName annotation) {
     this.annotation = annotation;
-    this.creatorKind = ComponentCreatorKind.valueOf(toUpperCase(annotation.getSimpleName()));
-    this.componentAnnotation = (Class<? extends Annotation>) annotation.getEnclosingClass();
+    this.creatorKind = ComponentCreatorKind.valueOf(toUpperCase(annotation.simpleName()));
+    this.componentAnnotation = annotation.enclosingClassName();
   }
 
   /** The actual annotation type. */
-  public Class<? extends Annotation> annotation() {
+  public ClassName annotation() {
     return annotation;
   }
 
   /** The component annotation type that encloses this creator annotation type. */
-  public final Class<? extends Annotation> componentAnnotation() {
+  public final ClassName componentAnnotation() {
     return componentAnnotation;
   }
 
   /** Returns {@code true} if the creator annotation is for a subcomponent. */
   public final boolean isSubcomponentCreatorAnnotation() {
-    return componentAnnotation().getSimpleName().endsWith("Subcomponent");
+    return componentAnnotation().simpleName().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");
+    return componentAnnotation().simpleName().startsWith("Production");
   }
 
   /** The creator kind the annotation is associated with. */
@@ -86,16 +80,16 @@
 
   @Override
   public final String toString() {
-    return annotation().getName();
+    return annotation().canonicalName();
   }
 
   /** Returns all component creator annotations. */
-  public static ImmutableSet<Class<? extends Annotation>> allCreatorAnnotations() {
+  public static ImmutableSet<ClassName> allCreatorAnnotations() {
     return stream().collect(toAnnotationClasses());
   }
 
   /** Returns all root component creator annotations. */
-  public static ImmutableSet<Class<? extends Annotation>> rootComponentCreatorAnnotations() {
+  public static ImmutableSet<ClassName> rootComponentCreatorAnnotations() {
     return stream()
         .filter(
             componentCreatorAnnotation ->
@@ -104,7 +98,7 @@
   }
 
   /** Returns all subcomponent creator annotations. */
-  public static ImmutableSet<Class<? extends Annotation>> subcomponentCreatorAnnotations() {
+  public static ImmutableSet<ClassName> subcomponentCreatorAnnotations() {
     return stream()
         .filter(
             componentCreatorAnnotation ->
@@ -113,7 +107,7 @@
   }
 
   /** Returns all production component creator annotations. */
-  public static ImmutableSet<Class<? extends Annotation>> productionCreatorAnnotations() {
+  public static ImmutableSet<ClassName> productionCreatorAnnotations() {
     return stream()
         .filter(
             componentCreatorAnnotation ->
@@ -122,30 +116,28 @@
   }
 
   /** Returns the legal creator annotations for the given {@code componentAnnotation}. */
-  public static ImmutableSet<Class<? extends Annotation>> creatorAnnotationsFor(
+  public static ImmutableSet<ClassName> creatorAnnotationsFor(
       ComponentAnnotation componentAnnotation) {
     return stream()
         .filter(
             creatorAnnotation ->
                 creatorAnnotation
                     .componentAnnotation()
-                    .getSimpleName()
+                    .simpleName()
                     .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());
+  public static ImmutableSet<ComponentCreatorAnnotation> getCreatorAnnotations(XTypeElement type) {
+    return stream().filter(cca -> type.hasAnnotation(cca.annotation())).collect(toImmutableSet());
   }
 
   private static Stream<ComponentCreatorAnnotation> stream() {
     return valuesOf(ComponentCreatorAnnotation.class);
   }
 
-  private static Collector<ComponentCreatorAnnotation, ?, ImmutableSet<Class<? extends Annotation>>>
+  private static Collector<ComponentCreatorAnnotation, ?, ImmutableSet<ClassName>>
       toAnnotationClasses() {
     return mapping(ComponentCreatorAnnotation::annotation, toImmutableSet());
   }
diff --git a/java/dagger/internal/codegen/binding/ComponentCreatorKind.java b/java/dagger/internal/codegen/base/ComponentCreatorKind.java
similarity index 96%
rename from java/dagger/internal/codegen/binding/ComponentCreatorKind.java
rename to java/dagger/internal/codegen/base/ComponentCreatorKind.java
index b2581d6..3af94bb 100644
--- a/java/dagger/internal/codegen/binding/ComponentCreatorKind.java
+++ b/java/dagger/internal/codegen/base/ComponentCreatorKind.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package dagger.internal.codegen.binding;
+package dagger.internal.codegen.base;
 
 import static com.google.common.base.CaseFormat.UPPER_CAMEL;
 import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
diff --git a/java/dagger/internal/codegen/base/ComponentKind.java b/java/dagger/internal/codegen/base/ComponentKind.java
new file mode 100644
index 0000000..9d08763
--- /dev/null
+++ b/java/dagger/internal/codegen/base/ComponentKind.java
@@ -0,0 +1,100 @@
+/*
+ * 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.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 androidx.room.compiler.processing.XTypeElement;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.javapoet.TypeNames;
+import java.util.Optional;
+
+/** Enumeration of the different kinds of components. */
+public enum ComponentKind {
+  COMPONENT(TypeNames.COMPONENT),
+  SUBCOMPONENT(TypeNames.SUBCOMPONENT),
+  PRODUCTION_COMPONENT(TypeNames.PRODUCTION_COMPONENT),
+  PRODUCTION_SUBCOMPONENT(TypeNames.PRODUCTION_SUBCOMPONENT),
+  MODULE(TypeNames.MODULE),
+  PRODUCER_MODULE(TypeNames.PRODUCER_MODULE);
+
+  private static final ImmutableSet<ComponentKind> PRODUCER_KINDS =
+      ImmutableSet.of(PRODUCTION_COMPONENT, PRODUCTION_SUBCOMPONENT, PRODUCER_MODULE);
+
+  /** Returns the annotations for components of the given kinds. */
+  public static ImmutableSet<ClassName> 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(XTypeElement element) {
+    return valuesOf(ComponentKind.class)
+        .filter(kind -> element.hasAnnotation(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(XTypeElement 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 ClassName annotation;
+
+  ComponentKind(ClassName annotation) {
+    this.annotation = annotation;
+  }
+
+  /** Returns the annotation that marks a component of this kind. */
+  public ClassName 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 true if this is a production component. */
+  public boolean isProducer() {
+    return PRODUCER_KINDS.contains(this);
+  }
+}
diff --git a/java/dagger/internal/codegen/base/ContributionType.java b/java/dagger/internal/codegen/base/ContributionType.java
index c046daa..93d423e 100644
--- a/java/dagger/internal/codegen/base/ContributionType.java
+++ b/java/dagger/internal/codegen/base/ContributionType.java
@@ -16,8 +16,10 @@
 
 package dagger.internal.codegen.base;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 import static com.google.auto.common.MoreElements.isAnnotationPresent;
 
+import androidx.room.compiler.processing.XElement;
 import dagger.multibindings.ElementsIntoSet;
 import dagger.multibindings.IntoMap;
 import dagger.multibindings.IntoSet;
@@ -54,6 +56,17 @@
    * dagger.internal.codegen.validation.BindsInstanceProcessingStep} validate correctness on their
    * own.
    */
+  public static ContributionType fromBindingElement(XElement element) {
+    return fromBindingElement(toJavac(element));
+  }
+
+  /**
+   * 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)) {
diff --git a/java/dagger/internal/codegen/base/DaggerSuperficialValidation.java b/java/dagger/internal/codegen/base/DaggerSuperficialValidation.java
new file mode 100644
index 0000000..2f53a71
--- /dev/null
+++ b/java/dagger/internal/codegen/base/DaggerSuperficialValidation.java
@@ -0,0 +1,823 @@
+/*
+ * 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.base;
+
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static com.google.auto.common.MoreElements.asType;
+import static com.google.auto.common.MoreElements.isType;
+import static com.google.auto.common.MoreTypes.asDeclared;
+
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XExecutableElement;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
+import com.google.auto.common.AnnotationMirrors;
+import com.google.auto.common.MoreTypes;
+import com.google.common.base.Ascii;
+import com.google.common.collect.ImmutableList;
+import com.squareup.javapoet.ClassName;
+import dagger.Reusable;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+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.AnnotationValueVisitor;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+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.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVisitor;
+import javax.lang.model.type.WildcardType;
+import javax.lang.model.util.AbstractElementVisitor8;
+import javax.lang.model.util.SimpleAnnotationValueVisitor8;
+import javax.lang.model.util.SimpleTypeVisitor8;
+
+/**
+ * A fork of {@link com.google.auto.common.SuperficialValidation}.
+ *
+ * <p>This fork makes a couple changes from the original:
+ *
+ * <ul>
+ *   <li>Throws {@link ValidationException} rather than returning {@code false} for invalid types.
+ *   <li>Fixes a bug that incorrectly validates error types in annotations (b/213880825)
+ *   <li>Exposes extra methods needed to validate various parts of an element rather than just the
+ *       entire element.
+ * </ul>
+ */
+@Reusable
+public final class DaggerSuperficialValidation {
+
+  /**
+   * Returns the type element with the given class name or throws {@link ValidationException} if it
+   * is not accessible in the current compilation.
+   */
+  public static XTypeElement requireTypeElement(XProcessingEnv processingEnv, ClassName className) {
+    return requireTypeElement(processingEnv, className.canonicalName());
+  }
+
+  /**
+   * Returns the type element with the given class name or throws {@link ValidationException} if it
+   * is not accessible in the current compilation.
+   */
+  public static XTypeElement requireTypeElement(XProcessingEnv processingEnv, String className) {
+    XTypeElement type = processingEnv.findTypeElement(className);
+    if (type == null) {
+      throw new ValidationException.KnownErrorType(className);
+    }
+    return type;
+  }
+
+  private final boolean isStrictValidationEnabled;
+
+  @Inject
+  DaggerSuperficialValidation(CompilerOptions compilerOptions) {
+    this(compilerOptions.strictSuperficialValidation());
+  }
+
+  private DaggerSuperficialValidation(boolean isStrictValidationEnabled) {
+    this.isStrictValidationEnabled = isStrictValidationEnabled;
+  }
+
+  /**
+   * Validates the {@link XElement#getType()} type of the given element.
+   *
+   * <p>Validating the type also validates any types it references, such as any type arguments or
+   * type bounds. For an {@link ExecutableType}, the parameter and return types must be fully
+   * defined, as must types declared in a {@code throws} clause or in the bounds of any type
+   * parameters.
+   */
+  public void validateTypeOf(XElement element) {
+    validateTypeOf(toJavac(element));
+  }
+
+  private void validateTypeOf(Element element) {
+    try {
+      validateType(Ascii.toLowerCase(element.getKind().name()), element.asType());
+    } catch (RuntimeException exception) {
+      throw ValidationException.from(exception).append(element);
+    }
+  }
+
+  /**
+   * Validates the {@link XElement#getSuperType()} type of the given element.
+   *
+   * <p>Validating the type also validates any types it references, such as any type arguments or
+   * type bounds.
+   */
+  public void validateSuperTypeOf(XTypeElement element) {
+    validateSuperTypeOf(toJavac(element));
+  }
+
+  private void validateSuperTypeOf(TypeElement element) {
+    try {
+      validateType("superclass", element.getSuperclass());
+    } catch (RuntimeException exception) {
+      throw ValidationException.from(exception).append(element);
+    }
+  }
+
+  /**
+   * Validates the {@link XExecutableElement#getThrownTypes()} types of the given element.
+   *
+   * <p>Validating the type also validates any types it references, such as any type arguments or
+   * type bounds.
+   */
+  public void validateThrownTypesOf(XExecutableElement element) {
+    validateThrownTypesOf(toJavac(element));
+  }
+
+  private void validateThrownTypesOf(ExecutableElement element) {
+    try {
+      validateTypes("thrown type", element.getThrownTypes());
+    } catch (RuntimeException exception) {
+      throw ValidationException.from(exception).append(element);
+    }
+  }
+
+  /**
+   * Validates the annotation types of the given element.
+   *
+   * <p>Note: this method does not validate annotation values. This method is useful if you care
+   * about the annotation's annotations (e.g. to check for {@code Scope} or {@code Qualifier}). In
+   * such cases, we just need to validate the annotation's type.
+   */
+  public void validateAnnotationTypesOf(XElement element) {
+    validateAnnotationTypesOf(toJavac(element));
+  }
+
+  /**
+   * Validates the annotation types of the given element.
+   *
+   * <p>Note: this method does not validate annotation values. This method is useful if you care
+   * about the annotation's annotations (e.g. to check for {@code Scope} or {@code Qualifier}). In
+   * such cases, we just need to validate the annotation's type.
+   */
+  public void validateAnnotationTypesOf(Element element) {
+    element
+        .getAnnotationMirrors()
+        .forEach(annotation -> validateAnnotationTypeOf(element, annotation));
+  }
+
+  /**
+   * Validates the type of the given annotation.
+   *
+   * <p>The annotation is assumed to be annotating the given element, but this is not checked. The
+   * element is only in the error message if a {@link ValidatationException} is thrown.
+   *
+   * <p>Note: this method does not validate annotation values. This method is useful if you care
+   * about the annotation's annotations (e.g. to check for {@code Scope} or {@code Qualifier}). In
+   * such cases, we just need to validate the annotation's type.
+   */
+  // TODO(bcorso): See CL/427767370 for suggestions to make this API clearer.
+  public void validateAnnotationTypeOf(XElement element, XAnnotation annotation) {
+    validateAnnotationTypeOf(toJavac(element), toJavac(annotation));
+  }
+
+  /**
+   * Validates the type of the given annotation.
+   *
+   * <p>The annotation is assumed to be annotating the given element, but this is not checked. The
+   * element is only in the error message if a {@link ValidatationException} is thrown.
+   *
+   * <p>Note: this method does not validate annotation values. This method is useful if you care
+   * about the annotation's annotations (e.g. to check for {@code Scope} or {@code Qualifier}). In
+   * such cases, we just need to validate the annotation's type.
+   */
+  public void validateAnnotationTypeOf(Element element, AnnotationMirror annotation) {
+    try {
+      validateType("annotation type", annotation.getAnnotationType());
+    } catch (RuntimeException exception) {
+      throw ValidationException.from(exception).append(annotation).append(element);
+    }
+  }
+
+  /** Validate the annotations of the given element. */
+  public void validateAnnotationsOf(XElement element) {
+    validateAnnotationsOf(toJavac(element));
+  }
+
+  public void validateAnnotationsOf(Element element) {
+    try {
+      validateAnnotations(element.getAnnotationMirrors());
+    } catch (RuntimeException exception) {
+      throw ValidationException.from(exception).append(element);
+    }
+  }
+
+  public void validateAnnotationOf(XElement element, XAnnotation annotation) {
+    validateAnnotationOf(toJavac(element), toJavac(annotation));
+  }
+
+  public void validateAnnotationOf(Element element, AnnotationMirror annotation) {
+    try {
+      validateAnnotation(annotation);
+    } catch (RuntimeException exception) {
+      throw ValidationException.from(exception).append(element);
+    }
+  }
+
+  /**
+   * Validate the type hierarchy for the given type (with the given type description) within the
+   * given element.
+   *
+   * <p>Validation includes all superclasses, interfaces, and type parameters of those types.
+   */
+  public void validateTypeHierarchyOf(String typeDescription, XElement element, XType type) {
+    try {
+      validateTypeHierarchy(typeDescription, type);
+    } catch (RuntimeException exception) {
+      throw ValidationException.from(exception).append(toJavac(element));
+    }
+  }
+
+  private void validateTypeHierarchy(String desc, XType type) {
+    validateType(desc, toJavac(type));
+    try {
+      type.getSuperTypes().forEach(supertype -> validateTypeHierarchy("supertype", supertype));
+    } catch (RuntimeException exception) {
+      throw ValidationException.from(exception).append(desc, toJavac(type));
+    }
+  }
+
+  /**
+   * Returns true if all of the given elements return true from {@link #validateElement(Element)}.
+   */
+  public void validateElements(Iterable<? extends Element> elements) {
+    for (Element element : elements) {
+      validateElement(element);
+    }
+  }
+
+  private final ElementVisitor<Void, Void> elementValidatingVisitor =
+      new AbstractElementVisitor8<Void, Void>() {
+        @Override
+        public Void visitPackage(PackageElement e, Void p) {
+          // don't validate enclosed elements because it will return types in the package
+          validateAnnotations(e.getAnnotationMirrors());
+          return null;
+        }
+
+        @Override
+        public Void visitType(TypeElement e, Void p) {
+          validateBaseElement(e);
+          validateElements(e.getTypeParameters());
+          validateTypes("interface", e.getInterfaces());
+          validateType("superclass", e.getSuperclass());
+          return null;
+        }
+
+        @Override
+        public Void visitVariable(VariableElement e, Void p) {
+          validateBaseElement(e);
+          return null;
+        }
+
+        @Override
+        public Void visitExecutable(ExecutableElement e, Void p) {
+          AnnotationValue defaultValue = e.getDefaultValue();
+          validateBaseElement(e);
+          if (defaultValue != null) {
+            validateAnnotationValue(defaultValue, e.getReturnType());
+          }
+          validateType("return type", e.getReturnType());
+          validateTypes("thrown type", e.getThrownTypes());
+          validateElements(e.getTypeParameters());
+          validateElements(e.getParameters());
+          return null;
+        }
+
+        @Override
+        public Void visitTypeParameter(TypeParameterElement e, Void p) {
+          validateBaseElement(e);
+          validateTypes("bound type", e.getBounds());
+          return null;
+        }
+
+        @Override
+        public Void visitUnknown(Element e, Void p) {
+          // just assume that unknown elements are OK
+          return null;
+        }
+      };
+
+  /**
+   * Returns true if all types referenced by the given element are defined. The exact meaning of
+   * this depends on the kind of element. For packages, it means that all annotations on the package
+   * are fully defined. For other element kinds, it means that types referenced by the element,
+   * anything it contains, and any of its annotations element are all defined.
+   */
+  public void validateElement(XElement element) {
+    validateElement(toJavac(element));
+  }
+
+  /**
+   * Returns true if all types referenced by the given element are defined. The exact meaning of
+   * this depends on the kind of element. For packages, it means that all annotations on the package
+   * are fully defined. For other element kinds, it means that types referenced by the element,
+   * anything it contains, and any of its annotations element are all defined.
+   */
+  public void validateElement(Element element) {
+    try {
+      element.accept(elementValidatingVisitor, null);
+    } catch (RuntimeException exception) {
+      throw ValidationException.from(exception).append(element);
+    }
+  }
+
+  private void validateBaseElement(Element e) {
+    validateType(Ascii.toLowerCase(e.getKind().name()), e.asType());
+    validateAnnotations(e.getAnnotationMirrors());
+    validateElements(e.getEnclosedElements());
+  }
+
+  private void validateTypes(String desc, Iterable<? extends TypeMirror> types) {
+    for (TypeMirror type : types) {
+      validateType(desc, type);
+    }
+  }
+
+  /*
+   * This visitor does not test type variables specifically, but it seems that that is not actually
+   * an issue.  Javac turns the whole type parameter into an error type if it can't figure out the
+   * bounds.
+   */
+  private final TypeVisitor<Void, Void> typeValidatingVisitor =
+      new SimpleTypeVisitor8<Void, Void>() {
+        @Override
+        protected Void defaultAction(TypeMirror t, Void p) {
+          return null;
+        }
+
+        @Override
+        public Void visitArray(ArrayType t, Void p) {
+          validateType("array component type", t.getComponentType());
+          return null;
+        }
+
+        @Override
+        public Void visitDeclared(DeclaredType t, Void p) {
+          if (isStrictValidationEnabled) {
+            // There's a bug in TypeVisitor which will visit the visitDeclared() method rather than
+            // visitError() even when it's an ERROR kind. Thus, we check the kind directly here and
+            // fail validation if it's an ERROR kind (see b/213880825).
+            if (t.getKind() == TypeKind.ERROR) {
+              throw new ValidationException.KnownErrorType(t);
+            }
+          }
+          validateTypes("type argument", t.getTypeArguments());
+          return null;
+        }
+
+        @Override
+        public Void visitError(ErrorType t, Void p) {
+          throw new ValidationException.KnownErrorType(t);
+        }
+
+        @Override
+        public Void visitUnknown(TypeMirror t, Void p) {
+          // just make the default choice for unknown types
+          return defaultAction(t, p);
+        }
+
+        @Override
+        public Void visitWildcard(WildcardType t, Void p) {
+          TypeMirror extendsBound = t.getExtendsBound();
+          TypeMirror superBound = t.getSuperBound();
+          if (extendsBound != null) {
+            validateType("extends bound type", extendsBound);
+          }
+          if (superBound != null) {
+            validateType("super bound type", superBound);
+          }
+          return null;
+        }
+
+        @Override
+        public Void visitExecutable(ExecutableType t, Void p) {
+          validateTypes("parameter type", t.getParameterTypes());
+          validateType("return type", t.getReturnType());
+          validateTypes("thrown type", t.getThrownTypes());
+          validateTypes("type variable", t.getTypeVariables());
+          return null;
+        }
+      };
+
+  /**
+   * Returns true if the given type is fully defined. This means that the type itself is defined, as
+   * are any types it references, such as any type arguments or type bounds. For an {@link
+   * ExecutableType}, the parameter and return types must be fully defined, as must types declared
+   * in a {@code throws} clause or in the bounds of any type parameters.
+   */
+  private void validateType(String desc, TypeMirror type) {
+    try {
+      type.accept(typeValidatingVisitor, null);
+      if (isStrictValidationEnabled) {
+        // Note: We don't actually expect to get an ERROR type here as it should have been caught
+        // by the visitError() or visitDeclared()  methods above. However, we check here as a last
+        // resort.
+        if (type.getKind() == TypeKind.ERROR) {
+          // In this case, the type is not guaranteed to be a DeclaredType, so we report the
+          // toString() of the type. We could report using UnknownErrorType but the type's toString
+          // may actually contain useful information.
+          throw new ValidationException.KnownErrorType(type.toString());
+        }
+      }
+    } catch (RuntimeException e) {
+      throw ValidationException.from(e).append(desc, type);
+    }
+  }
+
+  private void validateAnnotations(Iterable<? extends AnnotationMirror> annotationMirrors) {
+    for (AnnotationMirror annotationMirror : annotationMirrors) {
+      validateAnnotation(annotationMirror);
+    }
+  }
+
+  private void validateAnnotation(AnnotationMirror annotationMirror) {
+    try {
+      validateType("annotation type", annotationMirror.getAnnotationType());
+      validateAnnotationValues(annotationMirror.getElementValues());
+    } catch (RuntimeException exception) {
+      throw ValidationException.from(exception).append(annotationMirror);
+    }
+  }
+
+  private void validateAnnotationValues(
+      Map<? extends ExecutableElement, ? extends AnnotationValue> valueMap) {
+    valueMap.forEach(
+        (method, annotationValue) -> {
+          try {
+            TypeMirror expectedType = method.getReturnType();
+            validateAnnotationValue(annotationValue, expectedType);
+          } catch (RuntimeException exception) {
+            throw ValidationException.from(exception)
+                .append(String.format("annotation method: %s %s", method.getReturnType(), method));
+          }
+        });
+  }
+
+  private void validateAnnotationValue(AnnotationValue annotationValue, TypeMirror expectedType) {
+    annotationValue.accept(valueValidatingVisitor, expectedType);
+  }
+
+  private final AnnotationValueVisitor<Void, TypeMirror> valueValidatingVisitor =
+      new SimpleAnnotationValueVisitor8<Void, TypeMirror>() {
+        @Override
+        protected Void defaultAction(Object o, TypeMirror expectedType) {
+          try {
+            validateIsTypeOf(o.getClass(), expectedType);
+          } catch (RuntimeException exception) {
+            throw ValidationException.from(exception)
+                .append(exceptionMessage("DEFAULT", o, expectedType));
+          }
+          return null;
+        }
+
+        @Override
+        public Void visitString(String str, TypeMirror expectedType) {
+          try {
+            if (!MoreTypes.isTypeOf(String.class, expectedType)) {
+              if (str.contentEquals("<error>")) {
+                // Invalid annotation value types will visit visitString() with a value of "<error>"
+                // Technically, we don't know the error type in this case, but it will be referred
+                // to as "<error>" in the dependency trace, so we use that.
+                throw new ValidationException.KnownErrorType("<error>");
+              } else {
+                throw new ValidationException.UnknownErrorType();
+              }
+            }
+          } catch (RuntimeException exception) {
+            throw ValidationException.from(exception)
+                .append(exceptionMessage("STRING", str, expectedType));
+          }
+          return null;
+        }
+
+        @Override
+        public Void visitUnknown(AnnotationValue av, TypeMirror expectedType) {
+          // just take the default action for the unknown
+          defaultAction(av, expectedType);
+          return null;
+        }
+
+        @Override
+        public Void visitAnnotation(AnnotationMirror a, TypeMirror expectedType) {
+          try {
+            validateIsEquivalentType(a.getAnnotationType(), expectedType);
+            validateAnnotation(a);
+          } catch (RuntimeException exception) {
+            throw ValidationException.from(exception)
+                .append(exceptionMessage("ANNOTATION", a, expectedType));
+          }
+          return null;
+        }
+
+        @Override
+        public Void visitArray(List<? extends AnnotationValue> values, TypeMirror expectedType) {
+          try {
+            if (!expectedType.getKind().equals(TypeKind.ARRAY)) {
+              throw new ValidationException.UnknownErrorType();
+            }
+            TypeMirror componentType = MoreTypes.asArray(expectedType).getComponentType();
+            for (AnnotationValue value : values) {
+              value.accept(this, componentType);
+            }
+          } catch (RuntimeException exception) {
+            throw ValidationException.from(exception)
+                .append(exceptionMessage("ARRAY", values, expectedType));
+          }
+          return null;
+        }
+
+        @Override
+        public Void visitEnumConstant(VariableElement enumConstant, TypeMirror expectedType) {
+          try {
+            validateIsEquivalentType(asDeclared(enumConstant.asType()), expectedType);
+            validateElement(enumConstant);
+          } catch (RuntimeException exception) {
+            throw ValidationException.from(exception)
+                .append(exceptionMessage("ENUM_CONSTANT", enumConstant, expectedType));
+          }
+          return null;
+        }
+
+        @Override
+        public Void visitType(TypeMirror type, TypeMirror expectedType) {
+          try {
+            // We could check assignability here, but would require a Types instance. Since this
+            // isn't really the sort of thing that shows up in a bad AST from upstream compilation
+            // we ignore the expected type and just validate the type.  It might be wrong, but
+            // it's valid.
+            validateType("annotation value type", type);
+          } catch (RuntimeException exception) {
+            throw ValidationException.from(exception)
+                .append(exceptionMessage("TYPE", type, expectedType));
+          }
+          return null;
+        }
+
+        @Override
+        public Void visitBoolean(boolean b, TypeMirror expectedType) {
+          try {
+            validateIsTypeOf(Boolean.TYPE, expectedType);
+          } catch (RuntimeException exception) {
+            throw ValidationException.from(exception)
+                .append(exceptionMessage("BOOLEAN", b, expectedType));
+          }
+          return null;
+        }
+
+        @Override
+        public Void visitByte(byte b, TypeMirror expectedType) {
+          try {
+            validateIsTypeOf(Byte.TYPE, expectedType);
+          } catch (RuntimeException exception) {
+            throw ValidationException.from(exception)
+                .append(exceptionMessage("BYTE", b, expectedType));
+          }
+          return null;
+        }
+
+        @Override
+        public Void visitChar(char c, TypeMirror expectedType) {
+          try {
+            validateIsTypeOf(Character.TYPE, expectedType);
+          } catch (RuntimeException exception) {
+            throw ValidationException.from(exception)
+                .append(exceptionMessage("CHAR", c, expectedType));
+          }
+          return null;
+        }
+
+        @Override
+        public Void visitDouble(double d, TypeMirror expectedType) {
+          try {
+            validateIsTypeOf(Double.TYPE, expectedType);
+          } catch (RuntimeException exception) {
+            throw ValidationException.from(exception)
+                .append(exceptionMessage("DOUBLE", d, expectedType));
+          }
+          return null;
+        }
+
+        @Override
+        public Void visitFloat(float f, TypeMirror expectedType) {
+          try {
+            validateIsTypeOf(Float.TYPE, expectedType);
+          } catch (RuntimeException exception) {
+            throw ValidationException.from(exception)
+                .append(exceptionMessage("FLOAT", f, expectedType));
+          }
+          return null;
+        }
+
+        @Override
+        public Void visitInt(int i, TypeMirror expectedType) {
+          try {
+            validateIsTypeOf(Integer.TYPE, expectedType);
+          } catch (RuntimeException exception) {
+            throw ValidationException.from(exception)
+                .append(exceptionMessage("INT", i, expectedType));
+          }
+          return null;
+        }
+
+        @Override
+        public Void visitLong(long l, TypeMirror expectedType) {
+          try {
+            validateIsTypeOf(Long.TYPE, expectedType);
+          } catch (RuntimeException exception) {
+            throw ValidationException.from(exception)
+                .append(exceptionMessage("LONG", l, expectedType));
+          }
+          return null;
+        }
+
+        @Override
+        public Void visitShort(short s, TypeMirror expectedType) {
+          try {
+            validateIsTypeOf(Short.TYPE, expectedType);
+          } catch (RuntimeException exception) {
+            throw ValidationException.from(exception)
+                .append(exceptionMessage("SHORT", s, expectedType));
+          }
+          return null;
+        }
+
+        private <T> String exceptionMessage(String valueType, T value, TypeMirror expectedType) {
+          return String.format(
+              "annotation value (%s): value '%s' with expected type %s",
+              valueType, value, expectedType);
+        }
+      };
+
+  private void validateIsTypeOf(Class<?> clazz, TypeMirror expectedType) {
+    if (!MoreTypes.isTypeOf(clazz, expectedType)) {
+      throw new ValidationException.UnknownErrorType();
+    }
+  }
+
+  private void validateIsEquivalentType(DeclaredType type, TypeMirror expectedType) {
+    if (!MoreTypes.equivalence().equivalent(type, expectedType)) {
+      throw new ValidationException.KnownErrorType(type);
+    }
+  }
+
+  /**
+   * A runtime exception that can be used during superficial validation to collect information about
+   * unexpected exceptions during validation.
+   */
+  public abstract static class ValidationException extends RuntimeException {
+    /** A {@link ValidationException} that originated from an unexpected exception. */
+    public static final class UnexpectedException extends ValidationException {
+      private UnexpectedException(Throwable throwable) {
+        super(throwable);
+      }
+    }
+
+    /** A {@link ValidationException} that originated from a known error type. */
+    public static final class KnownErrorType extends ValidationException {
+      private final String errorTypeName;
+
+      private KnownErrorType(DeclaredType errorType) {
+        Element errorElement = errorType.asElement();
+        this.errorTypeName =
+            isType(errorElement)
+                ? asType(errorElement).getQualifiedName().toString()
+                // Maybe this case should be handled by UnknownErrorType?
+                : errorElement.getSimpleName().toString();
+      }
+
+      private KnownErrorType(String errorTypeName) {
+        this.errorTypeName = errorTypeName;
+      }
+
+      public String getErrorTypeName() {
+        return errorTypeName;
+      }
+    }
+
+    /** A {@link ValidationException} that originated from an unknown error type. */
+    public static final class UnknownErrorType extends ValidationException {}
+
+    private static ValidationException from(Throwable throwable) {
+      // We only ever create one instance of the ValidationException.
+      return throwable instanceof ValidationException
+          ? ((ValidationException) throwable)
+          : new UnexpectedException(throwable);
+    }
+
+    private Optional<Element> lastReportedElement = Optional.empty();
+    private final List<String> messages = new ArrayList<>();
+
+    private ValidationException() {
+      super("");
+    }
+
+    private ValidationException(Throwable throwable) {
+      super("", throwable);
+    }
+
+    /**
+     * Appends a message for the given element and returns this instance of {@link
+     * ValidationException}
+     */
+    private ValidationException append(Element element) {
+      lastReportedElement = Optional.of(element);
+      return append(getMessageForElement(element));
+    }
+
+    /**
+     * Appends a message for the given type mirror and returns this instance of {@link
+     * ValidationException}
+     */
+    private ValidationException append(String desc, TypeMirror type) {
+      return append(String.format("type (%s %s): %s", type.getKind().name(), desc, type));
+    }
+
+    /**
+     * Appends a message for the given annotation mirror and returns this instance of {@link
+     * ValidationException}
+     */
+    private ValidationException append(AnnotationMirror annotationMirror) {
+      // Note: Calling #toString() directly on the annotation throws NPE (b/216180336).
+      return append(String.format("annotation: %s", AnnotationMirrors.toString(annotationMirror)));
+    }
+
+    /** Appends the given message and returns this instance of {@link ValidationException} */
+    private ValidationException append(String message) {
+      messages.add(message);
+      return this;
+    }
+
+    @Override
+    public String getMessage() {
+      return String.format("\n  Validation trace:\n    => %s", getTrace());
+    }
+
+    public String getTrace() {
+      return String.join("\n    => ", getMessageInternal().reverse());
+    }
+
+    private ImmutableList<String> getMessageInternal() {
+      if (!lastReportedElement.isPresent()) {
+        return ImmutableList.copyOf(messages);
+      }
+      // Append any enclosing element information if needed.
+      List<String> newMessages = new ArrayList<>(messages);
+      Element element = lastReportedElement.get();
+      while (shouldAppendEnclosingElement(element)) {
+        element = element.getEnclosingElement();
+        newMessages.add(getMessageForElement(element));
+      }
+      return ImmutableList.copyOf(newMessages);
+    }
+
+    private static boolean shouldAppendEnclosingElement(Element element) {
+      return element.getEnclosingElement() != null
+          // We don't report enclosing elements for types because the type name should contain any
+          // enclosing type and package information we need.
+          && !isType(element)
+          && (isExecutable(element.getEnclosingElement()) || isType(element.getEnclosingElement()));
+    }
+
+    private static boolean isExecutable(Element element) {
+      return element.getKind() == ElementKind.METHOD
+          || element.getKind() == ElementKind.CONSTRUCTOR;
+    }
+
+    private String getMessageForElement(Element element) {
+      return String.format("element (%s): %s", element.getKind().name(), element);
+    }
+  }
+}
diff --git a/java/dagger/internal/codegen/base/ElementFormatter.java b/java/dagger/internal/codegen/base/ElementFormatter.java
index f85fbfd..65f9385 100644
--- a/java/dagger/internal/codegen/base/ElementFormatter.java
+++ b/java/dagger/internal/codegen/base/ElementFormatter.java
@@ -16,9 +16,11 @@
 
 package dagger.internal.codegen.base;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 import static com.google.auto.common.MoreElements.asExecutable;
 import static java.util.stream.Collectors.joining;
 
+import androidx.room.compiler.processing.XElement;
 import javax.inject.Inject;
 import javax.lang.model.element.Element;
 import javax.lang.model.element.ElementVisitor;
@@ -50,6 +52,17 @@
    *
    * <p>Parameters are given with their enclosing executable, with other parameters elided.
    */
+  public static String elementToString(XElement element) {
+    return elementToString(toJavac(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);
   }
diff --git a/java/dagger/internal/codegen/base/FrameworkTypes.java b/java/dagger/internal/codegen/base/FrameworkTypes.java
index 4cd54a3..2336256 100644
--- a/java/dagger/internal/codegen/base/FrameworkTypes.java
+++ b/java/dagger/internal/codegen/base/FrameworkTypes.java
@@ -16,16 +16,16 @@
 
 package dagger.internal.codegen.base;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 import static com.google.auto.common.MoreTypes.isType;
+import static dagger.internal.codegen.langmodel.DaggerTypes.isTypeOf;
+import static dagger.internal.codegen.xprocessing.XTypes.isTypeOf;
 
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XType;
 import com.google.common.collect.ImmutableSet;
-import dagger.Lazy;
-import dagger.MembersInjector;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.javapoet.TypeNames;
 import java.util.Set;
-import javax.inject.Provider;
 import javax.lang.model.type.TypeMirror;
 
 /**
@@ -33,17 +33,22 @@
  * 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);
+  private static final ImmutableSet<ClassName> PROVISION_TYPES =
+      ImmutableSet.of(TypeNames.PROVIDER, TypeNames.LAZY, TypeNames.MEMBERS_INJECTOR);
 
   // 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);
+  private static final ImmutableSet<ClassName> PRODUCTION_TYPES =
+      ImmutableSet.of(TypeNames.PRODUCED, TypeNames.PRODUCER);
 
   /** Returns true if the type represents a producer-related framework type. */
-  public static boolean isProducerType(TypeMirror type) {
-    return isType(type) && typeIsOneOf(PRODUCTION_TYPES, type);
+  public static boolean isProducerType(XType type) {
+    return PRODUCTION_TYPES.stream().anyMatch(className -> isTypeOf(type, className));
+  }
+
+  /** Returns true if the type represents a framework type. */
+  public static boolean isFrameworkType(XType type) {
+    return isFrameworkType(toJavac(type));
   }
 
   /** Returns true if the type represents a framework type. */
@@ -53,13 +58,8 @@
             || 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 static boolean typeIsOneOf(Set<ClassName> classNames, TypeMirror type) {
+    return classNames.stream().anyMatch(className -> isTypeOf(className, type));
   }
 
   private FrameworkTypes() {}
diff --git a/java/dagger/internal/codegen/base/Keys.java b/java/dagger/internal/codegen/base/Keys.java
index 4d13926..dc061fe 100644
--- a/java/dagger/internal/codegen/base/Keys.java
+++ b/java/dagger/internal/codegen/base/Keys.java
@@ -16,38 +16,33 @@
 
 package dagger.internal.codegen.base;
 
-import static com.google.auto.common.MoreTypes.asTypeElement;
 import static dagger.internal.codegen.base.ComponentAnnotation.allComponentAndCreatorAnnotations;
-import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
+import static dagger.internal.codegen.xprocessing.XElements.hasAnyAnnotation;
+import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
+import static dagger.internal.codegen.xprocessing.XTypes.isRawParameterizedType;
 
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.Key;
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
+import dagger.spi.model.DaggerAnnotation;
+import dagger.spi.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);
+        && isDeclared(key.type().xprocessing());
   }
 
   /**
    * 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);
+  public static boolean isValidImplicitProvisionKey(Key key) {
+    return isValidImplicitProvisionKey(
+        key.qualifier().map(DaggerAnnotation::xprocessing), key.type().xprocessing());
   }
 
   /**
@@ -55,42 +50,36 @@
    * 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) {
+  public static boolean isValidImplicitProvisionKey(Optional<XAnnotation> qualifier, XType type) {
     // 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;
-            }
+    // A provision type must be a declared type
+    if (!isDeclared(type)) {
+      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;
-              }
-            }
+    // Non-classes or abstract classes aren't allowed.
+    XTypeElement typeElement = type.getTypeElement();
+    if (!typeElement.isClass() || typeElement.isAbstract()) {
+      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);
+    // 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 (XType arg : type.getTypeArguments()) {
+      if (!isDeclared(arg)) {
+        return false;
+      }
+    }
+
+    // Also validate that if the type represents a parameterized type the user didn't refer to its
+    // raw type, which we don't allow. (This is a judgement call -- we *could* allow it and
+    // instantiate the type bounds... but we don't.)
+    return !isRawParameterizedType(type);
   }
 
   /**
@@ -99,7 +88,8 @@
    */
   public static boolean isComponentOrCreator(Key key) {
     return !key.qualifier().isPresent()
-        && key.type().getKind() == TypeKind.DECLARED
-        && isAnyAnnotationPresent(asTypeElement(key.type()), allComponentAndCreatorAnnotations());
+        && isDeclared(key.type().xprocessing())
+        && hasAnyAnnotation(
+            key.type().xprocessing().getTypeElement(), allComponentAndCreatorAnnotations());
   }
 }
diff --git a/java/dagger/internal/codegen/base/MapType.java b/java/dagger/internal/codegen/base/MapType.java
index 4e2307a..c48b170 100644
--- a/java/dagger/internal/codegen/base/MapType.java
+++ b/java/dagger/internal/codegen/base/MapType.java
@@ -18,38 +18,33 @@
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
+import static dagger.internal.codegen.langmodel.DaggerTypes.unwrapType;
+import static dagger.internal.codegen.xprocessing.XTypes.isTypeOf;
 
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XType;
 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 com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.spi.model.Key;
 import javax.lang.model.type.TypeMirror;
 
-/**
- * Information about a {@link Map} {@link TypeMirror}.
- */
+/** Information about a {@link java.util.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();
+  private XType type;
 
-  /**
-   * The map type itself.
-   */
-  private DeclaredType declaredMapType() {
-    return wrappedDeclaredMapType().get();
+  /** The map type itself. */
+  abstract TypeName typeName();
+
+  /** The map type itself. */
+  private XType type() {
+    return type;
   }
 
-  /**
-   * {@code true} if the map type is the raw {@link Map} type.
-   */
+  /** {@code true} if the map type is the raw {@link java.util.Map} type. */
   public boolean isRawType() {
-    return declaredMapType().getTypeArguments().isEmpty();
+    return type().getTypeArguments().isEmpty();
   }
 
   /**
@@ -57,9 +52,9 @@
    *
    * @throws IllegalStateException if {@link #isRawType()} is true.
    */
-  public TypeMirror keyType() {
+  public XType keyType() {
     checkState(!isRawType());
-    return declaredMapType().getTypeArguments().get(0);
+    return type().getTypeArguments().get(0);
   }
 
   /**
@@ -67,24 +62,17 @@
    *
    * @throws IllegalStateException if {@link #isRawType()} is true.
    */
-  public TypeMirror valueType() {
+  public XType valueType() {
     checkState(!isRawType());
-    return declaredMapType().getTypeArguments().get(1);
+    return type().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 raw type of {@link #valueType()} is {@code className}. */
+  public boolean valuesAreTypeOf(ClassName className) {
+    return !isRawType() && isTypeOf(valueType(), className);
   }
 
-  /**
-   * Returns {@code true} if the {@linkplain #valueType() value type} of the {@link Map} is a
-   * {@linkplain FrameworkTypes#isFrameworkType(TypeMirror) framework type}.
-   */
+  /** Returns {@code true} if the raw type of {@link #valueType()} is a framework type. */
   public boolean valuesAreFrameworkType() {
     return FrameworkTypes.isFrameworkType(valueType());
   }
@@ -96,9 +84,8 @@
    * @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());
+  public XType unwrappedFrameworkValueType() {
+    checkState(valuesAreFrameworkType(), "called unwrappedFrameworkValueType() on %s", type());
     return uncheckedUnwrappedValueType();
   }
 
@@ -107,52 +94,45 @@
    *
    * @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);
+  // TODO(b/202033221): Consider using stricter input type, e.g. FrameworkType.
+  public XType unwrappedValueType(ClassName wrappingClass) {
     checkState(valuesAreTypeOf(wrappingClass), "expected values to be %s: %s", wrappingClass, this);
     return uncheckedUnwrappedValueType();
   }
 
-  private TypeMirror uncheckedUnwrappedValueType() {
-    return MoreTypes.asDeclared(valueType()).getTypeArguments().get(0);
+  private XType uncheckedUnwrappedValueType() {
+    return unwrapType(valueType());
   }
 
-  /**
-   * {@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 type} is a {@link java.util.Map} type. */
+  public static boolean isMap(XType type) {
+    return isTypeOf(type, TypeNames.MAP);
   }
 
-  /**
-   * {@code true} if {@code key.type()} is a {@link Map} type.
-   */
+  /** {@code true} if {@code key.type()} is a {@link java.util.Map} type. */
   public static boolean isMap(Key key) {
-    return isMap(key.type());
+    return isMap(key.type().xprocessing());
   }
 
   /**
    * Returns a {@link MapType} for {@code type}.
    *
-   * @throws IllegalArgumentException if {@code type} is not a {@link Map} type
+   * @throws IllegalArgumentException if {@code type} is not a {@link java.util.Map} type
    */
-  public static MapType from(TypeMirror type) {
+  public static MapType from(XType type) {
     checkArgument(isMap(type), "%s is not a Map", type);
-    return new AutoValue_MapType(MoreTypes.equivalence().wrap(MoreTypes.asDeclared(type)));
+    MapType mapType = new AutoValue_MapType(type.getTypeName());
+    mapType.type = type;
+    return mapType;
   }
 
   /**
    * Returns a {@link MapType} for {@code key}'s {@link Key#type() type}.
    *
-   * @throws IllegalArgumentException if {@code key.type()} is not a {@link Map} type
+   * @throws IllegalArgumentException if {@code key.type()} is not a {@link java.util.Map} type
    */
   public static MapType from(Key key) {
-    return from(key.type());
+    return from(key.type().xprocessing());
   }
 }
diff --git a/java/dagger/internal/codegen/base/ModuleAnnotation.java b/java/dagger/internal/codegen/base/ModuleAnnotation.java
index 1688736..faca1d3 100644
--- a/java/dagger/internal/codegen/base/ModuleAnnotation.java
+++ b/java/dagger/internal/codegen/base/ModuleAnnotation.java
@@ -16,40 +16,42 @@
 
 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 static dagger.internal.codegen.xprocessing.XAnnotations.getClassName;
+import static dagger.internal.codegen.xprocessing.XElements.getAnyAnnotation;
 
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 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 com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.javapoet.TypeNames;
 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);
+  private static final ImmutableSet<ClassName> MODULE_ANNOTATIONS =
+      ImmutableSet.of(TypeNames.MODULE, TypeNames.PRODUCER_MODULE);
+
+  private XAnnotation annotation;
 
   /** The annotation itself. */
-  // This does not use AnnotationMirrors.equivalence() because we want the actual annotation
-  // instance.
-  public abstract AnnotationMirror annotation();
+  public final XAnnotation annotation() {
+    return annotation;
+  }
+
+  /** Returns the {@link ClassName} name of the annotation. */
+  public abstract ClassName className();
 
   /** The simple name of the annotation. */
-  public String annotationName() {
-    return annotation().getAnnotationType().asElement().getSimpleName().toString();
+  public String simpleName() {
+    return className().simpleName();
   }
 
   /**
@@ -58,70 +60,55 @@
    * @throws IllegalArgumentException if any of the values are error types
    */
   @Memoized
-  public ImmutableList<TypeElement> includes() {
-    return includesAsAnnotationValues().stream()
-        .map(MoreAnnotationValues::asType)
-        .map(MoreTypes::asTypeElement)
+  public ImmutableList<XTypeElement> includes() {
+    return annotation.getAsTypeList("includes").stream()
+        .map(XType::getTypeElement)
         .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)
+  public ImmutableList<XTypeElement> subcomponents() {
+    return annotation.getAsTypeList("subcomponents").stream()
+        .map(XType::getTypeElement)
         .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);
+  public static boolean isModuleAnnotation(XAnnotation annotation) {
+    return MODULE_ANNOTATIONS.contains(getClassName(annotation));
   }
 
   /** The module annotation types. */
-  public static ImmutableSet<Class<? extends Annotation>> moduleAnnotations() {
+  public static ImmutableSet<ClassName> 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) {
+  private static ModuleAnnotation create(XAnnotation annotation) {
     checkArgument(
         isModuleAnnotation(annotation),
         "%s is not a Module or ProducerModule annotation",
         annotation);
-    return new AutoValue_ModuleAnnotation(annotation);
+    ModuleAnnotation moduleAnnotation = new AutoValue_ModuleAnnotation(getClassName(annotation));
+    moduleAnnotation.annotation = annotation;
+    return moduleAnnotation;
   }
 
   /**
    * 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);
+  public static Optional<ModuleAnnotation> moduleAnnotation(
+      XElement element, DaggerSuperficialValidation superficialValidation) {
+    return getAnyAnnotation(element, TypeNames.MODULE, TypeNames.PRODUCER_MODULE)
+        .map(
+            annotation -> {
+              superficialValidation.validateAnnotationOf(element, annotation);
+              return create(annotation);
+            });
   }
 }
diff --git a/java/dagger/internal/codegen/binding/ModuleKind.java b/java/dagger/internal/codegen/base/ModuleKind.java
similarity index 64%
rename from java/dagger/internal/codegen/binding/ModuleKind.java
rename to java/dagger/internal/codegen/base/ModuleKind.java
index 6b52f04..7d08aee 100644
--- a/java/dagger/internal/codegen/binding/ModuleKind.java
+++ b/java/dagger/internal/codegen/base/ModuleKind.java
@@ -14,36 +14,31 @@
  * limitations under the License.
  */
 
-package dagger.internal.codegen.binding;
+package dagger.internal.codegen.base;
 
-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 androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XTypeElement;
 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 com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.javapoet.TypeNames;
 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),
+  MODULE(TypeNames.MODULE),
 
   /** {@code @ProducerModule} */
-  PRODUCER_MODULE(ProducerModule.class);
+  PRODUCER_MODULE(TypeNames.PRODUCER_MODULE);
 
   /** Returns the annotations for modules of the given kinds. */
-  public static ImmutableSet<Class<? extends Annotation>> annotationsFor(Set<ModuleKind> kinds) {
+  private static ImmutableSet<ClassName> annotationsFor(Set<ModuleKind> kinds) {
     return kinds.stream().map(ModuleKind::annotation).collect(toImmutableSet());
   }
 
@@ -54,10 +49,10 @@
    * @throws IllegalArgumentException if the element is annotated with more than one of the module
    *     annotations
    */
-  public static Optional<ModuleKind> forAnnotatedElement(TypeElement element) {
+  public static Optional<ModuleKind> forAnnotatedElement(XTypeElement element) {
     Set<ModuleKind> kinds = EnumSet.noneOf(ModuleKind.class);
     for (ModuleKind kind : values()) {
-      if (MoreElements.isAnnotationPresent(element, kind.annotation())) {
+      if (element.hasAnnotation(kind.annotation())) {
         kinds.add(kind);
       }
     }
@@ -69,19 +64,19 @@
     return kinds.stream().findAny();
   }
 
-  public static void checkIsModule(TypeElement moduleElement, KotlinMetadataUtil metadataUtil) {
+  public static void checkIsModule(XTypeElement moduleElement) {
     // 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());
+    if (moduleElement.isCompanionObject()) {
+      checkArgument(forAnnotatedElement(moduleElement.getEnclosingTypeElement()).isPresent());
     } else {
       checkArgument(forAnnotatedElement(moduleElement).isPresent());
     }
   }
 
-  private final Class<? extends Annotation> moduleAnnotation;
+  private final ClassName moduleAnnotation;
 
-  ModuleKind(Class<? extends Annotation> moduleAnnotation) {
+  ModuleKind(ClassName moduleAnnotation) {
     this.moduleAnnotation = moduleAnnotation;
   }
 
@@ -90,15 +85,17 @@
    *
    * @throws IllegalArgumentException if the annotation is not present on the type
    */
-  public AnnotationMirror getModuleAnnotation(TypeElement element) {
-    Optional<AnnotationMirror> result = getAnnotationMirror(element, moduleAnnotation);
+  public XAnnotation getModuleAnnotation(XTypeElement element) {
     checkArgument(
-        result.isPresent(), "annotation %s is not present on type %s", moduleAnnotation, element);
-    return result.get();
+        element.hasAnnotation(moduleAnnotation),
+        "annotation %s is not present on type %s",
+        moduleAnnotation,
+        element);
+    return element.getAnnotation(moduleAnnotation);
   }
 
   /** Returns the annotation that marks a module of this kind. */
-  public Class<? extends Annotation> annotation() {
+  public ClassName annotation() {
     return moduleAnnotation;
   }
 
diff --git a/java/dagger/internal/codegen/base/MoreAnnotationMirrors.java b/java/dagger/internal/codegen/base/MoreAnnotationMirrors.java
index 234ecc1..708b9dc 100644
--- a/java/dagger/internal/codegen/base/MoreAnnotationMirrors.java
+++ b/java/dagger/internal/codegen/base/MoreAnnotationMirrors.java
@@ -16,24 +16,16 @@
 
 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() {}
 
   /**
@@ -53,21 +45,4 @@
       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
index 1ee50da..26175b8 100644
--- a/java/dagger/internal/codegen/base/MoreAnnotationValues.java
+++ b/java/dagger/internal/codegen/base/MoreAnnotationValues.java
@@ -25,7 +25,6 @@
 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. */
@@ -54,28 +53,6 @@
             }
           };
 
-  /**
-   * 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();
diff --git a/java/dagger/internal/codegen/base/MultibindingAnnotations.java b/java/dagger/internal/codegen/base/MultibindingAnnotations.java
deleted file mode 100644
index 424f92a..0000000
--- a/java/dagger/internal/codegen/base/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.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
index 1505682..d4f3c05 100644
--- a/java/dagger/internal/codegen/base/OptionalType.java
+++ b/java/dagger/internal/codegen/base/OptionalType.java
@@ -17,22 +17,21 @@
 package dagger.internal.codegen.base;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
+import static dagger.internal.codegen.extension.DaggerStreams.valuesOf;
+import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
 
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.auto.value.AutoValue;
-import com.google.common.base.Equivalence;
+import com.google.common.collect.ImmutableMap;
 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 dagger.internal.codegen.javapoet.TypeNames;
+import dagger.spi.model.Key;
 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}.
@@ -41,44 +40,58 @@
  */
 @AutoValue
 public abstract class OptionalType {
+  private XType type;
 
   /** A variant of {@code Optional}. */
   public enum OptionalKind {
     /** {@link com.google.common.base.Optional}. */
-    GUAVA_OPTIONAL(com.google.common.base.Optional.class, "absent"),
+    GUAVA_OPTIONAL(TypeNames.GUAVA_OPTIONAL, "absent"),
 
     /** {@link java.util.Optional}. */
-    JDK_OPTIONAL(java.util.Optional.class, "empty"),
-    ;
+    JDK_OPTIONAL(TypeNames.JDK_OPTIONAL, "empty");
 
-    private final Class<?> clazz;
-    private final String absentFactoryMethodName;
+    // Keep a cache from class name to OptionalKind for quick look-up.
+    private static final ImmutableMap<ClassName, OptionalKind> OPTIONAL_KIND_BY_CLASS_NAME =
+        valuesOf(OptionalKind.class)
+            .collect(toImmutableMap(value -> value.className, value -> value));
 
-    OptionalKind(Class<?> clazz, String absentFactoryMethodName) {
-      this.clazz = clazz;
-      this.absentFactoryMethodName = absentFactoryMethodName;
+    private final ClassName className;
+    private final String absentMethodName;
+
+    OptionalKind(ClassName className, String absentMethodName) {
+      this.className = className;
+      this.absentMethodName = absentMethodName;
+    }
+
+    private static boolean isOptionalKind(XTypeElement type) {
+      return OPTIONAL_KIND_BY_CLASS_NAME.containsKey(type.getClassName());
+    }
+
+    private static OptionalKind of(XTypeElement type) {
+      return OPTIONAL_KIND_BY_CLASS_NAME.get(type.getClassName());
     }
 
     /** Returns {@code valueType} wrapped in the correct class. */
     public ParameterizedTypeName of(TypeName valueType) {
-      return ParameterizedTypeName.get(ClassName.get(clazz), valueType);
+      return ParameterizedTypeName.get(className, valueType);
     }
 
     /** Returns an expression for the absent/empty value. */
     public CodeBlock absentValueExpression() {
-      return CodeBlock.of("$T.$L()", clazz, absentFactoryMethodName);
+      return CodeBlock.of("$T.$L()", className, absentMethodName);
     }
 
     /**
      * 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);
+      return CodeBlock.of(
+          "$T.<$T>$L()", className, optionalType.valueType().getTypeName(), absentMethodName);
     }
 
     /** Returns an expression for the present {@code value}. */
     public CodeBlock presentExpression(CodeBlock value) {
-      return CodeBlock.of("$T.of($L)", clazz, value);
+      return CodeBlock.of("$T.of($L)", className, value);
     }
 
     /**
@@ -86,56 +99,36 @@
      * matter what type the value is.
      */
     public CodeBlock presentObjectExpression(CodeBlock value) {
-      return CodeBlock.of("$T.<$T>of($L)", clazz, Object.class, value);
+      return CodeBlock.of("$T.<$T>of($L)", className, TypeName.OBJECT, 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. */
+  abstract TypeName typeName();
 
   /** The optional type itself. */
-  @SuppressWarnings("deprecation")
-  private DeclaredType declaredOptionalType() {
-    return wrappedDeclaredOptionalType().get();
+  private XType type() {
+    return type;
   }
 
   /** Which {@code Optional} type is used. */
   public OptionalKind kind() {
-    return declaredOptionalType().accept(OPTIONAL_KIND, null).get();
+    return OptionalKind.of(type().getTypeElement());
   }
 
   /** The value type. */
-  public TypeMirror valueType() {
-    return declaredOptionalType().getTypeArguments().get(0);
+  public XType valueType() {
+    return type().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();
+  private static boolean isOptional(XType type) {
+    return isDeclared(type) && OptionalKind.isOptionalKind(type.getTypeElement());
   }
 
   /** Returns {@code true} if {@code key.type()} is an {@code Optional} type. */
   public static boolean isOptional(Key key) {
-    return isOptional(key.type());
+    return isOptional(key.type().xprocessing());
   }
 
   /**
@@ -143,9 +136,11 @@
    *
    * @throws IllegalArgumentException if {@code type} is not an {@code Optional} type
    */
-  public static OptionalType from(TypeMirror type) {
+  public static OptionalType from(XType type) {
     checkArgument(isOptional(type), "%s must be an Optional", type);
-    return new AutoValue_OptionalType(MoreTypes.equivalence().wrap(MoreTypes.asDeclared(type)));
+    OptionalType optionalType = new AutoValue_OptionalType(type.getTypeName());
+    optionalType.type = type;
+    return optionalType;
   }
 
   /**
@@ -154,6 +149,6 @@
    * @throws IllegalArgumentException if {@code key.type()} is not an {@code Optional} type
    */
   public static OptionalType from(Key key) {
-    return from(key.type());
+    return from(key.type().xprocessing());
   }
 }
diff --git a/java/dagger/internal/codegen/base/ProducerAnnotations.java b/java/dagger/internal/codegen/base/ProducerAnnotations.java
new file mode 100644
index 0000000..ad3b9b3
--- /dev/null
+++ b/java/dagger/internal/codegen/base/ProducerAnnotations.java
@@ -0,0 +1,67 @@
+/*
+ * 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.base;
+
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XProcessingEnv;
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.javapoet.TypeNames;
+
+/**
+ * Helper methods for getting types of producer annotations.
+ *
+ * <p>Note:These should only be used for cases where the annotations don't exist in the user's code.
+ * For example, all producer components implicitly have {@code @ProductionScope}, but it doesn't
+ * appear in the user's code. We need to get a reference to the scope annotation though to reuse
+ * classes from regular Dagger like the {@code ComponentDescriptor}.
+ */
+public final class ProducerAnnotations {
+  private static final ClassName ANNOTATION_USAGES =
+      ClassName.get("dagger.producers.internal", "AnnotationUsages");
+  private static final ClassName PRODUCTION_USAGE =
+      ANNOTATION_USAGES.nestedClass("ProductionUsage");
+  private static final ClassName PRODUCTION_IMPLEMENTATION_USAGE =
+      ANNOTATION_USAGES.nestedClass("ProductionImplementationUsage");
+  private static final ClassName PRODUCTION_SCOPE_USAGE =
+      ANNOTATION_USAGES.nestedClass("ProductionScopeUsage");
+
+  /** Returns a {@link dagger.producers.internal.ProductionImplementation} qualifier. */
+  // TODO(bcorso): We could probably remove the need for this if we define a new type,
+  //  "ProductionImplementationExecutor", rather than binding "@ProductionImplementation Executor".
+  public static XAnnotation productionImplementationQualifier(XProcessingEnv processingEnv) {
+    return processingEnv.findTypeElement(PRODUCTION_IMPLEMENTATION_USAGE)
+        .getAnnotation(TypeNames.PRODUCTION_IMPLEMENTATION);
+  }
+
+  /** Returns a {@link dagger.producers.Production} qualifier. */
+  // TODO(bcorso): We could probably remove the need for this. It's currently only used in
+  //  "DependsOnProductionExecutorValidator", but we could implement that without this.
+  public static XAnnotation productionQualifier(XProcessingEnv processingEnv) {
+    return processingEnv.findTypeElement(PRODUCTION_USAGE).getAnnotation(TypeNames.PRODUCTION);
+  }
+
+  /** Returns a {@link dagger.producers.ProductionScope} scope. */
+  // TODO(bcorso): We could probably remove the need for this, but it would require changing
+  //  Dagger SPI's public API. In particular, Scope should probably only require a ClassName rather
+  //  than an actual annotation type.
+  public static XAnnotation productionScope(XProcessingEnv processingEnv) {
+    return processingEnv.findTypeElement(PRODUCTION_SCOPE_USAGE)
+        .getAnnotation(TypeNames.PRODUCTION_SCOPE);
+  }
+
+  private ProducerAnnotations() {}
+}
diff --git a/java/dagger/internal/codegen/base/RequestKinds.java b/java/dagger/internal/codegen/base/RequestKinds.java
index 95d5ef4..b4a1764 100644
--- a/java/dagger/internal/codegen/base/RequestKinds.java
+++ b/java/dagger/internal/codegen/base/RequestKinds.java
@@ -16,9 +16,9 @@
 
 package dagger.internal.codegen.base;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 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;
@@ -26,22 +26,22 @@
 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 dagger.internal.codegen.langmodel.DaggerTypes.isTypeOf;
+import static dagger.internal.codegen.langmodel.DaggerTypes.unwrapType;
+import static dagger.spi.model.RequestKind.LAZY;
+import static dagger.spi.model.RequestKind.PRODUCED;
+import static dagger.spi.model.RequestKind.PRODUCER;
+import static dagger.spi.model.RequestKind.PROVIDER;
+import static dagger.spi.model.RequestKind.PROVIDER_OF_LAZY;
 import static javax.lang.model.type.TypeKind.DECLARED;
 
+import androidx.room.compiler.processing.XType;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.util.concurrent.ListenableFuture;
+import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.TypeName;
-import dagger.Lazy;
+import dagger.internal.codegen.javapoet.TypeNames;
 import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.RequestKind;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
-import javax.inject.Provider;
+import dagger.spi.model.RequestKind;
 import javax.lang.model.type.TypeMirror;
 
 /** Utility methods for {@link RequestKind}s. */
@@ -55,13 +55,13 @@
         return type;
 
       case PROVIDER_OF_LAZY:
-        return types.wrapType(requestType(LAZY, type, types), Provider.class);
+        return types.wrapType(requestType(LAZY, type, types), TypeNames.PROVIDER);
 
       case FUTURE:
-        return types.wrapType(type, ListenableFuture.class);
+        return types.wrapType(type, TypeNames.LISTENABLE_FUTURE);
 
       default:
-        return types.wrapType(type, frameworkClass(requestKind));
+        return types.wrapType(type, frameworkClassName(requestKind));
     }
   }
 
@@ -94,12 +94,17 @@
     }
   }
 
-  private static final ImmutableMap<RequestKind, Class<?>> FRAMEWORK_CLASSES =
+  private static final ImmutableMap<RequestKind, ClassName> FRAMEWORK_CLASSES =
       ImmutableMap.of(
-          PROVIDER, Provider.class,
-          LAZY, Lazy.class,
-          PRODUCER, Producer.class,
-          PRODUCED, Produced.class);
+          PROVIDER, TypeNames.PROVIDER,
+          LAZY, TypeNames.LAZY,
+          PRODUCER, TypeNames.PRODUCER,
+          PRODUCED, TypeNames.PRODUCED);
+
+  /** Returns the {@link RequestKind} that matches the wrapping types (if any) of {@code type}. */
+  public static RequestKind getRequestKind(XType type) {
+    return getRequestKind(toJavac(type));
+  }
 
   /** Returns the {@link RequestKind} that matches the wrapping types (if any) of {@code type}. */
   public static RequestKind getRequestKind(TypeMirror type) {
@@ -112,8 +117,8 @@
       return RequestKind.INSTANCE;
     }
     for (RequestKind kind : FRAMEWORK_CLASSES.keySet()) {
-      if (isTypeOf(frameworkClass(kind), type)) {
-        if (kind.equals(PROVIDER) && getRequestKind(DaggerTypes.unwrapType(type)).equals(LAZY)) {
+      if (isTypeOf(frameworkClassName(kind), type)) {
+        if (kind.equals(PROVIDER) && getRequestKind(unwrapType(type)).equals(LAZY)) {
           return PROVIDER_OF_LAZY;
         }
         return kind;
@@ -131,6 +136,30 @@
    * @throws IllegalArgumentException if {@code type} is not wrapped with {@code requestKind}'s
    *     framework class(es).
    */
+  public static XType extractKeyType(XType type) {
+    return extractKeyType(getRequestKind(type), type);
+  }
+
+  private static XType extractKeyType(RequestKind requestKind, XType type) {
+    switch (requestKind) {
+      case INSTANCE:
+        return type;
+      case PROVIDER_OF_LAZY:
+        return extractKeyType(LAZY, extractKeyType(PROVIDER, type));
+      default:
+        return unwrapType(type);
+    }
+  }
+
+  /**
+   * 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);
   }
@@ -143,13 +172,13 @@
         return extractKeyType(LAZY, extractKeyType(PROVIDER, type));
       default:
         checkArgument(isType(type));
-        return DaggerTypes.unwrapType(type);
+        return 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}.
+   * another type but share the same {@link dagger.spi.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.
@@ -159,10 +188,10 @@
    * 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;
+  public static ClassName frameworkClassName(RequestKind requestKind) {
+    checkArgument(
+        FRAMEWORK_CLASSES.containsKey(requestKind), "no framework class for %s", requestKind);
+    return FRAMEWORK_CLASSES.get(requestKind);
   }
 
   /**
diff --git a/java/dagger/internal/codegen/base/Scopes.java b/java/dagger/internal/codegen/base/Scopes.java
index f2c39ce..b4cc323 100644
--- a/java/dagger/internal/codegen/base/Scopes.java
+++ b/java/dagger/internal/codegen/base/Scopes.java
@@ -16,48 +16,17 @@
 
 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;
+import androidx.room.compiler.processing.XProcessingEnv;
+import dagger.spi.model.DaggerAnnotation;
+import dagger.spi.model.Scope;
 
 /** 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 a representation for {@link dagger.producers.ProductionScope} scope. */
+  public static Scope productionScope(XProcessingEnv processingEnv) {
+    return Scope.scope(DaggerAnnotation.from(ProducerAnnotations.productionScope(processingEnv)));
   }
 
   /**
@@ -69,12 +38,4 @@
   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
index a75a6d3..54d4228 100644
--- a/java/dagger/internal/codegen/base/SetType.java
+++ b/java/dagger/internal/codegen/base/SetType.java
@@ -16,105 +16,96 @@
 
 package dagger.internal.codegen.base;
 
+import static com.google.auto.common.MoreTypes.isType;
 import static com.google.common.base.Preconditions.checkArgument;
+import static dagger.internal.codegen.langmodel.DaggerTypes.isTypeOf;
+import static dagger.internal.codegen.langmodel.DaggerTypes.unwrapType;
+import static dagger.internal.codegen.xprocessing.XTypes.isTypeOf;
 
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XType;
 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 com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.spi.model.Key;
 import javax.lang.model.type.TypeMirror;
 
-/**
- * Information about a {@link Set} {@link TypeMirror}.
- */
+/** Information about a {@link java.util.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();
+  private XType type;
 
-  /**
-   * The set type itself.
-   */
-  private DeclaredType declaredSetType() {
-    return wrappedDeclaredSetType().get();
+  /** The set type itself. */
+  abstract TypeName typeName();
+
+  /** The set type itself. */
+  private XType type() {
+    return type;
   }
 
-  /**
-   * {@code true} if the set type is the raw {@link Set} type.
-   */
+  /** {@code true} if the set type is the raw {@link java.util.Set} type. */
   public boolean isRawType() {
-    return declaredSetType().getTypeArguments().isEmpty();
+    return type().getTypeArguments().isEmpty();
   }
 
-  /**
-   * The element type.
-   */
-  public TypeMirror elementType() {
-    return declaredSetType().getTypeArguments().get(0);
+  /** Returns the element type. */
+  public XType elementType() {
+    return unwrapType(type());
   }
 
-  /**
-   * {@code true} if {@link #elementType()} is a {@code clazz}.
-   */
-  public boolean elementsAreTypeOf(Class<?> clazz) {
-    return MoreTypes.isType(elementType()) && MoreTypes.isTypeOf(clazz, elementType());
+  /** Returns {@code true} if {@link #elementType()} is of type {@code className}. */
+  public boolean elementsAreTypeOf(ClassName className) {
+    return !isRawType() && isTypeOf(elementType(), className);
   }
 
   /**
    * {@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);
+  // TODO(b/202033221): Consider using stricter input type, e.g. FrameworkType.
+  public XType unwrappedElementType(ClassName wrappingClass) {
     checkArgument(
         elementsAreTypeOf(wrappingClass),
         "expected elements to be %s, but this type is %s",
         wrappingClass,
-        declaredSetType());
-    return MoreTypes.asDeclared(elementType()).getTypeArguments().get(0);
+        type());
+    return unwrapType(elementType());
   }
 
-  /**
-   * {@code true} if {@code type} is a {@link Set} type.
-   */
+  /** {@code true} if {@code type} is a {@link java.util.Set} type. */
+  public static boolean isSet(XType type) {
+    return isTypeOf(type, TypeNames.SET);
+  }
+
+  /** {@code true} if {@code type} is a {@link java.util.Set} type. */
   public static boolean isSet(TypeMirror type) {
-    return MoreTypes.isType(type) && MoreTypes.isTypeOf(Set.class, type);
+    return isType(type) && isTypeOf(TypeNames.SET, type);
   }
 
-  /**
-   * {@code true} if {@code key.type()} is a {@link Set} type.
-   */
+  /** {@code true} if {@code key.type()} is a {@link java.util.Set} type. */
   public static boolean isSet(Key key) {
-    return isSet(key.type());
+    return isSet(key.type().xprocessing());
   }
 
   /**
    * Returns a {@link SetType} for {@code type}.
    *
-   * @throws IllegalArgumentException if {@code type} is not a {@link Set} type
+   * @throws IllegalArgumentException if {@code type} is not a {@link java.util.Set} type
    */
-  public static SetType from(TypeMirror type) {
+  public static SetType from(XType type) {
     checkArgument(isSet(type), "%s must be a Set", type);
-    return new AutoValue_SetType(MoreTypes.equivalence().wrap(MoreTypes.asDeclared(type)));
+    SetType setType = new AutoValue_SetType(type.getTypeName());
+    setType.type = type;
+    return setType;
   }
 
   /**
    * Returns a {@link SetType} for {@code key}'s {@link Key#type() type}.
    *
-   * @throws IllegalArgumentException if {@code key.type()} is not a {@link Set} type
+   * @throws IllegalArgumentException if {@code key.type()} is not a {@link java.util.Set} type
    */
   public static SetType from(Key key) {
-    return from (key.type());
+    return from(key.type().xprocessing());
   }
 }
diff --git a/java/dagger/internal/codegen/base/SimpleAnnotationMirror.java b/java/dagger/internal/codegen/base/SimpleAnnotationMirror.java
deleted file mode 100644
index 9690691..0000000
--- a/java/dagger/internal/codegen/base/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.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
deleted file mode 100644
index d595bcb..0000000
--- a/java/dagger/internal/codegen/base/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.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
index 5553dd8..9ca72b8 100644
--- a/java/dagger/internal/codegen/base/SourceFileGenerationException.java
+++ b/java/dagger/internal/codegen/base/SourceFileGenerationException.java
@@ -19,10 +19,10 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 import static javax.tools.Diagnostic.Kind.ERROR;
 
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XMessager;
 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.
@@ -32,10 +32,10 @@
  * for that.
  */
 public final class SourceFileGenerationException extends Exception {
-  private final Element associatedElement;
+  private final XElement associatedElement;
 
   SourceFileGenerationException(
-      Optional<ClassName> generatedClassName, Throwable cause, Element associatedElement) {
+      Optional<ClassName> generatedClassName, Throwable cause, XElement associatedElement) {
     super(createMessage(generatedClassName, cause.getMessage()), cause);
     this.associatedElement = checkNotNull(associatedElement);
   }
@@ -48,7 +48,7 @@
         message);
   }
 
-  public void printMessageTo(Messager messager) {
+  public void printMessageTo(XMessager messager) {
     messager.printMessage(ERROR, getMessage(), associatedElement);
   }
 }
diff --git a/java/dagger/internal/codegen/base/SourceFileGenerator.java b/java/dagger/internal/codegen/base/SourceFileGenerator.java
index ada67d3..d55ac11 100644
--- a/java/dagger/internal/codegen/base/SourceFileGenerator.java
+++ b/java/dagger/internal/codegen/base/SourceFileGenerator.java
@@ -16,12 +16,17 @@
 
 package dagger.internal.codegen.base;
 
-
+import static androidx.room.compiler.processing.JavaPoetExtKt.addOriginatingElement;
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 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 androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XFiler;
+import androidx.room.compiler.processing.XMessager;
+import androidx.room.compiler.processing.compat.XConverters;
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
@@ -34,10 +39,7 @@
 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
@@ -49,11 +51,11 @@
 public abstract class SourceFileGenerator<T> {
   private static final String GENERATED_COMMENTS = "https://dagger.dev";
 
-  private final Filer filer;
+  private final XFiler filer;
   private final DaggerElements elements;
   private final SourceVersion sourceVersion;
 
-  public SourceFileGenerator(Filer filer, DaggerElements elements, SourceVersion sourceVersion) {
+  public SourceFileGenerator(XFiler filer, DaggerElements elements, SourceVersion sourceVersion) {
     this.filer = checkNotNull(filer);
     this.elements = checkNotNull(elements);
     this.sourceVersion = checkNotNull(sourceVersion);
@@ -67,7 +69,7 @@
    * 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) {
+  public void generate(T input, XMessager messager) {
     try {
       generate(input);
     } catch (SourceFileGenerationException e) {
@@ -79,7 +81,7 @@
   public void generate(T input) throws SourceFileGenerationException {
     for (TypeSpec.Builder type : topLevelTypes(input)) {
       try {
-        buildJavaFile(input, type).writeTo(filer);
+        buildJavaFile(input, type).writeTo(XConverters.toJavac(filer));
       } catch (Exception e) {
         // if the code above threw a SFGE, use that
         Throwables.propagateIfPossible(e, SourceFileGenerationException.class);
@@ -90,7 +92,7 @@
   }
 
   private JavaFile buildJavaFile(T input, TypeSpec.Builder typeSpecBuilder) {
-    typeSpecBuilder.addOriginatingElement(originatingElement(input));
+    addOriginatingElement(typeSpecBuilder, originatingElement(input));
     typeSpecBuilder.addAnnotation(DaggerGenerated.class);
     Optional<AnnotationSpec> generatedAnnotation =
         generatedAnnotation(elements, sourceVersion)
@@ -112,7 +114,10 @@
 
     JavaFile.Builder javaFileBuilder =
         JavaFile.builder(
-                elements.getPackageOf(originatingElement(input)).getQualifiedName().toString(),
+                elements
+                    .getPackageOf(toJavac(originatingElement(input)))
+                    .getQualifiedName()
+                    .toString(),
                 typeSpecBuilder.build())
             .skipJavaLangImports(true);
     if (!generatedAnnotation.isPresent()) {
@@ -122,7 +127,7 @@
   }
 
   /** Returns the originating element of the generating type. */
-  public abstract Element originatingElement(T input);
+  public abstract XElement originatingElement(T input);
 
   /**
    * Returns {@link TypeSpec.Builder types} be generated for {@code T}, or an empty list if no types
diff --git a/java/dagger/internal/codegen/base/TarjanSCCs.java b/java/dagger/internal/codegen/base/TarjanSCCs.java
new file mode 100644
index 0000000..ab9a0fd
--- /dev/null
+++ b/java/dagger/internal/codegen/base/TarjanSCCs.java
@@ -0,0 +1,110 @@
+/*
+ * 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.base;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.lang.Math.min;
+
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.graph.SuccessorsFunction;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * An implementation of Tarjan's algorithm for finding the SCC of a graph. This is based on the
+ * psuedo code algorithm here:
+ * http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
+ */
+public final class TarjanSCCs {
+
+  /** Returns the set of strongly connected components in reverse topological order. */
+  public static <NodeT> ImmutableSet<ImmutableSet<NodeT>> compute(
+      ImmutableCollection<NodeT> nodes, SuccessorsFunction<NodeT> successorsFunction) {
+    return new TarjanSCC<>(nodes, successorsFunction).compute();
+  }
+
+  private static class TarjanSCC<NodeT> {
+    private final ImmutableCollection<NodeT> nodes;
+    private final SuccessorsFunction<NodeT> successorsFunction;
+    private final Deque<NodeT> stack;
+    private final Set<NodeT> onStack;
+    private final Map<NodeT, Integer> indexes;
+    private final Map<NodeT, Integer> lowLinks;
+    private final List<ImmutableSet<NodeT>> stronglyConnectedComponents = new ArrayList<>();
+
+    TarjanSCC(ImmutableCollection<NodeT> nodes, SuccessorsFunction<NodeT> successorsFunction) {
+      this.nodes = nodes;
+      this.successorsFunction = successorsFunction;
+      this.stack = new ArrayDeque<>(nodes.size());
+      this.onStack = Sets.newHashSetWithExpectedSize(nodes.size());
+      this.indexes = Maps.newHashMapWithExpectedSize(nodes.size());
+      this.lowLinks = Maps.newHashMapWithExpectedSize(nodes.size());
+    }
+
+    private ImmutableSet<ImmutableSet<NodeT>> compute() {
+      checkState(indexes.isEmpty(), "TarjanSCC#compute() can only be called once per instance!");
+      for (NodeT node : nodes) {
+        if (!indexes.containsKey(node)) {
+          stronglyConnect(node);
+        }
+      }
+      return ImmutableSet.copyOf(stronglyConnectedComponents);
+    }
+
+    private void stronglyConnect(NodeT node) {
+      // Set the index and lowLink for node to the smallest unused index and add it to the stack
+      lowLinks.put(node, indexes.size());
+      indexes.put(node, indexes.size());
+      stack.push(node);
+      onStack.add(node);
+
+      for (NodeT successor : successorsFunction.successors(node)) {
+        if (!indexes.containsKey(successor)) {
+          // Successor has not been processed.
+          stronglyConnect(successor);
+          lowLinks.put(node, min(lowLinks.get(node), lowLinks.get(successor)));
+        } else if (onStack.contains(successor)) {
+          // Successor is on the stack and hence in the current SCC.
+          lowLinks.put(node, min(lowLinks.get(node), indexes.get(successor)));
+        } else {
+          // Successor is not on the stack and hence in an already processed SCC, so ignore.
+        }
+      }
+
+      // If node is the root of the SCC, pop the stack until reaching the root to get all SCC nodes.
+      if (lowLinks.get(node).equals(indexes.get(node))) {
+        ImmutableSet.Builder<NodeT> scc = ImmutableSet.builder();
+        NodeT currNode;
+        do {
+          currNode = stack.pop();
+          onStack.remove(currNode);
+          scc.add(currNode);
+        } while (!node.equals(currNode));
+        stronglyConnectedComponents.add(scc.build());
+      }
+    }
+  }
+
+  private TarjanSCCs() {}
+}
diff --git a/java/dagger/internal/codegen/binding/AnnotationExpression.java b/java/dagger/internal/codegen/binding/AnnotationExpression.java
index de0aea5..82f282d 100644
--- a/java/dagger/internal/codegen/binding/AnnotationExpression.java
+++ b/java/dagger/internal/codegen/binding/AnnotationExpression.java
@@ -17,8 +17,10 @@
 package dagger.internal.codegen.binding;
 
 import static com.google.auto.common.AnnotationMirrors.getAnnotationValuesWithDefaults;
+import static com.google.auto.common.MoreTypes.asArray;
 import static dagger.internal.codegen.binding.SourceFiles.classFileName;
 import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
+import static dagger.internal.codegen.javapoet.TypeNames.rawTypeName;
 import static java.util.stream.Collectors.toList;
 
 import com.google.auto.common.MoreElements;
@@ -26,17 +28,14 @@
 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.TypeKind;
 import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.SimpleAnnotationValueVisitor6;
-import javax.lang.model.util.SimpleTypeVisitor6;
+import javax.lang.model.util.SimpleAnnotationValueVisitor8;
 
 /**
  * Returns an expression creating an instance of the visited annotation type. Its parameter must be
@@ -50,7 +49,7 @@
  * but in code it would have to be {@code new int[] {1, 2, 3}}.
  */
 public class AnnotationExpression
-    extends SimpleAnnotationValueVisitor6<CodeBlock, AnnotationValue> {
+    extends SimpleAnnotationValueVisitor8<CodeBlock, AnnotationValue> {
 
   private final AnnotationMirror annotation;
   private final ClassName creatorClass;
@@ -104,7 +103,10 @@
    * annotation}.
    */
   CodeBlock getValueExpression(TypeMirror valueType, AnnotationValue value) {
-    return ARRAY_LITERAL_PREFIX.visit(valueType, this.visit(value, value));
+    CodeBlock codeBlock = visit(value, value);
+    return valueType.getKind() == TypeKind.ARRAY
+        ? CodeBlock.of("new $T[] $L", rawTypeName(asArray(valueType).getComponentType()), codeBlock)
+        : codeBlock;
   }
 
   @Override
@@ -170,39 +172,4 @@
     }
     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
index f9929ae..49ecd88 100644
--- a/java/dagger/internal/codegen/binding/AssistedInjectionAnnotations.java
+++ b/java/dagger/internal/codegen/binding/AssistedInjectionAnnotations.java
@@ -16,25 +16,28 @@
 
 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 static dagger.internal.codegen.langmodel.DaggerElements.isAnnotationPresent;
+import static dagger.internal.codegen.xprocessing.XElements.asConstructor;
+import static dagger.internal.codegen.xprocessing.XElements.asTypeElement;
+import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
+import static kotlin.streams.jdk8.StreamsKt.asStream;
 
+import androidx.room.compiler.processing.XConstructorElement;
+import androidx.room.compiler.processing.XConstructorType;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XHasModifiers;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XMethodType;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
+import androidx.room.compiler.processing.XVariableElement;
 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;
@@ -42,46 +45,36 @@
 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 dagger.internal.codegen.javapoet.TypeNames;
+import dagger.spi.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 java.util.Optional;
 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) {
-    return getOnlyElement(assistedFactoryMethods(factory, elements));
+  /** Returns the factory method for the given factory {@link XTypeElement}. */
+  public static XMethodElement assistedFactoryMethod(XTypeElement factory) {
+    return getOnlyElement(assistedFactoryMethods(factory));
   }
 
-  /** Returns the list of abstract factory methods for the given factory {@link TypeElement}. */
-  public static ImmutableSet<ExecutableElement> assistedFactoryMethods(
-      TypeElement factory, DaggerElements elements) {
-    return elements.getLocalAndInheritedMethods(factory).stream()
-        .filter(method -> method.getModifiers().contains(ABSTRACT))
-        .filter(method -> !method.isDefault())
+  /** Returns the list of abstract factory methods for the given factory {@link XTypeElement}. */
+  public static ImmutableSet<XMethodElement> assistedFactoryMethods(XTypeElement factory) {
+    return asStream(factory.getAllNonPrivateInstanceMethods())
+        .filter(XHasModifiers::isAbstract)
+        .filter(method -> !method.isJavaDefault())
         .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);
+  public static boolean isAssistedInjectionType(XTypeElement typeElement) {
+    return assistedInjectedConstructors(typeElement).stream()
+        .anyMatch(constructor -> constructor.hasAnnotation(TypeNames.ASSISTED_INJECT));
   }
 
   /** Returns {@code true} if this binding is an assisted factory. */
-  public static boolean isAssistedFactoryType(Element element) {
-    return isAnnotationPresent(element, AssistedFactory.class);
+  public static boolean isAssistedFactoryType(XElement element) {
+    return element.hasAnnotation(TypeNames.ASSISTED_FACTORY);
   }
 
   /**
@@ -91,25 +84,22 @@
    * 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) {
+  public static ImmutableList<ParameterSpec> assistedParameterSpecs(Binding binding) {
     checkArgument(binding.kind() == BindingKind.ASSISTED_INJECTION);
-    ExecutableElement constructor = asExecutable(binding.bindingElement().get());
-    ExecutableType constructorType =
-        asExecutable(types.asMemberOf(asDeclared(binding.key().type()), constructor));
+    XConstructorElement constructor = asConstructor(binding.bindingElement().get());
+    XConstructorType constructorType = constructor.asMemberOf(binding.key().type().xprocessing());
     return assistedParameterSpecs(constructor.getParameters(), constructorType.getParameterTypes());
   }
 
   private static ImmutableList<ParameterSpec> assistedParameterSpecs(
-      List<? extends VariableElement> paramElements, List<? extends TypeMirror> paramTypes) {
+      List<? extends XVariableElement> paramElements, List<XType> 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);
+      XVariableElement paramElement = paramElements.get(i);
+      XType paramType = paramTypes.get(i);
       if (isAssistedParameter(paramElement)) {
         assistedParameterSpecs.add(
-            ParameterSpec.builder(TypeName.get(paramType), paramElement.getSimpleName().toString())
-                .build());
+            ParameterSpec.builder(paramType.getTypeName(), getSimpleName(paramElement)).build());
       }
     }
     return assistedParameterSpecs.build();
@@ -122,14 +112,13 @@
    * 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) {
+  public static ImmutableList<ParameterSpec> assistedFactoryParameterSpecs(Binding binding) {
     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()));
+    XTypeElement factory = asTypeElement(binding.bindingElement().get());
+    AssistedFactoryMetadata metadata = AssistedFactoryMetadata.create(factory.getType());
+    XMethodType factoryMethodType =
+        metadata.factoryMethod().asMemberOf(binding.key().type().xprocessing());
     return assistedParameterSpecs(
         // Use the order of the parameters from the @AssistedFactory method but use the parameter
         // names of the @AssistedInject constructor.
@@ -140,81 +129,82 @@
   }
 
   /** 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))
+  public static ImmutableSet<XConstructorElement> assistedInjectedConstructors(XTypeElement type) {
+    return type.getConstructors().stream()
+        .filter(constructor -> constructor.hasAnnotation(TypeNames.ASSISTED_INJECT))
         .collect(toImmutableSet());
   }
 
-  public static ImmutableList<VariableElement> assistedParameters(Binding binding) {
+  public static ImmutableList<XVariableElement> assistedParameters(Binding binding) {
     return binding.kind() == BindingKind.ASSISTED_INJECTION
-        ? assistedParameters(asExecutable(binding.bindingElement().get()))
+        ? asConstructor(binding.bindingElement().get()).getParameters().stream()
+            .filter(AssistedInjectionAnnotations::isAssistedParameter)
+            .collect(toImmutableList())
         : 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(XVariableElement param) {
+    return param.hasAnnotation(TypeNames.ASSISTED);
   }
 
   /** Returns {@code true} if this binding is uses assisted injection. */
   public static boolean isAssistedParameter(VariableElement param) {
-    return isAnnotationPresent(MoreElements.asVariable(param), Assisted.class);
+    return isAnnotationPresent(MoreElements.asVariable(param), TypeNames.ASSISTED);
   }
 
   /** 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);
-      ExecutableType factoryMethodType = asExecutable(types.asMemberOf(factoryType, factoryMethod));
-      DeclaredType assistedInjectType = asDeclared(factoryMethodType.getReturnType());
+    public static AssistedFactoryMetadata create(XType factoryType) {
+      XTypeElement factoryElement = factoryType.getTypeElement();
+      XMethodElement factoryMethod = assistedFactoryMethod(factoryElement);
+      XMethodType factoryMethodType = factoryMethod.asMemberOf(factoryType);
+      XType assistedInjectType = factoryMethodType.getReturnType();
+      XTypeElement assistedInjectElement = assistedInjectType.getTypeElement();
       return new AutoValue_AssistedInjectionAnnotations_AssistedFactoryMetadata(
           factoryElement,
           factoryType,
           factoryMethod,
           factoryMethodType,
-          asTypeElement(assistedInjectType),
+          assistedInjectElement,
           assistedInjectType,
-          AssistedInjectionAnnotations.assistedInjectAssistedParameters(assistedInjectType, types),
+          AssistedInjectionAnnotations.assistedInjectAssistedParameters(assistedInjectType),
           AssistedInjectionAnnotations.assistedFactoryAssistedParameters(
               factoryMethod, factoryMethodType));
     }
 
-    public abstract TypeElement factory();
+    public abstract XTypeElement factory();
 
-    public abstract DeclaredType factoryType();
+    public abstract XType factoryType();
 
-    public abstract ExecutableElement factoryMethod();
+    public abstract XMethodElement factoryMethod();
 
-    public abstract ExecutableType factoryMethodType();
+    public abstract XMethodType factoryMethodType();
 
-    public abstract TypeElement assistedInjectElement();
+    public abstract XTypeElement assistedInjectElement();
 
-    public abstract DeclaredType assistedInjectType();
+    public abstract XType assistedInjectType();
 
     public abstract ImmutableList<AssistedParameter> assistedInjectAssistedParameters();
 
     public abstract ImmutableList<AssistedParameter> assistedFactoryAssistedParameters();
 
     @Memoized
-    public ImmutableMap<AssistedParameter, VariableElement> assistedInjectAssistedParametersMap() {
-      ImmutableMap.Builder<AssistedParameter, VariableElement> builder = ImmutableMap.builder();
+    public ImmutableMap<AssistedParameter, XVariableElement> assistedInjectAssistedParametersMap() {
+      ImmutableMap.Builder<AssistedParameter, XVariableElement> builder = ImmutableMap.builder();
       for (AssistedParameter assistedParameter : assistedInjectAssistedParameters()) {
-        builder.put(assistedParameter, assistedParameter.variableElement);
+        builder.put(assistedParameter, assistedParameter.element());
       }
       return builder.build();
     }
 
     @Memoized
-    public ImmutableMap<AssistedParameter, VariableElement> assistedFactoryAssistedParametersMap() {
-      ImmutableMap.Builder<AssistedParameter, VariableElement> builder = ImmutableMap.builder();
+    public ImmutableMap<AssistedParameter, XVariableElement>
+        assistedFactoryAssistedParametersMap() {
+      ImmutableMap.Builder<AssistedParameter, XVariableElement> builder = ImmutableMap.builder();
       for (AssistedParameter assistedParameter : assistedFactoryAssistedParameters()) {
-        builder.put(assistedParameter, assistedParameter.variableElement);
+        builder.put(assistedParameter, assistedParameter.element());
       }
       return builder.build();
     }
@@ -228,32 +218,34 @@
    */
   @AutoValue
   public abstract static class AssistedParameter {
-    public static AssistedParameter create(VariableElement parameter, TypeMirror parameterType) {
+    public static AssistedParameter create(XVariableElement parameter, XType parameterType) {
       AssistedParameter assistedParameter =
           new AutoValue_AssistedInjectionAnnotations_AssistedParameter(
-              getAnnotationMirror(parameter, Assisted.class)
-                  .map(assisted -> getStringValue(assisted, "value"))
+              Optional.ofNullable(parameter.getAnnotation(TypeNames.ASSISTED))
+                  .map(assisted -> assisted.getAsString("value"))
                   .orElse(""),
-              MoreTypes.equivalence().wrap(parameterType));
-      assistedParameter.variableElement = parameter;
+              parameterType.getTypeName());
+      assistedParameter.parameterElement = parameter;
+      assistedParameter.parameterType = parameterType;
       return assistedParameter;
     }
 
-    private VariableElement variableElement;
+    private XVariableElement parameterElement;
+    private XType parameterType;
 
     /** 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}. */
+    abstract TypeName typeName();
 
     /** Returns the type annotated with {@link Assisted}. */
-    public final TypeMirror type() {
-      return wrappedType().get();
+    public final XType type() {
+      return parameterType;
     }
 
-    public final VariableElement variableElement() {
-      return variableElement;
+    public final XVariableElement element() {
+      return parameterElement;
     }
 
     @Override
@@ -265,31 +257,31 @@
   }
 
   public static ImmutableList<AssistedParameter> assistedInjectAssistedParameters(
-      DeclaredType assistedInjectType, DaggerTypes types) {
+      XType assistedInjectType) {
     // 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));
+    XConstructorElement assistedInjectConstructor =
+        getOnlyElement(assistedInjectedConstructors(assistedInjectType.getTypeElement()));
+    XConstructorType assistedInjectConstructorType =
+        assistedInjectConstructor.asMemberOf(assistedInjectType);
 
     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)) {
+      XVariableElement parameter = assistedInjectConstructor.getParameters().get(i);
+      XType parameterType = assistedInjectConstructorType.getParameterTypes().get(i);
+      if (parameter.hasAnnotation(TypeNames.ASSISTED)) {
         builder.add(AssistedParameter.create(parameter, parameterType));
       }
     }
     return builder.build();
   }
 
-  public static ImmutableList<AssistedParameter> assistedFactoryAssistedParameters(
-      ExecutableElement factoryMethod, ExecutableType factoryMethodType) {
+  private static ImmutableList<AssistedParameter> assistedFactoryAssistedParameters(
+      XMethodElement factoryMethod, XMethodType 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);
+      XVariableElement parameter = factoryMethod.getParameters().get(i);
+      XType parameterType = factoryMethodType.getParameterTypes().get(i);
       builder.add(AssistedParameter.create(parameter, parameterType));
     }
     return builder.build();
diff --git a/java/dagger/internal/codegen/binding/BUILD b/java/dagger/internal/codegen/binding/BUILD
index ff8db8f..54211d8 100644
--- a/java/dagger/internal/codegen/binding/BUILD
+++ b/java/dagger/internal/codegen/binding/BUILD
@@ -32,16 +32,17 @@
         "//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/internal/codegen/xprocessing",
         "//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",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:value",
+        "//third_party/java/error_prone:annotations",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/graph",
+        "//third_party/java/guava/util/concurrent",
+        "//third_party/java/javapoet",
+        "@maven//:org_jetbrains_kotlin_kotlin_stdlib_jdk8",
     ],
 )
diff --git a/java/dagger/internal/codegen/binding/Binding.java b/java/dagger/internal/codegen/binding/Binding.java
index 0d4eef6..47d5d40 100644
--- a/java/dagger/internal/codegen/binding/Binding.java
+++ b/java/dagger/internal/codegen/binding/Binding.java
@@ -16,34 +16,25 @@
 
 package dagger.internal.codegen.binding;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 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 dagger.spi.model.BindingKind;
+import dagger.spi.model.DependencyRequest;
+import dagger.spi.model.Scope;
 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
+ * An abstract type for classes representing a Dagger binding. Particularly, contains the 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 {
@@ -56,7 +47,7 @@
     if (!bindingElement().isPresent() || !contributingModule().isPresent()) {
       return false;
     }
-    Set<Modifier> modifiers = bindingElement().get().getModifiers();
+    Set<Modifier> modifiers = toJavac(bindingElement().get()).getModifiers();
     return !modifiers.contains(ABSTRACT) && !modifiers.contains(STATIC);
   }
 
@@ -121,45 +112,4 @@
   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
index 712260f..353921a 100644
--- a/java/dagger/internal/codegen/binding/BindingDeclaration.java
+++ b/java/dagger/internal/codegen/binding/BindingDeclaration.java
@@ -16,16 +16,17 @@
 
 package dagger.internal.codegen.binding;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 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 androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XTypeElement;
+import dagger.internal.codegen.xprocessing.XElements;
+import dagger.spi.model.BindingKind;
+import dagger.spi.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 {
@@ -48,18 +49,18 @@
                   declaration.contributingModule().isPresent()
                       ? declaration.contributingModule()
                       : declaration.bindingTypeElement(),
-              emptiesLast(comparing((TypeElement type) -> type.getQualifiedName().toString())))
+              emptiesLast(comparing(XTypeElement::getQualifiedName)))
           .thenComparing(
               (BindingDeclaration declaration) -> declaration.bindingElement(),
               emptiesLast(
-                  comparing((Element element) -> element.getSimpleName().toString())
-                      .thenComparing((Element element) -> element.asType().toString())));
+                  comparing((XElement element) -> toJavac(element).getSimpleName().toString())
+                      .thenComparing((XElement element) -> toJavac(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
+   * The {@link XElement} 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
@@ -68,14 +69,14 @@
    * 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();
+  public abstract Optional<XElement> 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);
+  public final Optional<XTypeElement> bindingTypeElement() {
+    return bindingElement().map(XElements::closestEnclosingTypeElement);
   }
 
   /**
@@ -83,5 +84,5 @@
    * the class that contains {@link #bindingElement()}. Absent if {@link #bindingElement()} is
    * empty.
    */
-  public abstract Optional<TypeElement> contributingModule();
+  public abstract Optional<XTypeElement> contributingModule();
 }
diff --git a/java/dagger/internal/codegen/binding/BindingDeclarationFormatter.java b/java/dagger/internal/codegen/binding/BindingDeclarationFormatter.java
index 8476497..a69ddaa 100644
--- a/java/dagger/internal/codegen/binding/BindingDeclarationFormatter.java
+++ b/java/dagger/internal/codegen/binding/BindingDeclarationFormatter.java
@@ -16,30 +16,24 @@
 
 package dagger.internal.codegen.binding;
 
-import static com.google.common.collect.Sets.immutableEnumSet;
+import static androidx.room.compiler.processing.XElementKt.isMethodParameter;
+import static androidx.room.compiler.processing.XElementKt.isTypeElement;
 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 static dagger.internal.codegen.xprocessing.XElements.asExecutable;
+import static dagger.internal.codegen.xprocessing.XElements.asTypeElement;
+import static dagger.internal.codegen.xprocessing.XElements.isExecutable;
 
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XTypeElement;
 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
@@ -57,9 +51,10 @@
       return true;
     }
     if (bindingDeclaration.bindingElement().isPresent()) {
-      Element bindingElement = bindingDeclaration.bindingElement().get();
-      return bindingElement.getKind().equals(PARAMETER)
-          || FORMATTABLE_ELEMENT_TYPE_KINDS.contains(bindingElement.asType().getKind());
+      XElement bindingElement = bindingDeclaration.bindingElement().get();
+      return isMethodParameter(bindingElement)
+          || isTypeElement(bindingElement)
+          || isExecutable(bindingElement);
     }
     // TODO(dpb): validate whether what this is doing is correct
     return false;
@@ -72,26 +67,17 @@
     }
 
     if (bindingDeclaration.bindingElement().isPresent()) {
-      Element bindingElement = bindingDeclaration.bindingElement().get();
-      if (bindingElement.getKind().equals(PARAMETER)) {
+      XElement bindingElement = bindingDeclaration.bindingElement().get();
+      if (isMethodParameter(bindingElement)) {
         return elementToString(bindingElement);
+      } else if (isTypeElement(bindingElement)) {
+        return stripCommonTypePrefixes(asTypeElement(bindingElement).getType().toString());
+      } else if (isExecutable(bindingElement)) {
+        return methodSignatureFormatter.format(
+            asExecutable(bindingElement),
+            bindingDeclaration.contributingModule().map(XTypeElement::getType));
       }
-
-      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);
-      }
+      throw new IllegalArgumentException("Formatting unsupported for element: " + bindingElement);
     }
 
     return String.format(
@@ -100,7 +86,7 @@
   }
 
   private String formatSubcomponentDeclaration(SubcomponentDeclaration subcomponentDeclaration) {
-    ImmutableList<TypeElement> moduleSubcomponents =
+    ImmutableList<XTypeElement> moduleSubcomponents =
         subcomponentDeclaration.moduleAnnotation().subcomponents();
     int index = moduleSubcomponents.indexOf(subcomponentDeclaration.subcomponentType());
     StringBuilder annotationValue = new StringBuilder();
@@ -118,7 +104,7 @@
 
     return String.format(
         "@%s(subcomponents = %s) for %s",
-        subcomponentDeclaration.moduleAnnotation().annotationName(),
+        subcomponentDeclaration.moduleAnnotation().simpleName(),
         annotationValue,
         subcomponentDeclaration.contributingModule().get());
   }
diff --git a/java/dagger/internal/codegen/binding/BindingFactory.java b/java/dagger/internal/codegen/binding/BindingFactory.java
index eb7c132..5146c35 100644
--- a/java/dagger/internal/codegen/binding/BindingFactory.java
+++ b/java/dagger/internal/codegen/binding/BindingFactory.java
@@ -16,69 +16,68 @@
 
 package dagger.internal.codegen.binding;
 
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.auto.common.MoreTypes.asDeclared;
+import static androidx.room.compiler.processing.XElementKt.isMethod;
+import static androidx.room.compiler.processing.XElementKt.isTypeElement;
+import static androidx.room.compiler.processing.XElementKt.isVariableElement;
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 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 static dagger.internal.codegen.xprocessing.XElements.asMethod;
+import static dagger.internal.codegen.xprocessing.XElements.asTypeElement;
+import static dagger.internal.codegen.xprocessing.XElements.asVariable;
+import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
+import static dagger.spi.model.BindingKind.ASSISTED_FACTORY;
+import static dagger.spi.model.BindingKind.ASSISTED_INJECTION;
+import static dagger.spi.model.BindingKind.BOUND_INSTANCE;
+import static dagger.spi.model.BindingKind.COMPONENT;
+import static dagger.spi.model.BindingKind.COMPONENT_DEPENDENCY;
+import static dagger.spi.model.BindingKind.COMPONENT_PRODUCTION;
+import static dagger.spi.model.BindingKind.COMPONENT_PROVISION;
+import static dagger.spi.model.BindingKind.DELEGATE;
+import static dagger.spi.model.BindingKind.INJECTION;
+import static dagger.spi.model.BindingKind.MEMBERS_INJECTOR;
+import static dagger.spi.model.BindingKind.OPTIONAL;
+import static dagger.spi.model.BindingKind.PRODUCTION;
+import static dagger.spi.model.BindingKind.PROVISION;
+import static dagger.spi.model.BindingKind.SUBCOMPONENT_CREATOR;
 
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XConstructorElement;
+import androidx.room.compiler.processing.XConstructorType;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XExecutableParameterElement;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XMethodType;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
+import androidx.room.compiler.processing.XVariableElement;
 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 com.squareup.javapoet.ClassName;
 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.javapoet.TypeNames;
 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 dagger.spi.model.BindingKind;
+import dagger.spi.model.DaggerType;
+import dagger.spi.model.DependencyRequest;
+import dagger.spi.model.Key;
+import dagger.spi.model.RequestKind;
 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 {
@@ -86,30 +85,24 @@
   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) {
+      InjectionAnnotations injectionAnnotations) {
     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.
+   * Returns an {@link dagger.spi.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
@@ -117,91 +110,69 @@
    */
   // 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());
+      XConstructorElement constructorElement, Optional<XType> resolvedEnclosingType) {
+    checkArgument(InjectionAnnotations.hasInjectOrAssistedInjectAnnotation(constructorElement));
 
-    ExecutableType constructorType = MoreTypes.asExecutable(constructorElement.asType());
-    DeclaredType constructedType =
-        MoreTypes.asDeclared(constructorElement.getEnclosingElement().asType());
+    XConstructorType constructorType = constructorElement.getExecutableType();
+    XType enclosingType = constructorElement.getEnclosingElement().getType();
     // 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;
+    if (!enclosingType.getTypeArguments().isEmpty() && resolvedEnclosingType.isPresent()) {
+      checkIsSameErasedType(resolvedEnclosingType.get(), enclosingType);
+      enclosingType = resolvedEnclosingType.get();
+      constructorType = constructorElement.asMemberOf(enclosingType);
     }
 
     // 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);
+      XExecutableParameterElement parameter = constructorElement.getParameters().get(i);
+      XType 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)
+            .key(keyFactory.forInjectConstructorWithResolvedType(enclosingType))
             .provisionDependencies(provisionDependencies.build())
-            .injectionSites(injectionSiteFactory.getInjectionSites(constructedType))
+            .injectionSites(injectionSiteFactory.getInjectionSites(enclosingType))
             .kind(
-                isAnnotationPresent(constructorElement, AssistedInject.class)
+                constructorElement.hasAnnotation(TypeNames.ASSISTED_INJECT)
                     ? ASSISTED_INJECTION
                     : INJECTION)
-            .scope(uniqueScopeOf(constructorElement.getEnclosingElement()));
+            .scope(injectionAnnotations.getScope(constructorElement.getEnclosingElement()));
 
-    TypeElement bindingTypeElement = MoreElements.asType(constructorElement.getEnclosingElement());
-    if (hasNonDefaultTypeParameters(bindingTypeElement, key.type(), types)) {
+    if (hasNonDefaultTypeParameters(enclosingType)) {
       builder.unresolved(injectionBinding(constructorElement, Optional.empty()));
     }
     return builder.build();
   }
 
   public ProvisionBinding assistedFactoryBinding(
-      TypeElement factory, Optional<TypeMirror> resolvedType) {
+      XTypeElement factory, Optional<XType> resolvedFactoryType) {
 
     // 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;
+    XType factoryType = factory.getType();
+    if (!factoryType.getTypeArguments().isEmpty() && resolvedFactoryType.isPresent()) {
+      checkIsSameErasedType(resolvedFactoryType.get(), factoryType);
+      factoryType = resolvedFactoryType.get();
     }
 
-    ExecutableElement factoryMethod =
-        AssistedInjectionAnnotations.assistedFactoryMethod(factory, elements);
-    ExecutableType factoryMethodType =
-        MoreTypes.asExecutable(types.asMemberOf(factoryType, factoryMethod));
+    XMethodElement factoryMethod = AssistedInjectionAnnotations.assistedFactoryMethod(factory);
+    XMethodType factoryMethodType = factoryMethod.asMemberOf(factoryType);
     return ProvisionBinding.builder()
         .contributionType(ContributionType.UNIQUE)
-        .key(Key.builder(factoryType).build())
+        .key(Key.builder(DaggerType.from(factoryType)).build())
         .bindingElement(factory)
         .provisionDependencies(
             ImmutableSet.of(
                 DependencyRequest.builder()
-                    .key(Key.builder(factoryMethodType.getReturnType()).build())
+                    .key(Key.builder(DaggerType.from(factoryMethodType.getReturnType())).build())
                     .kind(RequestKind.PROVIDER)
                     .build()))
         .kind(ASSISTED_FACTORY)
@@ -209,13 +180,13 @@
   }
 
   /**
-   * Returns a {@link dagger.model.BindingKind#PROVISION} binding for a {@code @Provides}-annotated
-   * method.
+   * Returns a {@link dagger.spi.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) {
+      XMethodElement providesMethod, XTypeElement contributedBy) {
     return setMethodBindingProperties(
             ProvisionBinding.builder(),
             providesMethod,
@@ -223,19 +194,19 @@
             keyFactory.forProvidesMethod(providesMethod, contributedBy),
             this::providesMethodBinding)
         .kind(PROVISION)
-        .scope(uniqueScopeOf(providesMethod))
+        .scope(injectionAnnotations.getScope(providesMethod))
         .nullableType(getNullableType(providesMethod))
         .build();
   }
 
   /**
-   * Returns a {@link dagger.model.BindingKind#PRODUCTION} binding for a {@code @Produces}-annotated
-   * method.
+   * Returns a {@link dagger.spi.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) {
+      XMethodElement producesMethod, XTypeElement contributedBy) {
     // TODO(beder): Add nullability checking with Java 8.
     ProductionBinding.Builder builder =
         setMethodBindingProperties(
@@ -255,35 +226,29 @@
   private <C extends ContributionBinding, B extends ContributionBinding.Builder<C, B>>
       B setMethodBindingProperties(
           B builder,
-          ExecutableElement method,
-          TypeElement contributedBy,
+          XMethodElement method,
+          XTypeElement 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())));
+          BiFunction<XMethodElement, XTypeElement, C> create) {
+    XMethodType methodType = method.asMemberOf(contributedBy.getType());
+    if (!types.isSameType(toJavac(methodType), toJavac(method.getExecutableType()))) {
+      checkState(isTypeElement(method.getEnclosingElement()));
+      builder.unresolved(create.apply(method, asTypeElement(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(
+            dependencyRequestFactory.forRequiredResolvedXVariables(
                 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
+   * Returns a {@link dagger.spi.model.BindingKind#MULTIBOUND_MAP} or {@link
+   * dagger.spi.model.BindingKind#MULTIBOUND_SET} binding given a set of multibinding contribution
    * bindings.
    *
    * @param key a key that may be satisfied by a multibinding
@@ -303,33 +268,44 @@
         .build();
   }
 
+  private 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));
+    }
+  }
+
   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)) {
+      if (mapType.valuesAreTypeOf(TypeNames.PRODUCER)
+          || mapType.valuesAreTypeOf(TypeNames.PRODUCED)) {
         return true;
       }
-    } else if (SetType.isSet(key) && SetType.from(key).elementsAreTypeOf(Produced.class)) {
+    } else if (SetType.isSet(key) && SetType.from(key).elementsAreTypeOf(TypeNames.PRODUCED)) {
       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) {
+  /** Returns a {@link dagger.spi.model.BindingKind#COMPONENT} binding for the component. */
+  public ProvisionBinding componentBinding(XTypeElement componentDefinitionType) {
     checkNotNull(componentDefinitionType);
     return ProvisionBinding.builder()
         .contributionType(ContributionType.UNIQUE)
         .bindingElement(componentDefinitionType)
-        .key(keyFactory.forType(componentDefinitionType.asType()))
+        .key(keyFactory.forType(componentDefinitionType.getType()))
         .kind(COMPONENT)
         .build();
   }
 
   /**
-   * Returns a {@link dagger.model.BindingKind#COMPONENT_DEPENDENCY} binding for a component's
+   * Returns a {@link dagger.spi.model.BindingKind#COMPONENT_DEPENDENCY} binding for a component's
    * dependency.
    */
   public ProvisionBinding componentDependencyBinding(ComponentRequirement dependency) {
@@ -343,20 +319,18 @@
   }
 
   /**
-   * Returns a {@link dagger.model.BindingKind#COMPONENT_PROVISION} or {@link
-   * dagger.model.BindingKind#COMPONENT_PRODUCTION} binding for a method on a component's
+   * Returns a {@link dagger.spi.model.BindingKind#COMPONENT_PROVISION} or {@link
+   * dagger.spi.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));
+      ComponentDescriptor componentDescriptor, XMethodElement dependencyMethod) {
     checkArgument(dependencyMethod.getParameters().isEmpty());
     ContributionBinding.Builder<?, ?> builder;
-    if (componentDescriptor.isProduction()
-        && isComponentProductionMethod(elements, dependencyMethod)) {
+    if (componentDescriptor.isProduction() && isComponentProductionMethod(dependencyMethod)) {
       builder =
           ProductionBinding.builder()
               .key(keyFactory.forProductionComponentMethod(dependencyMethod))
@@ -368,7 +342,7 @@
               .key(keyFactory.forComponentMethod(dependencyMethod))
               .nullableType(getNullableType(dependencyMethod))
               .kind(COMPONENT_PROVISION)
-              .scope(uniqueScopeOf(dependencyMethod));
+              .scope(injectionAnnotations.getScope(dependencyMethod));
     }
     return builder
         .contributionType(ContributionType.UNIQUE)
@@ -377,15 +351,15 @@
   }
 
   /**
-   * Returns a {@link dagger.model.BindingKind#BOUND_INSTANCE} binding for a
+   * Returns a {@link dagger.spi.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());
+  ProvisionBinding boundInstanceBinding(ComponentRequirement requirement, XElement element) {
+    checkArgument(isVariableElement(element) || isMethod(element));
+    XVariableElement parameterElement =
+        isVariableElement(element)
+            ? asVariable(element)
+            : getOnlyElement(asMethod(element).getParameters());
     return ProvisionBinding.builder()
         .contributionType(ContributionType.UNIQUE)
         .bindingElement(element)
@@ -396,20 +370,18 @@
   }
 
   /**
-   * Returns a {@link dagger.model.BindingKind#SUBCOMPONENT_CREATOR} binding declared by a component
-   * method that returns a subcomponent builder. Use {{@link
+   * Returns a {@link dagger.spi.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));
+      XMethodElement subcomponentCreatorMethod, XTypeElement component) {
     checkArgument(subcomponentCreatorMethod.getParameters().isEmpty());
     Key key =
-        keyFactory.forSubcomponentCreatorMethod(
-            subcomponentCreatorMethod, asDeclared(component.asType()));
+        keyFactory.forSubcomponentCreatorMethod(subcomponentCreatorMethod, component.getType());
     return ProvisionBinding.builder()
         .contributionType(ContributionType.UNIQUE)
         .bindingElement(subcomponentCreatorMethod)
@@ -419,8 +391,8 @@
   }
 
   /**
-   * Returns a {@link dagger.model.BindingKind#SUBCOMPONENT_CREATOR} binding declared using {@link
-   * Module#subcomponents()}.
+   * Returns a {@link dagger.spi.model.BindingKind#SUBCOMPONENT_CREATOR} binding declared using
+   * {@link Module#subcomponents()}.
    */
   ProvisionBinding subcomponentCreatorBinding(
       ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations) {
@@ -433,7 +405,7 @@
   }
 
   /**
-   * Returns a {@link dagger.model.BindingKind#DELEGATE} binding.
+   * Returns a {@link dagger.spi.model.BindingKind#DELEGATE} binding.
    *
    * @param delegateDeclaration the {@code @Binds}-annotated declaration
    * @param actualBinding the binding that satisfies the {@code @Binds} declaration
@@ -445,15 +417,15 @@
         return buildDelegateBinding(
             ProductionBinding.builder().nullableType(actualBinding.nullableType()),
             delegateDeclaration,
-            Producer.class);
+            TypeNames.PRODUCER);
 
       case PROVISION:
         return buildDelegateBinding(
             ProvisionBinding.builder()
-                .scope(uniqueScopeOf(delegateDeclaration.bindingElement().get()))
+                .scope(injectionAnnotations.getScope(delegateDeclaration.bindingElement().get()))
                 .nullableType(actualBinding.nullableType()),
             delegateDeclaration,
-            Provider.class);
+            TypeNames.PROVIDER);
 
       case MEMBERS_INJECTION: // fall-through to throw
     }
@@ -461,28 +433,25 @@
   }
 
   /**
-   * Returns a {@link dagger.model.BindingKind#DELEGATE} binding used when there is no binding that
-   * satisfies the {@code @Binds} declaration.
+   * Returns a {@link dagger.spi.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())),
+        ProvisionBinding.builder()
+            .scope(injectionAnnotations.getScope(delegateDeclaration.bindingElement().get())),
         delegateDeclaration,
-        Provider.class);
+        TypeNames.PROVIDER);
   }
 
   private ContributionBinding buildDelegateBinding(
       ContributionBinding.Builder<?, ?> builder,
       DelegateDeclaration delegateDeclaration,
-      Class<?> frameworkType) {
-    boolean isKotlinObject =
-        metadataUtil.isObjectClass(delegateDeclaration.contributingModule().get())
-            || metadataUtil.isCompanionObjectClass(delegateDeclaration.contributingModule().get());
+      ClassName frameworkType) {
     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())
@@ -491,7 +460,7 @@
   }
 
   /**
-   * Returns an {@link dagger.model.BindingKind#OPTIONAL} binding for {@code key}.
+   * Returns an {@link dagger.spi.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
@@ -523,56 +492,78 @@
         .build();
   }
 
-  /** Returns a {@link dagger.model.BindingKind#MEMBERS_INJECTOR} binding. */
+  /** Returns a {@link dagger.spi.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()))
+        .bindingElement(membersInjectionBinding.key().type().xprocessing().getTypeElement())
         .provisionDependencies(membersInjectionBinding.dependencies())
         .injectionSites(membersInjectionBinding.injectionSites())
         .build();
   }
 
   /**
-   * Returns a {@link dagger.model.BindingKind#MEMBERS_INJECTION} binding.
+   * Returns a {@link dagger.spi.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) {
+  public MembersInjectionBinding membersInjectionBinding(XType type, Optional<XType> 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;
+    if (!type.getTypeArguments().isEmpty() && resolvedType.isPresent()) {
+      checkIsSameErasedType(resolvedType.get(), type);
+      type = resolvedType.get();
     }
-    ImmutableSortedSet<InjectionSite> injectionSites =
-        injectionSiteFactory.getInjectionSites(declaredType);
+    ImmutableSortedSet<InjectionSite> injectionSites = injectionSiteFactory.getInjectionSites(type);
     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,
+    return MembersInjectionBinding.create(
+        keyFactory.forMembersInjectedType(type),
         dependencies,
-        typeElement,
-        hasNonDefaultTypeParameters(typeElement, key.type(), types)
+        hasNonDefaultTypeParameters(type)
             ? Optional.of(
-                membersInjectionBinding(asDeclared(typeElement.asType()), Optional.empty()))
+                membersInjectionBinding(type.getTypeElement().getType(), Optional.empty()))
             : Optional.empty(),
         injectionSites);
   }
+
+  private void checkIsSameErasedType(XType type1, XType type2) {
+    checkState(
+        types.isSameType(types.erasure(toJavac(type1)), types.erasure(toJavac(type2))),
+        "erased expected type: %s, erased actual type: %s",
+        types.erasure(toJavac(type1)),
+        types.erasure(toJavac(type2)));
+  }
+
+  private static boolean hasNonDefaultTypeParameters(XType type) {
+    // If the type is not declared, then it can't have type parameters.
+    if (!isDeclared(type)) {
+      return false;
+    }
+
+    // If the element has no type parameters, none can be non-default.
+    XType defaultType = type.getTypeElement().getType();
+    if (defaultType.getTypeArguments().isEmpty()) {
+      return false;
+    }
+
+    // The actual type parameter size can be different if the user is using a raw type.
+    if (defaultType.getTypeArguments().size() != type.getTypeArguments().size()) {
+      return true;
+    }
+
+    for (int i = 0; i < defaultType.getTypeArguments().size(); i++) {
+      if (!defaultType.getTypeArguments().get(i).isSameType(type.getTypeArguments().get(i))) {
+        return true;
+      }
+    }
+    return false;
+  }
 }
diff --git a/java/dagger/internal/codegen/binding/BindingGraph.java b/java/dagger/internal/codegen/binding/BindingGraph.java
index 533d113..ee5a54b 100644
--- a/java/dagger/internal/codegen/binding/BindingGraph.java
+++ b/java/dagger/internal/codegen/binding/BindingGraph.java
@@ -18,12 +18,16 @@
 
 import static com.google.common.collect.Iterables.transform;
 import static dagger.internal.codegen.extension.DaggerCollectors.toOptional;
+import static dagger.internal.codegen.extension.DaggerStreams.instancesOf;
 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 androidx.room.compiler.processing.XExecutableElement;
+import androidx.room.compiler.processing.XExecutableParameterElement;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.auto.value.AutoValue;
 import com.google.auto.value.extension.memoized.Memoized;
 import com.google.common.collect.ImmutableList;
@@ -31,24 +35,28 @@
 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.Multimaps;
 import com.google.common.collect.Sets;
 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.ArrayList;
-import java.util.HashMap;
-import java.util.List;
+import dagger.internal.codegen.base.TarjanSCCs;
+import dagger.spi.model.BindingGraph.ChildFactoryMethodEdge;
+import dagger.spi.model.BindingGraph.ComponentNode;
+import dagger.spi.model.BindingGraph.DependencyEdge;
+import dagger.spi.model.BindingGraph.Edge;
+import dagger.spi.model.BindingGraph.Node;
+import dagger.spi.model.ComponentPath;
+import dagger.spi.model.DaggerTypeElement;
+import dagger.spi.model.DependencyRequest;
+import dagger.spi.model.Key;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
 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;
+import java.util.Set;
+import java.util.stream.Stream;
 
 /**
  * A graph that represents a single component or subcomponent within a fully validated top-level
@@ -57,8 +65,12 @@
 @AutoValue
 public abstract class BindingGraph {
 
+  /**
+   * A graph that represents the entire network of nodes from all components, subcomponents and
+   * their bindings.
+   */
   @AutoValue
-  abstract static class TopLevelBindingGraph extends dagger.model.BindingGraph {
+  public abstract static class TopLevelBindingGraph extends dagger.spi.model.BindingGraph {
     static TopLevelBindingGraph create(
         ImmutableNetwork<Node, Edge> network, boolean isFullBindingGraph) {
       TopLevelBindingGraph topLevelBindingGraph =
@@ -82,15 +94,18 @@
       // AutoValue to prevent exposing this data outside of the class.
       topLevelBindingGraph.componentNodes = componentNodes;
       topLevelBindingGraph.subcomponentNodes = subcomponentNodesBuilder.build();
+      topLevelBindingGraph.frameworkTypeBindings =
+          frameworkRequestBindingSet(network, topLevelBindingGraph.bindings());
       return topLevelBindingGraph;
     }
 
     private ImmutableMap<ComponentPath, ComponentNode> componentNodes;
     private ImmutableSetMultimap<ComponentNode, ComponentNode> subcomponentNodes;
+    private ImmutableSet<Binding> frameworkTypeBindings;
 
     TopLevelBindingGraph() {}
 
-    // This overrides dagger.model.BindingGraph with a more efficient implementation.
+    // This overrides dagger.spi.model.BindingGraph with a more efficient implementation.
     @Override
     public Optional<ComponentNode> componentNode(ComponentPath componentPath) {
       return componentNodes.containsKey(componentPath)
@@ -117,6 +132,61 @@
     ImmutableListMultimap<ComponentPath, BindingNode> bindingsByComponent() {
       return Multimaps.index(transform(bindings(), BindingNode.class::cast), Node::componentPath);
     }
+
+    /** Returns a {@link Comparator} in the same order as {@link Network#nodes()}. */
+    @Memoized
+    Comparator<Node> nodeOrder() {
+      Map<Node, Integer> nodeOrderMap = Maps.newHashMapWithExpectedSize(network().nodes().size());
+      int i = 0;
+      for (Node node : network().nodes()) {
+        nodeOrderMap.put(node, i++);
+      }
+      return (n1, n2) -> nodeOrderMap.get(n1).compareTo(nodeOrderMap.get(n2));
+    }
+
+    /** Returns the set of strongly connected nodes in this graph in reverse topological order. */
+    @Memoized
+    public ImmutableSet<ImmutableSet<Node>> stronglyConnectedNodes() {
+      return TarjanSCCs.<Node>compute(
+          ImmutableSet.copyOf(network().nodes()),
+          // NetworkBuilder does not have a stable successor order, so we have to roll our own
+          // based on the node order, which is stable.
+          // TODO(bcorso): Fix once https://github.com/google/guava/issues/2650 is fixed.
+          node ->
+              network().successors(node).stream().sorted(nodeOrder()).collect(toImmutableList()));
+    }
+
+    public boolean hasFrameworkRequest(Binding binding) {
+      return frameworkTypeBindings.contains(binding);
+    }
+
+    private static ImmutableSet<Binding> frameworkRequestBindingSet(
+        ImmutableNetwork<Node, Edge> network, ImmutableSet<dagger.spi.model.Binding> bindings) {
+      Set<Binding> frameworkRequestBindings = new HashSet<>();
+      for (dagger.spi.model.Binding binding : bindings) {
+        ImmutableList<DependencyEdge> edges =
+            network.inEdges(binding).stream()
+                .flatMap(instancesOf(DependencyEdge.class))
+                .collect(toImmutableList());
+        for (DependencyEdge edge : edges) {
+          DependencyRequest request = edge.dependencyRequest();
+          switch (request.kind()) {
+            case INSTANCE:
+            case FUTURE:
+              continue;
+            case PRODUCED:
+            case PRODUCER:
+            case MEMBERS_INJECTION:
+            case PROVIDER_OF_LAZY:
+            case LAZY:
+            case PROVIDER:
+              frameworkRequestBindings.add(((BindingNode) binding).delegate());
+              break;
+          }
+        }
+      }
+      return ImmutableSet.copyOf(frameworkRequestBindings);
+    }
   }
 
   static BindingGraph create(
@@ -128,42 +198,34 @@
       Optional<BindingGraph> parent,
       ComponentNode componentNode,
       TopLevelBindingGraph topLevelBindingGraph) {
-    List<BindingNode> reachableBindingNodes = new ArrayList<>();
-    for (ComponentPath path = componentNode.componentPath();
-        !path.components().isEmpty();
-        path = ComponentPath.create(path.components().subList(0, path.components().size() - 1))) {
-      reachableBindingNodes.addAll(topLevelBindingGraph.bindingsByComponent().get(path));
-    }
+    // 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 consistent 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.
+    Map<Key, BindingNode> contributionBindings = new LinkedHashMap<>();
+    Map<Key, BindingNode> membersInjectionBindings = new LinkedHashMap<>();
 
-    // 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);
-      }
-    }
+    // Construct the maps of the ContributionBindings and MembersInjectionBindings by iterating
+    // bindings from this component and then from each successive parent. If a binding exists in
+    // multple components, this order ensures that the child-most binding is always chosen first.
+    Stream.iterate(componentNode.componentPath(), ComponentPath::parent)
+        // Stream.iterate is inifinte stream so we need limit it to the known size of the path.
+        .limit(componentNode.componentPath().components().size())
+        .flatMap(path -> topLevelBindingGraph.bindingsByComponent().get(path).stream())
+        .forEach(
+            bindingNode -> {
+              if (bindingNode.delegate() instanceof ContributionBinding) {
+                contributionBindings.putIfAbsent(bindingNode.key(), bindingNode);
+              } else if (bindingNode.delegate() instanceof MembersInjectionBinding) {
+                membersInjectionBindings.putIfAbsent(bindingNode.key(), bindingNode);
+              } else {
+                throw new AssertionError("Unexpected binding node type: " + bindingNode.delegate());
+              }
+            });
 
     BindingGraph bindingGraph = new AutoValue_BindingGraph(componentNode, topLevelBindingGraph);
 
@@ -185,6 +247,7 @@
         contributionBindings.values().stream()
             .map(BindingNode::contributingModule)
             .flatMap(presentValues())
+            .map(DaggerTypeElement::xprocessing)
             .collect(toImmutableSet());
 
     return bindingGraph;
@@ -194,7 +257,7 @@
   private ImmutableMap<Key, BindingNode> membersInjectionBindings;
   private ImmutableSet<ModuleDescriptor> inheritedModules;
   private ImmutableSet<ModuleDescriptor> ownedModules;
-  private ImmutableSet<TypeElement> bindingModules;
+  private ImmutableSet<XTypeElement> bindingModules;
 
   BindingGraph() {}
 
@@ -214,6 +277,30 @@
     return ((ComponentNodeImpl) componentNode()).componentDescriptor();
   }
 
+  /**
+   * Returns the {@link ContributionBinding} for the given {@link Key} in this component or {@link
+   * Optional#empty()} if one doesn't exist.
+   */
+  public final Optional<Binding> localContributionBinding(Key key) {
+    return contributionBindings.containsKey(key)
+        ? Optional.of(contributionBindings.get(key))
+            .filter(bindingNode -> bindingNode.componentPath().equals(componentPath()))
+            .map(BindingNode::delegate)
+        : Optional.empty();
+  }
+
+  /**
+   * Returns the {@link MembersInjectionBinding} for the given {@link Key} in this component or
+   * {@link Optional#empty()} if one doesn't exist.
+   */
+  public final Optional<Binding> localMembersInjectionBinding(Key key) {
+    return membersInjectionBindings.containsKey(key)
+        ? Optional.of(membersInjectionBindings.get(key))
+            .filter(bindingNode -> bindingNode.componentPath().equals(componentPath()))
+            .map(BindingNode::delegate)
+        : Optional.empty();
+  }
+
   /** Returns the {@link ContributionBinding} for the given {@link Key}. */
   public final ContributionBinding contributionBinding(Key key) {
     return (ContributionBinding) contributionBindings.get(key).delegate();
@@ -229,9 +316,9 @@
         : Optional.empty();
   }
 
-  /** Returns the {@link TypeElement} for the component this graph represents. */
-  public final TypeElement componentTypeElement() {
-    return componentPath().currentComponent();
+  /** Returns the {@link XTypeElement} for the component this graph represents. */
+  public final XTypeElement componentTypeElement() {
+    return componentPath().currentComponent().xprocessing();
   }
 
   /**
@@ -242,8 +329,10 @@
    * 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());
+  public final ImmutableSet<XTypeElement> ownedModuleTypes() {
+    return ownedModules.stream()
+        .map(ModuleDescriptor::moduleElement)
+        .collect(toImmutableSet());
   }
 
   /**
@@ -252,7 +341,7 @@
    * <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)} .
+   * would return the {@link XExecutableElement}: {@code childComponent(ChildModule1)} .
    *
    * <pre><code>
    *   {@literal @Component}
@@ -262,24 +351,25 @@
    * </code></pre>
    */
   // TODO(b/73294201): Consider returning the resolved ExecutableType for the factory method.
-  public final Optional<ExecutableElement> factoryMethod() {
+  public final Optional<XExecutableElement> factoryMethod() {
     return topLevelBindingGraph().network().inEdges(componentNode()).stream()
         .filter(edge -> edge instanceof ChildFactoryMethodEdge)
-        .map(edge -> ((ChildFactoryMethodEdge) edge).factoryMethod())
+        .map(edge -> ((ChildFactoryMethodEdge) edge).factoryMethod().xprocessing())
         .collect(toOptional());
   }
 
   /**
    * Returns a map between the {@linkplain ComponentRequirement component requirement} and the
-   * corresponding {@link VariableElement} for each module parameter in the {@linkplain
+   * corresponding {@link XExecutableParameterElement} 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() {
+  public final ImmutableMap<ComponentRequirement, XExecutableParameterElement>
+      factoryMethodParameters() {
     return factoryMethod().get().getParameters().stream()
         .collect(
             toImmutableMap(
-                parameter -> ComponentRequirement.forModule(parameter.asType()),
+                parameter -> ComponentRequirement.forModule(parameter.getType()),
                 parameter -> parameter));
   }
 
@@ -294,7 +384,7 @@
    */
   @Memoized
   public ImmutableSet<ComponentRequirement> componentRequirements() {
-    ImmutableSet<TypeElement> requiredModules =
+    ImmutableSet<XTypeElement> requiredModules =
         stream(Traverser.forTree(BindingGraph::subgraphs).depthFirstPostOrder(this))
             .flatMap(graph -> graph.bindingModules.stream())
             .filter(ownedModuleTypes()::contains)
@@ -312,11 +402,16 @@
     return requirements.build();
   }
 
-  /** Returns all {@link ComponentDescriptor}s in the {@link TopLevelBindingGraph}. */
-  public final ImmutableSet<ComponentDescriptor> componentDescriptors() {
+  /**
+   * Returns all {@link ComponentDescriptor}s in the {@link TopLevelBindingGraph} mapped by the
+   * component path.
+   */
+  @Memoized
+  public ImmutableMap<ComponentPath, ComponentDescriptor> componentDescriptorsByPath() {
     return topLevelBindingGraph().componentNodes().stream()
-        .map(componentNode -> ((ComponentNodeImpl) componentNode).componentDescriptor())
-        .collect(toImmutableSet());
+        .map(ComponentNodeImpl.class::cast)
+        .collect(
+            toImmutableMap(ComponentNode::componentPath, ComponentNodeImpl::componentDescriptor));
   }
 
   @Memoized
@@ -326,15 +421,9 @@
         .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();
+  /** Returns the list of all {@link BindingNode}s local to this component. */
+  public ImmutableList<BindingNode> localBindingNodes() {
+    return topLevelBindingGraph().bindingsByComponent().get(componentPath());
   }
 
   @Memoized
diff --git a/java/dagger/internal/codegen/binding/BindingGraphConverter.java b/java/dagger/internal/codegen/binding/BindingGraphConverter.java
index b882b37..7d24bca 100644
--- a/java/dagger/internal/codegen/binding/BindingGraphConverter.java
+++ b/java/dagger/internal/codegen/binding/BindingGraphConverter.java
@@ -16,13 +16,18 @@
 
 package dagger.internal.codegen.binding;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static androidx.room.compiler.processing.compat.XConverters.toXProcessing;
 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 static dagger.spi.model.BindingKind.SUBCOMPONENT_CREATOR;
 
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.auto.value.AutoValue;
 import com.google.auto.value.extension.memoized.Memoized;
 import com.google.common.collect.ImmutableList;
@@ -35,14 +40,16 @@
 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 dagger.spi.model.BindingGraph.ComponentNode;
+import dagger.spi.model.BindingGraph.DependencyEdge;
+import dagger.spi.model.BindingGraph.Edge;
+import dagger.spi.model.BindingGraph.MissingBinding;
+import dagger.spi.model.BindingGraph.Node;
+import dagger.spi.model.ComponentPath;
+import dagger.spi.model.DaggerExecutableElement;
+import dagger.spi.model.DaggerTypeElement;
+import dagger.spi.model.DependencyRequest;
+import dagger.spi.model.Key;
 import java.util.ArrayDeque;
 import java.util.Deque;
 import java.util.HashMap;
@@ -54,18 +61,21 @@
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.type.TypeMirror;
 
-/** Converts {@link BindingGraph}s to {@link dagger.model.BindingGraph}s. */
+/** Converts {@link BindingGraph}s to {@link dagger.spi.model.BindingGraph}s. */
 final class BindingGraphConverter {
+  private final XProcessingEnv processingEnv;
   private final BindingDeclarationFormatter bindingDeclarationFormatter;
 
   @Inject
-  BindingGraphConverter(BindingDeclarationFormatter bindingDeclarationFormatter) {
+  BindingGraphConverter(
+      XProcessingEnv processingEnv, BindingDeclarationFormatter bindingDeclarationFormatter) {
+    this.processingEnv = processingEnv;
     this.bindingDeclarationFormatter = bindingDeclarationFormatter;
   }
 
   /**
-   * Creates the external {@link dagger.model.BindingGraph} representing the given internal {@link
-   * BindingGraph}.
+   * Creates the external {@link dagger.spi.model.BindingGraph} representing the given internal
+   * {@link BindingGraph}.
    */
   BindingGraph convert(LegacyBindingGraph legacyBindingGraph, boolean isFullBindingGraph) {
     MutableNetwork<Node, Edge> network = asNetwork(legacyBindingGraph);
@@ -86,7 +96,7 @@
   }
 
   private MutableNetwork<Node, Edge> asNetwork(LegacyBindingGraph graph) {
-    Converter converter = new Converter(bindingDeclarationFormatter);
+    Converter converter = new Converter();
     converter.visitRootComponent(graph);
     return converter.network;
   }
@@ -116,14 +126,13 @@
     }
   }
 
-  private static final class Converter {
+  private 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<>();
@@ -131,11 +140,6 @@
     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);
     }
@@ -164,6 +168,7 @@
               bindingGraphPath.stream()
                   .map(LegacyBindingGraph::componentDescriptor)
                   .map(ComponentDescriptor::typeElement)
+                  .map(DaggerTypeElement::from)
                   .collect(toImmutableList()));
       componentPaths.addLast(graphPath);
       ComponentNode currentComponent =
@@ -188,7 +193,7 @@
               && binding.componentPath().equals(currentComponent.componentPath())) {
             network.addEdge(
                 binding,
-                subcomponentNode(binding.key().type(), graph),
+                subcomponentNode(binding.key().type().java(), graph),
                 new SubcomponentCreatorBindingEdgeImpl(
                     resolvedBindings.subcomponentDeclarations()));
           }
@@ -235,11 +240,11 @@
     private void visitSubcomponentFactoryMethod(
         ComponentNode parentComponent,
         ComponentNode currentComponent,
-        ExecutableElement factoryMethod) {
+        XMethodElement factoryMethod) {
       network.addEdge(
           parentComponent,
           currentComponent,
-          new ChildFactoryMethodEdgeImpl(factoryMethod));
+          new ChildFactoryMethodEdgeImpl(DaggerExecutableElement.from(factoryMethod)));
     }
 
     /**
@@ -256,7 +261,7 @@
      */
     private ComponentPath pathFromRootToAncestor(TypeElement ancestor) {
       for (ComponentPath componentPath : componentPaths) {
-        if (componentPath.currentComponent().equals(ancestor)) {
+        if (componentPath.currentComponent().java().equals(ancestor)) {
           return componentPath;
         }
       }
@@ -271,7 +276,7 @@
      */
     private LegacyBindingGraph graphForAncestor(TypeElement ancestor) {
       for (LegacyBindingGraph graph : bindingGraphPath) {
-        if (graph.componentDescriptor().typeElement().equals(ancestor)) {
+        if (toJavac(graph.componentDescriptor().typeElement()).equals(ancestor)) {
           return graph;
         }
       }
@@ -281,8 +286,8 @@
     }
 
     /**
-     * Adds a {@link dagger.model.BindingGraph.DependencyEdge} from a node to the binding(s) that
-     * satisfy a dependency request.
+     * Adds a {@link dagger.spi.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);
@@ -325,7 +330,7 @@
 
     private ResolvedBindings resolvedDependencies(
         Node source, DependencyRequest dependencyRequest) {
-      return graphForAncestor(source.componentPath().currentComponent())
+      return graphForAncestor(source.componentPath().currentComponent().java())
           .resolvedBindings(bindingRequest(dependencyRequest));
     }
 
@@ -373,11 +378,13 @@
 
     private ComponentNode subcomponentNode(
         TypeMirror subcomponentBuilderType, LegacyBindingGraph graph) {
-      TypeElement subcomponentBuilderElement = asTypeElement(subcomponentBuilderType);
+      XTypeElement subcomponentBuilderElement =
+          toXProcessing(asTypeElement(subcomponentBuilderType), processingEnv);
       ComponentDescriptor subcomponent =
           graph.componentDescriptor().getChildComponentWithBuilderType(subcomponentBuilderElement);
       return ComponentNodeImpl.create(
-          componentPath().childPath(subcomponent.typeElement()), subcomponent);
+          componentPath().childPath(DaggerTypeElement.from(subcomponent.typeElement())),
+          subcomponent);
     }
   }
 
diff --git a/java/dagger/internal/codegen/binding/BindingGraphFactory.java b/java/dagger/internal/codegen/binding/BindingGraphFactory.java
index a94f6b0..ab0e907 100644
--- a/java/dagger/internal/codegen/binding/BindingGraphFactory.java
+++ b/java/dagger/internal/codegen/binding/BindingGraphFactory.java
@@ -16,7 +16,8 @@
 
 package dagger.internal.codegen.binding;
 
-import static com.google.auto.common.MoreTypes.asTypeElement;
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static androidx.room.compiler.processing.compat.XConverters.toXProcessing;
 import static com.google.auto.common.MoreTypes.isType;
 import static com.google.auto.common.MoreTypes.isTypeOf;
 import static com.google.common.base.Preconditions.checkArgument;
@@ -26,16 +27,19 @@
 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 dagger.internal.codegen.xprocessing.XElements.asMethod;
+import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
+import static dagger.spi.model.BindingKind.ASSISTED_INJECTION;
+import static dagger.spi.model.BindingKind.DELEGATE;
+import static dagger.spi.model.BindingKind.INJECTION;
+import static dagger.spi.model.BindingKind.OPTIONAL;
+import static dagger.spi.model.BindingKind.SUBCOMPONENT_CREATOR;
+import static dagger.spi.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 androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -47,17 +51,17 @@
 import dagger.Reusable;
 import dagger.internal.codegen.base.ClearableCache;
 import dagger.internal.codegen.base.ContributionType;
+import dagger.internal.codegen.base.DaggerSuperficialValidation;
 import dagger.internal.codegen.base.Keys;
 import dagger.internal.codegen.base.MapType;
 import dagger.internal.codegen.base.OptionalType;
 import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.javapoet.TypeNames;
 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 dagger.spi.model.DependencyRequest;
+import dagger.spi.model.Key;
+import dagger.spi.model.Scope;
 import java.util.ArrayDeque;
 import java.util.Deque;
 import java.util.HashMap;
@@ -70,16 +74,15 @@
 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 XProcessingEnv processingEnv;
   private final DaggerElements elements;
   private final InjectBindingRegistry injectBindingRegistry;
   private final KeyFactory keyFactory;
@@ -91,6 +94,7 @@
 
   @Inject
   BindingGraphFactory(
+      XProcessingEnv processingEnv,
       DaggerElements elements,
       InjectBindingRegistry injectBindingRegistry,
       KeyFactory keyFactory,
@@ -98,6 +102,7 @@
       ModuleDescriptor.Factory moduleDescriptorFactory,
       BindingGraphConverter bindingGraphConverter,
       CompilerOptions compilerOptions) {
+    this.processingEnv = processingEnv;
     this.elements = elements;
     this.injectBindingRegistry = injectBindingRegistry;
     this.keyFactory = keyFactory;
@@ -138,7 +143,7 @@
     for (ComponentRequirement dependency : componentDescriptor.dependencies()) {
       explicitBindingsBuilder.add(bindingFactory.componentDependencyBinding(dependency));
       List<ExecutableElement> dependencyMethods =
-          methodsIn(elements.getAllMembers(dependency.typeElement()));
+          methodsIn(elements.getAllMembers(toJavac(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
@@ -147,9 +152,10 @@
       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 (isComponentContributionMethod(method)) {
+          ContributionBinding binding =
+              bindingFactory.componentDependencyMethodBinding(
+                  componentDescriptor, asMethod(toXProcessing(method, processingEnv)));
           if (dedupeBindings.put(
               method.getSimpleName().toString(),
               // Remove the binding element since we know that will be different, but everything
@@ -284,15 +290,16 @@
    * @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) {
+  private ModuleDescriptor descriptorForMonitoringModule(XTypeElement componentDefinitionType) {
     return moduleDescriptorFactory.create(
-        elements.checkTypePresent(
-            generatedMonitoringModuleName(componentDefinitionType).toString()));
+        DaggerSuperficialValidation.requireTypeElement(
+            processingEnv, generatedMonitoringModuleName(componentDefinitionType)));
   }
 
   /** Returns a descriptor {@link ProductionExecutorModule}. */
   private ModuleDescriptor descriptorForProductionExecutorModule() {
-    return moduleDescriptorFactory.create(elements.getTypeElement(ProductionExecutorModule.class));
+    return moduleDescriptorFactory.create(
+        processingEnv.findTypeElement(TypeNames.PRODUCTION_EXECTUTOR_MODULE));
   }
 
   /** Indexes {@code bindingDeclarations} by {@link BindingDeclaration#key()}. */
@@ -411,19 +418,21 @@
       }
 
       // Add members injector binding
-      if (isType(requestKey.type()) && isTypeOf(MembersInjector.class, requestKey.type())) {
+      if (isType(requestKey.type().java())
+          && isTypeOf(MembersInjector.class, requestKey.type().java())) {
         injectBindingRegistry
             .getOrFindMembersInjectorProvisionBinding(requestKey)
             .ifPresent(bindings::add);
       }
 
       // Add Assisted Factory binding
-      if (isType(requestKey.type())
-          && requestKey.type().getKind() == TypeKind.DECLARED
-          && isAssistedFactoryType(asTypeElement(requestKey.type()))) {
+      if (isType(requestKey.type().java())
+          && isDeclared(requestKey.type().xprocessing())
+          && isAssistedFactoryType(requestKey.type().xprocessing().getTypeElement())) {
         bindings.add(
             bindingFactory.assistedFactoryBinding(
-                asTypeElement(requestKey.type()), Optional.of(requestKey.type())));
+                requestKey.type().xprocessing().getTypeElement(),
+                Optional.of(requestKey.type().xprocessing())));
       }
 
       // If there are no bindings, add the implicit @Inject-constructed binding if there is one.
@@ -486,7 +495,8 @@
       checkArgument(subcomponentCreatorBinding.kind().equals(SUBCOMPONENT_CREATOR));
       Resolver owningResolver = getOwningResolver(subcomponentCreatorBinding).get();
 
-      TypeElement builderType = MoreTypes.asTypeElement(subcomponentCreatorBinding.key().type());
+      XTypeElement builderType =
+          subcomponentCreatorBinding.key().type().xprocessing().getTypeElement();
       owningResolver.subcomponentsToResolve.add(
           owningResolver.componentDescriptor.getChildComponentWithBuilderType(builderType));
     }
@@ -515,9 +525,13 @@
     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);
+      keyFactory.unwrapSetKey(requestKey, TypeNames.PRODUCED).ifPresent(keys::add);
+      keyFactory
+          .rewrapMapKey(requestKey, TypeNames.PRODUCER, TypeNames.PROVIDER)
+          .ifPresent(keys::add);
+      keyFactory
+          .rewrapMapKey(requestKey, TypeNames.PROVIDER, TypeNames.PRODUCER)
+          .ifPresent(keys::add);
       keys.addAll(keyFactory.implicitFrameworkMapKeys(requestKey));
       return keys.build();
     }
@@ -585,7 +599,7 @@
             parentResolver.get().resolvedContributionBindings.get(requestKey);
         return parentResolvedBindings.owningComponent(binding);
       } else {
-        return componentDescriptor.typeElement();
+        return toJavac(componentDescriptor.typeElement());
       }
     }
 
diff --git a/java/dagger/internal/codegen/binding/BindingNode.java b/java/dagger/internal/codegen/binding/BindingNode.java
index 78d440d..aa0f6cb 100644
--- a/java/dagger/internal/codegen/binding/BindingNode.java
+++ b/java/dagger/internal/codegen/binding/BindingNode.java
@@ -24,25 +24,25 @@
 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 dagger.spi.model.BindingKind;
+import dagger.spi.model.ComponentPath;
+import dagger.spi.model.DaggerElement;
+import dagger.spi.model.DaggerTypeElement;
+import dagger.spi.model.DependencyRequest;
+import dagger.spi.model.Key;
+import dagger.spi.model.Scope;
 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.
+ * An implementation of {@link dagger.spi.model.Binding} that also exposes {@link
+ * BindingDeclaration}s associated with the binding.
  */
-// TODO(dpb): Consider a supertype of dagger.model.Binding that
+// TODO(dpb): Consider a supertype of dagger.spi.model.Binding that
 // dagger.internal.codegen.binding.Binding
 // could also implement.
 @AutoValue
-public abstract class BindingNode implements dagger.model.Binding {
+public abstract class BindingNode implements dagger.spi.model.Binding {
   public static BindingNode create(
       ComponentPath component,
       Binding delegate,
@@ -72,8 +72,8 @@
   public abstract ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations();
 
   /**
-   * The {@link Element}s (other than the binding's {@link #bindingElement()}) that are associated
-   * with the binding.
+   * The elements (other than the binding's {@link #bindingElement()}) that are associated with the
+   * binding.
    *
    * <ul>
    *   <li>{@linkplain BindsOptionalOf optional binding} declarations
@@ -97,13 +97,13 @@
   }
 
   @Override
-  public Optional<Element> bindingElement() {
-    return delegate().bindingElement();
+  public Optional<DaggerElement> bindingElement() {
+    return delegate().bindingElement().map(DaggerElement::from);
   }
 
   @Override
-  public Optional<TypeElement> contributingModule() {
-    return delegate().contributingModule();
+  public Optional<DaggerTypeElement> contributingModule() {
+    return delegate().contributingModule().map(DaggerTypeElement::from);
   }
 
   @Override
diff --git a/java/dagger/internal/codegen/binding/BindingRequest.java b/java/dagger/internal/codegen/binding/BindingRequest.java
index d61d9cf..6bb572d 100644
--- a/java/dagger/internal/codegen/binding/BindingRequest.java
+++ b/java/dagger/internal/codegen/binding/BindingRequest.java
@@ -16,13 +16,15 @@
 
 package dagger.internal.codegen.binding;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 import static dagger.internal.codegen.base.RequestKinds.requestType;
 
+import androidx.room.compiler.processing.XType;
 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 dagger.spi.model.DependencyRequest;
+import dagger.spi.model.Key;
+import dagger.spi.model.RequestKind;
 import java.util.Optional;
 import javax.lang.model.type.TypeMirror;
 
@@ -47,12 +49,12 @@
     // 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.
+    // TODO(cgdecker): Instead of doing this, make ComponentRequestRepresentations create a
+    // RequestRepresentation for the RequestKind that simply delegates to the RequestRepresentation
+    // for the FrameworkType. Then there are separate RequestRepresentations, 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));
+        key, requestKind, FrameworkType.forRequestKind(requestKind));
   }
 
   /**
@@ -67,30 +69,27 @@
   /** 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 request kind associated with this request. */
+  public abstract 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));
+    return requestKind.equals(requestKind());
+  }
+
+  public final TypeMirror requestedType(XType contributedType, DaggerTypes types) {
+    return requestedType(toJavac(contributedType), types);
   }
 
   public final TypeMirror requestedType(TypeMirror contributedType, DaggerTypes types) {
-    if (requestKind().isPresent()) {
-      return requestType(requestKind().get(), contributedType, types);
-    }
-    return types.wrapType(contributedType, frameworkType().get().frameworkClass());
+    return requestType(requestKind(), contributedType, types);
   }
 
   /** 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();
+    return requestKind().toString();
   }
 }
diff --git a/java/dagger/internal/codegen/binding/BindsTypeChecker.java b/java/dagger/internal/codegen/binding/BindsTypeChecker.java
index d850fd3..2010597 100644
--- a/java/dagger/internal/codegen/binding/BindsTypeChecker.java
+++ b/java/dagger/internal/codegen/binding/BindsTypeChecker.java
@@ -16,15 +16,16 @@
 
 package dagger.internal.codegen.binding;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 import static com.google.common.collect.Iterables.getOnlyElement;
 
+import androidx.room.compiler.processing.XType;
 import com.google.auto.common.MoreTypes;
 import com.google.common.collect.ImmutableList;
 import dagger.internal.codegen.base.ContributionType;
+import dagger.internal.codegen.javapoet.TypeNames;
 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;
@@ -35,14 +36,14 @@
  * 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.
+ * dagger.internal.codegen.writing.DelegateRequestRepresentation} 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.
+  // TODO(bcorso): Make this pkg-private. Used by DelegateRequestRepresentation.
   @Inject
   public BindsTypeChecker(DaggerTypes types, DaggerElements elements) {
     this.types = types;
@@ -54,6 +55,15 @@
    * ContributionType} context.
    */
   public boolean isAssignable(
+      XType rightHandSide, XType leftHandSide, ContributionType contributionType) {
+    return isAssignable(toJavac(rightHandSide), toJavac(leftHandSide), contributionType);
+  }
+
+  /**
+   * 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));
   }
@@ -67,6 +77,7 @@
         DeclaredType parameterizedSetType = types.getDeclaredType(setElement(), leftHandSide);
         return methodParameterType(parameterizedSetType, "add");
       case SET_VALUES:
+        // TODO(b/211774331): The left hand side type should be limited to Set types.
         return methodParameterType(MoreTypes.asDeclared(leftHandSide), "addAll");
       case MAP:
         DeclaredType parameterizedMapType =
@@ -98,11 +109,11 @@
   }
 
   private TypeElement setElement() {
-    return elements.getTypeElement(Set.class);
+    return elements.getTypeElement(TypeNames.SET);
   }
 
   private TypeElement mapElement() {
-    return elements.getTypeElement(Map.class);
+    return elements.getTypeElement(TypeNames.MAP);
   }
 
   private TypeMirror unboundedWildcard() {
diff --git a/java/dagger/internal/codegen/binding/ChildFactoryMethodEdgeImpl.java b/java/dagger/internal/codegen/binding/ChildFactoryMethodEdgeImpl.java
index c056588..465d17e 100644
--- a/java/dagger/internal/codegen/binding/ChildFactoryMethodEdgeImpl.java
+++ b/java/dagger/internal/codegen/binding/ChildFactoryMethodEdgeImpl.java
@@ -18,25 +18,25 @@
 
 import static dagger.internal.codegen.base.ElementFormatter.elementToString;
 
-import dagger.model.BindingGraph.ChildFactoryMethodEdge;
-import javax.lang.model.element.ExecutableElement;
+import dagger.spi.model.BindingGraph.ChildFactoryMethodEdge;
+import dagger.spi.model.DaggerExecutableElement;
 
 /** An implementation of {@link ChildFactoryMethodEdge}. */
 public final class ChildFactoryMethodEdgeImpl implements ChildFactoryMethodEdge {
 
-  private final ExecutableElement factoryMethod;
+  private final DaggerExecutableElement factoryMethod;
 
-  ChildFactoryMethodEdgeImpl(ExecutableElement factoryMethod) {
+  ChildFactoryMethodEdgeImpl(DaggerExecutableElement factoryMethod) {
     this.factoryMethod = factoryMethod;
   }
 
   @Override
-  public ExecutableElement factoryMethod() {
+  public DaggerExecutableElement factoryMethod() {
     return factoryMethod;
   }
 
   @Override
   public String toString() {
-    return elementToString(factoryMethod);
+    return elementToString(factoryMethod.java());
   }
 }
diff --git a/java/dagger/internal/codegen/binding/ComponentCreatorDescriptor.java b/java/dagger/internal/codegen/binding/ComponentCreatorDescriptor.java
index 5ea30ed..29d5b08 100644
--- a/java/dagger/internal/codegen/binding/ComponentCreatorDescriptor.java
+++ b/java/dagger/internal/codegen/binding/ComponentCreatorDescriptor.java
@@ -16,15 +16,19 @@
 
 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.base.ComponentCreatorAnnotation.getCreatorAnnotations;
+import static dagger.internal.codegen.base.ModuleAnnotation.moduleAnnotations;
 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.xprocessing.XTypeElements.getAllUnimplementedMethods;
 
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XExecutableParameterElement;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XMethodType;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.auto.value.AutoValue;
 import com.google.auto.value.extension.memoized.Memoized;
 import com.google.common.collect.ImmutableMap;
@@ -32,18 +36,12 @@
 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.base.ComponentCreatorAnnotation;
+import dagger.internal.codegen.base.ComponentCreatorKind;
+import dagger.internal.codegen.javapoet.TypeNames;
 import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.DependencyRequest;
+import dagger.spi.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
@@ -61,10 +59,10 @@
   }
 
   /** The annotated creator type. */
-  public abstract TypeElement typeElement();
+  public abstract XTypeElement typeElement();
 
   /** The method that creates and returns a component instance. */
-  public abstract ExecutableElement factoryMethod();
+  public abstract XMethodElement factoryMethod();
 
   /**
    * Multimap of component requirements to setter methods that set that requirement.
@@ -72,7 +70,7 @@
    * <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();
+  abstract ImmutableSetMultimap<ComponentRequirement, XMethodElement> unvalidatedSetterMethods();
 
   /**
    * Multimap of component requirements to factory method parameters that set that requirement.
@@ -80,7 +78,7 @@
    * <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>
+  abstract ImmutableSetMultimap<ComponentRequirement, XExecutableParameterElement>
       unvalidatedFactoryParameters();
 
   /**
@@ -90,7 +88,7 @@
    * <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>
+  public final ImmutableSetMultimap<ComponentRequirement, XElement>
       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
@@ -106,19 +104,19 @@
    * set them.
    */
   @Memoized
-  ImmutableMap<ComponentRequirement, Element> requirementElements() {
+  ImmutableMap<ComponentRequirement, XElement> requirementElements() {
     return flatten(unvalidatedRequirementElements());
   }
 
   /** Map of component requirements to setter methods for those requirements. */
   @Memoized
-  public ImmutableMap<ComponentRequirement, ExecutableElement> setterMethods() {
+  public ImmutableMap<ComponentRequirement, XMethodElement> setterMethods() {
     return flatten(unvalidatedSetterMethods());
   }
 
   /** Map of component requirements to factory method parameters for those requirements. */
   @Memoized
-  public ImmutableMap<ComponentRequirement, VariableElement> factoryParameters() {
+  public ImmutableMap<ComponentRequirement, XExecutableParameterElement> factoryParameters() {
     return flatten(unvalidatedFactoryParameters());
   }
 
@@ -148,31 +146,26 @@
   }
 
   /** Returns the element in this creator that sets the given {@code requirement}. */
-  final Element elementForRequirement(ComponentRequirement requirement) {
+  final XElement 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();
+      XTypeElement creator, DaggerTypes types, DependencyRequestFactory dependencyRequestFactory) {
+    XType componentType = creator.getEnclosingTypeElement().getType();
 
-    ImmutableSetMultimap.Builder<ComponentRequirement, ExecutableElement> setterMethods =
+    ImmutableSetMultimap.Builder<ComponentRequirement, XMethodElement> setterMethods =
         ImmutableSetMultimap.builder();
-
-    ExecutableElement factoryMethod = null;
-    for (ExecutableElement method : elements.getUnimplementedMethods(typeElement)) {
-      ExecutableType resolvedMethodType = MoreTypes.asExecutable(types.asMemberOf(type, method));
-
+    XMethodElement factoryMethod = null;
+    for (XMethodElement method : getAllUnimplementedMethods(creator)) {
+      XMethodType resolvedMethodType = method.asMemberOf(creator.getType());
       if (types.isSubtype(componentType, resolvedMethodType.getReturnType())) {
+        verify(factoryMethod == null); // validation should have ensured there's only 1.
         factoryMethod = method;
       } else {
-        VariableElement parameter = getOnlyElement(method.getParameters());
-        TypeMirror parameterType = getOnlyElement(resolvedMethodType.getParameterTypes());
+        XExecutableParameterElement parameter = getOnlyElement(method.getParameters());
+        XType parameterType = getOnlyElement(resolvedMethodType.getParameterTypes());
         setterMethods.put(
             requirement(method, parameter, parameterType, dependencyRequestFactory, method),
             method);
@@ -180,44 +173,46 @@
     }
     verify(factoryMethod != null); // validation should have ensured this.
 
-    ImmutableSetMultimap.Builder<ComponentRequirement, VariableElement> factoryParameters =
-        ImmutableSetMultimap.builder();
+    ImmutableSetMultimap.Builder<ComponentRequirement, XExecutableParameterElement>
+        factoryParameters = ImmutableSetMultimap.builder();
 
-    ExecutableType resolvedFactoryMethodType =
-        MoreTypes.asExecutable(types.asMemberOf(type, factoryMethod));
-    List<? extends VariableElement> parameters = factoryMethod.getParameters();
-    List<? extends TypeMirror> parameterTypes = resolvedFactoryMethodType.getParameterTypes();
+    XMethodType resolvedFactoryMethodType = factoryMethod.asMemberOf(creator.getType());
+    List<XExecutableParameterElement> parameters = factoryMethod.getParameters();
+    List<XType> parameterTypes = resolvedFactoryMethodType.getParameterTypes();
     for (int i = 0; i < parameters.size(); i++) {
-      VariableElement parameter = parameters.get(i);
-      TypeMirror parameterType = parameterTypes.get(i);
+      XExecutableParameterElement parameter = parameters.get(i);
+      XType parameterType = parameterTypes.get(i);
       factoryParameters.put(
-          requirement(factoryMethod, parameter, parameterType, dependencyRequestFactory, parameter),
+          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));
+    ComponentCreatorAnnotation annotation = getOnlyElement(getCreatorAnnotations(creator));
     return new AutoValue_ComponentCreatorDescriptor(
-        annotation, typeElement, factoryMethod, setterMethods.build(), factoryParameters.build());
+        annotation, creator, factoryMethod, setterMethods.build(), factoryParameters.build());
   }
 
   private static ComponentRequirement requirement(
-      ExecutableElement method,
-      VariableElement parameter,
-      TypeMirror type,
+      XMethodElement method,
+      XExecutableParameterElement parameter,
+      XType parameterType,
       DependencyRequestFactory dependencyRequestFactory,
-      Element elementForVariableName) {
-    if (isAnnotationPresent(method, BindsInstance.class)
-        || isAnnotationPresent(parameter, BindsInstance.class)) {
+      XElement elementForVariableName) {
+    if (method.hasAnnotation(TypeNames.BINDS_INSTANCE)
+        || parameter.hasAnnotation(TypeNames.BINDS_INSTANCE)) {
       DependencyRequest request =
-          dependencyRequestFactory.forRequiredResolvedVariable(parameter, type);
-      String variableName = elementForVariableName.getSimpleName().toString();
+          dependencyRequestFactory.forRequiredResolvedVariable(parameter, parameterType);
       return ComponentRequirement.forBoundInstance(
-          request.key(), request.isNullable(), variableName);
+          request.key(), request.isNullable(), elementForVariableName);
     }
 
-    return moduleAnnotation(asTypeElement(type)).isPresent()
-        ? ComponentRequirement.forModule(type)
-        : ComponentRequirement.forDependency(type);
+    return parameterType.getTypeElement().hasAnyAnnotation(moduleAnnotations())
+        ? ComponentRequirement.forModule(parameterType)
+        : ComponentRequirement.forDependency(parameterType);
   }
 }
diff --git a/java/dagger/internal/codegen/binding/ComponentDescriptor.java b/java/dagger/internal/codegen/binding/ComponentDescriptor.java
index f6ea62c..83a8355 100644
--- a/java/dagger/internal/codegen/binding/ComponentDescriptor.java
+++ b/java/dagger/internal/codegen/binding/ComponentDescriptor.java
@@ -16,15 +16,23 @@
 
 package dagger.internal.codegen.binding;
 
+import static androidx.room.compiler.processing.XElementKt.isMethod;
+import static androidx.room.compiler.processing.XTypeKt.isVoid;
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 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 dagger.internal.codegen.langmodel.DaggerTypes.isTypeOf;
+import static dagger.internal.codegen.xprocessing.XTypes.isPrimitive;
 import static javax.lang.model.type.TypeKind.VOID;
 
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.auto.value.AutoValue;
 import com.google.auto.value.extension.memoized.Memoized;
 import com.google.common.base.Supplier;
@@ -35,24 +43,21 @@
 import com.google.common.collect.Maps;
 import com.google.errorprone.annotations.CanIgnoreReturnValue;
 import com.google.errorprone.annotations.CheckReturnValue;
+import com.squareup.javapoet.TypeName;
 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 dagger.spi.model.DependencyRequest;
+import dagger.spi.model.Scope;
 import java.util.HashMap;
 import java.util.Map;
 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;
 
 /**
@@ -67,6 +72,35 @@
  */
 @AutoValue
 public abstract class ComponentDescriptor {
+  /** Creates a {@link ComponentDescriptor}. */
+  static ComponentDescriptor create(
+      ComponentAnnotation componentAnnotation,
+      XTypeElement component,
+      ImmutableSet<ComponentRequirement> componentDependencies,
+      ImmutableSet<ModuleDescriptor> transitiveModules,
+      ImmutableMap<XMethodElement, ComponentRequirement> dependenciesByDependencyMethod,
+      ImmutableSet<Scope> scopes,
+      ImmutableSet<ComponentDescriptor> subcomponentsFromModules,
+      ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor> subcomponentsByFactoryMethod,
+      ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor> subcomponentsByBuilderMethod,
+      ImmutableSet<ComponentMethodDescriptor> componentMethods,
+      Optional<ComponentCreatorDescriptor> creator) {
+    ComponentDescriptor descriptor =
+        new AutoValue_ComponentDescriptor(
+            componentAnnotation,
+            component,
+            componentDependencies,
+            transitiveModules,
+            dependenciesByDependencyMethod,
+            scopes,
+            subcomponentsFromModules,
+            subcomponentsByFactoryMethod,
+            subcomponentsByBuilderMethod,
+            componentMethods,
+            creator);
+    return descriptor;
+  }
+
   /** The annotation that specifies that {@link #typeElement()} is a component. */
   public abstract ComponentAnnotation annotation();
 
@@ -95,11 +129,11 @@
    * The element that defines the component. This is the element to which the {@link #annotation()}
    * was applied.
    */
-  public abstract TypeElement typeElement();
+  public abstract XTypeElement typeElement();
 
   /**
    * The set of component dependencies listed in {@link Component#dependencies} or {@link
-   * ProductionComponent#dependencies()}.
+   * dagger.producers.ProductionComponent#dependencies()}.
    */
   public abstract ImmutableSet<ComponentRequirement> dependencies();
 
@@ -107,8 +141,8 @@
   public final ImmutableSet<ComponentRequirement> dependenciesAndConcreteModules() {
     return Stream.concat(
             moduleTypes().stream()
-                .filter(dep -> !dep.getModifiers().contains(ABSTRACT))
-                .map(module -> ComponentRequirement.forModule(module.asType())),
+                .filter(dep -> !dep.isAbstract())
+                .map(module -> ComponentRequirement.forModule(module.getType())),
             dependencies().stream())
         .collect(toImmutableSet());
   }
@@ -120,7 +154,7 @@
   public abstract ImmutableSet<ModuleDescriptor> modules();
 
   /** The types of the {@link #modules()}. */
-  public final ImmutableSet<TypeElement> moduleTypes() {
+  public final ImmutableSet<XTypeElement> moduleTypes() {
     return modules().stream().map(ModuleDescriptor::moduleElement).collect(toImmutableSet());
   }
 
@@ -142,7 +176,7 @@
         .filter(
             module ->
                 module.bindings().stream().anyMatch(ContributionBinding::requiresModuleInstance))
-        .map(module -> ComponentRequirement.forModule(module.moduleElement().asType()))
+        .map(module -> ComponentRequirement.forModule(module.moduleElement().getType()))
         .forEach(requirements::add);
     requirements.addAll(dependencies());
     requirements.addAll(
@@ -158,15 +192,17 @@
    * the enclosing type of the method; a method may be declared by a supertype of the actual
    * dependency.
    */
-  public abstract ImmutableMap<ExecutableElement, ComponentRequirement>
+  public abstract ImmutableMap<XMethodElement, 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);
+  public final ComponentRequirement getDependencyThatDefinesMethod(XElement method) {
+    checkArgument(isMethod(method), "method must be an executable element: %s", method);
+    checkState(
+        dependenciesByDependencyMethod().containsKey(method),
+        "no dependency implements %s",
+        method);
+    return dependenciesByDependencyMethod().get(method);
   }
 
   /** The scopes of the component. */
@@ -201,7 +237,7 @@
 
   /** Returns a map of {@link #childComponents()} indexed by {@link #typeElement()}. */
   @Memoized
-  public ImmutableMap<TypeElement, ComponentDescriptor> childComponentsByElement() {
+  public ImmutableMap<XTypeElement, ComponentDescriptor> childComponentsByElement() {
     return Maps.uniqueIndex(childComponents(), ComponentDescriptor::typeElement);
   }
 
@@ -219,7 +255,7 @@
   abstract ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor>
       childComponentsDeclaredByBuilderEntryPoints();
 
-  private final Supplier<ImmutableMap<TypeElement, ComponentDescriptor>>
+  private final Supplier<ImmutableMap<XTypeElement, ComponentDescriptor>>
       childComponentsByBuilderType =
           Suppliers.memoize(
               () ->
@@ -231,7 +267,7 @@
                               child -> child)));
 
   /** Returns the child component with the given builder type. */
-  final ComponentDescriptor getChildComponentWithBuilderType(TypeElement builderType) {
+  final ComponentDescriptor getChildComponentWithBuilderType(XTypeElement builderType) {
     return checkNotNull(
         childComponentsByBuilderType.get().get(builderType),
         "no child component found for builder type %s",
@@ -283,7 +319,8 @@
    */
   public final Optional<CancellationPolicy> cancellationPolicy() {
     return isProduction()
-        ? Optional.ofNullable(typeElement().getAnnotation(CancellationPolicy.class))
+        // TODO(bcorso): Get values from XAnnotation instead of using CancellationPolicy directly.
+        ? Optional.ofNullable(toJavac(typeElement()).getAnnotation(CancellationPolicy.class))
         : Optional.empty();
   }
 
@@ -302,7 +339,7 @@
   @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();
+    public abstract XMethodElement methodElement();
 
     /**
      * The dependency request for production, provision, and subcomponent creator methods. Absent
@@ -321,16 +358,16 @@
     public TypeMirror resolvedReturnType(DaggerTypes types) {
       checkState(dependencyRequest().isPresent());
 
-      TypeMirror returnType = methodElement().getReturnType();
-      if (returnType.getKind().isPrimitive() || returnType.getKind().equals(VOID)) {
-        return returnType;
+      XType returnType = methodElement().getReturnType();
+      if (isPrimitive(returnType) || isVoid(returnType)) {
+        return toJavac(returnType);
       }
       return BindingRequest.bindingRequest(dependencyRequest().get())
-          .requestedType(dependencyRequest().get().key().type(), types);
+          .requestedType(dependencyRequest().get().key().type().java(), types);
     }
 
     /** A {@link ComponentMethodDescriptor}builder for a method. */
-    public static Builder builder(ExecutableElement method) {
+    public static Builder builder(XMethodElement method) {
       return new AutoValue_ComponentDescriptor_ComponentMethodDescriptor.Builder()
           .methodElement(method);
     }
@@ -340,7 +377,7 @@
     @CanIgnoreReturnValue
     public interface Builder {
       /** @see ComponentMethodDescriptor#methodElement() */
-      Builder methodElement(ExecutableElement methodElement);
+      Builder methodElement(XMethodElement methodElement);
 
       /** @see ComponentMethodDescriptor#dependencyRequest() */
       Builder dependencyRequest(DependencyRequest dependencyRequest);
@@ -362,15 +399,23 @@
    * 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) {
+  static boolean isComponentContributionMethod(XMethodElement method) {
+    return isComponentContributionMethod(toJavac(method));
+  }
+
+  /**
+   * Returns {@code true} if a method could be a component entry point but not a members-injection
+   * method.
+   */
+  static boolean isComponentContributionMethod(ExecutableElement method) {
     return method.getParameters().isEmpty()
         && !method.getReturnType().getKind().equals(VOID)
-        && !elements.getTypeElement(Object.class).equals(method.getEnclosingElement())
+        && !isTypeOf(TypeName.OBJECT, method.getEnclosingElement().asType())
         && !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());
+  static boolean isComponentProductionMethod(XMethodElement method) {
+    return isComponentContributionMethod(method) && isFutureType(method.getReturnType());
   }
 }
diff --git a/java/dagger/internal/codegen/binding/ComponentDescriptorFactory.java b/java/dagger/internal/codegen/binding/ComponentDescriptorFactory.java
index f13aa50..94d32cd 100644
--- a/java/dagger/internal/codegen/binding/ComponentDescriptorFactory.java
+++ b/java/dagger/internal/codegen/binding/ComponentDescriptorFactory.java
@@ -16,126 +16,120 @@
 
 package dagger.internal.codegen.binding;
 
-import static com.google.auto.common.MoreElements.asType;
-import static com.google.auto.common.MoreTypes.asTypeElement;
+import static androidx.room.compiler.processing.XTypeKt.isVoid;
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static androidx.room.compiler.processing.compat.XConverters.toXProcessing;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.base.ComponentAnnotation.rootComponentAnnotation;
 import static dagger.internal.codegen.base.ComponentAnnotation.subcomponentAnnotation;
+import static dagger.internal.codegen.base.ComponentAnnotation.subcomponentAnnotations;
+import static dagger.internal.codegen.base.ComponentCreatorAnnotation.creatorAnnotationsFor;
+import static dagger.internal.codegen.base.ModuleAnnotation.moduleAnnotation;
 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 dagger.internal.codegen.xprocessing.XTypeElements.getAllUnimplementedMethods;
+import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
 import static javax.lang.model.util.ElementFilter.methodsIn;
 
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XMethodType;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 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.DaggerSuperficialValidation;
 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 dagger.spi.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 XProcessingEnv processingEnv;
   private final DaggerElements elements;
   private final DaggerTypes types;
   private final DependencyRequestFactory dependencyRequestFactory;
   private final ModuleDescriptor.Factory moduleDescriptorFactory;
   private final InjectionAnnotations injectionAnnotations;
+  private final DaggerSuperficialValidation superficialValidation;
 
   @Inject
   ComponentDescriptorFactory(
+      XProcessingEnv processingEnv,
       DaggerElements elements,
       DaggerTypes types,
       DependencyRequestFactory dependencyRequestFactory,
       ModuleDescriptor.Factory moduleDescriptorFactory,
-      InjectionAnnotations injectionAnnotations) {
+      InjectionAnnotations injectionAnnotations,
+      DaggerSuperficialValidation superficialValidation) {
+    this.processingEnv = processingEnv;
     this.elements = elements;
     this.types = types;
     this.dependencyRequestFactory = dependencyRequestFactory;
     this.moduleDescriptorFactory = moduleDescriptorFactory;
     this.injectionAnnotations = injectionAnnotations;
+    this.superficialValidation = superficialValidation;
   }
 
   /** 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"));
+  public ComponentDescriptor rootComponentDescriptor(XTypeElement typeElement) {
+    Optional<ComponentAnnotation> annotation =
+        rootComponentAnnotation(typeElement, superficialValidation);
+    checkArgument(annotation.isPresent(), "%s must have a component annotation", typeElement);
+    return create(typeElement, annotation.get());
   }
 
   /** Returns a descriptor for a subcomponent type. */
-  public ComponentDescriptor subcomponentDescriptor(TypeElement typeElement) {
-    return create(
-        typeElement,
-        checkAnnotation(
-            typeElement,
-            ComponentAnnotation::subcomponentAnnotation,
-            "must have a subcomponent annotation"));
+  public ComponentDescriptor subcomponentDescriptor(XTypeElement typeElement) {
+    Optional<ComponentAnnotation> annotation =
+        subcomponentAnnotation(typeElement, superficialValidation);
+    checkArgument(annotation.isPresent(), "%s must have a subcomponent annotation", typeElement);
+    return create(typeElement, annotation.get());
   }
 
   /**
    * 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));
+  public ComponentDescriptor moduleComponentDescriptor(XTypeElement typeElement) {
+    Optional<ModuleAnnotation> annotation = moduleAnnotation(typeElement, superficialValidation);
+    checkArgument(annotation.isPresent(), "%s must have a module annotation", typeElement);
+    return create(typeElement, ComponentAnnotation.fromModuleAnnotation(annotation.get()));
   }
 
   private ComponentDescriptor create(
-      TypeElement typeElement, ComponentAnnotation componentAnnotation) {
+      XTypeElement typeElement, ComponentAnnotation componentAnnotation) {
     ImmutableSet<ComponentRequirement> componentDependencies =
         componentAnnotation.dependencyTypes().stream()
             .map(ComponentRequirement::forDependency)
             .collect(toImmutableSet());
 
-    ImmutableMap.Builder<ExecutableElement, ComponentRequirement> dependenciesByDependencyMethod =
+    ImmutableMap.Builder<XMethodElement, ComponentRequirement> dependenciesByDependencyMethod =
         ImmutableMap.builder();
-
     for (ComponentRequirement componentDependency : componentDependencies) {
       for (ExecutableElement dependencyMethod :
-          methodsIn(elements.getAllMembers(componentDependency.typeElement()))) {
-        if (isComponentContributionMethod(elements, dependencyMethod)) {
-          dependenciesByDependencyMethod.put(dependencyMethod, componentDependency);
+          methodsIn(elements.getAllMembers(toJavac(componentDependency.typeElement())))) {
+        if (isComponentContributionMethod(dependencyMethod)) {
+          dependenciesByDependencyMethod.put(
+              (XMethodElement) toXProcessing(dependencyMethod, processingEnv), componentDependency);
         }
       }
     }
 
     // Start with the component's modules. For fictional components built from a module, start with
     // that module.
-    ImmutableSet<TypeElement> modules =
+    ImmutableSet<XTypeElement> modules =
         componentAnnotation.isRealComponent()
             ? componentAnnotation.modules()
             : ImmutableSet.of(typeElement);
@@ -143,13 +137,12 @@
     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<ComponentDescriptor> subcomponentsFromModules =
+        transitiveModules.stream()
+            .flatMap(transitiveModule -> transitiveModule.subcomponentDeclarations().stream())
+            .map(SubcomponentDeclaration::subcomponentType)
+            .map(this::subcomponentDescriptor)
+            .collect(toImmutableSet());
 
     ImmutableSet.Builder<ComponentMethodDescriptor> componentMethodsBuilder =
         ImmutableSet.builder();
@@ -158,11 +151,9 @@
     ImmutableBiMap.Builder<ComponentMethodDescriptor, ComponentDescriptor>
         subcomponentsByBuilderMethod = ImmutableBiMap.builder();
     if (componentAnnotation.isRealComponent()) {
-      ImmutableSet<ExecutableElement> unimplementedMethods =
-          elements.getUnimplementedMethods(typeElement);
-      for (ExecutableElement componentMethod : unimplementedMethods) {
+      for (XMethodElement componentMethod : getAllUnimplementedMethods(typeElement)) {
         ComponentMethodDescriptor componentMethodDescriptor =
-            getDescriptorForComponentMethod(typeElement, componentAnnotation, componentMethod);
+            getDescriptorForComponentMethod(componentAnnotation, typeElement, componentMethod);
         componentMethodsBuilder.add(componentMethodDescriptor);
         componentMethodDescriptor
             .subcomponent()
@@ -180,32 +171,29 @@
     }
 
     // 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());
+    ImmutableSet<XTypeElement> enclosedCreators =
+        enclosedAnnotatedTypes(typeElement, creatorAnnotationsFor(componentAnnotation));
     Optional<ComponentCreatorDescriptor> creatorDescriptor =
         enclosedCreators.isEmpty()
             ? Optional.empty()
             : Optional.of(
                 ComponentCreatorDescriptor.create(
-                    getOnlyElement(enclosedCreators), elements, types, dependencyRequestFactory));
+                    getOnlyElement(enclosedCreators), types, dependencyRequestFactory));
 
-    ImmutableSet<Scope> scopes = scopesOf(typeElement);
+    ImmutableSet<Scope> scopes = injectionAnnotations.getScopes(typeElement);
     if (componentAnnotation.isProduction()) {
-      scopes = ImmutableSet.<Scope>builder().addAll(scopes).add(productionScope(elements)).build();
+      scopes =
+          ImmutableSet.<Scope>builder().addAll(scopes).add(productionScope(processingEnv)).build();
     }
 
-    return new AutoValue_ComponentDescriptor(
+    return ComponentDescriptor.create(
         componentAnnotation,
         typeElement,
         componentDependencies,
         transitiveModules,
         dependenciesByDependencyMethod.build(),
         scopes,
-        subcomponentsFromModules.build(),
+        subcomponentsFromModules,
         subcomponentsByFactoryMethod.build(),
         subcomponentsByBuilderMethod.build(),
         componentMethodsBuilder.build(),
@@ -213,36 +201,30 @@
   }
 
   private ComponentMethodDescriptor getDescriptorForComponentMethod(
-      TypeElement componentElement,
       ComponentAnnotation componentAnnotation,
-      ExecutableElement componentMethod) {
+      XTypeElement componentElement,
+      XMethodElement 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()) {
+    XMethodType resolvedComponentMethod = componentMethod.asMemberOf(componentElement.getType());
+    XType returnType = resolvedComponentMethod.getReturnType();
+    if (isDeclared(returnType) && !injectionAnnotations.getQualifier(componentMethod).isPresent()) {
+      XTypeElement returnTypeElement = returnType.getTypeElement();
+      if (returnTypeElement.hasAnyAnnotation(subcomponentAnnotations())) {
         // 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())));
+            subcomponentDescriptor(returnTypeElement.getEnclosingTypeElement()));
       }
     }
 
     switch (componentMethod.getParameters().size()) {
       case 0:
-        checkArgument(
-            !returnType.getKind().equals(VOID),
-            "component method cannot be void: %s",
-            componentMethod);
+        checkArgument(!isVoid(returnType), "component method cannot be void: %s", componentMethod);
         descriptor.dependencyRequest(
             componentAnnotation.isProduction()
                 ? dependencyRequestFactory.forComponentProductionMethod(
@@ -253,9 +235,11 @@
 
       case 1:
         checkArgument(
-            returnType.getKind().equals(VOID)
-                || MoreTypes.equivalence()
-                    .equivalent(returnType, resolvedComponentMethod.getParameterTypes().get(0)),
+            isVoid(returnType)
+                // TODO(bcorso): Replace this with isSameType()?
+                || returnType
+                    .getTypeName()
+                    .equals(resolvedComponentMethod.getParameterTypes().get(0).getTypeName()),
             "members injection method must return void or parameter type: %s",
             componentMethod);
         descriptor.dependencyRequest(
diff --git a/java/dagger/internal/codegen/binding/ComponentKind.java b/java/dagger/internal/codegen/binding/ComponentKind.java
deleted file mode 100644
index 1cb3d7c..0000000
--- a/java/dagger/internal/codegen/binding/ComponentKind.java
+++ /dev/null
@@ -1,171 +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.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
index 0947c25..5dbf633 100644
--- a/java/dagger/internal/codegen/binding/ComponentNodeImpl.java
+++ b/java/dagger/internal/codegen/binding/ComponentNodeImpl.java
@@ -20,10 +20,10 @@
 
 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;
+import dagger.spi.model.BindingGraph.ComponentNode;
+import dagger.spi.model.ComponentPath;
+import dagger.spi.model.DependencyRequest;
+import dagger.spi.model.Scope;
 
 /** An implementation of {@link ComponentNode} that also exposes the {@link ComponentDescriptor}. */
 @AutoValue
diff --git a/java/dagger/internal/codegen/binding/ComponentRequirement.java b/java/dagger/internal/codegen/binding/ComponentRequirement.java
index 9d54f4d..d021eb6 100644
--- a/java/dagger/internal/codegen/binding/ComponentRequirement.java
+++ b/java/dagger/internal/codegen/binding/ComponentRequirement.java
@@ -16,36 +16,28 @@
 
 package dagger.internal.codegen.binding;
 
+import static androidx.room.compiler.processing.XElementKt.isConstructor;
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 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 static dagger.internal.codegen.xprocessing.XElements.asConstructor;
+import static dagger.internal.codegen.xprocessing.XElements.hasAnyAnnotation;
+import static dagger.internal.codegen.xprocessing.XTypeElements.isNested;
+import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
+import static kotlin.streams.jdk8.StreamsKt.asStream;
 
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 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.model.BindingKind;
-import dagger.model.Key;
-import dagger.multibindings.Multibinds;
-import dagger.producers.Produces;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.spi.model.BindingKind;
+import dagger.spi.model.Key;
 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
@@ -73,6 +65,8 @@
     }
   }
 
+  private XType type;
+
   /** The kind of requirement. */
   public abstract Kind kind();
 
@@ -82,20 +76,17 @@
     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. */
+  abstract TypeName typeName();
 
   /** The type of the instance the component must have. */
-  public TypeMirror type() {
-    return wrappedType().get();
+  public XType type() {
+    return type;
   }
 
   /** The element associated with the type of this requirement. */
-  public TypeElement typeElement() {
-    return MoreTypes.asTypeElement(type());
+  public XTypeElement typeElement() {
+    return type.getTypeElement();
   }
 
   /** The action a component builder should take if it {@code null} is passed. */
@@ -118,15 +109,15 @@
   abstract Optional<NullPolicy> overrideNullPolicy();
 
   /** The requirement's null policy. */
-  public NullPolicy nullPolicy(DaggerElements elements, KotlinMetadataUtil metadataUtil) {
+  public NullPolicy nullPolicy() {
     if (overrideNullPolicy().isPresent()) {
       return overrideNullPolicy().get();
     }
     switch (kind()) {
       case MODULE:
-        return componentCanMakeNewInstances(typeElement(), metadataUtil)
+        return componentCanMakeNewInstances(typeElement())
             ? NullPolicy.NEW
-            : requiresAPassedInstance(elements, metadataUtil) ? NullPolicy.THROW : NullPolicy.ALLOW;
+            : requiresAPassedInstance() ? NullPolicy.THROW : NullPolicy.ALLOW;
       case DEPENDENCY:
       case BOUND_INSTANCE:
         return NullPolicy.THROW;
@@ -138,13 +129,12 @@
    * Returns true if the passed {@link ComponentRequirement} requires a passed instance in order to
    * be used within a component.
    */
-  public boolean requiresAPassedInstance(DaggerElements elements, KotlinMetadataUtil metadataUtil) {
+  public boolean requiresAPassedInstance() {
     if (!kind().isModule()) {
       // Bound instances and dependencies always require the user to provide an instance.
       return true;
     }
-    return requiresModuleInstance(elements, metadataUtil)
-        && !componentCanMakeNewInstances(typeElement(), metadataUtil);
+    return requiresModuleInstance() && !componentCanMakeNewInstances(typeElement());
   }
 
   /**
@@ -157,33 +147,27 @@
    * <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, KotlinMetadataUtil metadataUtil) {
-    boolean isKotlinObject =
-        metadataUtil.isObjectClass(typeElement())
-            || metadataUtil.isCompanionObjectClass(typeElement());
-    if (isKotlinObject) {
+  private boolean requiresModuleInstance() {
+    if (typeElement().isKotlinObject() || typeElement().isCompanionObject()) {
       return false;
     }
-
-    ImmutableSet<ExecutableElement> methods = elements.getLocalAndInheritedMethods(typeElement());
-    return methods.stream()
+    return asStream(typeElement().getAllNonPrivateInstanceMethods())
         .filter(this::isBindingMethod)
-        .map(ExecutableElement::getModifiers)
-        .anyMatch(modifiers -> !modifiers.contains(ABSTRACT) && !modifiers.contains(STATIC));
+        .anyMatch(method -> !method.isAbstract() && !method.isStatic());
   }
 
-  private boolean isBindingMethod(ExecutableElement method) {
+  private boolean isBindingMethod(XMethodElement 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(
+    return hasAnyAnnotation(
         method,
-        Provides.class,
-        Produces.class,
+        TypeNames.PROVIDES,
+        TypeNames.PRODUCES,
         // TODO(ronshapiro): it would be cool to have internal meta-annotations that could describe
         // these, like @AbstractBindingMethod
-        Binds.class,
-        Multibinds.class,
-        BindsOptionalOf.class);
+        TypeNames.BINDS,
+        TypeNames.MULTIBINDS,
+        TypeNames.BINDS_OPTIONAL_OF);
   }
 
   /** The key for this requirement, if one is available. */
@@ -194,42 +178,57 @@
 
   /** Returns a parameter spec for this requirement. */
   public ParameterSpec toParameterSpec() {
-    return ParameterSpec.builder(TypeName.get(type()), variableName()).build();
+    return ParameterSpec.builder(type().getTypeName(), 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 forDependency(XType type) {
+    checkArgument(isDeclared(checkNotNull(type)));
+    ComponentRequirement requirement =
+        new AutoValue_ComponentRequirement(
+            Kind.DEPENDENCY,
+            type.getTypeName(),
+            Optional.empty(),
+            Optional.empty(),
+            simpleVariableName(type.getTypeElement().getClassName()));
+    requirement.type = type;
+    return requirement;
   }
 
-  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)));
+  public static ComponentRequirement forModule(XType type) {
+    checkArgument(isDeclared(checkNotNull(type)));
+    ComponentRequirement requirement =
+        new AutoValue_ComponentRequirement(
+            Kind.MODULE,
+            type.getTypeName(),
+            Optional.empty(),
+            Optional.empty(),
+            simpleVariableName(type.getTypeElement().getClassName()));
+    requirement.type = type;
+    return requirement;
   }
 
-  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(
+      Key key, boolean nullable, XElement elementForVariableName) {
+    ComponentRequirement requirement =
+        new AutoValue_ComponentRequirement(
+            Kind.BOUND_INSTANCE,
+            key.type().xprocessing().getTypeName(),
+            nullable ? Optional.of(NullPolicy.ALLOW) : Optional.empty(),
+            Optional.of(key),
+            toJavac(elementForVariableName).getSimpleName().toString());
+    requirement.type = key.type().xprocessing();
+    return requirement;
   }
 
   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());
+    ComponentRequirement requirement =
+        forBoundInstance(
+            binding.key(),
+            binding.nullableType().isPresent(),
+            binding.bindingElement().get());
+    requirement.type = binding.key().type().xprocessing();
+    return requirement;
   }
 
   /**
@@ -237,9 +236,11 @@
    * 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()) {
+  public static boolean componentCanMakeNewInstances(XTypeElement typeElement) {
+    // TODO(bcorso): Investigate how we should replace this in XProcessing. It's not clear what the
+    // complete set of kinds are in XProcessing and if they're mutually exclusive. For example,
+    // does XTypeElement#isClass() cover XTypeElement#isDataClass(), etc?
+    switch (toJavac(typeElement).getKind()) {
       case CLASS:
         break;
       case ENUM:
@@ -247,10 +248,10 @@
       case INTERFACE:
         return false;
       default:
-        throw new AssertionError("TypeElement cannot have kind: " + typeElement.getKind());
+        throw new AssertionError("TypeElement cannot have kind: " + toJavac(typeElement).getKind());
     }
 
-    if (typeElement.getModifiers().contains(ABSTRACT)) {
+    if (typeElement.isAbstract()) {
       return false;
     }
 
@@ -258,15 +259,10 @@
       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)) {
+    for (XElement enclosed : typeElement.getEnclosedElements()) {
+      if (isConstructor(enclosed)
+          && asConstructor(enclosed).getParameters().isEmpty()
+          && !asConstructor(enclosed).isPrivate()) {
         return true;
       }
     }
@@ -276,17 +272,7 @@
     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());
+  private static boolean requiresEnclosingInstance(XTypeElement typeElement) {
+    return isNested(typeElement) && !typeElement.isStatic();
   }
 }
diff --git a/java/dagger/internal/codegen/binding/ConfigurationAnnotations.java b/java/dagger/internal/codegen/binding/ConfigurationAnnotations.java
index 539a66a..75ae97e 100644
--- a/java/dagger/internal/codegen/binding/ConfigurationAnnotations.java
+++ b/java/dagger/internal/codegen/binding/ConfigurationAnnotations.java
@@ -17,37 +17,25 @@
 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 static dagger.internal.codegen.base.ComponentAnnotation.subcomponentAnnotations;
+import static dagger.internal.codegen.base.ComponentCreatorAnnotation.subcomponentCreatorAnnotations;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.xprocessing.XAnnotations.getClassName;
+import static dagger.internal.codegen.xprocessing.XElements.hasAnyAnnotation;
 
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableList;
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
+import com.squareup.javapoet.ClassName;
 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
@@ -55,24 +43,27 @@
  */
 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();
+  public static Optional<XTypeElement> getSubcomponentCreator(XTypeElement subcomponent) {
+    checkArgument(subcomponent.hasAnyAnnotation(subcomponentAnnotations()));
+    return subcomponent.getEnclosedTypeElements().stream()
+        .filter(ConfigurationAnnotations::isSubcomponentCreator)
+        // TODO(bcorso): Consider doing toOptional() instead since there should be at most 1.
+        .findFirst();
   }
 
-  static boolean isSubcomponentCreator(Element element) {
-    return isAnyAnnotationPresent(element, subcomponentCreatorAnnotations());
+  static boolean isSubcomponentCreator(XElement element) {
+    return hasAnyAnnotation(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<XAnnotation> getNullableAnnotation(XElement element) {
+    return element.getAllAnnotations().stream()
+        .filter(annotation -> getClassName(annotation).simpleName().contentEquals("Nullable"))
+        .findFirst();
+  }
+
+  public static Optional<XType> getNullableType(XElement element) {
+    return getNullableAnnotation(element).map(XAnnotation::getType);
   }
 
   /** Returns the first type that specifies this' nullability, or empty if none. */
@@ -86,71 +77,12 @@
     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();
-    }
+  public static ImmutableSet<XTypeElement> enclosedAnnotatedTypes(
+      XTypeElement typeElement, ImmutableSet<ClassName> annotations) {
+    return typeElement.getEnclosedTypeElements().stream()
+        .filter(enclosedType -> hasAnyAnnotation(enclosedType, annotations))
+        .collect(toImmutableSet());
   }
 
   private ConfigurationAnnotations() {}
diff --git a/java/dagger/internal/codegen/binding/ContributionBinding.java b/java/dagger/internal/codegen/binding/ContributionBinding.java
index 1942e8c..2c55560 100644
--- a/java/dagger/internal/codegen/binding/ContributionBinding.java
+++ b/java/dagger/internal/codegen/binding/ContributionBinding.java
@@ -17,30 +17,26 @@
 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 dagger.internal.codegen.xprocessing.XElements.asMethod;
 import static java.util.Arrays.asList;
 
-import com.google.auto.common.MoreElements;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XElementKt;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 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 dagger.internal.codegen.xprocessing.XTypes;
+import dagger.spi.model.BindingKind;
+import dagger.spi.model.DependencyRequest;
+import dagger.spi.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
@@ -49,7 +45,7 @@
 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<XType> nullableType();
 
   public abstract Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedMapKeyAnnotation();
 
@@ -58,16 +54,16 @@
   }
 
   /** If {@link #bindingElement()} is a method that returns a primitive type, returns that type. */
-  public final Optional<TypeMirror> contributedPrimitiveType() {
+  public final Optional<XType> contributedPrimitiveType() {
     return bindingElement()
-        .filter(bindingElement -> bindingElement instanceof ExecutableElement)
-        .map(bindingElement -> MoreElements.asExecutable(bindingElement).getReturnType())
-        .filter(type -> type.getKind().isPrimitive());
+        .filter(XElementKt::isMethod)
+        .map(bindingElement -> asMethod(bindingElement).getReturnType())
+        .filter(XTypes::isPrimitive);
   }
 
   @Override
   public boolean requiresModuleInstance() {
-    return !isContributingModuleKotlinObject().orElse(false) && super.requiresModuleInstance();
+    return !isContributingModuleKotlinObject() && super.requiresModuleInstance();
   }
 
   @Override
@@ -79,51 +75,18 @@
    * 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,
+  private boolean isContributingModuleKotlinObject() {
+    return contributingModule().isPresent()
+        && (contributingModule().get().isKotlinObject()
+            || contributingModule().get().isCompanionObject());
   }
 
   /**
-   * 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.
+   * The {@link XType 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 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() {
+  public final XType contributedType() {
     switch (contributionType()) {
       case MAP:
         return MapType.from(key()).unwrappedFrameworkValueType();
@@ -131,27 +94,11 @@
         return SetType.from(key()).elementType();
       case SET_VALUES:
       case UNIQUE:
-        return key().type();
+        return key().type().xprocessing();
     }
     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();
 
   /**
@@ -170,21 +117,19 @@
 
     public abstract B contributionType(ContributionType contributionType);
 
-    public abstract B bindingElement(Element bindingElement);
+    public abstract B bindingElement(XElement bindingElement);
 
-    abstract B bindingElement(Optional<Element> bindingElement);
+    abstract B bindingElement(Optional<XElement> bindingElement);
 
     public final B clearBindingElement() {
       return bindingElement(Optional.empty());
     };
 
-    abstract B contributingModule(TypeElement contributingModule);
-
-    abstract B isContributingModuleKotlinObject(boolean isModuleKotlinObject);
+    abstract B contributingModule(XTypeElement contributingModule);
 
     public abstract B key(Key key);
 
-    public abstract B nullableType(Optional<DeclaredType> nullableType);
+    public abstract B nullableType(Optional<XType> nullableType);
 
     abstract B wrappedMapKeyAnnotation(
         Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedMapKeyAnnotation);
@@ -192,16 +137,6 @@
     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;
-    }
+    abstract C build();
   }
 }
diff --git a/java/dagger/internal/codegen/binding/DelegateDeclaration.java b/java/dagger/internal/codegen/binding/DelegateDeclaration.java
index b6c3c38..32ecf67 100644
--- a/java/dagger/internal/codegen/binding/DelegateDeclaration.java
+++ b/java/dagger/internal/codegen/binding/DelegateDeclaration.java
@@ -16,12 +16,15 @@
 
 package dagger.internal.codegen.binding;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 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 androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XMethodType;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.auto.value.AutoValue;
 import com.google.auto.value.extension.memoized.Memoized;
 import com.google.common.base.Equivalence;
@@ -29,15 +32,11 @@
 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 dagger.internal.codegen.javapoet.TypeNames;
+import dagger.spi.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
@@ -56,26 +55,20 @@
 
   /** 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));
+    public DelegateDeclaration create(XMethodElement bindsMethod, XTypeElement contributingModule) {
+      checkArgument(bindsMethod.hasAnnotation(TypeNames.BINDS));
+      XMethodType resolvedMethod = bindsMethod.asMemberOf(contributingModule.getType());
       DependencyRequest delegateRequest =
           dependencyRequestFactory.forRequiredResolvedVariable(
               Iterables.getOnlyElement(bindsMethod.getParameters()),
@@ -83,10 +76,10 @@
       return new AutoValue_DelegateDeclaration(
           ContributionType.fromBindingElement(bindsMethod),
           keyFactory.forBindsMethod(bindsMethod, contributingModule),
-          Optional.<Element>of(bindsMethod),
+          Optional.<XElement>of(bindsMethod),
           Optional.of(contributingModule),
           delegateRequest,
-          wrapOptionalInEquivalence(getMapKey(bindsMethod)));
+          wrapOptionalInEquivalence(getMapKey(toJavac(bindsMethod))));
     }
   }
 }
diff --git a/java/dagger/internal/codegen/binding/DependencyEdgeImpl.java b/java/dagger/internal/codegen/binding/DependencyEdgeImpl.java
index f11517e..11e1dcf 100644
--- a/java/dagger/internal/codegen/binding/DependencyEdgeImpl.java
+++ b/java/dagger/internal/codegen/binding/DependencyEdgeImpl.java
@@ -17,8 +17,9 @@
 package dagger.internal.codegen.binding;
 
 import dagger.internal.codegen.base.ElementFormatter;
-import dagger.model.BindingGraph.DependencyEdge;
-import dagger.model.DependencyRequest;
+import dagger.spi.model.BindingGraph.DependencyEdge;
+import dagger.spi.model.DaggerElement;
+import dagger.spi.model.DependencyRequest;
 
 /** An implementation of {@link DependencyEdge}. */
 final class DependencyEdgeImpl implements DependencyEdge {
@@ -46,6 +47,7 @@
     String string =
         dependencyRequest
             .requestElement()
+            .map(DaggerElement::java)
             .map(ElementFormatter::elementToString)
             .orElseGet(
                 () ->
diff --git a/java/dagger/internal/codegen/binding/DependencyRequestFactory.java b/java/dagger/internal/codegen/binding/DependencyRequestFactory.java
index 707de4c..01c3a40 100644
--- a/java/dagger/internal/codegen/binding/DependencyRequestFactory.java
+++ b/java/dagger/internal/codegen/binding/DependencyRequestFactory.java
@@ -16,40 +16,51 @@
 
 package dagger.internal.codegen.binding;
 
-import static com.google.auto.common.MoreTypes.isTypeOf;
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static androidx.room.compiler.processing.compat.XConverters.toXProcessing;
 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.frameworkClassName;
 import static dagger.internal.codegen.base.RequestKinds.getRequestKind;
+import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.isAssistedParameter;
 import static dagger.internal.codegen.binding.ConfigurationAnnotations.getNullableType;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
 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 static dagger.internal.codegen.xprocessing.XTypes.isTypeOf;
+import static dagger.spi.model.RequestKind.FUTURE;
+import static dagger.spi.model.RequestKind.INSTANCE;
+import static dagger.spi.model.RequestKind.MEMBERS_INJECTION;
+import static dagger.spi.model.RequestKind.PRODUCER;
+import static dagger.spi.model.RequestKind.PROVIDER;
 
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XMethodType;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XVariableElement;
+import androidx.room.compiler.processing.compat.XConverters;
 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 dagger.internal.codegen.javapoet.TypeNames;
+import dagger.spi.model.DaggerElement;
+import dagger.spi.model.DependencyRequest;
+import dagger.spi.model.Key;
+import dagger.spi.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;
 
 /**
@@ -59,15 +70,27 @@
  * may mean that the type will be generated in a later round of processing.
  */
 public final class DependencyRequestFactory {
+  private final XProcessingEnv processingEnv;
   private final KeyFactory keyFactory;
   private final InjectionAnnotations injectionAnnotations;
 
   @Inject
-  DependencyRequestFactory(KeyFactory keyFactory, InjectionAnnotations injectionAnnotations) {
+  DependencyRequestFactory(
+      XProcessingEnv processingEnv,
+      KeyFactory keyFactory,
+      InjectionAnnotations injectionAnnotations) {
+    this.processingEnv = processingEnv;
     this.keyFactory = keyFactory;
     this.injectionAnnotations = injectionAnnotations;
   }
 
+  ImmutableSet<DependencyRequest> forRequiredResolvedXVariables(
+      List<? extends XVariableElement> variables, List<XType> resolvedTypes) {
+    return forRequiredResolvedVariables(
+        variables.stream().map(XConverters::toJavac).collect(toImmutableList()),
+        resolvedTypes.stream().map(XConverters::toJavac).collect(toImmutableList()));
+  }
+
   ImmutableSet<DependencyRequest> forRequiredResolvedVariables(
       List<? extends VariableElement> variables, List<? extends TypeMirror> resolvedTypes) {
     checkState(resolvedTypes.size() == variables.size());
@@ -114,7 +137,7 @@
       case MAP:
         MapType mapType = MapType.from(multibindingKey);
         for (RequestKind kind : WRAPPING_MAP_VALUE_FRAMEWORK_TYPES) {
-          if (mapType.valuesAreTypeOf(frameworkClass(kind))) {
+          if (mapType.valuesAreTypeOf(frameworkClassName(kind))) {
             return kind;
           }
         }
@@ -130,44 +153,49 @@
   }
 
   DependencyRequest forRequiredResolvedVariable(
+      XVariableElement variableElement, XType resolvedType) {
+    return forRequiredResolvedVariable(toJavac(variableElement), toJavac(resolvedType));
+  }
+
+  DependencyRequest forRequiredResolvedVariable(
       VariableElement variableElement, TypeMirror resolvedType) {
     checkNotNull(variableElement);
     checkNotNull(resolvedType);
     // Ban @Assisted parameters, they are not considered dependency requests.
-    checkArgument(!AssistedInjectionAnnotations.isAssistedParameter(variableElement));
+    checkArgument(!isAssistedParameter(toXProcessing(variableElement, processingEnv)));
     Optional<AnnotationMirror> qualifier = injectionAnnotations.getQualifier(variableElement);
     return newDependencyRequest(variableElement, resolvedType, qualifier);
   }
 
   public DependencyRequest forComponentProvisionMethod(
-      ExecutableElement provisionMethod, ExecutableType provisionMethodType) {
+      XMethodElement provisionMethod, XMethodType provisionMethodType) {
     checkNotNull(provisionMethod);
     checkNotNull(provisionMethodType);
     checkArgument(
         provisionMethod.getParameters().isEmpty(),
         "Component provision methods must be empty: %s",
         provisionMethod);
-    Optional<AnnotationMirror> qualifier = injectionAnnotations.getQualifier(provisionMethod);
+    Optional<XAnnotation> qualifier = injectionAnnotations.getQualifier(provisionMethod);
     return newDependencyRequest(provisionMethod, provisionMethodType.getReturnType(), qualifier);
   }
 
   public DependencyRequest forComponentProductionMethod(
-      ExecutableElement productionMethod, ExecutableType productionMethodType) {
+      XMethodElement productionMethod, XMethodType 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);
+    XType type = productionMethodType.getReturnType();
+    Optional<XAnnotation> 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)) {
+    if (isTypeOf(type, TypeNames.LISTENABLE_FUTURE)) {
       return DependencyRequest.builder()
           .kind(FUTURE)
           .key(keyFactory.forQualifiedType(qualifier, unwrapType(type)))
-          .requestElement(productionMethod)
+          .requestElement(DaggerElement.from(productionMethod))
           .build();
     } else {
       return newDependencyRequest(productionMethod, type, qualifier);
@@ -175,17 +203,16 @@
   }
 
   DependencyRequest forComponentMembersInjectionMethod(
-      ExecutableElement membersInjectionMethod, ExecutableType membersInjectionMethodType) {
+      XMethodElement membersInjectionMethod, XMethodType membersInjectionMethodType) {
     checkNotNull(membersInjectionMethod);
     checkNotNull(membersInjectionMethodType);
-    Optional<AnnotationMirror> qualifier =
-        injectionAnnotations.getQualifier(membersInjectionMethod);
+    Optional<XAnnotation> qualifier = injectionAnnotations.getQualifier(membersInjectionMethod);
     checkArgument(!qualifier.isPresent());
-    TypeMirror membersInjectedType = getOnlyElement(membersInjectionMethodType.getParameterTypes());
+    XType membersInjectedType = getOnlyElement(membersInjectionMethodType.getParameterTypes());
     return DependencyRequest.builder()
         .kind(MEMBERS_INJECTION)
         .key(keyFactory.forMembersInjectedType(membersInjectedType))
-        .requestElement(membersInjectionMethod)
+        .requestElement(DaggerElement.from(membersInjectionMethod))
         .build();
   }
 
@@ -219,12 +246,18 @@
   }
 
   private DependencyRequest newDependencyRequest(
+      XElement requestElement, XType type, Optional<XAnnotation> qualifier) {
+    return newDependencyRequest(
+        toJavac(requestElement), toJavac(type), qualifier.map(XConverters::toJavac));
+  }
+
+  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)
+        .requestElement(DaggerElement.from(toXProcessing(requestElement, processingEnv)))
         .isNullable(allowsNull(requestKind, getNullableType(requestElement)))
         .build();
   }
diff --git a/java/dagger/internal/codegen/binding/DependencyRequestFormatter.java b/java/dagger/internal/codegen/binding/DependencyRequestFormatter.java
index 888dec2..8fe9249 100644
--- a/java/dagger/internal/codegen/binding/DependencyRequestFormatter.java
+++ b/java/dagger/internal/codegen/binding/DependencyRequestFormatter.java
@@ -23,11 +23,11 @@
 import dagger.Provides;
 import dagger.internal.codegen.base.Formatter;
 import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.DependencyRequest;
 import dagger.producers.Produces;
+import dagger.spi.model.DaggerAnnotation;
+import dagger.spi.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.ElementVisitor;
 import javax.lang.model.element.ExecutableElement;
@@ -67,7 +67,7 @@
   public String format(DependencyRequest request) {
     return request
         .requestElement()
-        .map(element -> element.accept(formatVisitor, request))
+        .map(element -> element.java().accept(formatVisitor, request))
         .orElse("");
   }
 
@@ -101,7 +101,8 @@
 
         @Override
         public String visitVariable(VariableElement variable, DependencyRequest request) {
-          TypeMirror requestedType = requestType(request.kind(), request.key().type(), types);
+          TypeMirror requestedType =
+              requestType(request.kind(), request.key().type().java(), types);
           return INDENT
               + formatQualifier(request.key().qualifier())
               + requestedType
@@ -122,7 +123,7 @@
         }
       };
 
-  private String formatQualifier(Optional<AnnotationMirror> maybeQualifier) {
+  private String formatQualifier(Optional<DaggerAnnotation> maybeQualifier) {
     return maybeQualifier.map(qualifier -> qualifier + " ").orElse("");
   }
 
diff --git a/java/dagger/internal/codegen/binding/DependencyVariableNamer.java b/java/dagger/internal/codegen/binding/DependencyVariableNamer.java
index e01d22e..45076fe 100644
--- a/java/dagger/internal/codegen/binding/DependencyVariableNamer.java
+++ b/java/dagger/internal/codegen/binding/DependencyVariableNamer.java
@@ -22,7 +22,7 @@
 import com.google.common.base.Ascii;
 import com.google.common.base.CaseFormat;
 import dagger.Lazy;
-import dagger.model.DependencyRequest;
+import dagger.spi.model.DependencyRequest;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import javax.inject.Provider;
@@ -39,10 +39,10 @@
 
   static String name(DependencyRequest dependency) {
     if (!dependency.requestElement().isPresent()) {
-      return simpleVariableName(MoreTypes.asTypeElement(dependency.key().type()));
+      return simpleVariableName(MoreTypes.asTypeElement(dependency.key().type().java()));
     }
 
-    String variableName = dependency.requestElement().get().getSimpleName().toString();
+    String variableName = dependency.requestElement().get().java().getSimpleName().toString();
     if (Ascii.isUpperCase(variableName.charAt(0))) {
       variableName = toLowerCamel(variableName);
     }
diff --git a/java/dagger/internal/codegen/binding/ErrorMessages.java b/java/dagger/internal/codegen/binding/ErrorMessages.java
index 8962ade..0bc43b2 100644
--- a/java/dagger/internal/codegen/binding/ErrorMessages.java
+++ b/java/dagger/internal/codegen/binding/ErrorMessages.java
@@ -16,15 +16,19 @@
 
 package dagger.internal.codegen.binding;
 
+import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
+
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableMap;
 import dagger.internal.codegen.base.ComponentAnnotation;
+import dagger.internal.codegen.base.ComponentCreatorAnnotation;
+import dagger.internal.codegen.base.ComponentKind;
 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 {
@@ -199,18 +203,18 @@
     }
 
     public final String factoryMethodReturnsSupertypeWithMissingMethods(
-        TypeElement component,
-        TypeElement componentBuilder,
-        TypeMirror returnType,
-        ExecutableElement buildMethod,
-        Set<ExecutableElement> additionalMethods) {
+        XTypeElement component,
+        XTypeElement componentBuilder,
+        XType returnType,
+        XMethodElement buildMethod,
+        Set<XMethodElement> 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,
+          getSimpleName(buildMethod),
+          returnType.getTypeName(),
           component.getQualifiedName(),
           Joiner.on(", ").join(additionalMethods));
     }
diff --git a/java/dagger/internal/codegen/binding/FrameworkField.java b/java/dagger/internal/codegen/binding/FrameworkField.java
index 3b0b73f..5f72fe2 100644
--- a/java/dagger/internal/codegen/binding/FrameworkField.java
+++ b/java/dagger/internal/codegen/binding/FrameworkField.java
@@ -16,8 +16,10 @@
 
 package dagger.internal.codegen.binding;
 
-import static dagger.model.BindingKind.MEMBERS_INJECTOR;
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static dagger.spi.model.BindingKind.MEMBERS_INJECTOR;
 
+import androidx.room.compiler.processing.XType;
 import com.google.auto.value.AutoValue;
 import com.google.common.base.CaseFormat;
 import com.squareup.javapoet.ClassName;
@@ -29,7 +31,6 @@
 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;
 
 /**
@@ -69,24 +70,22 @@
    *     one for the binding's type.
    */
   public static FrameworkField forBinding(
-      ContributionBinding binding, Optional<ClassName> frameworkClass) {
+      ContributionBinding binding, Optional<ClassName> frameworkClassName) {
     return create(
-        frameworkClass.orElse(
-            ClassName.get(
-                FrameworkType.forBindingType(binding.bindingType()).frameworkClass())),
-        TypeName.get(fieldValueType(binding)),
+        frameworkClassName.orElse(binding.frameworkType().frameworkClassName()),
+        fieldValueType(binding).getTypeName(),
         frameworkFieldName(binding));
   }
 
-  private static TypeMirror fieldValueType(ContributionBinding binding) {
+  private static XType fieldValueType(ContributionBinding binding) {
     return binding.contributionType().isMultibinding()
         ? binding.contributedType()
-        : binding.key().type();
+        : binding.key().type().xprocessing();
   }
 
   private static String frameworkFieldName(ContributionBinding binding) {
     if (binding.bindingElement().isPresent()) {
-      String name = BINDING_ELEMENT_NAME.visit(binding.bindingElement().get(), binding);
+      String name = BINDING_ELEMENT_NAME.visit(toJavac(binding.bindingElement().get()), binding);
       return binding.kind().equals(MEMBERS_INJECTOR) ? name + "MembersInjector" : name;
     }
     return KeyVariableNamer.name(binding.key());
diff --git a/java/dagger/internal/codegen/binding/FrameworkType.java b/java/dagger/internal/codegen/binding/FrameworkType.java
index 6b160b6..0c0868c 100644
--- a/java/dagger/internal/codegen/binding/FrameworkType.java
+++ b/java/dagger/internal/codegen/binding/FrameworkType.java
@@ -18,66 +18,49 @@
 
 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.javapoet.TypeNames;
 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 dagger.spi.model.DependencyRequest;
+import dagger.spi.model.RequestKind;
 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}. */
+  /** A {@link javax.inject.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);
+          return CodeBlock.of("$T.lazy($L)", TypeNames.DOUBLE_CHECK, from);
 
         case PROVIDER:
           return from;
 
         case PROVIDER_OF_LAZY:
-          return CodeBlock.of("$T.create($L)", ProviderOfLazy.class, from);
+          return CodeBlock.of("$T.create($L)", TypeNames.PROVIDER_OF_LAZY, from);
 
         case PRODUCER:
-          return CodeBlock.of("$T.producerFromProvider($L)", Producers.class, from);
+          return CodeBlock.of("$T.producerFromProvider($L)", TypeNames.PRODUCERS, from);
 
         case FUTURE:
-          return CodeBlock.of("$T.immediateFuture($L)", Futures.class, to(INSTANCE, from));
+          return CodeBlock.of(
+              "$T.immediateFuture($L)", TypeNames.FUTURES, to(RequestKind.INSTANCE, from));
 
         case PRODUCED:
-          return CodeBlock.of("$T.successful($L)", Produced.class, to(INSTANCE, from));
+          return CodeBlock.of(
+              "$T.successful($L)", TypeNames.PRODUCED, to(RequestKind.INSTANCE, from));
 
         default:
           throw new IllegalArgumentException(
@@ -96,36 +79,24 @@
           return from;
 
         case PROVIDER_OF_LAZY:
-          TypeMirror lazyType = types.rewrapType(from.type(), Lazy.class);
-          return Expression.create(types.wrapType(lazyType, Provider.class), codeBlock);
+          TypeMirror lazyType = types.rewrapType(from.type(), TypeNames.LAZY);
+          return Expression.create(types.wrapType(lazyType, TypeNames.PROVIDER), codeBlock);
 
         case FUTURE:
           return Expression.create(
-              types.rewrapType(from.type(), ListenableFuture.class), codeBlock);
+              types.rewrapType(from.type(), TypeNames.LISTENABLE_FUTURE), codeBlock);
 
         default:
           return Expression.create(
-              types.rewrapType(from.type(), RequestKinds.frameworkClass(requestKind)), codeBlock);
+              types.rewrapType(from.type(), RequestKinds.frameworkClassName(requestKind)),
+              codeBlock);
       }
     }
   },
 
-  /** A {@link Producer}. */
+  /** A {@link dagger.producers.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:
@@ -145,7 +116,7 @@
       switch (requestKind) {
         case FUTURE:
           return Expression.create(
-              types.rewrapType(from.type(), ListenableFuture.class),
+              types.rewrapType(from.type(), TypeNames.LISTENABLE_FUTURE),
               to(requestKind, from.codeBlock()));
 
         case PRODUCER:
@@ -156,8 +127,7 @@
               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) {
@@ -182,15 +152,34 @@
   }
 
   /** The class of fields of this type. */
-  public abstract Class<?> frameworkClass();
+  public ClassName frameworkClassName() {
+    switch (this) {
+      case PROVIDER:
+        return TypeNames.PROVIDER;
+      case PRODUCER_NODE:
+        // 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 TypeNames.PRODUCER;
+    }
+    throw new AssertionError("Unknown value: " + this.name());
+  }
 
-  /** Returns the {@link #frameworkClass()} parameterized with a type. */
+  /** Returns the {@link #frameworkClassName()} parameterized with a type. */
   public ParameterizedTypeName frameworkClassOf(TypeName valueType) {
-    return ParameterizedTypeName.get(ClassName.get(frameworkClass()), valueType);
+    return ParameterizedTypeName.get(frameworkClassName(), valueType);
   }
 
   /** The request kind that an instance of this framework type can satisfy directly, if any. */
-  public abstract Optional<RequestKind> requestKind();
+  public RequestKind requestKind() {
+    switch (this) {
+      case PROVIDER:
+        return RequestKind.PROVIDER;
+      case PRODUCER_NODE:
+        return RequestKind.PRODUCER;
+    }
+    throw new AssertionError("Unknown value: " + this.name());
+  }
 
   /**
    * Returns a {@link CodeBlock} that evaluates to a requested object given an expression that
diff --git a/java/dagger/internal/codegen/binding/FrameworkTypeMapper.java b/java/dagger/internal/codegen/binding/FrameworkTypeMapper.java
index 85463af..776fac6 100644
--- a/java/dagger/internal/codegen/binding/FrameworkTypeMapper.java
+++ b/java/dagger/internal/codegen/binding/FrameworkTypeMapper.java
@@ -18,8 +18,8 @@
 
 import static dagger.internal.codegen.binding.BindingType.PRODUCTION;
 
-import dagger.model.RequestKind;
 import dagger.producers.Producer;
+import dagger.spi.model.RequestKind;
 import javax.inject.Provider;
 
 /**
diff --git a/java/dagger/internal/codegen/binding/InjectBindingRegistry.java b/java/dagger/internal/codegen/binding/InjectBindingRegistry.java
index 5203130..ceb5024 100644
--- a/java/dagger/internal/codegen/binding/InjectBindingRegistry.java
+++ b/java/dagger/internal/codegen/binding/InjectBindingRegistry.java
@@ -16,16 +16,17 @@
 
 package dagger.internal.codegen.binding;
 
+import androidx.room.compiler.processing.XConstructorElement;
+import androidx.room.compiler.processing.XFieldElement;
+import androidx.room.compiler.processing.XMethodElement;
 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 dagger.spi.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
@@ -53,10 +54,13 @@
   Optional<ProvisionBinding> getOrFindMembersInjectorProvisionBinding(Key key);
 
   @CanIgnoreReturnValue
-  Optional<ProvisionBinding> tryRegisterConstructor(ExecutableElement constructorElement);
+  Optional<ProvisionBinding> tryRegisterInjectConstructor(XConstructorElement constructorElement);
 
   @CanIgnoreReturnValue
-  Optional<MembersInjectionBinding> tryRegisterMembersInjectedType(TypeElement typeElement);
+  Optional<MembersInjectionBinding> tryRegisterInjectField(XFieldElement fieldElement);
+
+  @CanIgnoreReturnValue
+  Optional<MembersInjectionBinding> tryRegisterInjectMethod(XMethodElement methodElement);
 
   /**
    * This method ensures that sources for all registered {@link Binding bindings} (either explicitly
diff --git a/java/dagger/internal/codegen/binding/InjectionAnnotations.java b/java/dagger/internal/codegen/binding/InjectionAnnotations.java
index 748755e..250f5ee 100644
--- a/java/dagger/internal/codegen/binding/InjectionAnnotations.java
+++ b/java/dagger/internal/codegen/binding/InjectionAnnotations.java
@@ -16,33 +16,59 @@
 
 package dagger.internal.codegen.binding;
 
+import static androidx.room.compiler.processing.XElementKt.isConstructor;
+import static androidx.room.compiler.processing.XElementKt.isField;
+import static androidx.room.compiler.processing.XElementKt.isMethod;
+import static androidx.room.compiler.processing.XElementKt.isMethodParameter;
+import static androidx.room.compiler.processing.XElementKt.isTypeElement;
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static androidx.room.compiler.processing.compat.XConverters.toXProcessing;
 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 com.google.common.collect.Iterables.getOnlyElement;
 import static dagger.internal.codegen.base.MoreAnnotationValues.getStringValue;
+import static dagger.internal.codegen.binding.SourceFiles.factoryNameForElement;
 import static dagger.internal.codegen.binding.SourceFiles.memberInjectedFieldSignatureForVariable;
 import static dagger.internal.codegen.binding.SourceFiles.membersInjectorNameForType;
+import static dagger.internal.codegen.extension.DaggerCollectors.onlyElement;
+import static dagger.internal.codegen.extension.DaggerCollectors.toOptional;
+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 dagger.internal.codegen.xprocessing.XElements.asMethod;
+import static dagger.internal.codegen.xprocessing.XElements.asMethodParameter;
+import static dagger.internal.codegen.xprocessing.XElements.asTypeElement;
+import static dagger.internal.codegen.xprocessing.XElements.closestEnclosingTypeElement;
 import static javax.lang.model.element.Modifier.STATIC;
 import static javax.lang.model.util.ElementFilter.constructorsIn;
 
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XConstructorElement;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XExecutableElement;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XTypeElement;
 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.ImmutableList;
 import com.google.common.collect.ImmutableSet;
-import dagger.internal.InjectedFieldSignature;
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.base.DaggerSuperficialValidation;
+import dagger.internal.codegen.compileroption.CompilerOptions;
 import dagger.internal.codegen.extension.DaggerCollectors;
 import dagger.internal.codegen.extension.DaggerStreams;
+import dagger.internal.codegen.javapoet.TypeNames;
 import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
 import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.spi.model.DaggerAnnotation;
+import dagger.spi.model.Scope;
 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;
@@ -56,21 +82,148 @@
 
   private static final Equivalence<AnnotationMirror> EQUIVALENCE = AnnotationMirrors.equivalence();
 
+  private final XProcessingEnv processingEnv;
   private final DaggerElements elements;
   private final KotlinMetadataUtil kotlinMetadataUtil;
+  private final DaggerSuperficialValidation superficialValidation;
+  private final CompilerOptions compilerOptions;
 
   @Inject
-  InjectionAnnotations(DaggerElements elements, KotlinMetadataUtil kotlinMetadataUtil) {
+  InjectionAnnotations(
+      XProcessingEnv processingEnv,
+      DaggerElements elements,
+      KotlinMetadataUtil kotlinMetadataUtil,
+      DaggerSuperficialValidation superficialValidation,
+      CompilerOptions compilerOptions) {
+    this.processingEnv = processingEnv;
     this.elements = elements;
     this.kotlinMetadataUtil = kotlinMetadataUtil;
+    this.superficialValidation = superficialValidation;
+    this.compilerOptions = compilerOptions;
   }
 
-  public Optional<AnnotationMirror> getQualifier(Element e) {
-    if (!SuperficialValidation.validateElement(e)) {
-      throw new TypeNotPresentException(e.toString(), null);
+  /**
+   * Returns the scope on the given element if it exists.
+   *
+   * <p>The {@code ScopeMetadata} is used to avoid superficial validation on unnecessary
+   * annotations. If the {@code ScopeMetadata} does not exist, then all annotations must be
+   * superficially validated before we can determine if they are scopes or not.
+   *
+   * @throws IllegalArgumentException if the given element has more than one scope.
+   */
+  public Optional<Scope> getScope(XElement element) {
+    return getScopes(element).stream().collect(toOptional());
+  }
+
+  /**
+   * Returns the scopes on the given element, or an empty set if none exist.
+   *
+   * <p>Note: Use {@link #getScope(XElement)} if the usage of the scope on the given element has
+   * already been validated and known to be unique. This method should typically only be used in the
+   * process of such validation.
+   *
+   * <p>The {@code ScopeMetadata} is used to avoid superficial validation on unnecessary
+   * annotations. If the {@code ScopeMetadata} does not exist, then all annotations must be
+   * superficially validated before we can determine if they are scopes or not.
+   */
+  public ImmutableSet<Scope> getScopes(XElement element) {
+    superficialValidation.validateTypeOf(element);
+    ImmutableSet<Scope> scopes =
+        getScopesFromScopeMetadata(element)
+            .orElseGet(
+                () -> {
+                  // Validate the annotation types before we check for @Scope, otherwise the @Scope
+                  // annotation may appear to be missing (b/213880825).
+                  superficialValidation.validateAnnotationTypesOf(element);
+                  return element.getAllAnnotations().stream()
+                      .filter(InjectionAnnotations::hasScopeAnnotation)
+                      .map(DaggerAnnotation::from)
+                      .map(Scope::scope)
+                      .collect(toImmutableSet());
+                });
+
+    // Fully validate each scope to ensure its values are also valid.
+    scopes.stream()
+        .map(scope -> scope.scopeAnnotation().xprocessing())
+        .forEach(scope -> superficialValidation.validateAnnotationOf(element, scope));
+    return scopes;
+  }
+
+  private Optional<ImmutableSet<Scope>> getScopesFromScopeMetadata(XElement element) {
+    Optional<XAnnotation> scopeMetadata = getScopeMetadata(element);
+    if (!scopeMetadata.isPresent()) {
+      return Optional.empty();
     }
+    String scopeName = scopeMetadata.get().getAsString("value");
+    if (scopeName.isEmpty()) {
+      return Optional.of(ImmutableSet.of());
+    }
+    XAnnotation scopeAnnotation =
+        element.getAllAnnotations().stream()
+            .filter(
+                annotation ->
+                    scopeName.contentEquals(
+                        annotation.getType().getTypeElement().getQualifiedName()))
+            .collect(onlyElement());
+    // Do superficial validation before we convert to a Scope, otherwise the @Scope annotation may
+    // appear to be missing from the annotation if it's no longer on the classpath.
+    superficialValidation.validateAnnotationTypeOf(element, scopeAnnotation);
+
+    // If strictSuperficialValidation is disabled, then we fall back to the old behavior where
+    // we may potentially miss a scope rather than report an exception.
+    if (compilerOptions.strictSuperficialValidation()) {
+      return Optional.of(ImmutableSet.of(Scope.scope(DaggerAnnotation.from(scopeAnnotation))));
+    } else {
+      return Scope.isScope(DaggerAnnotation.from(scopeAnnotation))
+          ? Optional.of(ImmutableSet.of(Scope.scope(DaggerAnnotation.from(scopeAnnotation))))
+          : Optional.empty();
+    }
+  }
+
+  private Optional<XAnnotation> getScopeMetadata(XElement element) {
+    return getGeneratedNameForScopeMetadata(element)
+        .flatMap(factoryName -> Optional.ofNullable(processingEnv.findTypeElement(factoryName)))
+        .flatMap(factory -> Optional.ofNullable(factory.getAnnotation(TypeNames.SCOPE_METADATA)));
+  }
+
+  private Optional<ClassName> getGeneratedNameForScopeMetadata(XElement element) {
+    // Currently, we only support ScopeMetadata for inject-constructor types and provides methods.
+    if (isTypeElement(element)) {
+      return asTypeElement(element).getConstructors().stream()
+          .filter(InjectionAnnotations::hasInjectOrAssistedInjectAnnotation)
+          .findFirst()
+          .map(SourceFiles::factoryNameForElement);
+    } else if (isMethod(element) && element.hasAnnotation(TypeNames.PROVIDES)) {
+      return Optional.of(factoryNameForElement(asMethod(element)));
+    }
+    return Optional.empty();
+  }
+
+  /*
+   * Returns the qualifier on the given element if it exists.
+   *
+   * <p>The {@code QualifierMetadata} is used to avoid superficial validation on unnecessary
+   * annotations. If the {@code QualifierMetadata} does not exist, then all annotations must be
+   * superficially validated before we can determine if they are qualifiers or not.
+   *
+   * @throws IllegalArgumentException if the given element has more than one qualifier.
+   */
+  public Optional<XAnnotation> getQualifier(XElement element) {
+    return getQualifier(toJavac(element)).map(qualifier -> toXProcessing(qualifier, processingEnv));
+  }
+
+  /*
+   * Returns the qualifier on the given element if it exists.
+   *
+   * <p>The {@code QualifierMetadata} is used to avoid superficial validation on unnecessary
+   * annotations. If the {@code QualifierMetadata} does not exist, then all annotations must be
+   * superficially validated before we can determine if they are qualifiers or not.
+   *
+   * @throws IllegalArgumentException if the given element has more than one qualifier.
+   */
+  public Optional<AnnotationMirror> getQualifier(Element e) {
     checkNotNull(e);
-    ImmutableCollection<? extends AnnotationMirror> qualifierAnnotations = getQualifiers(e);
+    ImmutableList<? extends AnnotationMirror> qualifierAnnotations = getQualifiers(e);
     switch (qualifierAnnotations.size()) {
       case 0:
         return Optional.empty();
@@ -82,32 +235,187 @@
     }
   }
 
-  public ImmutableCollection<? extends AnnotationMirror> getQualifiers(Element element) {
+  /*
+   * Returns the qualifiers on the given element, or an empty set if none exist.
+   *
+   * <p>The {@code QualifierMetadata} is used to avoid superficial validation on unnecessary
+   * annotations. If the {@code QualifierMetadata} does not exist, then all annotations must be
+   * superficially validated before we can determine if they are qualifiers or not.
+   */
+  public ImmutableSet<XAnnotation> getQualifiers(XElement element) {
+    return getQualifiers(toJavac(element)).stream()
+        .map(qualifier -> toXProcessing(qualifier, processingEnv))
+        .collect(toImmutableSet());
+  }
+
+  /*
+   * Returns the qualifiers on the given element, or an empty set if none exist.
+   *
+   * <p>The {@code QualifierMetadata} is used to avoid superficial validation on unnecessary
+   * annotations. If the {@code QualifierMetadata} does not exist, then all annotations must be
+   * superficially validated before we can determine if they are qualifiers or not.
+   */
+  public ImmutableList<? extends AnnotationMirror> getQualifiers(Element element) {
+    superficialValidation.validateTypeOf(toXProcessing(element, processingEnv));
     ImmutableSet<? extends AnnotationMirror> qualifiers =
-        AnnotationMirrors.getAnnotatedAnnotations(element, Qualifier.class);
+        getQualifiersFromQualifierMetadata(element)
+            .orElseGet(
+                () -> {
+                  // Validate the annotation types before we check for @Qualifier, otherwise the
+                  // @Qualifier annotation may appear to be missing (b/213880825).
+                  superficialValidation.validateAnnotationTypesOf(element);
+                  return element.getAnnotationMirrors().stream()
+                      .filter(InjectionAnnotations::hasQualifierAnnotation)
+                      .collect(toImmutableSet());
+                });
+
     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)
+        && hasInjectAnnotation(element)
         && 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();
+      qualifiers =
+          Stream.concat(
+                  qualifiers.stream(), getQualifiersForKotlinProperty(asVariable(element)).stream())
+              .map(EQUIVALENCE::wrap) // Wrap in equivalence to deduplicate
+              .distinct()
+              .map(Wrapper::get)
+              .collect(DaggerStreams.toImmutableSet());
     }
+
+    // Fully validate each qualifier to ensure its values are also valid.
+    qualifiers.forEach(
+        qualifier -> {
+          superficialValidation.validateAnnotationOf(element, qualifier);
+        });
+
+    return qualifiers.asList();
+  }
+
+  private Optional<ImmutableSet<? extends AnnotationMirror>> getQualifiersFromQualifierMetadata(
+      Element javaElement) {
+    XElement element = toXProcessing(javaElement, processingEnv);
+    Optional<XAnnotation> qualifierMetadata = getQualifierMetadata(element);
+    if (!qualifierMetadata.isPresent()) {
+      return Optional.empty();
+    }
+    ImmutableSet<String> qualifierNames =
+        ImmutableSet.copyOf(qualifierMetadata.get().getAsStringList("value"));
+    if (qualifierNames.isEmpty()) {
+      return Optional.of(ImmutableSet.of());
+    }
+    ImmutableSet<XAnnotation> qualifierAnnotations =
+        element.getAllAnnotations().stream()
+            .filter(
+                annotation ->
+                    qualifierNames.contains(
+                        annotation.getType().getTypeElement().getQualifiedName()))
+            .collect(toImmutableSet());
+    if (qualifierAnnotations.isEmpty()) {
+      return Optional.of(ImmutableSet.of());
+    }
+    // We should be guaranteed that there's exactly one qualifier since the existance of
+    // @QualifierMetadata means that this element has already been processed and multiple
+    // qualifiers would have been caught already.
+    XAnnotation qualifierAnnotation = getOnlyElement(qualifierAnnotations);
+
+    // Ensure the annotation type is superficially valid before we check for @Qualifier, otherwise
+    // the @Qualifier marker may appear to be missing from the annotation (b/213880825).
+    superficialValidation.validateAnnotationTypeOf(element, qualifierAnnotation);
+    if (compilerOptions.strictSuperficialValidation()) {
+      return Optional.of(ImmutableSet.of(toJavac(qualifierAnnotation)));
+    } else {
+      // If strictSuperficialValidation is disabled, then we fall back to the old behavior where
+      // we may potentially miss a qualifier rather than report an exception.
+      return hasQualifierAnnotation(toJavac(qualifierAnnotation))
+          ? Optional.of(ImmutableSet.of(toJavac(qualifierAnnotation)))
+          : Optional.empty();
+    }
+  }
+
+  /**
+   * Returns {@code QualifierMetadata} annotation.
+   *
+   * <p>Currently, {@code QualifierMetadata} is only associated with inject constructor parameters,
+   * inject fields, inject method parameters, provide methods, and provide method parameters.
+   */
+  private Optional<XAnnotation> getQualifierMetadata(XElement element) {
+    return getGeneratedNameForQualifierMetadata(element)
+        .flatMap(name -> Optional.ofNullable(processingEnv.findTypeElement(name)))
+        .flatMap(type -> Optional.ofNullable(type.getAnnotation(TypeNames.QUALIFIER_METADATA)));
+  }
+
+  private Optional<ClassName> getGeneratedNameForQualifierMetadata(XElement element) {
+    // Currently we only support @QualifierMetadata for @Inject fields, @Inject method parameters,
+    // @Inject constructor parameters, @Provides methods, and @Provides method parameters.
+    if (isField(element) && hasInjectAnnotation(element)) {
+      return Optional.of(membersInjectorNameForType(closestEnclosingTypeElement(element)));
+    } else if (isMethod(element) && element.hasAnnotation(TypeNames.PROVIDES)) {
+      return Optional.of(factoryNameForElement(asMethod(element)));
+    } else if (isMethodParameter(element)) {
+      XExecutableElement executableElement = asMethodParameter(element).getEnclosingMethodElement();
+      if (isConstructor(executableElement)
+          && hasInjectOrAssistedInjectAnnotation(executableElement)) {
+        return Optional.of(factoryNameForElement(executableElement));
+      }
+      if (isMethod(executableElement) && hasInjectAnnotation(executableElement)) {
+        return Optional.of(membersInjectorNameForType(closestEnclosingTypeElement(element)));
+      }
+      if (isMethod(executableElement) && executableElement.hasAnnotation(TypeNames.PROVIDES)) {
+        return Optional.of(factoryNameForElement(executableElement));
+      }
+    }
+    return Optional.empty();
+  }
+
+  /** Returns the constructors in {@code type} that are annotated with {@link Inject}. */
+  public static ImmutableSet<XConstructorElement> injectedConstructors(XTypeElement type) {
+    return type.getConstructors().stream()
+        .filter(InjectionAnnotations::hasInjectAnnotation)
+        .collect(toImmutableSet());
   }
 
   /** 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))
+        .filter(InjectionAnnotations::hasInjectAnnotation)
         .toSet();
   }
 
+  private static boolean hasQualifierAnnotation(AnnotationMirror annotation) {
+    return isAnyAnnotationPresent(
+        annotation.getAnnotationType().asElement(), TypeNames.QUALIFIER, TypeNames.QUALIFIER_JAVAX);
+  }
+
+  private static boolean hasScopeAnnotation(XAnnotation annotation) {
+    return annotation
+        .getType()
+        .getTypeElement()
+        .hasAnyAnnotation(TypeNames.SCOPE, TypeNames.SCOPE_JAVAX);
+  }
+
+  /** Returns true if the given element is annotated with {@link Inject}. */
+  public static boolean hasInjectAnnotation(XElement element) {
+    return element.hasAnyAnnotation(TypeNames.INJECT, TypeNames.INJECT_JAVAX);
+  }
+
+  /** Returns true if the given element is annotated with {@link Inject}. */
+  public static boolean hasInjectAnnotation(Element element) {
+    return isAnyAnnotationPresent(element, TypeNames.INJECT, TypeNames.INJECT_JAVAX);
+  }
+
+  /** Returns true if the given element is annotated with {@link Inject}. */
+  public static boolean hasInjectOrAssistedInjectAnnotation(XElement element) {
+    return element.hasAnyAnnotation(
+        TypeNames.INJECT, TypeNames.INJECT_JAVAX, TypeNames.ASSISTED_INJECT);
+  }
+
+  /** Returns true if the given element is annotated with {@link Inject}. */
+  public static boolean hasInjectOrAssistedInjectAnnotation(Element element) {
+    return isAnyAnnotationPresent(
+        element, TypeNames.INJECT, TypeNames.INJECT_JAVAX, TypeNames.ASSISTED_INJECT);
+  }
+
   /**
    * 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
@@ -131,7 +439,7 @@
         return ElementFilter.methodsIn(membersInjector.getEnclosedElements()).stream()
             .filter(
                 method ->
-                    getAnnotationMirror(method, InjectedFieldSignature.class)
+                    getAnnotationMirror(method, TypeNames.INJECTED_FIELD_SIGNATURE)
                         .map(annotation -> getStringValue(annotation, "value"))
                         .map(memberInjectedFieldSignature::equals)
                         // If a method is not an @InjectedFieldSignature method then filter it out
@@ -152,7 +460,13 @@
             "No MembersInjector found for " + fieldElement.getEnclosingElement());
       }
     } else {
-      return kotlinMetadataUtil.getSyntheticPropertyAnnotations(fieldElement, Qualifier.class);
+      return ImmutableSet.<AnnotationMirror>builder()
+          .addAll(
+              kotlinMetadataUtil.getSyntheticPropertyAnnotations(fieldElement, TypeNames.QUALIFIER))
+          .addAll(
+              kotlinMetadataUtil.getSyntheticPropertyAnnotations(
+                  fieldElement, TypeNames.QUALIFIER_JAVAX))
+          .build();
     }
   }
 }
diff --git a/java/dagger/internal/codegen/binding/InjectionSiteFactory.java b/java/dagger/internal/codegen/binding/InjectionSiteFactory.java
index 7a6f8d2..e620c05 100644
--- a/java/dagger/internal/codegen/binding/InjectionSiteFactory.java
+++ b/java/dagger/internal/codegen/binding/InjectionSiteFactory.java
@@ -16,11 +16,15 @@
 
 package dagger.internal.codegen.binding;
 
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static com.google.auto.common.MoreTypes.asDeclared;
+import static com.google.common.base.Preconditions.checkArgument;
 import static dagger.internal.codegen.langmodel.DaggerElements.DECLARATION_ORDER;
+import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
 import static javax.lang.model.element.Modifier.PRIVATE;
 import static javax.lang.model.element.Modifier.STATIC;
 
+import androidx.room.compiler.processing.XType;
 import com.google.auto.common.MoreElements;
 import com.google.auto.common.MoreTypes;
 import com.google.common.collect.ImmutableSortedSet;
@@ -63,6 +67,12 @@
   }
 
   /** Returns the injection sites for a type. */
+  ImmutableSortedSet<InjectionSite> getInjectionSites(XType type) {
+    checkArgument(isDeclared(type));
+    return getInjectionSites(asDeclared(toJavac(type)));
+  }
+
+  /** Returns the injection sites for a type. */
   ImmutableSortedSet<InjectionSite> getInjectionSites(DeclaredType declaredType) {
     Set<InjectionSite> injectionSites = new HashSet<>();
     List<TypeElement> ancestors = new ArrayList<>();
@@ -138,7 +148,7 @@
     }
 
     private boolean shouldBeInjected(Element injectionSite) {
-      return isAnnotationPresent(injectionSite, Inject.class)
+      return InjectionAnnotations.hasInjectAnnotation(injectionSite)
           && !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
index d923666..daae7d7 100644
--- a/java/dagger/internal/codegen/binding/KeyFactory.java
+++ b/java/dagger/internal/codegen/binding/KeyFactory.java
@@ -16,24 +16,35 @@
 
 package dagger.internal.codegen.binding;
 
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.auto.common.MoreTypes.asExecutable;
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static androidx.room.compiler.processing.compat.XConverters.toXProcessing;
 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.base.Preconditions.checkState;
 import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.base.ProducerAnnotations.productionImplementationQualifier;
+import static dagger.internal.codegen.base.ProducerAnnotations.productionQualifier;
 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.DaggerElements.isAnnotationPresent;
 import static dagger.internal.codegen.langmodel.DaggerTypes.isFutureType;
 import static dagger.internal.codegen.langmodel.DaggerTypes.unwrapType;
+import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
 import static java.util.Arrays.asList;
 import static javax.lang.model.element.ElementKind.METHOD;
 
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XMethodType;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.auto.common.MoreTypes;
 import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
 import dagger.Binds;
 import dagger.BindsOptionalOf;
 import dagger.internal.codegen.base.ContributionType;
@@ -42,25 +53,19 @@
 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.javapoet.TypeNames;
 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 dagger.spi.model.DaggerAnnotation;
+import dagger.spi.model.DaggerType;
+import dagger.spi.model.Key;
+import dagger.spi.model.Key.MultibindingContributionIdentifier;
+import dagger.spi.model.RequestKind;
 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;
@@ -71,15 +76,20 @@
 
 /** A factory for {@link Key}s. */
 public final class KeyFactory {
+  private final XProcessingEnv processingEnv;
   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);
+      XProcessingEnv processingEnv,
+      DaggerTypes types,
+      DaggerElements elements,
+      InjectionAnnotations injectionAnnotations) {
+    this.processingEnv = processingEnv;
+    this.types = types;
+    this.elements = elements;
     this.injectionAnnotations = injectionAnnotations;
   }
 
@@ -88,95 +98,124 @@
   }
 
   private DeclaredType setOf(TypeMirror elementType) {
-    return types.getDeclaredType(elements.getTypeElement(Set.class), boxPrimitives(elementType));
+    return types.getDeclaredType(
+        elements.getTypeElement(TypeNames.SET), boxPrimitives(elementType));
+  }
+
+  private DeclaredType mapOf(XType keyType, XType valueType) {
+    return mapOf(toJavac(keyType), toJavac(valueType));
   }
 
   private DeclaredType mapOf(TypeMirror keyType, TypeMirror valueType) {
     return types.getDeclaredType(
-        elements.getTypeElement(Map.class), boxPrimitives(keyType), boxPrimitives(valueType));
+        elements.getTypeElement(TypeNames.MAP), 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)));
+      XType keyType, ClassName frameworkClassName, XType valueType) {
+    return mapOfFrameworkType(toJavac(keyType), frameworkClassName, toJavac(valueType));
   }
 
-  Key forComponentMethod(ExecutableElement componentMethod) {
-    checkArgument(componentMethod.getKind().equals(METHOD));
+  /** Returns {@code Map<KeyType, FrameworkType<ValueType>>}. */
+  private TypeMirror mapOfFrameworkType(
+      TypeMirror keyType, ClassName frameworkClassName, TypeMirror valueType) {
+    return mapOf(
+        keyType,
+        types.getDeclaredType(
+            elements.getTypeElement(frameworkClassName), boxPrimitives(valueType)));
+  }
+
+  Key forComponentMethod(XMethodElement componentMethod) {
     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;
+  Key forProductionComponentMethod(XMethodElement componentMethod) {
+    XType returnType = componentMethod.getReturnType();
+    XType keyType =
+        isFutureType(returnType) ? getOnlyElement(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();
+      XMethodElement subcomponentCreatorMethod, XType declaredContainer) {
+    checkArgument(isDeclared(declaredContainer));
+    XMethodType resolvedMethod = subcomponentCreatorMethod.asMemberOf(declaredContainer);
+    return Key.builder(DaggerType.from(resolvedMethod.getReturnType())).build();
   }
 
-  public Key forSubcomponentCreator(TypeMirror creatorType) {
-    return Key.builder(creatorType).build();
+  public Key forSubcomponentCreator(XType creatorType) {
+    return Key.builder(DaggerType.from(creatorType)).build();
+  }
+
+  public Key forProvidesMethod(XMethodElement method, XTypeElement contributingModule) {
+    return forProvidesMethod(toJavac(method), toJavac(contributingModule));
   }
 
   public Key forProvidesMethod(ExecutableElement method, TypeElement contributingModule) {
-    return forBindingMethod(
-        method, contributingModule, Optional.of(elements.getTypeElement(Provider.class)));
+    return forBindingMethod(method, contributingModule, Optional.of(TypeNames.PROVIDER));
+  }
+
+  public Key forProducesMethod(XMethodElement method, XTypeElement contributingModule) {
+    return forProducesMethod(toJavac(method), toJavac(contributingModule));
   }
 
   public Key forProducesMethod(ExecutableElement method, TypeElement contributingModule) {
-    return forBindingMethod(
-        method, contributingModule, Optional.of(elements.getTypeElement(Producer.class)));
+    return forBindingMethod(method, contributingModule, Optional.of(TypeNames.PRODUCER));
+  }
+
+  /** Returns the key bound by a {@link Binds} method. */
+  Key forBindsMethod(XMethodElement method, XTypeElement contributingModule) {
+    return forBindsMethod(toJavac(method), toJavac(contributingModule));
   }
 
   /** Returns the key bound by a {@link Binds} method. */
   Key forBindsMethod(ExecutableElement method, TypeElement contributingModule) {
-    checkArgument(isAnnotationPresent(method, Binds.class));
+    checkArgument(isAnnotationPresent(method, TypeNames.BINDS));
     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));
+  Key forBindsOptionalOfMethod(XMethodElement method, XTypeElement contributingModule) {
+    checkArgument(method.hasAnnotation(TypeNames.BINDS_OPTIONAL_OF));
     return forBindingMethod(method, contributingModule, Optional.empty());
   }
 
   private Key forBindingMethod(
+      XMethodElement method,
+      XTypeElement contributingModule,
+      Optional<ClassName> frameworkClassName) {
+    return forBindingMethod(toJavac(method), toJavac(contributingModule), frameworkClassName);
+  }
+
+  private Key forBindingMethod(
       ExecutableElement method,
       TypeElement contributingModule,
-      Optional<TypeElement> frameworkType) {
+      Optional<ClassName> frameworkClassName) {
     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))
+    if (frameworkClassName.isPresent()
+        && frameworkClassName.get().equals(TypeNames.PRODUCER)
         && 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);
+        SetType setType = SetType.from(toXProcessing(returnType, processingEnv));
         if (isFutureType(setType.elementType())) {
           returnType =
               types.getDeclaredType(
-                  elements.getTypeElement(Set.class), unwrapType(setType.elementType()));
+                  elements.getTypeElement(TypeNames.SET),
+                  toJavac(unwrapType(setType.elementType())));
         }
       }
     }
-    TypeMirror keyType = bindingMethodKeyType(returnType, method, contributionType, frameworkType);
+    TypeMirror keyType =
+        bindingMethodKeyType(returnType, method, contributionType, frameworkClassName);
     Key key = forMethod(method, keyType);
     return contributionType.equals(ContributionType.UNIQUE)
         ? key
@@ -192,33 +231,48 @@
    * <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();
+  Key forMultibindsMethod(XMethodElement method, XMethodType methodType) {
+    XType returnType = method.getReturnType();
     TypeMirror keyType =
         MapType.isMap(returnType)
             ? mapOfFrameworkType(
                 MapType.from(returnType).keyType(),
-                elements.getTypeElement(Provider.class),
+                TypeNames.PROVIDER,
                 MapType.from(returnType).valueType())
-            : returnType;
-    return forMethod(method, keyType);
+            : toJavac(returnType);
+    return forMethod(toJavac(method), keyType);
   }
 
   private TypeMirror bindingMethodKeyType(
       TypeMirror returnType,
       ExecutableElement method,
       ContributionType contributionType,
-      Optional<TypeElement> frameworkType) {
+      Optional<ClassName> frameworkClassName) {
     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)
+        Optional<AnnotationMirror> mapKey = getMapKey(method);
+        // TODO(bcorso): We've added a special checkState here since a number of people have run
+        // into this particular case, but technically it shouldn't be necessary if we are properly
+        // doing superficial validation and deferring on unresolvable types. We should revisit
+        // whether this is necessary once we're able to properly defer this case.
+        checkState(
+            mapKey.isPresent(),
+            "Missing map key annotation for method: %s#%s. That method was annotated with: %s. If a"
+                + " map key annotation is included in that list, it means Dagger wasn't able to"
+                + " detect that it was a map key because the dependency is missing from the"
+                + " classpath of the current build. To fix, add a dependency for the map key to the"
+                + " current build. For more details, see"
+                + " https://github.com/google/dagger/issues/3133#issuecomment-1002790894.",
+            method.getEnclosingElement(),
+            method,
+            method.getAnnotationMirrors());
+        TypeMirror mapKeyType = mapKeyType(toXProcessing(mapKey.get(), processingEnv));
+        return frameworkClassName.isPresent()
+            ? mapOfFrameworkType(mapKeyType, frameworkClassName.get(), returnType)
             : mapOf(mapKeyType, returnType);
       case SET_VALUES:
         // TODO(gak): do we want to allow people to use "covariant return" here?
@@ -235,47 +289,69 @@
    * 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) {
+  Key forDelegateBinding(DelegateDeclaration delegateDeclaration, ClassName frameworkType) {
     return delegateDeclaration.contributionType().equals(ContributionType.MAP)
         ? wrapMapValue(delegateDeclaration.key(), frameworkType)
         : delegateDeclaration.key();
   }
 
+  private Key forMethod(XMethodElement method, XType keyType) {
+    return forMethod(toJavac(method), toJavac(keyType));
+  }
+
   private Key forMethod(ExecutableElement method, TypeMirror keyType) {
     return forQualifiedType(injectionAnnotations.getQualifier(method), keyType);
   }
 
+  public Key forInjectConstructorWithResolvedType(XType type) {
+    return forInjectConstructorWithResolvedType(toJavac(type));
+  }
+
   public Key forInjectConstructorWithResolvedType(TypeMirror type) {
-    return Key.builder(type).build();
+    return Key.builder(fromJava(type)).build();
   }
 
   // TODO(ronshapiro): Remove these conveniences which are simple wrappers around Key.Builder
-  Key forType(TypeMirror type) {
-    return Key.builder(type).build();
+  Key forType(XType type) {
+    return Key.builder(DaggerType.from(type)).build();
   }
 
   public Key forMembersInjectedType(TypeMirror type) {
-    return Key.builder(type).build();
+    return forMembersInjectedType(toXProcessing(type, processingEnv));
+  }
+
+  public Key forMembersInjectedType(XType type) {
+    return Key.builder(DaggerType.from(type)).build();
   }
 
   Key forQualifiedType(Optional<AnnotationMirror> qualifier, TypeMirror type) {
-    return Key.builder(boxPrimitives(type)).qualifier(qualifier).build();
+    return forQualifiedType(
+        qualifier.map(annotation -> toXProcessing(annotation, processingEnv)),
+        toXProcessing(type, processingEnv));
+  }
+
+  Key forQualifiedType(Optional<XAnnotation> qualifier, XType type) {
+    return Key.builder(DaggerType.from(type.boxed()))
+        .qualifier(qualifier.map(DaggerAnnotation::from))
+        .build();
   }
 
   public Key forProductionExecutor() {
-    return Key.builder(elements.getTypeElement(Executor.class).asType())
-        .qualifier(SimpleAnnotationMirror.of(elements.getTypeElement(Production.class)))
+    return Key.builder(fromJava(elements.getTypeElement(TypeNames.EXECUTOR).asType()))
+        .qualifier(fromJava(toJavac(productionQualifier(processingEnv))))
         .build();
   }
 
   public Key forProductionImplementationExecutor() {
-    return Key.builder(elements.getTypeElement(Executor.class).asType())
-        .qualifier(SimpleAnnotationMirror.of(elements.getTypeElement(ProductionImplementation.class)))
+    return Key.builder(fromJava(elements.getTypeElement(TypeNames.EXECUTOR).asType()))
+        .qualifier(fromJava(toJavac(productionImplementationQualifier(processingEnv))))
         .build();
   }
 
   public Key forProductionComponentMonitor() {
-    return Key.builder(elements.getTypeElement(ProductionComponentMonitor.class).asType()).build();
+    return Key.builder(
+            fromJava(elements.getTypeElement(TypeNames.PRODUCTION_COMPONENT_MONITOR).asType()))
+        .build();
   }
 
   /**
@@ -298,8 +374,8 @@
    */
   Optional<Key> implicitMapProviderKeyFrom(Key possibleMapKey) {
     return firstPresent(
-        rewrapMapKey(possibleMapKey, Produced.class, Provider.class),
-        wrapMapKey(possibleMapKey, Provider.class));
+        rewrapMapKey(possibleMapKey, TypeNames.PRODUCED, TypeNames.PROVIDER),
+        wrapMapKey(possibleMapKey, TypeNames.PROVIDER));
   }
 
   /**
@@ -310,8 +386,8 @@
    */
   Optional<Key> implicitMapProducerKeyFrom(Key possibleMapKey) {
     return firstPresent(
-        rewrapMapKey(possibleMapKey, Produced.class, Producer.class),
-        wrapMapKey(possibleMapKey, Producer.class));
+        rewrapMapKey(possibleMapKey, TypeNames.PRODUCED, TypeNames.PRODUCER),
+        wrapMapKey(possibleMapKey, TypeNames.PRODUCER));
   }
 
   /**
@@ -325,10 +401,12 @@
     if (MapType.isMap(key)) {
       MapType mapType = MapType.from(key);
       if (!mapType.isRawType()) {
-        for (Class<?> frameworkClass : asList(Provider.class, Producer.class, Produced.class)) {
+        for (ClassName frameworkClass :
+            asList(TypeNames.PROVIDER, TypeNames.PRODUCER, TypeNames.PRODUCED)) {
           if (mapType.valuesAreTypeOf(frameworkClass)) {
             return key.toBuilder()
-                .type(mapOf(mapType.keyType(), mapType.unwrappedValueType(frameworkClass)))
+                .type(
+                    fromJava(mapOf(mapType.keyType(), mapType.unwrappedValueType(frameworkClass))))
                 .build();
           }
         }
@@ -337,13 +415,11 @@
     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) {
+  /** Converts a {@link Key} of type {@code Map<K, V>} to {@code Map<K, Provider<V>>}. */
+  private Key wrapMapValue(Key key, ClassName newWrappingClassName) {
     checkArgument(
-        FrameworkTypes.isFrameworkType(elements.getTypeElement(newWrappingClass).asType()));
-    return wrapMapKey(key, newWrappingClass).get();
+        FrameworkTypes.isFrameworkType(elements.getTypeElement(newWrappingClassName).asType()));
+    return wrapMapKey(key, newWrappingClassName).get();
   }
 
   /**
@@ -357,12 +433,12 @@
    *     currentWrappingClass}
    */
   public Optional<Key> rewrapMapKey(
-      Key possibleMapKey, Class<?> currentWrappingClass, Class<?> newWrappingClass) {
-    checkArgument(!currentWrappingClass.equals(newWrappingClass));
+      Key possibleMapKey, ClassName currentWrappingClassName, ClassName newWrappingClassName) {
+    checkArgument(!currentWrappingClassName.equals(newWrappingClassName));
     if (MapType.isMap(possibleMapKey)) {
       MapType mapType = MapType.from(possibleMapKey);
-      if (!mapType.isRawType() && mapType.valuesAreTypeOf(currentWrappingClass)) {
-        TypeElement wrappingElement = elements.getTypeElement(newWrappingClass);
+      if (!mapType.isRawType() && mapType.valuesAreTypeOf(currentWrappingClassName)) {
+        TypeElement wrappingElement = elements.getTypeElement(newWrappingClassName);
         if (wrappingElement == null) {
           // This target might not be compiled with Producers, so wrappingClass might not have an
           // associated element.
@@ -370,9 +446,11 @@
         }
         DeclaredType wrappedValueType =
             types.getDeclaredType(
-                wrappingElement, mapType.unwrappedValueType(currentWrappingClass));
+                wrappingElement, toJavac(mapType.unwrappedValueType(currentWrappingClassName)));
         return Optional.of(
-            possibleMapKey.toBuilder().type(mapOf(mapType.keyType(), wrappedValueType)).build());
+            possibleMapKey.toBuilder()
+                .type(fromJava(mapOf(toJavac(mapType.keyType()), wrappedValueType)))
+                .build());
       }
     }
     return Optional.empty();
@@ -385,19 +463,22 @@
    *
    * <p>Returns {@link Optional#empty()} if {@code WrappingClass} is not in the classpath.
    */
-  private Optional<Key> wrapMapKey(Key possibleMapKey, Class<?> wrappingClass) {
+  private Optional<Key> wrapMapKey(Key possibleMapKey, ClassName wrappingClassName) {
     if (MapType.isMap(possibleMapKey)) {
       MapType mapType = MapType.from(possibleMapKey);
-      if (!mapType.isRawType() && !mapType.valuesAreTypeOf(wrappingClass)) {
-        TypeElement wrappingElement = elements.getTypeElement(wrappingClass);
+      if (!mapType.isRawType() && !mapType.valuesAreTypeOf(wrappingClassName)) {
+        TypeElement wrappingElement = elements.getTypeElement(wrappingClassName);
         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());
+        DeclaredType wrappedValueType =
+            types.getDeclaredType(wrappingElement, toJavac(mapType.valueType()));
         return Optional.of(
-            possibleMapKey.toBuilder().type(mapOf(mapType.keyType(), wrappedValueType)).build());
+            possibleMapKey.toBuilder()
+                .type(fromJava(mapOf(toJavac(mapType.keyType()), wrappedValueType)))
+                .build());
       }
     }
     return Optional.empty();
@@ -407,12 +488,14 @@
    * 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) {
+  Optional<Key> unwrapSetKey(Key key, ClassName wrappingClassName) {
     if (SetType.isSet(key)) {
       SetType setType = SetType.from(key);
-      if (!setType.isRawType() && setType.elementsAreTypeOf(wrappingClass)) {
+      if (!setType.isRawType() && setType.elementsAreTypeOf(wrappingClassName)) {
         return Optional.of(
-            key.toBuilder().type(setOf(setType.unwrappedElementType(wrappingClass))).build());
+            key.toBuilder()
+                .type(fromJava(setOf(toJavac(setType.unwrappedElementType(wrappingClassName)))))
+                .build());
       }
     }
     return Optional.empty();
@@ -428,7 +511,16 @@
       return Optional.empty();
     }
 
-    TypeMirror optionalValueType = OptionalType.from(key).valueType();
-    return Optional.of(key.toBuilder().type(extractKeyType(optionalValueType)).build());
+    XType optionalValueType = OptionalType.from(key).valueType();
+    return Optional.of(
+        key.toBuilder().type(DaggerType.from(extractKeyType(optionalValueType))).build());
+  }
+
+  private DaggerAnnotation fromJava(AnnotationMirror annotation) {
+    return DaggerAnnotation.from(toXProcessing(annotation, processingEnv));
+  }
+
+  private DaggerType fromJava(TypeMirror typeMirror) {
+    return DaggerType.from(toXProcessing(typeMirror, processingEnv));
   }
 }
diff --git a/java/dagger/internal/codegen/binding/KeyVariableNamer.java b/java/dagger/internal/codegen/binding/KeyVariableNamer.java
index 9ac0efa..fb42410 100644
--- a/java/dagger/internal/codegen/binding/KeyVariableNamer.java
+++ b/java/dagger/internal/codegen/binding/KeyVariableNamer.java
@@ -16,22 +16,21 @@
 
 package dagger.internal.codegen.binding;
 
+import static androidx.room.compiler.processing.XTypeKt.isArray;
 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 static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
+import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
+import static dagger.internal.codegen.xprocessing.XTypes.isPrimitive;
 
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XArrayType;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.common.collect.ImmutableSet;
-import dagger.model.DependencyRequest;
-import dagger.model.Key;
+import dagger.spi.model.DependencyRequest;
+import dagger.spi.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
@@ -47,45 +46,6 @@
           "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) {
@@ -97,11 +57,36 @@
 
     if (key.qualifier().isPresent()) {
       // TODO(gak): Use a better name for fields with qualifiers with members.
-      builder.append(key.qualifier().get().getAnnotationType().asElement().getSimpleName());
+      builder.append(key.qualifier().get().java().getAnnotationType().asElement().getSimpleName());
     }
 
-    key.type().accept(TYPE_NAMER, builder);
-
+    typeNamer(key.type().xprocessing(), builder);
     return protectAgainstKeywords(UPPER_CAMEL.to(LOWER_CAMEL, builder.toString()));
   }
+
+  private static void typeNamer(XType type, StringBuilder builder) {
+    if (isDeclared(type)) {
+      XTypeElement element = type.getTypeElement();
+      if (element.isNested() && VERY_SIMPLE_NAMES.contains(getSimpleName(element))) {
+        builder.append(getSimpleName(element.getEnclosingTypeElement()));
+      }
+
+      builder.append(getSimpleName(element));
+      Iterator<? extends XType> argumentIterator = type.getTypeArguments().iterator();
+      if (argumentIterator.hasNext()) {
+        builder.append("Of");
+        XType first = argumentIterator.next();
+        typeNamer(first, builder);
+        while (argumentIterator.hasNext()) {
+          builder.append("And");
+          typeNamer(argumentIterator.next(), builder);
+        }
+      }
+    } else if (isPrimitive(type)) {
+      builder.append(LOWER_CAMEL.to(UPPER_CAMEL, type.toString()));
+    } else if (isArray(type)) {
+      typeNamer(((XArrayType) type).getComponentType(), builder);
+      builder.append("Array");
+    }
+  }
 }
diff --git a/java/dagger/internal/codegen/binding/LegacyBindingGraph.java b/java/dagger/internal/codegen/binding/LegacyBindingGraph.java
index 7c00401..2f8d39a 100644
--- a/java/dagger/internal/codegen/binding/LegacyBindingGraph.java
+++ b/java/dagger/internal/codegen/binding/LegacyBindingGraph.java
@@ -16,16 +16,16 @@
 
 package dagger.internal.codegen.binding;
 
+import androidx.room.compiler.processing.XTypeElement;
 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 dagger.spi.model.Key;
+import dagger.spi.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. */
@@ -69,7 +69,7 @@
 
   private static ImmutableList<LegacyBindingGraph> checkForDuplicates(
       ImmutableList<LegacyBindingGraph> graphs) {
-    Map<TypeElement, Collection<LegacyBindingGraph>> duplicateGraphs =
+    Map<XTypeElement, Collection<LegacyBindingGraph>> duplicateGraphs =
         Maps.filterValues(
             Multimaps.index(graphs, graph -> graph.componentDescriptor().typeElement()).asMap(),
             overlapping -> overlapping.size() > 1);
diff --git a/java/dagger/internal/codegen/binding/MapKeys.java b/java/dagger/internal/codegen/binding/MapKeys.java
index ec7d79d..7b2c4da 100644
--- a/java/dagger/internal/codegen/binding/MapKeys.java
+++ b/java/dagger/internal/codegen/binding/MapKeys.java
@@ -16,18 +16,29 @@
 
 package dagger.internal.codegen.binding;
 
+import static androidx.room.compiler.processing.XTypeKt.isArray;
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static androidx.room.compiler.processing.compat.XConverters.toXProcessing;
 import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
 import static com.google.auto.common.AnnotationMirrors.getAnnotationValuesWithDefaults;
+import static com.google.auto.common.MoreElements.asExecutable;
 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 dagger.internal.codegen.xprocessing.XElements.getSimpleName;
+import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
+import static dagger.internal.codegen.xprocessing.XTypes.isPrimitive;
 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 androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.auto.common.MoreTypes;
 import com.google.common.collect.ImmutableSet;
 import com.squareup.javapoet.ClassName;
@@ -35,22 +46,18 @@
 import com.squareup.javapoet.MethodSpec;
 import com.squareup.javapoet.TypeName;
 import dagger.MapKey;
+import dagger.internal.codegen.base.DaggerSuperficialValidation;
 import dagger.internal.codegen.base.MapKeyAccessibility;
+import dagger.internal.codegen.javapoet.TypeNames;
 import dagger.internal.codegen.langmodel.DaggerElements;
 import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.xprocessing.XElements;
 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 {
@@ -61,6 +68,16 @@
    * @throws IllegalArgumentException if the element is annotated with more than one {@code MapKey}
    *     annotation
    */
+  static Optional<AnnotationMirror> getMapKey(XElement bindingElement) {
+    return getMapKey(toJavac(bindingElement));
+  }
+
+  /**
+   * 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()
@@ -73,6 +90,11 @@
     return getAnnotatedAnnotations(bindingElement, MapKey.class);
   }
 
+  /** Returns all of the {@link MapKey} annotations that annotate {@code bindingElement}. */
+  public static ImmutableSet<XAnnotation> getMapKeys(XElement bindingElement) {
+    return XElements.getAnnotatedAnnotations(bindingElement, TypeNames.MAP_KEY);
+  }
+
   /**
    * Returns the annotation value if {@code mapKey}'s type is annotated with
    * {@link MapKey @MapKey(unwrapValue = true)}.
@@ -89,10 +111,10 @@
         : Optional.empty();
   }
 
-  static TypeMirror mapKeyType(AnnotationMirror mapKeyAnnotation, DaggerTypes types) {
-    return unwrapValue(mapKeyAnnotation).isPresent()
-        ? getUnwrappedMapKeyType(mapKeyAnnotation.getAnnotationType(), types)
-        : mapKeyAnnotation.getAnnotationType();
+  static TypeMirror mapKeyType(XAnnotation mapKeyAnnotation) {
+    return unwrapValue(toJavac(mapKeyAnnotation)).isPresent()
+        ? toJavac(getUnwrappedMapKeyType(mapKeyAnnotation.getType()))
+        : toJavac(mapKeyAnnotation.getType());
   }
 
   /**
@@ -103,36 +125,24 @@
    *     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) {
+  public static XType getUnwrappedMapKeyType(XType mapKeyAnnotationType) {
     checkArgument(
-        MoreTypes.asTypeElement(mapKeyAnnotationType).getKind() == ElementKind.ANNOTATION_TYPE,
+        isDeclared(mapKeyAnnotationType)
+            && mapKeyAnnotationType.getTypeElement().isAnnotationClass(),
         "%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());
+    XMethodElement annotationValueMethod =
+        getOnlyElement(mapKeyAnnotationType.getTypeElement().getDeclaredMethods());
+    XType annotationValueType = annotationValueMethod.getReturnType();
+    if (isArray(annotationValueType)) {
+      throw new IllegalArgumentException(
+          mapKeyAnnotationType
+              + "."
+              + getSimpleName(annotationValueMethod)
+              + " cannot be an array");
+    }
+    return isPrimitive(annotationValueType) ? annotationValueType.boxed() : annotationValueType;
   }
 
   /**
@@ -145,11 +155,11 @@
    *     map} contribution.
    */
   public static CodeBlock getMapKeyExpression(
-      ContributionBinding binding, ClassName requestingClass, DaggerElements elements) {
+      ContributionBinding binding, ClassName requestingClass, XProcessingEnv processingEnv) {
     AnnotationMirror mapKeyAnnotation = binding.mapKeyAnnotation().get();
     return MapKeyAccessibility.isMapKeyAccessibleFrom(
             mapKeyAnnotation, requestingClass.packageName())
-        ? directMapKeyExpression(mapKeyAnnotation, elements)
+        ? directMapKeyExpression(mapKeyAnnotation, processingEnv)
         : CodeBlock.of("$T.create()", mapKeyProxyClassName(binding));
   }
 
@@ -166,19 +176,20 @@
    *     annotation
    */
   private static CodeBlock directMapKeyExpression(
-      AnnotationMirror mapKey, DaggerElements elements) {
+      AnnotationMirror mapKey, XProcessingEnv processingEnv) {
     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());
+      XTypeElement unwrappedType =
+          DaggerSuperficialValidation.requireTypeElement(
+              processingEnv, (String) unwrappedValue.get().getValue());
       return CodeBlock.of(
           "$T.of($S)",
           ClassName.get("dagger.android.internal", "AndroidInjectionKeys"),
-          ClassName.get(unwrappedType).reflectionName());
+          unwrappedType.getClassName().reflectionName());
     }
 
     if (unwrappedValue.isPresent()) {
@@ -195,8 +206,7 @@
    * DaggerTypes, DaggerElements)} is generated.
    */
   public static ClassName mapKeyProxyClassName(ContributionBinding binding) {
-    return elementBasedClassName(
-        MoreElements.asExecutable(binding.bindingElement().get()), "MapKey");
+    return elementBasedClassName(asExecutable(toJavac(binding.bindingElement().get())), "MapKey");
   }
 
   /**
@@ -205,7 +215,7 @@
    * accessible.
    */
   public static Optional<MethodSpec> mapKeyFactoryMethod(
-      ContributionBinding binding, DaggerTypes types, DaggerElements elements) {
+      ContributionBinding binding, XProcessingEnv processingEnv) {
     return binding
         .mapKeyAnnotation()
         .filter(mapKey -> !isMapKeyPubliclyAccessible(mapKey))
@@ -213,8 +223,8 @@
             mapKey ->
                 methodBuilder("create")
                     .addModifiers(PUBLIC, STATIC)
-                    .returns(TypeName.get(mapKeyType(mapKey, types)))
-                    .addStatement("return $L", directMapKeyExpression(mapKey, elements))
+                    .returns(TypeName.get(mapKeyType(toXProcessing(mapKey, processingEnv))))
+                    .addStatement("return $L", directMapKeyExpression(mapKey, processingEnv))
                     .build());
   }
 
diff --git a/java/dagger/internal/codegen/binding/MembersInjectionBinding.java b/java/dagger/internal/codegen/binding/MembersInjectionBinding.java
index 3dd1016..695aea6 100644
--- a/java/dagger/internal/codegen/binding/MembersInjectionBinding.java
+++ b/java/dagger/internal/codegen/binding/MembersInjectionBinding.java
@@ -16,38 +16,49 @@
 
 package dagger.internal.codegen.binding;
 
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 import static java.util.stream.Collectors.toList;
 
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XTypeElement;
 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 dagger.spi.model.BindingKind;
+import dagger.spi.model.DependencyRequest;
+import dagger.spi.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.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 {
+  static MembersInjectionBinding create(
+      Key key,
+      ImmutableSet<DependencyRequest> dependencies,
+      Optional<MembersInjectionBinding> unresolved,
+      ImmutableSortedSet<InjectionSite> injectionSites) {
+    return new AutoValue_MembersInjectionBinding(key, dependencies, unresolved, injectionSites);
+  }
+
   @Override
-  public final Optional<Element> bindingElement() {
+  public final Optional<XElement> bindingElement() {
     return Optional.of(membersInjectedType());
   }
 
-  public abstract TypeElement membersInjectedType();
+  public final XTypeElement membersInjectedType() {
+    return key().type().xprocessing().getTypeElement();
+  }
 
   @Override
   public abstract Optional<MembersInjectionBinding> unresolved();
 
   @Override
-  public Optional<TypeElement> contributingModule() {
+  public Optional<XTypeElement> contributingModule() {
     return Optional.empty();
   }
 
@@ -73,11 +84,13 @@
    * Returns {@code true} if any of this binding's injection sites are directly on the bound type.
    */
   public boolean hasLocalInjectionSites() {
-    return injectionSites()
-        .stream()
+    return injectionSites().stream()
         .anyMatch(
             injectionSite ->
-                injectionSite.element().getEnclosingElement().equals(membersInjectedType()));
+                injectionSite
+                    .element()
+                    .getEnclosingElement()
+                    .equals(toJavac(membersInjectedType())));
   }
 
   @Override
@@ -119,7 +132,7 @@
           .getEnclosingElement()
           .getEnclosedElements()
           .stream()
-          .filter(element -> isAnnotationPresent(element, Inject.class))
+          .filter(InjectionAnnotations::hasInjectAnnotation)
           .filter(element -> !element.getModifiers().contains(Modifier.PRIVATE))
           .filter(element -> element.getSimpleName().equals(this.element().getSimpleName()))
           .collect(toList())
diff --git a/java/dagger/internal/codegen/binding/MethodSignature.java b/java/dagger/internal/codegen/binding/MethodSignature.java
new file mode 100644
index 0000000..e982ca5
--- /dev/null
+++ b/java/dagger/internal/codegen/binding/MethodSignature.java
@@ -0,0 +1,52 @@
+/*
+ * 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 androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
+
+import androidx.room.compiler.processing.XMethodType;
+import androidx.room.compiler.processing.XType;
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
+
+/** A class that defines proper {@code equals} and {@code hashcode} for a method signature. */
+@AutoValue
+public abstract class MethodSignature {
+
+  abstract String name();
+
+  abstract ImmutableList<TypeName> parameterTypes();
+
+  abstract ImmutableList<TypeName> thrownTypes();
+
+  public static MethodSignature forComponentMethod(
+      ComponentMethodDescriptor componentMethod, XType componentType) {
+    XMethodType methodType = componentMethod.methodElement().asMemberOf(componentType);
+    return new AutoValue_MethodSignature(
+        getSimpleName(componentMethod.methodElement()),
+        toJavac(methodType).getParameterTypes().stream()
+            .map(TypeName::get)
+            .collect(toImmutableList()),
+        toJavac(methodType).getThrownTypes().stream()
+            .map(TypeName::get)
+            .collect(toImmutableList()));
+  }
+}
diff --git a/java/dagger/internal/codegen/binding/MethodSignatureFormatter.java b/java/dagger/internal/codegen/binding/MethodSignatureFormatter.java
index 97c680f..6a1a5a1 100644
--- a/java/dagger/internal/codegen/binding/MethodSignatureFormatter.java
+++ b/java/dagger/internal/codegen/binding/MethodSignatureFormatter.java
@@ -16,54 +16,55 @@
 
 package dagger.internal.codegen.binding;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
 import static dagger.internal.codegen.base.DiagnosticFormatting.stripCommonTypePrefixes;
+import static dagger.internal.codegen.xprocessing.XElements.closestEnclosingTypeElement;
+import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
+import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
 
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XExecutableElement;
+import androidx.room.compiler.processing.XExecutableParameterElement;
+import androidx.room.compiler.processing.XExecutableType;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XMethodType;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
+import androidx.room.compiler.processing.XVariableElement;
 import dagger.internal.codegen.base.Formatter;
-import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.xprocessing.XAnnotations;
 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;
+/** Formats the signature of an {@link XExecutableElement} suitable for use in error messages. */
+public final class MethodSignatureFormatter extends Formatter<XExecutableElement> {
   private final InjectionAnnotations injectionAnnotations;
 
   @Inject
-  public MethodSignatureFormatter(DaggerTypes types, InjectionAnnotations injectionAnnotations) {
-    this.types = types;
+  MethodSignatureFormatter(InjectionAnnotations injectionAnnotations) {
     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.
+   * method, but the method's resolved type as a member of {@code type} for the key.
    */
-  public Formatter<ExecutableElement> typedFormatter(DeclaredType declaredType) {
-    return new Formatter<ExecutableElement>() {
+  public Formatter<XMethodElement> typedFormatter(XType type) {
+    checkArgument(isDeclared(type));
+    return new Formatter<XMethodElement>() {
       @Override
-      public String format(ExecutableElement method) {
+      public String format(XMethodElement method) {
         return MethodSignatureFormatter.this.format(
-            method,
-            MoreTypes.asExecutable(types.asMemberOf(declaredType, method)),
-            MoreElements.asType(method.getEnclosingElement()));
+            method, method.asMemberOf(type), closestEnclosingTypeElement(method));
       }
     };
   }
 
   @Override
-  public String format(ExecutableElement method) {
+  public String format(XExecutableElement method) {
     return format(method, Optional.empty());
   }
 
@@ -71,23 +72,18 @@
    * 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);
+  public String format(XExecutableElement method, Optional<XType> container) {
+    return container.isPresent()
+        ? format(method, method.asMemberOf(container.get()), container.get().getTypeElement())
+        : format(method, method.getExecutableType(), closestEnclosingTypeElement(method));
   }
 
   private String format(
-      ExecutableElement method, ExecutableType methodType, TypeElement declaringType) {
+      XExecutableElement method, XExecutableType methodType, XTypeElement container) {
     StringBuilder builder = new StringBuilder();
-    // TODO(user): AnnotationMirror formatter.
-    List<? extends AnnotationMirror> annotations = method.getAnnotationMirrors();
+    List<XAnnotation> annotations = method.getAllAnnotations();
     if (!annotations.isEmpty()) {
-      Iterator<? extends AnnotationMirror> annotationIterator = annotations.iterator();
+      Iterator<XAnnotation> annotationIterator = annotations.iterator();
       for (int i = 0; annotationIterator.hasNext(); i++) {
         if (i > 0) {
           builder.append(' ');
@@ -96,20 +92,20 @@
       }
       builder.append(' ');
     }
-    if (method.getSimpleName().contentEquals("<init>")) {
-      builder.append(declaringType.getQualifiedName());
+    if (getSimpleName(method).contentEquals("<init>")) {
+      builder.append(container.getQualifiedName());
     } else {
       builder
-          .append(nameOfType(methodType.getReturnType()))
+          .append(nameOfType(((XMethodType) methodType).getReturnType()))
           .append(' ')
-          .append(declaringType.getQualifiedName())
+          .append(container.getQualifiedName())
           .append('.')
-          .append(method.getSimpleName());
+          .append(getSimpleName(method));
     }
     builder.append('(');
     checkState(method.getParameters().size() == methodType.getParameterTypes().size());
-    Iterator<? extends VariableElement> parameters = method.getParameters().iterator();
-    Iterator<? extends TypeMirror> parameterTypes = methodType.getParameterTypes().iterator();
+    Iterator<XExecutableParameterElement> parameters = method.getParameters().iterator();
+    Iterator<XType> parameterTypes = methodType.getParameterTypes().iterator();
     for (int i = 0; parameters.hasNext(); i++) {
       if (i > 0) {
         builder.append(", ");
@@ -120,21 +116,19 @@
     return builder.toString();
   }
 
-  private void appendParameter(StringBuilder builder, VariableElement parameter, TypeMirror type) {
+  private void appendParameter(
+      StringBuilder builder, XVariableElement parameter, XType parameterType) {
     injectionAnnotations
         .getQualifier(parameter)
-        .ifPresent(
-            qualifier -> {
-              builder.append(formatAnnotation(qualifier)).append(' ');
-            });
-    builder.append(nameOfType(type));
+        .ifPresent(qualifier -> builder.append(formatAnnotation(qualifier)).append(' '));
+    builder.append(nameOfType(parameterType));
   }
 
-  private static String nameOfType(TypeMirror type) {
+  private static String nameOfType(XType type) {
     return stripCommonTypePrefixes(type.toString());
   }
 
-  private static String formatAnnotation(AnnotationMirror annotation) {
-    return stripCommonTypePrefixes(annotation.toString());
+  private static String formatAnnotation(XAnnotation annotation) {
+    return stripCommonTypePrefixes(XAnnotations.toString(annotation));
   }
 }
diff --git a/java/dagger/internal/codegen/binding/ModuleDescriptor.java b/java/dagger/internal/codegen/binding/ModuleDescriptor.java
index c471f94..c6271f7 100644
--- a/java/dagger/internal/codegen/binding/ModuleDescriptor.java
+++ b/java/dagger/internal/codegen/binding/ModuleDescriptor.java
@@ -16,59 +16,57 @@
 
 package dagger.internal.codegen.binding;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static androidx.room.compiler.processing.compat.XConverters.toXProcessing;
 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 com.google.common.collect.Collections2.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.DaggerCollectors.toOptional;
 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 dagger.internal.codegen.xprocessing.XElements.getSimpleName;
+import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
 import static javax.lang.model.util.ElementFilter.methodsIn;
 
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XElementKt;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 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 com.squareup.javapoet.TypeName;
 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.base.DaggerSuperficialValidation;
+import dagger.internal.codegen.base.ModuleKind;
+import dagger.internal.codegen.javapoet.TypeNames;
 import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.model.Key;
-import dagger.multibindings.Multibinds;
-import dagger.producers.Produces;
+import dagger.internal.codegen.xprocessing.XElements;
+import dagger.spi.model.Key;
+import java.util.Collection;
 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 XTypeElement moduleElement();
 
   public abstract ImmutableSet<ContributionBinding> bindings();
 
@@ -107,38 +105,41 @@
   /** A {@link ModuleDescriptor} factory. */
   @Singleton
   public static final class Factory implements ClearableCache {
+    private final XProcessingEnv processingEnv;
     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<>();
+    private final DaggerSuperficialValidation superficialValidation;
+    private final Map<XTypeElement, ModuleDescriptor> cache = new HashMap<>();
 
     @Inject
     Factory(
+        XProcessingEnv processingEnv,
         DaggerElements elements,
-        KotlinMetadataUtil metadataUtil,
         BindingFactory bindingFactory,
         MultibindingDeclaration.Factory multibindingDeclarationFactory,
         DelegateDeclaration.Factory bindingDelegateDeclarationFactory,
         SubcomponentDeclaration.Factory subcomponentDeclarationFactory,
-        OptionalBindingDeclaration.Factory optionalBindingDeclarationFactory) {
+        OptionalBindingDeclaration.Factory optionalBindingDeclarationFactory,
+        DaggerSuperficialValidation superficialValidation) {
+      this.processingEnv = processingEnv;
       this.elements = elements;
-      this.metadataUtil = metadataUtil;
       this.bindingFactory = bindingFactory;
       this.multibindingDeclarationFactory = multibindingDeclarationFactory;
       this.bindingDelegateDeclarationFactory = bindingDelegateDeclarationFactory;
       this.subcomponentDeclarationFactory = subcomponentDeclarationFactory;
       this.optionalBindingDeclarationFactory = optionalBindingDeclarationFactory;
+      this.superficialValidation = superficialValidation;
     }
 
-    public ModuleDescriptor create(TypeElement moduleElement) {
+    public ModuleDescriptor create(XTypeElement moduleElement) {
       return reentrantComputeIfAbsent(cache, moduleElement, this::createUncached);
     }
 
-    public ModuleDescriptor createUncached(TypeElement moduleElement) {
+    public ModuleDescriptor createUncached(XTypeElement moduleElement) {
       ImmutableSet.Builder<ContributionBinding> bindings = ImmutableSet.builder();
       ImmutableSet.Builder<DelegateDeclaration> delegates = ImmutableSet.builder();
       ImmutableSet.Builder<MultibindingDeclaration> multibindingDeclarations =
@@ -146,33 +147,40 @@
       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));
-        }
-      }
+      methodsIn(elements.getAllMembers(toJavac(moduleElement))).stream()
+          .map(method -> toXProcessing(method, processingEnv))
+          .filter(XElementKt::isMethod)
+          .map(XElements::asMethod)
+          .forEach(
+              moduleMethod -> {
+                if (moduleMethod.hasAnnotation(TypeNames.PROVIDES)) {
+                  bindings.add(bindingFactory.providesMethodBinding(moduleMethod, moduleElement));
+                }
+                if (moduleMethod.hasAnnotation(TypeNames.PRODUCES)) {
+                  bindings.add(bindingFactory.producesMethodBinding(moduleMethod, moduleElement));
+                }
+                if (moduleMethod.hasAnnotation(TypeNames.BINDS)) {
+                  delegates.add(
+                      bindingDelegateDeclarationFactory.create(moduleMethod, moduleElement));
+                }
+                if (moduleMethod.hasAnnotation(TypeNames.MULTIBINDS)) {
+                  multibindingDeclarations.add(
+                      multibindingDeclarationFactory.forMultibindsMethod(
+                          moduleMethod, moduleElement));
+                }
+                if (moduleMethod.hasAnnotation(TypeNames.BINDS_OPTIONAL_OF)) {
+                  optionalDeclarations.add(
+                      optionalBindingDeclarationFactory.forMethod(moduleMethod, moduleElement));
+                }
+              });
 
-      if (metadataUtil.hasEnclosedCompanionObject(moduleElement)) {
-        collectCompanionModuleBindings(moduleElement, bindings);
-      }
+      moduleElement.getEnclosedTypeElements().stream()
+          .filter(XTypeElement::isCompanionObject)
+          .collect(toOptional())
+          .ifPresent(companionModule -> collectCompanionModuleBindings(companionModule, bindings));
 
       return new AutoValue_ModuleDescriptor(
           moduleElement,
-          ImmutableSet.copyOf(collectIncludedModules(new LinkedHashSet<>(), moduleElement)),
           bindings.build(),
           multibindingDeclarations.build(),
           subcomponentDeclarationFactory.forModule(moduleElement),
@@ -182,17 +190,21 @@
     }
 
     private void collectCompanionModuleBindings(
-        TypeElement moduleElement, ImmutableSet.Builder<ContributionBinding> bindings) {
-      checkArgument(metadataUtil.hasEnclosedCompanionObject(moduleElement));
-      TypeElement companionModule = metadataUtil.getEnclosedCompanionObject(moduleElement);
+        XTypeElement companionModule, ImmutableSet.Builder<ContributionBinding> bindings) {
       ImmutableSet<String> bindingElementDescriptors =
           bindings.build().stream()
-              .map(binding -> getMethodDescriptor(asExecutable(binding.bindingElement().get())))
+              .map(
+                  binding ->
+                      getMethodDescriptor(asExecutable(toJavac(binding.bindingElement().get()))))
               .collect(toImmutableSet());
-      methodsIn(elements.getAllMembers(companionModule)).stream()
+
+      methodsIn(elements.getAllMembers(toJavac(companionModule))).stream()
+          .map(method -> toXProcessing(method, processingEnv))
+          .filter(XElementKt::isMethod)
+          .map(XElements::asMethod)
           // 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))
+          .filter(method -> !method.hasAnnotation(TypeNames.JVM_STATIC))
           // 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
@@ -200,35 +212,39 @@
           .filter(method -> !bindingElementDescriptors.contains(getMethodDescriptor(method)))
           .forEach(
               method -> {
-                if (isAnnotationPresent(method, Provides.class)) {
+                if (method.hasAnnotation(TypeNames.PROVIDES)) {
                   bindings.add(bindingFactory.providesMethodBinding(method, companionModule));
                 }
-                if (isAnnotationPresent(method, Produces.class)) {
+                if (method.hasAnnotation(TypeNames.PRODUCES)) {
                   bindings.add(bindingFactory.producesMethodBinding(method, companionModule));
                 }
               });
     }
 
     /** Returns all the modules transitively included by given modules, including the arguments. */
-    ImmutableSet<ModuleDescriptor> transitiveModules(Iterable<TypeElement> modules) {
+    ImmutableSet<ModuleDescriptor> transitiveModules(Collection<XTypeElement> modules) {
+      // Traverse as a graph to automatically handle modules with cyclic includes.
       return ImmutableSet.copyOf(
           Traverser.forGraph(
-                  (ModuleDescriptor module) -> transform(module.includedModules(), this::create))
+                  (ModuleDescriptor module) -> transform(includedModules(module), 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);
+    private ImmutableSet<XTypeElement> includedModules(ModuleDescriptor moduleDescriptor) {
+      return ImmutableSet.copyOf(
+          collectIncludedModules(new LinkedHashSet<>(), moduleDescriptor.moduleElement()));
+    }
+
+    private Set<XTypeElement> collectIncludedModules(
+        Set<XTypeElement> includedModules, XTypeElement moduleElement) {
+      XType superclass = moduleElement.getSuperType();
+      if (superclass != null) {
+        verify(isDeclared(superclass));
+        if (!TypeName.OBJECT.equals(superclass.getTypeName())) {
+          collectIncludedModules(includedModules, superclass.getTypeElement());
         }
       }
-      moduleAnnotation(moduleElement)
+      moduleAnnotation(moduleElement, superficialValidation)
           .ifPresent(
               moduleAnnotation -> {
                 includedModules.addAll(moduleAnnotation.includes());
@@ -237,26 +253,31 @@
       return includedModules;
     }
 
+    private static final ClassName CONTRIBUTES_ANDROID_INJECTOR =
+        ClassName.get("dagger.android", "ContributesAndroidInjector");
+
     // @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) {
+    private ImmutableSet<XTypeElement> implicitlyIncludedModules(XTypeElement module) {
+      if (processingEnv.findTypeElement(CONTRIBUTES_ANDROID_INJECTOR) == null) {
         return ImmutableSet.of();
       }
-      return methodsIn(moduleElement.getEnclosedElements()).stream()
-          .filter(method -> isAnnotationPresent(method, contributesAndroidInjector.asType()))
-          .map(method -> elements.checkTypePresent(implicitlyIncludedModuleName(method)))
+      return module.getDeclaredMethods().stream()
+          .filter(method -> method.hasAnnotation(CONTRIBUTES_ANDROID_INJECTOR))
+          .map(
+              method ->
+                  DaggerSuperficialValidation.requireTypeElement(
+                      processingEnv, implicitlyIncludedModuleName(module, 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());
+    private ClassName implicitlyIncludedModuleName(XTypeElement module, XMethodElement method) {
+      return ClassName.get(
+          module.getPackageName(),
+          String.format(
+              "%s_%s",
+              classFileName(module.getClassName()),
+              LOWER_CAMEL.to(UPPER_CAMEL, getSimpleName(method))));
     }
 
     @Override
diff --git a/java/dagger/internal/codegen/binding/MultibindingDeclaration.java b/java/dagger/internal/codegen/binding/MultibindingDeclaration.java
index f1d3f8d..746feaf 100644
--- a/java/dagger/internal/codegen/binding/MultibindingDeclaration.java
+++ b/java/dagger/internal/codegen/binding/MultibindingDeclaration.java
@@ -16,28 +16,25 @@
 
 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 androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XMethodType;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 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.internal.codegen.javapoet.TypeNames;
 import dagger.multibindings.Multibinds;
+import dagger.spi.model.Key;
 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
@@ -71,43 +68,36 @@
 
   /** 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;
+    Factory(KeyFactory keyFactory) {
       this.keyFactory = keyFactory;
     }
 
     /** A multibinding declaration for a {@link Multibinds @Multibinds} method. */
     MultibindingDeclaration forMultibindsMethod(
-        ExecutableElement moduleMethod, TypeElement moduleElement) {
-      checkArgument(isAnnotationPresent(moduleMethod, Multibinds.class));
+        XMethodElement moduleMethod, XTypeElement moduleElement) {
+      checkArgument(moduleMethod.hasAnnotation(TypeNames.MULTIBINDS));
       return forDeclaredMethod(
-          moduleMethod,
-          MoreTypes.asExecutable(
-              types.asMemberOf(MoreTypes.asDeclared(moduleElement.asType()), moduleMethod)),
-          moduleElement);
+          moduleMethod, moduleMethod.asMemberOf(moduleElement.getType()), moduleElement);
     }
 
     private MultibindingDeclaration forDeclaredMethod(
-        ExecutableElement method,
-        ExecutableType methodType,
-        TypeElement contributingType) {
-      TypeMirror returnType = methodType.getReturnType();
+        XMethodElement method, XMethodType methodType, XTypeElement contributingType) {
+      XType 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(method),
           Optional.of(contributingType),
-          keyFactory.forMultibindsMethod(methodType, method),
+          keyFactory.forMultibindsMethod(method, methodType),
           contributionType(returnType));
     }
 
-    private ContributionType contributionType(TypeMirror returnType) {
+    private ContributionType contributionType(XType returnType) {
       if (MapType.isMap(returnType)) {
         return ContributionType.MAP;
       } else if (SetType.isSet(returnType)) {
diff --git a/java/dagger/internal/codegen/binding/OptionalBindingDeclaration.java b/java/dagger/internal/codegen/binding/OptionalBindingDeclaration.java
index d7ba7bc..d708c0a 100644
--- a/java/dagger/internal/codegen/binding/OptionalBindingDeclaration.java
+++ b/java/dagger/internal/codegen/binding/OptionalBindingDeclaration.java
@@ -16,18 +16,17 @@
 
 package dagger.internal.codegen.binding;
 
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
 import static com.google.common.base.Preconditions.checkArgument;
 
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.auto.value.AutoValue;
 import com.google.auto.value.extension.memoized.Memoized;
 import dagger.BindsOptionalOf;
-import dagger.model.Key;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.spi.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
@@ -57,10 +56,10 @@
       this.keyFactory = keyFactory;
     }
 
-    OptionalBindingDeclaration forMethod(ExecutableElement method, TypeElement contributingModule) {
-      checkArgument(isAnnotationPresent(method, BindsOptionalOf.class));
+    OptionalBindingDeclaration forMethod(XMethodElement method, XTypeElement contributingModule) {
+      checkArgument(method.hasAnnotation(TypeNames.BINDS_OPTIONAL_OF));
       return new AutoValue_OptionalBindingDeclaration(
-          Optional.<Element>of(method),
+          Optional.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
index ad85a68..b803089 100644
--- a/java/dagger/internal/codegen/binding/ProductionBinding.java
+++ b/java/dagger/internal/codegen/binding/ProductionBinding.java
@@ -19,6 +19,8 @@
 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
 import static dagger.internal.codegen.langmodel.DaggerTypes.isFutureType;
 
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XType;
 import com.google.auto.value.AutoValue;
 import com.google.auto.value.extension.memoized.Memoized;
 import com.google.common.collect.ImmutableList;
@@ -26,12 +28,10 @@
 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 dagger.spi.model.DependencyRequest;
+import dagger.spi.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
@@ -63,7 +63,7 @@
     SET_OF_FUTURE;
 
     /** Returns the kind of object a {@code @Produces}-annotated method returns. */
-    public static ProductionKind fromProducesMethod(ExecutableElement producesMethod) {
+    public static ProductionKind fromProducesMethod(XMethodElement producesMethod) {
       if (isFutureType(producesMethod.getReturnType())) {
         return FUTURE;
       } else if (ContributionType.fromBindingElement(producesMethod)
@@ -84,7 +84,7 @@
   public abstract Optional<ProductionKind> productionKind();
 
   /** Returns the list of types in the throws clause of the method. */
-  public abstract ImmutableList<? extends TypeMirror> thrownTypes();
+  public abstract ImmutableList<XType> thrownTypes();
 
   /**
    * If this production requires an executor, this will be the corresponding request.  All
@@ -110,7 +110,7 @@
   public static Builder builder() {
     return new AutoValue_ProductionBinding.Builder()
         .explicitDependencies(ImmutableList.<DependencyRequest>of())
-        .thrownTypes(ImmutableList.<TypeMirror>of());
+        .thrownTypes(ImmutableList.<XType>of());
   }
 
   @Override
@@ -142,7 +142,7 @@
     @Override
     public abstract Builder unresolved(ProductionBinding unresolved);
 
-    abstract Builder thrownTypes(Iterable<? extends TypeMirror> thrownTypes);
+    abstract Builder thrownTypes(Iterable<XType> thrownTypes);
 
     abstract Builder executorRequest(DependencyRequest executorRequest);
 
diff --git a/java/dagger/internal/codegen/binding/ProvisionBinding.java b/java/dagger/internal/codegen/binding/ProvisionBinding.java
index c917dd6..2447e30 100644
--- a/java/dagger/internal/codegen/binding/ProvisionBinding.java
+++ b/java/dagger/internal/codegen/binding/ProvisionBinding.java
@@ -17,8 +17,8 @@
 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 static dagger.spi.model.BindingKind.COMPONENT_PROVISION;
+import static dagger.spi.model.BindingKind.PROVISION;
 
 import com.google.auto.value.AutoValue;
 import com.google.auto.value.extension.memoized.Memoized;
@@ -27,10 +27,10 @@
 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 dagger.spi.model.BindingKind;
+import dagger.spi.model.DependencyRequest;
+import dagger.spi.model.Key;
+import dagger.spi.model.Scope;
 import java.util.Optional;
 
 /** A value object representing the mechanism by which a {@link Key} can be provided. */
diff --git a/java/dagger/internal/codegen/binding/ResolvedBindings.java b/java/dagger/internal/codegen/binding/ResolvedBindings.java
index 74301fe..2ef9bd6 100644
--- a/java/dagger/internal/codegen/binding/ResolvedBindings.java
+++ b/java/dagger/internal/codegen/binding/ResolvedBindings.java
@@ -16,6 +16,7 @@
 
 package dagger.internal.codegen.binding;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.collect.Iterables.getOnlyElement;
 
@@ -26,7 +27,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSetMultimap;
 import com.google.common.collect.Multimap;
-import dagger.model.Key;
+import dagger.spi.model.Key;
 import javax.lang.model.element.TypeElement;
 
 /**
@@ -99,7 +100,7 @@
 
   /** All bindings for {@link #key()} that are owned by a component. */
   ImmutableSet<? extends Binding> bindingsOwnedBy(ComponentDescriptor component) {
-    return allBindings().get(component.typeElement());
+    return allBindings().get(toJavac(component.typeElement()));
   }
 
   /**
@@ -151,7 +152,7 @@
     return new AutoValue_ResolvedBindings(
         key,
         ImmutableSetMultimap.of(),
-        ImmutableMap.of(owningComponent.typeElement(), ownedMembersInjectionBinding),
+        ImmutableMap.of(toJavac(owningComponent.typeElement()), ownedMembersInjectionBinding),
         ImmutableSet.of(),
         ImmutableSet.of(),
         ImmutableSet.of());
diff --git a/java/dagger/internal/codegen/binding/SourceFiles.java b/java/dagger/internal/codegen/binding/SourceFiles.java
index 3b6d9d9..84bdd2b 100644
--- a/java/dagger/internal/codegen/binding/SourceFiles.java
+++ b/java/dagger/internal/codegen/binding/SourceFiles.java
@@ -16,6 +16,8 @@
 
 package dagger.internal.codegen.binding;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static com.google.auto.common.MoreElements.asType;
 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;
@@ -28,16 +30,21 @@
 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.PRODUCER;
+import static dagger.internal.codegen.javapoet.TypeNames.PROVIDER;
 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 dagger.internal.codegen.xprocessing.XElements.asExecutable;
+import static dagger.spi.model.BindingKind.ASSISTED_INJECTION;
+import static dagger.spi.model.BindingKind.INJECTION;
+import static dagger.spi.model.BindingKind.MULTIBOUND_MAP;
+import static dagger.spi.model.BindingKind.MULTIBOUND_SET;
 import static javax.lang.model.SourceVersion.isName;
 
+import androidx.room.compiler.processing.XExecutableElement;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.auto.common.MoreElements;
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;
@@ -51,17 +58,12 @@
 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 dagger.internal.codegen.javapoet.TypeNames;
+import dagger.spi.model.DependencyRequest;
+import dagger.spi.model.RequestKind;
 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;
@@ -97,9 +99,8 @@
         binding.dependencies(),
         dependency ->
             FrameworkField.create(
-                ClassName.get(
-                    frameworkTypeMapper.getFrameworkType(dependency.kind()).frameworkClass()),
-                TypeName.get(dependency.key().type()),
+                frameworkTypeMapper.getFrameworkType(dependency.kind()).frameworkClassName(),
+                TypeName.get(dependency.key().type().java()),
                 DependencyVariableNamer.name(dependency)));
   }
 
@@ -144,11 +145,10 @@
           case INJECTION:
           case PROVISION:
           case PRODUCTION:
-            return elementBasedClassName(
-                MoreElements.asExecutable(binding.bindingElement().get()), "Factory");
+            return factoryNameForElement(asExecutable(binding.bindingElement().get()));
 
           case ASSISTED_FACTORY:
-            return siblingClassName(MoreElements.asType(binding.bindingElement().get()), "_Impl");
+            return siblingClassName(asType(toJavac(binding.bindingElement().get())), "_Impl");
 
           default:
             throw new AssertionError();
@@ -162,6 +162,18 @@
   }
 
   /**
+   * Returns the generated factory name for the given element.
+   *
+   * <p>This method is useful during validation before a {@link Binding} can be created. If a
+   * binding already exists for the given element, prefer to call {@link
+   * #generatedClassNameForBinding(Binding)} instead since this method does not validate that the
+   * given element is actually a binding element or not.
+   */
+  public static ClassName factoryNameForElement(XExecutableElement element) {
+    return elementBasedClassName(toJavac(element), "Factory");
+  }
+
+  /**
    * Calculates an appropriate {@link ClassName} for a generated class that is based on {@code
    * element}, appending {@code suffix} at the end.
    *
@@ -188,8 +200,12 @@
         : ParameterizedTypeName.get(className, Iterables.toArray(typeParameters, TypeName.class));
   }
 
+  public static ClassName membersInjectorNameForType(XTypeElement typeElement) {
+    return membersInjectorNameForType(toJavac(typeElement));
+  }
+
   public static ClassName membersInjectorNameForType(TypeElement typeElement) {
-    return siblingClassName(typeElement,  "_MembersInjector");
+    return siblingClassName(typeElement, "_MembersInjector");
   }
 
   public static String memberInjectedFieldSignatureForVariable(VariableElement variableElement) {
@@ -202,8 +218,8 @@
     return CLASS_FILE_NAME_JOINER.join(className.simpleNames());
   }
 
-  public static ClassName generatedMonitoringModuleName(TypeElement componentElement) {
-    return siblingClassName(componentElement, "_MonitoringModule");
+  public static ClassName generatedMonitoringModuleName(XTypeElement componentElement) {
+    return siblingClassName(toJavac(componentElement), "_MonitoringModule");
   }
 
   // TODO(ronshapiro): when JavaPoet migration is complete, replace the duplicated code
@@ -217,9 +233,10 @@
    * 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>>}.
+   *   <li>{@link dagger.producers.internal.SetFactory} for provision bindings.
+   *   <li>{@link dagger.producers.internal.SetProducer} for production bindings for {@code Set<T>}.
+   *   <li>{@link dagger.producers.internal.SetOfProducedProducer} for production bindings for
+   *       {@code Set<Produced<T>>}.
    * </ul>
    */
   public static ClassName setFactoryClassName(ContributionBinding binding) {
@@ -228,7 +245,9 @@
       return SET_FACTORY;
     } else {
       SetType setType = SetType.from(binding.key());
-      return setType.elementsAreTypeOf(Produced.class) ? SET_OF_PRODUCED_PRODUCER : SET_PRODUCER;
+      return setType.elementsAreTypeOf(TypeNames.PRODUCED)
+          ? SET_OF_PRODUCED_PRODUCER
+          : SET_PRODUCER;
     }
   }
 
@@ -238,10 +257,10 @@
     MapType mapType = MapType.from(binding.key());
     switch (binding.bindingType()) {
       case PROVISION:
-        return mapType.valuesAreTypeOf(Provider.class) ? MAP_PROVIDER_FACTORY : MAP_FACTORY;
+        return mapType.valuesAreTypeOf(PROVIDER) ? MAP_PROVIDER_FACTORY : MAP_FACTORY;
       case PRODUCTION:
         return mapType.valuesAreFrameworkType()
-            ? mapType.valuesAreTypeOf(Producer.class)
+            ? mapType.valuesAreTypeOf(PRODUCER)
                 ? MAP_OF_PRODUCER_PRODUCER
                 : MAP_OF_PRODUCED_PRODUCER
             : MAP_PRODUCER;
@@ -261,7 +280,7 @@
       }
     }
     List<? extends TypeParameterElement> typeParameters =
-        binding.bindingTypeElement().get().getTypeParameters();
+        toJavac(binding.bindingTypeElement().get()).getTypeParameters();
     return typeParameters.stream().map(TypeVariableName::get).collect(toImmutableList());
   }
 
@@ -272,7 +291,16 @@
    */
   // 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());
+    return simpleVariableName(ClassName.get(typeElement));
+  }
+
+  /**
+   * Returns a name to be used for variables of the given {@linkplain ClassName}. Prefer
+   * semantically meaningful variable names, but if none can be derived, this will produce something
+   * readable.
+   */
+  public static String simpleVariableName(ClassName className) {
+    String candidateName = UPPER_CAMEL.to(LOWER_CAMEL, className.simpleName());
     String variableName = protectAgainstKeywords(candidateName);
     verify(isName(variableName), "'%s' was expected to be a valid variable name");
     return variableName;
diff --git a/java/dagger/internal/codegen/binding/SubcomponentCreatorBindingEdgeImpl.java b/java/dagger/internal/codegen/binding/SubcomponentCreatorBindingEdgeImpl.java
index 93e79b5..5998b98 100644
--- a/java/dagger/internal/codegen/binding/SubcomponentCreatorBindingEdgeImpl.java
+++ b/java/dagger/internal/codegen/binding/SubcomponentCreatorBindingEdgeImpl.java
@@ -22,12 +22,12 @@
 import static java.util.stream.Collectors.joining;
 
 import com.google.common.collect.ImmutableSet;
-import dagger.model.BindingGraph.SubcomponentCreatorBindingEdge;
-import javax.lang.model.element.TypeElement;
+import com.squareup.javapoet.ClassName;
+import dagger.spi.model.BindingGraph.SubcomponentCreatorBindingEdge;
+import dagger.spi.model.DaggerTypeElement;
 
 /** An implementation of {@link SubcomponentCreatorBindingEdge}. */
 public final class SubcomponentCreatorBindingEdgeImpl implements SubcomponentCreatorBindingEdge {
-
   private final ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations;
 
   SubcomponentCreatorBindingEdgeImpl(
@@ -36,10 +36,11 @@
   }
 
   @Override
-  public ImmutableSet<TypeElement> declaringModules() {
+  public ImmutableSet<DaggerTypeElement> declaringModules() {
     return subcomponentDeclarations.stream()
         .map(SubcomponentDeclaration::contributingModule)
         .flatMap(presentValues())
+        .map(DaggerTypeElement::from)
         .collect(toImmutableSet());
   }
 
@@ -47,9 +48,10 @@
   public String toString() {
     return "subcomponent declared by "
         + (subcomponentDeclarations.size() == 1
-            ? getOnlyElement(declaringModules()).getQualifiedName()
+            ? getOnlyElement(declaringModules()).className().canonicalName()
             : declaringModules().stream()
-                .map(TypeElement::getQualifiedName)
+                .map(DaggerTypeElement::className)
+                .map(ClassName::canonicalName)
                 .collect(joining(", ", "{", "}")));
   }
 }
diff --git a/java/dagger/internal/codegen/binding/SubcomponentDeclaration.java b/java/dagger/internal/codegen/binding/SubcomponentDeclaration.java
index 4f1f3ef..dfc2905 100644
--- a/java/dagger/internal/codegen/binding/SubcomponentDeclaration.java
+++ b/java/dagger/internal/codegen/binding/SubcomponentDeclaration.java
@@ -16,18 +16,20 @@
 
 package dagger.internal.codegen.binding;
 
-import static com.google.auto.common.AnnotationMirrors.getAnnotationElementAndValue;
 import static dagger.internal.codegen.binding.ConfigurationAnnotations.getSubcomponentCreator;
+import static dagger.internal.codegen.extension.DaggerCollectors.toOptional;
+import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
 
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XTypeElement;
 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.DaggerSuperficialValidation;
 import dagger.internal.codegen.base.ModuleAnnotation;
-import dagger.model.Key;
+import dagger.spi.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
@@ -46,7 +48,7 @@
    * The type element that defines the {@link dagger.Subcomponent} or {@link
    * dagger.producers.ProductionSubcomponent} for this declaration.
    */
-  abstract TypeElement subcomponentType();
+  abstract XTypeElement subcomponentType();
 
   /** The module annotation. */
   public abstract ModuleAnnotation moduleAnnotation();
@@ -61,24 +63,31 @@
   /** A {@link SubcomponentDeclaration} factory. */
   public static class Factory {
     private final KeyFactory keyFactory;
+    private final DaggerSuperficialValidation superficialValidation;
 
     @Inject
-    Factory(KeyFactory keyFactory) {
+    Factory(KeyFactory keyFactory, DaggerSuperficialValidation superficialValidation) {
       this.keyFactory = keyFactory;
+      this.superficialValidation = superficialValidation;
     }
 
-    ImmutableSet<SubcomponentDeclaration> forModule(TypeElement module) {
+    ImmutableSet<SubcomponentDeclaration> forModule(XTypeElement module) {
+      ModuleAnnotation moduleAnnotation =
+          ModuleAnnotation.moduleAnnotation(module, superficialValidation).get();
+      XElement subcomponentAttribute =
+          moduleAnnotation.annotation().getType().getTypeElement().getDeclaredMethods().stream()
+              .filter(method -> getSimpleName(method).contentEquals("subcomponents"))
+              .collect(toOptional())
+              .get();
+
       ImmutableSet.Builder<SubcomponentDeclaration> declarations = ImmutableSet.builder();
-      ModuleAnnotation moduleAnnotation = ModuleAnnotation.moduleAnnotation(module).get();
-      Element subcomponentAttribute =
-          getAnnotationElementAndValue(moduleAnnotation.annotation(), "subcomponents").getKey();
-      for (TypeElement subcomponent : moduleAnnotation.subcomponents()) {
+      for (XTypeElement subcomponent : moduleAnnotation.subcomponents()) {
         declarations.add(
             new AutoValue_SubcomponentDeclaration(
                 Optional.of(subcomponentAttribute),
                 Optional.of(module),
                 keyFactory.forSubcomponentCreator(
-                    getSubcomponentCreator(subcomponent).get().asType()),
+                    getSubcomponentCreator(subcomponent).get().getType()),
                 subcomponent,
                 moduleAnnotation));
       }
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/BUILD b/java/dagger/internal/codegen/bindinggraphvalidation/BUILD
index da35b51..cb7db87 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/BUILD
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/BUILD
@@ -30,16 +30,18 @@
         "//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/validation",
-        "//java/dagger/internal/guava:base",
-        "//java/dagger/internal/guava:collect",
-        "//java/dagger/internal/guava:graph",
-        "//java/dagger/producers",
+        "//java/dagger/internal/codegen/xprocessing",
         "//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",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:value",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/graph",
+        "//third_party/java/javapoet",
+        "//third_party/java/jsr330_inject",
     ],
 )
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/BindingGraphValidationModule.java b/java/dagger/internal/codegen/bindinggraphvalidation/BindingGraphValidationModule.java
index a1f1848..f689427 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/BindingGraphValidationModule.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/BindingGraphValidationModule.java
@@ -22,7 +22,7 @@
 import dagger.internal.codegen.compileroption.CompilerOptions;
 import dagger.internal.codegen.validation.CompositeBindingGraphPlugin;
 import dagger.internal.codegen.validation.Validation;
-import dagger.spi.BindingGraphPlugin;
+import dagger.spi.model.BindingGraphPlugin;
 
 /** Binds the set of {@link BindingGraphPlugin}s used to implement Dagger validation. */
 @Module
@@ -42,7 +42,8 @@
       MissingBindingValidator validation7,
       NullableBindingValidator validation8,
       ProvisionDependencyOnProducerBindingValidator validation9,
-      SubcomponentFactoryMethodValidator validation10) {
+      SetMultibindingValidator validation10,
+      SubcomponentFactoryMethodValidator validation11) {
     ImmutableSet<BindingGraphPlugin> plugins = ImmutableSet.of(
         validation1,
         validation2,
@@ -53,7 +54,8 @@
         validation7,
         validation8,
         validation9,
-        validation10);
+        validation10,
+        validation11);
     if (compilerOptions.experimentalDaggerErrorMessages()) {
       return ImmutableSet.of(factory.create(plugins, "Dagger/Validation"));
     } else {
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/DependencyCycleValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/DependencyCycleValidator.java
index bc89ebb..f2cb90b 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/DependencyCycleValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/DependencyCycleValidator.java
@@ -29,6 +29,7 @@
 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
 import static javax.tools.Diagnostic.Kind.ERROR;
 
+import androidx.room.compiler.processing.XType;
 import com.google.auto.value.AutoValue;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
@@ -38,25 +39,26 @@
 import com.google.common.graph.ImmutableNetwork;
 import com.google.common.graph.MutableNetwork;
 import com.google.common.graph.NetworkBuilder;
+import dagger.internal.codegen.base.Formatter;
 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 dagger.internal.codegen.javapoet.TypeNames;
+import dagger.spi.model.Binding;
+import dagger.spi.model.BindingGraph;
+import dagger.spi.model.BindingGraph.ComponentNode;
+import dagger.spi.model.BindingGraph.DependencyEdge;
+import dagger.spi.model.BindingGraph.Node;
+import dagger.spi.model.BindingGraphPlugin;
+import dagger.spi.model.BindingKind;
+import dagger.spi.model.DependencyRequest;
+import dagger.spi.model.DiagnosticReporter;
+import dagger.spi.model.RequestKind;
 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 {
@@ -143,7 +145,11 @@
     DependencyEdge dependencyToReport =
         chooseDependencyEdgeConnecting(previousNode, cycleStartNode, bindingGraph);
     diagnosticReporter.reportDependency(
-        ERROR, dependencyToReport, errorMessage(cycle.shift(cycleStartNode), bindingGraph));
+        ERROR,
+        dependencyToReport,
+        errorMessage(cycle.shift(cycleStartNode), bindingGraph)
+        // The actual dependency trace is included from the reportDependency call.
+            + "\n\nThe cycle is requested via:");
   }
 
   private ImmutableList<Node> shortestPathToCycleFromAnEntryPoint(
@@ -182,6 +188,10 @@
             .collect(toImmutableList())
             .reverse();
     dependencyRequestFormatter.formatIndentedList(message, cycleRequests, 0);
+    message.append("\n")
+        .append(dependencyRequestFormatter.format(cycleRequests.get(0)))
+        .append("\n")
+        .append(Formatter.INDENT).append("...");
     return message.toString();
   }
 
@@ -203,22 +213,22 @@
     if (edge.dependencyRequest().key().multibindingContributionIdentifier().isPresent()) {
       return false;
     }
-    if (breaksCycle(edge.dependencyRequest().key().type(), edge.dependencyRequest().kind())) {
+    if (breaksCycle(
+        edge.dependencyRequest().key().type().xprocessing(), 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)) {
+    if (target instanceof Binding && ((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();
+      XType 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) {
+  private boolean breaksCycle(XType requestedType, RequestKind requestKind) {
     switch (requestKind) {
       case PROVIDER:
       case LAZY:
@@ -227,8 +237,7 @@
 
       case INSTANCE:
         if (MapType.isMap(requestedType)) {
-          MapType mapType = MapType.from(requestedType);
-          return !mapType.isRawType() && mapType.valuesAreTypeOf(Provider.class);
+          return MapType.from(requestedType).valuesAreTypeOf(TypeNames.PROVIDER);
         }
         // fall through
 
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/DependsOnProductionExecutorValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/DependsOnProductionExecutorValidator.java
index 08e2c3e..1d44133 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/DependsOnProductionExecutorValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/DependsOnProductionExecutorValidator.java
@@ -21,11 +21,12 @@
 
 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 dagger.spi.model.Binding;
+import dagger.spi.model.BindingGraph;
+import dagger.spi.model.BindingGraph.MaybeBinding;
+import dagger.spi.model.BindingGraphPlugin;
+import dagger.spi.model.DiagnosticReporter;
+import dagger.spi.model.Key;
 import javax.inject.Inject;
 
 /**
@@ -64,7 +65,7 @@
         .forEach(binding -> reportError(diagnosticReporter, binding));
   }
 
-  private void reportError(DiagnosticReporter diagnosticReporter, dagger.model.Binding binding) {
+  private void reportError(DiagnosticReporter diagnosticReporter, 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
index 5c4da51..db8d382 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/DuplicateBindingsValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/DuplicateBindingsValidator.java
@@ -21,8 +21,8 @@
 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 dagger.spi.model.BindingKind.INJECTION;
+import static dagger.spi.model.BindingKind.MEMBERS_INJECTION;
 import static java.util.Comparator.comparing;
 import static javax.tools.Diagnostic.Kind.ERROR;
 
@@ -42,14 +42,16 @@
 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 dagger.spi.model.Binding;
+import dagger.spi.model.BindingGraph;
+import dagger.spi.model.BindingGraph.ComponentNode;
+import dagger.spi.model.BindingGraphPlugin;
+import dagger.spi.model.BindingKind;
+import dagger.spi.model.ComponentPath;
+import dagger.spi.model.DaggerElement;
+import dagger.spi.model.DaggerTypeElement;
+import dagger.spi.model.DiagnosticReporter;
+import dagger.spi.model.Key;
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.Optional;
@@ -238,7 +240,7 @@
   private String incompatibleBindingsMessage(
       Binding oneBinding, ImmutableSet<Binding> duplicateBindings, BindingGraph graph) {
     Key key = oneBinding.key();
-    ImmutableSet<dagger.model.Binding> multibindings =
+    ImmutableSet<dagger.spi.model.Binding> multibindings =
         duplicateBindings.stream()
             .filter(binding -> binding.kind().isMultibinding())
             .collect(toImmutableSet());
@@ -248,11 +250,11 @@
     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);
+    dagger.spi.model.Binding multibinding = getOnlyElement(multibindings);
     messageFormatter.format("%s bindings and declarations:", multibindingTypeString(multibinding));
     formatDeclarations(message, 2, declarations(graph, multibindings));
 
-    Set<dagger.model.Binding> uniqueBindings =
+    Set<dagger.spi.model.Binding> uniqueBindings =
         Sets.filter(duplicateBindings, binding -> !binding.equals(multibinding));
     message.append('\n').append(INDENT).append("Unique bindings and declarations:");
     formatDeclarations(
@@ -276,7 +278,7 @@
   }
 
   private ImmutableSet<BindingDeclaration> declarations(
-      BindingGraph graph, Set<dagger.model.Binding> bindings) {
+      BindingGraph graph, Set<dagger.spi.model.Binding> bindings) {
     return bindings.stream()
         .flatMap(binding -> declarations(graph, binding).stream())
         .distinct()
@@ -285,7 +287,7 @@
   }
 
   private ImmutableSet<BindingDeclaration> declarations(
-      BindingGraph graph, dagger.model.Binding binding) {
+      BindingGraph graph, dagger.spi.model.Binding binding) {
     ImmutableSet.Builder<BindingDeclaration> declarations = ImmutableSet.builder();
     BindingNode bindingNode = (BindingNode) binding;
     bindingNode.associatedDeclarations().forEach(declarations::add);
@@ -299,7 +301,7 @@
     return declarations.build();
   }
 
-  private String multibindingTypeString(dagger.model.Binding multibinding) {
+  private String multibindingTypeString(dagger.spi.model.Binding multibinding) {
     switch (multibinding.kind()) {
       case MULTIBOUND_MAP:
         return "Map";
@@ -339,7 +341,9 @@
 
     private static BindingElement forBinding(Binding binding) {
       return new AutoValue_DuplicateBindingsValidator_BindingElement(
-          binding.kind(), binding.bindingElement(), binding.contributingModule());
+          binding.kind(),
+          binding.bindingElement().map(DaggerElement::java),
+          binding.contributingModule().map(DaggerTypeElement::java));
     }
   }
 }
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/IncompatiblyScopedBindingsValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/IncompatiblyScopedBindingsValidator.java
index a01dbaf..b9cf89c 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/IncompatiblyScopedBindingsValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/IncompatiblyScopedBindingsValidator.java
@@ -18,22 +18,23 @@
 
 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 dagger.internal.codegen.xprocessing.XElements.asExecutable;
+import static dagger.internal.codegen.xprocessing.XElements.closestEnclosingTypeElement;
+import static dagger.spi.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 dagger.internal.codegen.validation.DiagnosticMessageGenerator;
+import dagger.spi.model.Binding;
+import dagger.spi.model.BindingGraph;
+import dagger.spi.model.BindingGraph.ComponentNode;
+import dagger.spi.model.BindingGraphPlugin;
+import dagger.spi.model.DiagnosticReporter;
 import java.util.Optional;
 import java.util.Set;
 import javax.inject.Inject;
@@ -47,12 +48,16 @@
 
   private final MethodSignatureFormatter methodSignatureFormatter;
   private final CompilerOptions compilerOptions;
+  private final DiagnosticMessageGenerator.Factory diagnosticMessageGeneratorFactory;
 
   @Inject
   IncompatiblyScopedBindingsValidator(
-      MethodSignatureFormatter methodSignatureFormatter, CompilerOptions compilerOptions) {
+      MethodSignatureFormatter methodSignatureFormatter,
+      CompilerOptions compilerOptions,
+      DiagnosticMessageGenerator.Factory diagnosticMessageGeneratorFactory) {
     this.methodSignatureFormatter = methodSignatureFormatter;
     this.compilerOptions = compilerOptions;
+    this.diagnosticMessageGeneratorFactory = diagnosticMessageGeneratorFactory;
   }
 
   @Override
@@ -62,9 +67,11 @@
 
   @Override
   public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
-    ImmutableSetMultimap.Builder<ComponentNode, dagger.model.Binding> incompatibleBindings =
+    DiagnosticMessageGenerator diagnosticMessageGenerator =
+        diagnosticMessageGeneratorFactory.create(bindingGraph);
+    ImmutableSetMultimap.Builder<ComponentNode, dagger.spi.model.Binding> incompatibleBindings =
         ImmutableSetMultimap.builder();
-    for (dagger.model.Binding binding : bindingGraph.bindings()) {
+    for (dagger.spi.model.Binding binding : bindingGraph.bindings()) {
       binding
           .scope()
           .filter(scope -> !scope.isReusable())
@@ -85,16 +92,19 @@
               });
     }
     Multimaps.asMap(incompatibleBindings.build())
-        .forEach((componentNode, bindings) -> report(componentNode, bindings, diagnosticReporter));
+        .forEach((componentNode, bindings) ->
+            report(componentNode, bindings, diagnosticReporter, diagnosticMessageGenerator));
   }
 
   private void report(
       ComponentNode componentNode,
       Set<Binding> bindings,
-      DiagnosticReporter diagnosticReporter) {
+      DiagnosticReporter diagnosticReporter,
+      DiagnosticMessageGenerator diagnosticMessageGenerator) {
     Diagnostic.Kind diagnosticKind = ERROR;
     StringBuilder message =
-        new StringBuilder(componentNode.componentPath().currentComponent().getQualifiedName());
+        new StringBuilder(
+            componentNode.componentPath().currentComponent().className().canonicalName());
 
     if (!componentNode.isRealComponent()) {
       // If the "component" is really a module, it will have no scopes attached. We want to report
@@ -125,7 +135,7 @@
         case PROVISION:
           message.append(
               methodSignatureFormatter.format(
-                  MoreElements.asExecutable(binding.bindingElement().get())));
+                  asExecutable(binding.bindingElement().get().xprocessing())));
           break;
 
         case INJECTION:
@@ -133,12 +143,17 @@
               .append(getReadableSource(binding.scope().get()))
               .append(" class ")
               .append(
-                  closestEnclosingTypeElement(binding.bindingElement().get()).getQualifiedName());
+                  closestEnclosingTypeElement(binding.bindingElement().get().xprocessing())
+                      .getQualifiedName())
+              .append(diagnosticMessageGenerator.getMessage(binding));
+
           break;
 
         default:
           throw new AssertionError(binding);
       }
+
+      message.append('\n');
     }
     diagnosticReporter.reportComponent(diagnosticKind, componentNode, message.toString());
   }
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/InjectBindingValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/InjectBindingValidator.java
index fe1c3e0..a250612 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/InjectBindingValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/InjectBindingValidator.java
@@ -16,25 +16,29 @@
 
 package dagger.internal.codegen.bindinggraphvalidation;
 
-import static dagger.model.BindingKind.INJECTION;
+import static androidx.room.compiler.processing.compat.XConverters.toXProcessing;
+import static com.google.auto.common.MoreTypes.asTypeElement;
+import static dagger.spi.model.BindingKind.INJECTION;
 
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XProcessingEnv;
 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 dagger.spi.model.Binding;
+import dagger.spi.model.BindingGraph;
+import dagger.spi.model.BindingGraphPlugin;
+import dagger.spi.model.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 XProcessingEnv processingEnv;
   private final InjectValidator injectValidator;
 
   @Inject
-  InjectBindingValidator(InjectValidator injectValidator) {
+  InjectBindingValidator(XProcessingEnv processingEnv, InjectValidator injectValidator) {
+    this.processingEnv = processingEnv;
     this.injectValidator = injectValidator.whenGeneratingCode();
   }
 
@@ -50,10 +54,10 @@
         .forEach(binding -> validateInjectionBinding(binding, diagnosticReporter));
   }
 
-  private void validateInjectionBinding(
-      dagger.model.Binding node, DiagnosticReporter diagnosticReporter) {
-    ValidationReport<TypeElement> typeReport =
-        injectValidator.validateType(MoreTypes.asTypeElement(node.key().type()));
+  private void validateInjectionBinding(Binding node, DiagnosticReporter diagnosticReporter) {
+    ValidationReport typeReport =
+        injectValidator.validate(
+            toXProcessing(asTypeElement(node.key().type().java()), processingEnv));
     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
index 481c6d8..feaf88b 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/MapMultibindingValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/MapMultibindingValidator.java
@@ -21,31 +21,29 @@
 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 dagger.spi.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 com.squareup.javapoet.TypeName;
 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 dagger.internal.codegen.javapoet.TypeNames;
+import dagger.spi.model.Binding;
+import dagger.spi.model.BindingGraph;
+import dagger.spi.model.BindingGraphPlugin;
+import dagger.spi.model.DiagnosticReporter;
+import dagger.spi.model.Key;
 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
@@ -91,36 +89,38 @@
    *   <li>{@code Map<K, Producer<V>>}
    * </ol>
    */
-  private ImmutableSet<dagger.model.Binding> mapMultibindings(BindingGraph bindingGraph) {
-    ImmutableSetMultimap<Key, dagger.model.Binding> mapMultibindings =
+  private ImmutableSet<Binding> mapMultibindings(BindingGraph bindingGraph) {
+    ImmutableSetMultimap<Key, Binding> mapMultibindings =
         bindingGraph.bindings().stream()
             .filter(node -> node.kind().equals(MULTIBOUND_MAP))
-            .collect(toImmutableSetMultimap(dagger.model.Binding::key, node -> node));
+            .collect(toImmutableSetMultimap(Binding::key, node -> node));
 
     // Mutlbindings for Map<K, V>
-    SetMultimap<Key, dagger.model.Binding> plainValueMapMultibindings =
+    SetMultimap<Key, 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 =
+    SetMultimap<Key, Binding> providerValueMapMultibindings =
         filterKeys(
             mapMultibindings,
             key ->
-                MapType.from(key).valuesAreTypeOf(Provider.class)
+                MapType.from(key).valuesAreTypeOf(TypeNames.PROVIDER)
                     && !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 =
+    SetMultimap<Key, Binding> producerValueMapMultibindings =
         filterKeys(
             mapMultibindings,
             key ->
-                MapType.from(key).valuesAreTypeOf(Producer.class)
+                MapType.from(key).valuesAreTypeOf(TypeNames.PRODUCER)
                     && !plainValueMapMultibindings.containsKey(keyFactory.unwrapMapValueType(key))
                     && !providerValueMapMultibindings.containsKey(
-                        keyFactory.rewrapMapKey(key, Producer.class, Provider.class).get()));
+                        keyFactory
+                            .rewrapMapKey(key, TypeNames.PRODUCER, TypeNames.PROVIDER)
+                            .get()));
 
-    return new ImmutableSet.Builder<dagger.model.Binding>()
+    return new ImmutableSet.Builder<Binding>()
         .addAll(plainValueMapMultibindings.values())
         .addAll(providerValueMapMultibindings.values())
         .addAll(producerValueMapMultibindings.values())
@@ -128,7 +128,7 @@
   }
 
   private ImmutableSet<ContributionBinding> mapBindingContributions(
-      dagger.model.Binding binding, BindingGraph bindingGraph) {
+      Binding binding, BindingGraph bindingGraph) {
     checkArgument(binding.kind().equals(MULTIBOUND_MAP));
     return bindingGraph.requestedBindings(binding).stream()
         .map(b -> (BindingNode) b)
@@ -137,7 +137,7 @@
   }
 
   private void checkForDuplicateMapKeys(
-      dagger.model.Binding multiboundMapBinding,
+      Binding multiboundMapBinding,
       ImmutableSet<ContributionBinding> contributions,
       DiagnosticReporter diagnosticReporter) {
     ImmutableSetMultimap<?, ContributionBinding> contributionsByMapKey =
@@ -156,11 +156,11 @@
   }
 
   private void checkForInconsistentMapKeyAnnotationTypes(
-      dagger.model.Binding multiboundMapBinding,
+      Binding multiboundMapBinding,
       ImmutableSet<ContributionBinding> contributions,
       DiagnosticReporter diagnosticReporter) {
-    ImmutableSetMultimap<Equivalence.Wrapper<DeclaredType>, ContributionBinding>
-        contributionsByMapKeyAnnotationType = indexByMapKeyAnnotationType(contributions);
+    ImmutableSetMultimap<TypeName, ContributionBinding> contributionsByMapKeyAnnotationType =
+        indexByMapKeyAnnotationType(contributions);
 
     if (contributionsByMapKeyAnnotationType.keySet().size() > 1) {
       diagnosticReporter.reportBinding(
@@ -171,19 +171,16 @@
     }
   }
 
-  private static ImmutableSetMultimap<Equivalence.Wrapper<DeclaredType>, ContributionBinding>
-      indexByMapKeyAnnotationType(ImmutableSet<ContributionBinding> contributions) {
+  private static ImmutableSetMultimap<TypeName, ContributionBinding> indexByMapKeyAnnotationType(
+      ImmutableSet<ContributionBinding> contributions) {
     return ImmutableSetMultimap.copyOf(
         Multimaps.index(
             contributions,
-            mapBinding ->
-                MoreTypes.equivalence()
-                    .wrap(mapBinding.mapKeyAnnotation().get().getAnnotationType())));
+            mapBinding -> TypeName.get(mapBinding.mapKeyAnnotation().get().getAnnotationType())));
   }
 
   private String inconsistentMapKeyAnnotationTypesErrorMessage(
-      ImmutableSetMultimap<Equivalence.Wrapper<DeclaredType>, ContributionBinding>
-          contributionsByMapKeyAnnotationType,
+      ImmutableSetMultimap<TypeName, ContributionBinding> contributionsByMapKeyAnnotationType,
       Key mapBindingKey) {
     StringBuilder message =
         new StringBuilder(mapBindingKey.toString())
@@ -191,7 +188,7 @@
     Multimaps.asMap(contributionsByMapKeyAnnotationType)
         .forEach(
             (annotationType, contributions) -> {
-              message.append('\n').append(INDENT).append(annotationType.get()).append(':');
+              message.append('\n').append(INDENT).append(annotationType).append(':');
               bindingDeclarationFormatter.formatIndentedList(message, contributions, 2);
             });
     return message.toString();
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/MissingBindingValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/MissingBindingValidator.java
index 7334cd9..fdcccf9 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/MissingBindingValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/MissingBindingValidator.java
@@ -17,36 +17,50 @@
 package dagger.internal.codegen.bindinggraphvalidation;
 
 import static com.google.common.base.Verify.verify;
+import static com.google.common.collect.Iterables.getLast;
 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.binding.DependencyRequestFormatter.DOUBLE_INDENT;
 import static dagger.internal.codegen.extension.DaggerStreams.instancesOf;
+import static dagger.internal.codegen.xprocessing.XTypes.isWildcard;
 import static javax.tools.Diagnostic.Kind.ERROR;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import dagger.internal.codegen.binding.DependencyRequestFormatter;
 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 dagger.internal.codegen.validation.DiagnosticMessageGenerator;
+import dagger.spi.model.Binding;
+import dagger.spi.model.BindingGraph;
+import dagger.spi.model.BindingGraph.ComponentNode;
+import dagger.spi.model.BindingGraph.DependencyEdge;
+import dagger.spi.model.BindingGraph.Edge;
+import dagger.spi.model.BindingGraph.MissingBinding;
+import dagger.spi.model.BindingGraph.Node;
+import dagger.spi.model.BindingGraphPlugin;
+import dagger.spi.model.ComponentPath;
+import dagger.spi.model.DiagnosticReporter;
+import dagger.spi.model.Key;
+import java.util.List;
+import java.util.stream.Collectors;
 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;
+  private final DependencyRequestFormatter dependencyRequestFormatter;
+  private final DiagnosticMessageGenerator.Factory diagnosticMessageGeneratorFactory;
 
   @Inject
   MissingBindingValidator(
-      DaggerTypes types, InjectBindingRegistry injectBindingRegistry) {
-    this.types = types;
+      InjectBindingRegistry injectBindingRegistry,
+      DependencyRequestFormatter dependencyRequestFormatter,
+      DiagnosticMessageGenerator.Factory diagnosticMessageGeneratorFactory) {
     this.injectBindingRegistry = injectBindingRegistry;
+    this.dependencyRequestFormatter = dependencyRequestFormatter;
+    this.diagnosticMessageGeneratorFactory = diagnosticMessageGeneratorFactory;
   }
 
   @Override
@@ -68,18 +82,33 @@
 
   private void reportMissingBinding(
       MissingBinding missingBinding, BindingGraph graph, DiagnosticReporter diagnosticReporter) {
-    diagnosticReporter.reportBinding(
-        ERROR, missingBinding, missingBindingErrorMessage(missingBinding, graph));
+    List<ComponentPath> alternativeComponents =
+        graph.bindings(missingBinding.key()).stream()
+            .map(Binding::componentPath)
+            .distinct()
+            .collect(Collectors.toList());
+    // Print component name for each binding along the dependency path if the missing binding
+    // exists in a different component than expected
+    if (alternativeComponents.isEmpty()) {
+      diagnosticReporter.reportBinding(
+          ERROR, missingBinding, missingBindingErrorMessage(missingBinding, graph));
+    } else {
+      diagnosticReporter.reportComponent(
+          ERROR,
+          graph.componentNode(missingBinding.componentPath()).get(),
+          missingBindingErrorMessage(missingBinding, graph)
+              + wrongComponentErrorMessage(missingBinding, alternativeComponents, 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);
+    verify(!isWildcard(key.type().xprocessing()), "unexpected wildcard request: %s", key);
     // TODO(ronshapiro): replace "provided" with "satisfied"?
     errorMessage.append(key).append(" cannot be provided without ");
-    if (isValidImplicitProvisionKey(key, types)) {
+    if (isValidImplicitProvisionKey(key)) {
       errorMessage.append("an @Inject constructor or ");
     }
     errorMessage.append("an @Provides-"); // TODO(dpb): s/an/a
@@ -91,17 +120,64 @@
       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 String wrongComponentErrorMessage(
+      MissingBinding missingBinding,
+      List<ComponentPath> alternativeComponentPath,
+      BindingGraph graph) {
+    ImmutableSet<DependencyEdge> entryPoints =
+        graph.entryPointEdgesDependingOnBinding(missingBinding);
+    DiagnosticMessageGenerator generator = diagnosticMessageGeneratorFactory.create(graph);
+    ImmutableList<DependencyEdge> dependencyTrace =
+        generator.dependencyTrace(missingBinding, entryPoints);
+    StringBuilder message =
+        graph.isFullBindingGraph()
+            ? new StringBuilder()
+            : new StringBuilder(dependencyTrace.size() * 100 /* a guess heuristic */);
+    // Check in which component the missing binding is requested. This can be different from the
+    // component the missing binding is in because we'll try to search up the parent components for
+    // a binding which makes missing bindings end up at the root component. This is different from
+    // the place we are logically requesting the binding from. Note that this is related to the
+    // particular dependency trace being shown and so is not necessarily stable.
+    String missingComponentName =
+        getComponentFromDependencyEdge(dependencyTrace.get(0), graph, false);
+    boolean hasSameComponentName = false;
+    for (ComponentPath component : alternativeComponentPath) {
+      message.append("\nA binding for ").append(missingBinding.key()).append(" exists in ");
+      String currentComponentName = component.currentComponent().className().canonicalName();
+      if (currentComponentName.contentEquals(missingComponentName)) {
+        hasSameComponentName = true;
+        message.append("[").append(component).append("]");
+      } else {
+        message.append(currentComponentName);
+      }
+      message.append(":");
+    }
+    for (DependencyEdge edge : dependencyTrace) {
+      String line = dependencyRequestFormatter.format(edge.dependencyRequest());
+      if (line.isEmpty()) {
+        continue;
+      }
+      // If we ran into a rare case where the component names collide and we need to show the full
+      // path, only show the full path for the first dependency request. This is guaranteed to be
+      // the component in question since the logic for checking for a collision uses the first
+      // edge in the trace. Do not expand subsequent component paths to reduce spam.
+      String componentName =
+          String.format("[%s] ", getComponentFromDependencyEdge(edge, graph, hasSameComponentName));
+      hasSameComponentName = false;
+      message.append("\n").append(line.replace(DOUBLE_INDENT, DOUBLE_INDENT + componentName));
+    }
+    if (!dependencyTrace.isEmpty()) {
+      generator.appendComponentPathUnlessAtRoot(message, source(getLast(dependencyTrace), graph));
+    }
+    message.append(
+        generator.getRequestsNotInTrace(
+            dependencyTrace, generator.requests(missingBinding), entryPoints));
+    return message.toString();
+  }
+
   private boolean allIncomingDependenciesCanUseProduction(
       MissingBinding missingBinding, BindingGraph graph) {
     return graph.network().inEdges(missingBinding).stream()
@@ -116,11 +192,11 @@
     if (source instanceof ComponentNode) {
       return canBeSatisfiedByProductionBinding(edge.dependencyRequest().kind());
     }
-    if (source instanceof dagger.model.Binding) {
-      return ((dagger.model.Binding) source).isProduction();
+    if (source instanceof dagger.spi.model.Binding) {
+      return ((dagger.spi.model.Binding) source).isProduction();
     }
     throw new IllegalArgumentException(
-        "expected a dagger.model.Binding or ComponentNode: " + source);
+        "expected a dagger.spi.model.Binding or ComponentNode: " + source);
   }
 
   private boolean typeHasInjectionSites(Key key) {
@@ -129,4 +205,16 @@
         .map(binding -> !binding.injectionSites().isEmpty())
         .orElse(false);
   }
+
+  private static String getComponentFromDependencyEdge(
+      DependencyEdge edge, BindingGraph graph, boolean completePath) {
+    ComponentPath componentPath = graph.network().incidentNodes(edge).source().componentPath();
+    return completePath
+        ? componentPath.toString()
+        : componentPath.currentComponent().className().canonicalName();
+  }
+
+  private Node source(Edge edge, BindingGraph graph) {
+    return graph.network().incidentNodes(edge).source();
+  }
 }
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/NullableBindingValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/NullableBindingValidator.java
index 9800b15..4130fe9 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/NullableBindingValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/NullableBindingValidator.java
@@ -24,10 +24,11 @@
 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 dagger.spi.model.Binding;
+import dagger.spi.model.BindingGraph;
+import dagger.spi.model.BindingGraph.DependencyEdge;
+import dagger.spi.model.BindingGraphPlugin;
+import dagger.spi.model.DiagnosticReporter;
 import javax.inject.Inject;
 
 /**
@@ -45,7 +46,7 @@
 
   @Override
   public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
-    for (dagger.model.Binding binding : nullableBindings(bindingGraph)) {
+    for (Binding binding : nullableBindings(bindingGraph)) {
       for (DependencyEdge dependencyEdge : nonNullableDependencies(bindingGraph, binding)) {
         diagnosticReporter.reportDependency(
             compilerOptions.nullableValidationKind(),
@@ -62,14 +63,14 @@
     return "Dagger/Nullable";
   }
 
-  private ImmutableList<dagger.model.Binding> nullableBindings(BindingGraph bindingGraph) {
+  private ImmutableList<Binding> nullableBindings(BindingGraph bindingGraph) {
     return bindingGraph.bindings().stream()
         .filter(binding -> binding.isNullable())
         .collect(toImmutableList());
   }
 
   private ImmutableSet<DependencyEdge> nonNullableDependencies(
-      BindingGraph bindingGraph, dagger.model.Binding binding) {
+      BindingGraph bindingGraph, Binding binding) {
     return bindingGraph.network().inEdges(binding).stream()
         .flatMap(instancesOf(DependencyEdge.class))
         .filter(edge -> !edge.dependencyRequest().isNullable())
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/ProvisionDependencyOnProducerBindingValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/ProvisionDependencyOnProducerBindingValidator.java
index 7d742f9..53904a7 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/ProvisionDependencyOnProducerBindingValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/ProvisionDependencyOnProducerBindingValidator.java
@@ -22,11 +22,12 @@
 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 dagger.spi.model.Binding;
+import dagger.spi.model.BindingGraph;
+import dagger.spi.model.BindingGraph.DependencyEdge;
+import dagger.spi.model.BindingGraph.Node;
+import dagger.spi.model.BindingGraphPlugin;
+import dagger.spi.model.DiagnosticReporter;
 import java.util.stream.Stream;
 import javax.inject.Inject;
 
@@ -68,8 +69,7 @@
 
   /** Returns the dependencies on {@code binding}. */
   // TODO(dpb): Move to BindingGraph.
-  private Stream<DependencyEdge> incomingDependencies(
-      dagger.model.Binding binding, BindingGraph bindingGraph) {
+  private Stream<DependencyEdge> incomingDependencies(Binding binding, BindingGraph bindingGraph) {
     return bindingGraph.network().inEdges(binding).stream()
         .flatMap(instancesOf(DependencyEdge.class));
   }
@@ -88,16 +88,16 @@
    *     DependencyEdge#isEntryPoint() entry point}.
    */
   // TODO(dpb): Move to BindingGraph.
-  private dagger.model.Binding bindingRequestingDependency(
+  private Binding bindingRequestingDependency(
       DependencyEdge dependency, BindingGraph bindingGraph) {
     checkArgument(!dependency.isEntryPoint());
     Node source = bindingGraph.network().incidentNodes(dependency).source();
     verify(
-        source instanceof dagger.model.Binding,
+        source instanceof Binding,
         "expected source of %s to be a binding, but was: %s",
         dependency,
         source);
-    return (dagger.model.Binding) source;
+    return (Binding) source;
   }
 
   private String entryPointErrorMessage(DependencyEdge entryPoint) {
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/SetMultibindingValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/SetMultibindingValidator.java
new file mode 100644
index 0000000..1f79f87
--- /dev/null
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/SetMultibindingValidator.java
@@ -0,0 +1,99 @@
+/*
+ * 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.bindinggraphvalidation;
+
+import static dagger.spi.model.BindingKind.DELEGATE;
+import static dagger.spi.model.BindingKind.MULTIBOUND_SET;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+import dagger.spi.model.Binding;
+import dagger.spi.model.BindingGraph;
+import dagger.spi.model.BindingGraphPlugin;
+import dagger.spi.model.DiagnosticReporter;
+import dagger.spi.model.Key;
+import javax.inject.Inject;
+
+/** Validates that there are not multiple set binding contributions to the same binding. */
+final class SetMultibindingValidator implements BindingGraphPlugin {
+
+  @Inject
+  SetMultibindingValidator() {
+  }
+
+  @Override
+  public String pluginName() {
+    return "Dagger/SetMultibinding";
+  }
+
+  @Override
+  public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+    bindingGraph.bindings().stream()
+        .filter(binding -> binding.kind().equals(MULTIBOUND_SET))
+        .forEach(
+            binding ->
+                checkForDuplicateSetContributions(binding, bindingGraph, diagnosticReporter));
+  }
+
+  private void checkForDuplicateSetContributions(
+      Binding binding, BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {
+    // Map of delegate target key to the original contribution binding
+    Multimap<Key, Binding> dereferencedBindsTargets = HashMultimap.create();
+    for (Binding dep : bindingGraph.requestedBindings(binding)) {
+      if (dep.kind().equals(DELEGATE)) {
+        dereferencedBindsTargets.put(dereferenceDelegateBinding(dep, bindingGraph), dep);
+      }
+    }
+
+    dereferencedBindsTargets
+        .asMap()
+        .forEach(
+            (targetKey, contributions) -> {
+              if (contributions.size() > 1) {
+                diagnosticReporter.reportComponent(
+                    ERROR,
+                    bindingGraph.componentNode(binding.componentPath()).get(),
+                    "Multiple set contributions into %s for the same contribution key: %s.\n\n"
+                        + "    %s\n",
+                    binding.key(),
+                    targetKey,
+                    Joiner.on("\n    ").join(contributions));
+              }
+            });
+  }
+
+  /** Returns the delegate target of a delegate binding (going through other delegates as well). */
+  private Key dereferenceDelegateBinding(Binding binding, BindingGraph bindingGraph) {
+    ImmutableSet<Binding> delegateSet = bindingGraph.requestedBindings(binding);
+    if (delegateSet.isEmpty()) {
+      // There may not be a delegate if the delegate is missing. In this case, we just take the
+      // requested key and return that.
+      return Iterables.getOnlyElement(binding.dependencies()).key();
+    }
+    // If there is a binding, first we check if that is a delegate binding so we can dereference
+    // that binding if needed.
+    Binding delegate = Iterables.getOnlyElement(delegateSet);
+    if (delegate.kind().equals(DELEGATE)) {
+      return dereferenceDelegateBinding(delegate, bindingGraph);
+    }
+    return delegate.key();
+  }
+}
diff --git a/java/dagger/internal/codegen/bindinggraphvalidation/SubcomponentFactoryMethodValidator.java b/java/dagger/internal/codegen/bindinggraphvalidation/SubcomponentFactoryMethodValidator.java
index bf83a69..6650c43 100644
--- a/java/dagger/internal/codegen/bindinggraphvalidation/SubcomponentFactoryMethodValidator.java
+++ b/java/dagger/internal/codegen/bindinggraphvalidation/SubcomponentFactoryMethodValidator.java
@@ -16,49 +16,39 @@
 
 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 androidx.room.compiler.processing.XExecutableType;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 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 dagger.spi.model.BindingGraph;
+import dagger.spi.model.BindingGraph.ChildFactoryMethodEdge;
+import dagger.spi.model.BindingGraph.ComponentNode;
+import dagger.spi.model.BindingGraphPlugin;
+import dagger.spi.model.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<>();
+  private final Map<ComponentNode, Set<XTypeElement>> inheritedModulesCache = new HashMap<>();
 
   @Inject
-  SubcomponentFactoryMethodValidator(DaggerTypes types, KotlinMetadataUtil metadataUtil) {
-    this.types = types;
-    this.metadataUtil = metadataUtil;
-  }
+  SubcomponentFactoryMethodValidator() {}
 
   @Override
   public String pluginName() {
@@ -77,7 +67,7 @@
         .flatMap(instancesOf(ChildFactoryMethodEdge.class))
         .forEach(
             edge -> {
-              ImmutableSet<TypeElement> missingModules = findMissingModules(edge, bindingGraph);
+              ImmutableSet<XTypeElement> missingModules = findMissingModules(edge, bindingGraph);
               if (!missingModules.isEmpty()) {
                 reportMissingModuleParameters(
                     edge, missingModules, bindingGraph, diagnosticReporter);
@@ -85,49 +75,50 @@
             });
   }
 
-  private ImmutableSet<TypeElement> findMissingModules(
+  private ImmutableSet<XTypeElement> findMissingModules(
       ChildFactoryMethodEdge edge, BindingGraph graph) {
-    ImmutableSet<TypeElement> factoryMethodParameters =
+    ImmutableSet<XTypeElement> factoryMethodParameters =
         subgraphFactoryMethodParameters(edge, graph);
     ComponentNode child = (ComponentNode) graph.network().incidentNodes(edge).target();
-    SetView<TypeElement> modulesOwnedByChild = ownedModules(child, graph);
+    SetView<XTypeElement> 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())
+        .map(binding -> binding.contributingModule().get().xprocessing())
         .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))
+        .filter(moduleType -> !componentCanMakeNewInstances(moduleType))
         .collect(toImmutableSet());
   }
 
-  private ImmutableSet<TypeElement> subgraphFactoryMethodParameters(
+  private ImmutableSet<XTypeElement> 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());
+    XType parentType = parent.componentPath().currentComponent().xprocessing().getType();
+    XExecutableType factoryMethodType = edge.factoryMethod().xprocessing().asMemberOf(parentType);
+    return factoryMethodType.getParameterTypes().stream()
+        .map(XType::getTypeElement)
+        .collect(toImmutableSet());
   }
 
-  private SetView<TypeElement> ownedModules(ComponentNode component, BindingGraph graph) {
+  private SetView<XTypeElement> ownedModules(ComponentNode component, BindingGraph graph) {
     return Sets.difference(
         ((ComponentNodeImpl) component).componentDescriptor().moduleTypes(),
         inheritedModules(component, graph));
   }
 
-  private Set<TypeElement> inheritedModules(ComponentNode component, BindingGraph graph) {
+  private Set<XTypeElement> inheritedModules(ComponentNode component, BindingGraph graph) {
     return Util.reentrantComputeIfAbsent(
         inheritedModulesCache, component, uncachedInheritedModules(graph));
   }
 
-  private Function<ComponentNode, Set<TypeElement>> uncachedInheritedModules(BindingGraph graph) {
+  private Function<ComponentNode, Set<XTypeElement>> uncachedInheritedModules(BindingGraph graph) {
     return componentNode ->
         componentNode.componentPath().atRoot()
             ? ImmutableSet.of()
@@ -139,7 +130,7 @@
 
   private void reportMissingModuleParameters(
       ChildFactoryMethodEdge edge,
-      ImmutableSet<TypeElement> missingModules,
+      ImmutableSet<XTypeElement> missingModules,
       BindingGraph graph,
       DiagnosticReporter diagnosticReporter) {
     diagnosticReporter.reportSubcomponentFactoryMethod(
@@ -153,7 +144,8 @@
             .target()
             .componentPath()
             .currentComponent()
-            .getQualifiedName(),
+            .className()
+            .canonicalName(),
         Joiner.on(", ").join(missingModules));
   }
 }
diff --git a/java/dagger/internal/codegen/bootstrap/bootstrap_compiler_deploy.jar b/java/dagger/internal/codegen/bootstrap/bootstrap_compiler_deploy.jar
index b69dc65..69e859d 100644
--- a/java/dagger/internal/codegen/bootstrap/bootstrap_compiler_deploy.jar
+++ b/java/dagger/internal/codegen/bootstrap/bootstrap_compiler_deploy.jar
Binary files differ
diff --git a/java/dagger/internal/codegen/compileroption/BUILD b/java/dagger/internal/codegen/compileroption/BUILD
index ef39b34..6af5401 100644
--- a/java/dagger/internal/codegen/compileroption/BUILD
+++ b/java/dagger/internal/codegen/compileroption/BUILD
@@ -27,12 +27,12 @@
     deps = [
         "//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/producers",
-        "@google_bazel_common//third_party/java/google_java_format",
-        "@google_bazel_common//third_party/java/jsr330_inject",
-        "@maven//:com_google_auto_auto_common",
+        "//java/dagger/internal/codegen/xprocessing",
+        "//third_party/java/auto:common",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/jsr330_inject",
     ],
 )
diff --git a/java/dagger/internal/codegen/compileroption/CompilerOptions.java b/java/dagger/internal/codegen/compileroption/CompilerOptions.java
index a0d1cda..bd65d43 100644
--- a/java/dagger/internal/codegen/compileroption/CompilerOptions.java
+++ b/java/dagger/internal/codegen/compileroption/CompilerOptions.java
@@ -16,7 +16,7 @@
 
 package dagger.internal.codegen.compileroption;
 
-import javax.lang.model.element.TypeElement;
+import androidx.room.compiler.processing.XTypeElement;
 import javax.tools.Diagnostic;
 
 /** A collection of options that dictate how the compiler will run. */
@@ -24,6 +24,16 @@
   public abstract boolean usesProducers();
 
   /**
+   * Returns true if the experimental Android mode is enabled.
+   *
+   * <p><b>Warning: Do Not use! This flag is for internal, experimental use only!</b>
+   *
+   * <p>Issues related to this flag will not be supported. This flag could break your build, cause
+   * memory leaks in your app, or cause other unknown issues at runtime.
+   */
+  public abstract boolean experimentalMergedMode(XTypeElement element);
+
+  /**
    * 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.
@@ -31,7 +41,7 @@
    * 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 fastInit(XTypeElement element);
 
   public abstract boolean formatGeneratedSource();
 
@@ -48,6 +58,15 @@
   public abstract Diagnostic.Kind staticMemberValidationKind();
 
   /**
+   * Returns {@code true} if the stacktrace should be included in the deferred error message.
+   *
+   * <p>The default for this option is {@code false}. The stacktrace is mostly useful for special
+   * debugging purposes to gather more information about where the exception was thrown from within
+   * Dagger's own processors.
+   */
+  public abstract boolean includeStacktraceWithDeferredErrorMessages();
+
+  /**
    * 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.
    *
@@ -83,7 +102,7 @@
    *
    * @throws IllegalArgumentException if {@code element} is not a module or (sub)component
    */
-  public abstract boolean pluginsVisitFullBindingGraphs(TypeElement element);
+  public abstract boolean pluginsVisitFullBindingGraphs(XTypeElement element);
 
   public abstract Diagnostic.Kind moduleHasDifferentScopesDiagnosticKind();
 
@@ -91,8 +110,27 @@
 
   public abstract boolean experimentalDaggerErrorMessages();
 
+  /**
+   * Returns {@code true} if strict superficial validation is enabled.
+   *
+   * <p>This option is enabled by default and allows Dagger to detect and fail if an element that
+   * supports being annotated with a scope or qualifier annotation is annotated with any
+   * unresolvable annotation types. This option is considered "strict" because in most cases we must
+   * fail for any unresolvable annotation types, not just scopes and qualifiers. In particular, if
+   * an annotation type is not resolvable, we don't have enough information to tell if it's a scope
+   * or qualifier, so we must fail for all unresolvable annotations.
+   *
+   * <p>This option can be disabled to allow easier migration from the legacy behavior of Dagger
+   * (i.e. versions less than or equal to 2.40.5). However, we will remove this option in a future
+   * version of Dagger.
+   *
+   * <p>Warning:Disabling this option means that Dagger may miss a scope or qualifier on a binding,
+   * leading to a (wrong) unscoped binding or a (wrong) unqualified binding, respectively.
+   */
+  public abstract boolean strictSuperficialValidation();
+
   /** Returns the number of bindings allowed per shard. */
-  public int keysPerComponentShard(TypeElement component) {
+  public int keysPerComponentShard(XTypeElement component) {
     return 3500;
   }
 
diff --git a/java/dagger/internal/codegen/compileroption/ProcessingEnvironmentCompilerOptions.java b/java/dagger/internal/codegen/compileroption/ProcessingEnvironmentCompilerOptions.java
index 06d15a2..3135d5c 100644
--- a/java/dagger/internal/codegen/compileroption/ProcessingEnvironmentCompilerOptions.java
+++ b/java/dagger/internal/codegen/compileroption/ProcessingEnvironmentCompilerOptions.java
@@ -19,6 +19,7 @@
 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.base.Preconditions.checkState;
 import static com.google.common.collect.Sets.immutableEnumSet;
 import static dagger.internal.codegen.compileroption.FeatureStatus.DISABLED;
 import static dagger.internal.codegen.compileroption.FeatureStatus.ENABLED;
@@ -29,8 +30,10 @@
 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.INCLUDE_STACKTRACE_WITH_DEFERRED_ERROR_MESSAGES;
 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.STRICT_SUPERFICIAL_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;
@@ -50,13 +53,14 @@
 import static java.util.stream.Collectors.joining;
 import static java.util.stream.Stream.concat;
 
-import com.google.auto.common.MoreElements;
+import androidx.room.compiler.processing.XMessager;
+import androidx.room.compiler.processing.XTypeElement;
 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.javapoet.TypeNames;
 import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.producers.Produces;
 import java.util.Arrays;
 import java.util.EnumSet;
 import java.util.HashMap;
@@ -64,34 +68,33 @@
 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}. */
+/** {@link CompilerOptions} for the given processor. */
 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 XMessager messager;
+  private final Map<String, String> options;
+  private final DaggerElements elements;
   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;
+      XMessager messager, @ProcessingOptions Map<String, String> options, DaggerElements elements) {
+    this.messager = messager;
+    this.options = options;
+    this.elements = elements;
     checkValid();
   }
 
   @Override
   public boolean usesProducers() {
-    return processingEnvironment.getElementUtils().getTypeElement(Produces.class.getCanonicalName())
-        != null;
+    return elements.getTypeElement(TypeNames.PRODUCES) != null;
   }
 
   @Override
@@ -100,10 +103,37 @@
   }
 
   @Override
-  public boolean fastInit(TypeElement component) {
+  public boolean experimentalMergedMode(XTypeElement component) {
+    boolean isExperimental = experimentalMergedModeInternal();
+    if (isExperimental) {
+      checkState(
+          !fastInitInternal(component),
+          "Both fast init and experimental merged mode were turned on, please specify exactly one"
+              + " compilation mode.");
+    }
+    return isExperimental;
+  }
+
+  @Override
+  public boolean fastInit(XTypeElement component) {
+    boolean isFastInit = fastInitInternal(component);
+    if (isFastInit) {
+      checkState(
+          !experimentalMergedModeInternal(),
+          "Both fast init and experimental merged mode were turned on, please specify exactly one"
+              + " compilation mode.");
+    }
+    return isFastInit;
+  }
+
+  private boolean fastInitInternal(XTypeElement component) {
     return isEnabled(FAST_INIT);
   }
 
+  private boolean experimentalMergedModeInternal() {
+    return false;
+  }
+
   @Override
   public boolean formatGeneratedSource() {
     return isEnabled(FORMAT_GENERATED_SOURCE);
@@ -130,6 +160,11 @@
   }
 
   @Override
+  public boolean includeStacktraceWithDeferredErrorMessages() {
+    return isEnabled(INCLUDE_STACKTRACE_WITH_DEFERRED_ERROR_MESSAGES);
+  }
+
+  @Override
   public boolean ignorePrivateAndStaticInjectionForComponent() {
     return isEnabled(IGNORE_PRIVATE_AND_STATIC_INJECTION_FOR_COMPONENT);
   }
@@ -155,7 +190,7 @@
   }
 
   @Override
-  public boolean pluginsVisitFullBindingGraphs(TypeElement component) {
+  public boolean pluginsVisitFullBindingGraphs(XTypeElement component) {
     return isEnabled(PLUGINS_VISIT_FULL_BINDING_GRAPHS);
   }
 
@@ -180,20 +215,23 @@
   }
 
   @Override
-  public int keysPerComponentShard(TypeElement component) {
-    if (processingEnvironment.getOptions().containsKey(KEYS_PER_COMPONENT_SHARD)) {
+  public boolean strictSuperficialValidation() {
+    return isEnabled(STRICT_SUPERFICIAL_VALIDATION);
+  }
+
+  @Override
+  public int keysPerComponentShard(XTypeElement component) {
+    if (options.containsKey(KEYS_PER_COMPONENT_SHARD)) {
       checkArgument(
-          "dagger.internal.codegen".contentEquals(
-              MoreElements.getPackage(component).getQualifiedName()),
+          component.getClassName().packageName().startsWith("dagger."),
           "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 Integer.parseInt(options.get(KEYS_PER_COMPONENT_SHARD));
     }
     return super.keysPerComponentShard(component);
   }
 
   private boolean isEnabled(KeyOnlyOption keyOnlyOption) {
-    return processingEnvironment.getOptions().containsKey(keyOnlyOption.toString());
+    return options.containsKey(keyOnlyOption.toString());
   }
 
   private boolean isEnabled(Feature feature) {
@@ -206,9 +244,6 @@
 
   @SuppressWarnings("CheckReturnValue")
   private ProcessingEnvironmentCompilerOptions checkValid() {
-    for (KeyOnlyOption keyOnlyOption : KeyOnlyOption.values()) {
-      isEnabled(keyOnlyOption);
-    }
     for (Feature feature : Feature.values()) {
       parseOption(feature);
     }
@@ -223,11 +258,9 @@
   }
 
   private void noLongerRecognized(CommandLineOption commandLineOption) {
-    if (processingEnvironment.getOptions().containsKey(commandLineOption.toString())) {
-      processingEnvironment
-          .getMessager()
-          .printMessage(
-              Diagnostic.Kind.WARNING, commandLineOption + " is no longer recognized by Dagger");
+    if (options.containsKey(commandLineOption.toString())) {
+      messager.printMessage(
+          Diagnostic.Kind.WARNING, commandLineOption + " is no longer recognized by Dagger");
     }
   }
 
@@ -290,6 +323,8 @@
 
     WARN_IF_INJECTION_FACTORY_NOT_GENERATED_UPSTREAM,
 
+    INCLUDE_STACKTRACE_WITH_DEFERRED_ERROR_MESSAGES,
+
     IGNORE_PRIVATE_AND_STATIC_INJECTION_FOR_COMPONENT,
 
     EXPERIMENTAL_AHEAD_OF_TIME_SUBCOMPONENTS,
@@ -306,6 +341,8 @@
 
     STRICT_MULTIBINDING_VALIDATION,
 
+    STRICT_SUPERFICIAL_VALIDATION(ENABLED),
+
     VALIDATE_TRANSITIVE_COMPONENT_DEPENDENCIES(ENABLED)
     ;
 
@@ -458,13 +495,11 @@
 
   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));
+    messager.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(
@@ -486,12 +521,10 @@
   }
 
   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);
+    checkArgument(options.containsKey(key), "key %s not found", key);
+    String stringValue = options.get(key);
     if (stringValue == null) {
-      processingEnvironment
-          .getMessager()
-          .printMessage(Diagnostic.Kind.ERROR, "Processor option -A" + key + " needs a value");
+      messager.printMessage(Diagnostic.Kind.ERROR, "Processor option -A" + key + " needs a value");
     } else {
       try {
         T value =
@@ -502,19 +535,16 @@
       } 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));
+      messager.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));
+    return option.allNames().filter(options::containsKey);
   }
 }
diff --git a/java/dagger/internal/codegen/componentgenerator/BUILD b/java/dagger/internal/codegen/componentgenerator/BUILD
index d898d4d..859bc0d 100644
--- a/java/dagger/internal/codegen/componentgenerator/BUILD
+++ b/java/dagger/internal/codegen/componentgenerator/BUILD
@@ -25,19 +25,15 @@
         "//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",
+        "//java/dagger/internal/codegen/xprocessing",
+        "//third_party/java/auto:common",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/javapoet",
+        "//third_party/java/jsr330_inject",
     ],
 )
diff --git a/java/dagger/internal/codegen/componentgenerator/ComponentGenerator.java b/java/dagger/internal/codegen/componentgenerator/ComponentGenerator.java
index 098da81..5442b64 100644
--- a/java/dagger/internal/codegen/componentgenerator/ComponentGenerator.java
+++ b/java/dagger/internal/codegen/componentgenerator/ComponentGenerator.java
@@ -17,52 +17,56 @@
 package dagger.internal.codegen.componentgenerator;
 
 import static com.google.common.base.Verify.verify;
-import static dagger.internal.codegen.binding.SourceFiles.classFileName;
+import static dagger.internal.codegen.writing.ComponentNames.getRootComponentClassName;
 
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XFiler;
 import com.google.common.collect.ImmutableList;
-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 javax.annotation.processing.Filer;
+import java.util.Optional;
 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;
+  private final TopLevelImplementationComponent.Factory topLevelImplementationComponentFactory;
 
   @Inject
   ComponentGenerator(
-      Filer filer,
+      XFiler filer,
       DaggerElements elements,
       SourceVersion sourceVersion,
-      ComponentImplementationFactory componentImplementationFactory) {
+      TopLevelImplementationComponent.Factory topLevelImplementationComponentFactory) {
     super(filer, elements, sourceVersion);
-    this.componentImplementationFactory = componentImplementationFactory;
-  }
-
-  static ClassName componentName(TypeElement componentDefinitionType) {
-    ClassName componentName = ClassName.get(componentDefinitionType);
-    return ClassName.get(componentName.packageName(), "Dagger" + classFileName(componentName));
+    this.topLevelImplementationComponentFactory = topLevelImplementationComponentFactory;
   }
 
   @Override
-  public Element originatingElement(BindingGraph input) {
+  public XElement originatingElement(BindingGraph input) {
     return input.componentTypeElement();
   }
 
   @Override
   public ImmutableList<TypeSpec.Builder> topLevelTypes(BindingGraph bindingGraph) {
     ComponentImplementation componentImplementation =
-        componentImplementationFactory.createComponentImplementation(bindingGraph);
+        topLevelImplementationComponentFactory
+            .create(bindingGraph)
+            .currentImplementationSubcomponentBuilder()
+            .bindingGraph(bindingGraph)
+            .parentImplementation(Optional.empty())
+            .parentRequestRepresentations(Optional.empty())
+            .parentRequirementExpressions(Optional.empty())
+            .build()
+            .componentImplementation();
     verify(
-        componentImplementation.name().equals(componentName(bindingGraph.componentTypeElement())));
-    return ImmutableList.of(componentImplementation.generate());
+        componentImplementation
+            .name()
+            .equals(getRootComponentClassName(bindingGraph.componentDescriptor())));
+    return ImmutableList.of(componentImplementation.generate().toBuilder());
   }
 }
diff --git a/java/dagger/internal/codegen/componentgenerator/ComponentHjarGenerator.java b/java/dagger/internal/codegen/componentgenerator/ComponentHjarGenerator.java
index c8b8c97..7bd9ddc 100644
--- a/java/dagger/internal/codegen/componentgenerator/ComponentHjarGenerator.java
+++ b/java/dagger/internal/codegen/componentgenerator/ComponentHjarGenerator.java
@@ -16,22 +16,27 @@
 
 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.google.common.collect.Iterables.getOnlyElement;
 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.base.ComponentCreatorKind.BUILDER;
 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 dagger.internal.codegen.writing.ComponentNames.getRootComponentClassName;
+import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
+import static dagger.internal.codegen.xprocessing.XTypeElements.getAllUnimplementedMethods;
 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 androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XFiler;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.common.base.Ascii;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
@@ -39,25 +44,19 @@
 import com.squareup.javapoet.MethodSpec;
 import com.squareup.javapoet.TypeName;
 import com.squareup.javapoet.TypeSpec;
-import dagger.BindsInstance;
+import dagger.internal.codegen.base.ComponentCreatorKind;
 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.binding.MethodSignature;
+import dagger.internal.codegen.javapoet.TypeNames;
 import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.producers.internal.CancellationListener;
+import dagger.internal.codegen.xprocessing.MethodSpecs;
 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.
@@ -73,40 +72,28 @@
  * 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) {
+  ComponentHjarGenerator(XFiler filer, DaggerElements elements, SourceVersion sourceVersion) {
     super(filer, elements, sourceVersion);
-    this.elements = elements;
-    this.types = types;
-    this.metadataUtil = metadataUtil;
   }
 
   @Override
-  public Element originatingElement(ComponentDescriptor input) {
+  public XElement originatingElement(ComponentDescriptor input) {
     return input.typeElement();
   }
 
   @Override
   public ImmutableList<TypeSpec.Builder> topLevelTypes(ComponentDescriptor componentDescriptor) {
-    ClassName generatedTypeName = componentName(componentDescriptor.typeElement());
+    ClassName generatedTypeName = getRootComponentClassName(componentDescriptor);
     TypeSpec.Builder generatedComponent =
         TypeSpec.classBuilder(generatedTypeName)
             .addModifiers(FINAL)
             .addMethod(privateConstructor());
-    if (componentDescriptor.typeElement().getModifiers().contains(PUBLIC)) {
+    if (componentDescriptor.typeElement().isPublic()) {
       generatedComponent.addModifiers(PUBLIC);
     }
 
-    TypeElement componentElement = componentDescriptor.typeElement();
+    XTypeElement componentElement = componentDescriptor.typeElement();
     addSupertype(generatedComponent, componentElement);
 
     TypeName builderMethodReturnType;
@@ -114,7 +101,7 @@
     boolean noArgFactoryMethod;
     if (componentDescriptor.creatorDescriptor().isPresent()) {
       ComponentCreatorDescriptor creatorDescriptor = componentDescriptor.creatorDescriptor().get();
-      builderMethodReturnType = ClassName.get(creatorDescriptor.typeElement());
+      builderMethodReturnType = creatorDescriptor.typeElement().getClassName();
       creatorKind = creatorDescriptor.kind();
       noArgFactoryMethod = creatorDescriptor.factoryParameters().isEmpty();
     } else {
@@ -122,7 +109,7 @@
           TypeSpec.classBuilder("Builder")
               .addModifiers(STATIC, FINAL)
               .addMethod(privateConstructor());
-      if (componentDescriptor.typeElement().getModifiers().contains(PUBLIC)) {
+      if (componentDescriptor.typeElement().isPublic()) {
         builder.addModifiers(PUBLIC);
       }
 
@@ -142,21 +129,18 @@
     if (noArgFactoryMethod
         && !hasBindsInstanceMethods(componentDescriptor)
         && componentRequirements(componentDescriptor)
-            .noneMatch(
-                requirement -> requirement.requiresAPassedInstance(elements, metadataUtil))) {
+            .noneMatch(ComponentRequirement::requiresAPassedInstance)) {
       generatedComponent.addMethod(createMethod(componentDescriptor));
     }
 
-    DeclaredType componentType = MoreTypes.asDeclared(componentElement.asType());
+    XType componentType = componentElement.getType();
     // 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));
-            })
+            method ->
+                methodSignatures.add(MethodSignature.forComponentMethod(method, componentType)))
         .forEach(
             method ->
                 generatedComponent.addMethod(
@@ -164,16 +148,15 @@
 
     if (componentDescriptor.isProduction()) {
       generatedComponent
-          .addSuperinterface(ClassName.get(CancellationListener.class))
+          .addSuperinterface(TypeNames.CANCELLATION_LISTENER)
           .addMethod(onProducerFutureCancelledMethod());
     }
 
     return ImmutableList.of(generatedComponent);
   }
 
-  private MethodSpec emptyComponentMethod(TypeElement typeElement, ExecutableElement baseMethod) {
-    return MethodSpec.overriding(baseMethod, MoreTypes.asDeclared(typeElement.asType()), types)
-        .build();
+  private MethodSpec emptyComponentMethod(XTypeElement typeElement, XMethodElement baseMethod) {
+    return MethodSpecs.overriding(baseMethod, typeElement.getType()).build();
   }
 
   private static MethodSpec privateConstructor() {
@@ -194,40 +177,32 @@
         component.modules().stream()
             .filter(
                 module ->
-                    !module.moduleElement().getModifiers().contains(ABSTRACT)
+                    !module.moduleElement().isAbstract()
                         && isElementAccessibleFrom(
                             module.moduleElement(),
-                            ClassName.get(component.typeElement()).packageName()))
-            .map(module -> ComponentRequirement.forModule(module.moduleElement().asType())));
+                            component.typeElement().getClassName().packageName()))
+            .map(module -> ComponentRequirement.forModule(module.moduleElement().getType())));
   }
 
   private boolean hasBindsInstanceMethods(ComponentDescriptor componentDescriptor) {
     return componentDescriptor.creatorDescriptor().isPresent()
-        && elements
-            .getUnimplementedMethods(componentDescriptor.creatorDescriptor().get().typeElement())
+        && getAllUnimplementedMethods(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 boolean isBindsInstance(XMethodElement method) {
+    return method.hasAnnotation(TypeNames.BINDS_INSTANCE)
+        || (method.getParameters().size() == 1
+            && getOnlyElement(method.getParameters()).hasAnnotation(TypeNames.BINDS_INSTANCE));
   }
 
   private static MethodSpec builderSetterMethod(
-      TypeElement componentRequirement, ClassName builderClass) {
-    String simpleName =
-        UPPER_CAMEL.to(LOWER_CAMEL, componentRequirement.getSimpleName().toString());
+      XTypeElement componentRequirement, ClassName builderClass) {
+    String simpleName = UPPER_CAMEL.to(LOWER_CAMEL, getSimpleName(componentRequirement));
     return MethodSpec.methodBuilder(simpleName)
         .addModifiers(PUBLIC)
-        .addParameter(ClassName.get(componentRequirement), simpleName)
+        .addParameter(componentRequirement.getClassName(), simpleName)
         .returns(builderClass)
         .build();
   }
@@ -235,7 +210,7 @@
   private static MethodSpec builderBuildMethod(ComponentDescriptor component) {
     return MethodSpec.methodBuilder("build")
         .addModifiers(PUBLIC)
-        .returns(ClassName.get(component.typeElement()))
+        .returns(component.typeElement().getClassName())
         .build();
   }
 
@@ -250,7 +225,7 @@
   private static MethodSpec createMethod(ComponentDescriptor componentDescriptor) {
     return MethodSpec.methodBuilder("create")
         .addModifiers(PUBLIC, STATIC)
-        .returns(ClassName.get(componentDescriptor.typeElement()))
+        .returns(componentDescriptor.typeElement().getClassName())
         .build();
   }
 
diff --git a/java/dagger/internal/codegen/componentgenerator/ComponentImplementationBuilder.java b/java/dagger/internal/codegen/componentgenerator/ComponentImplementationBuilder.java
deleted file mode 100644
index 2be7d38..0000000
--- a/java/dagger/internal/codegen/componentgenerator/ComponentImplementationBuilder.java
+++ /dev/null
@@ -1,524 +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.componentgenerator;
-
-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);
-
-    elements
-        .getLocalAndInheritedMethods(graph.componentTypeElement())
-        .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, 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
deleted file mode 100644
index 0d29b86..0000000
--- a/java/dagger/internal/codegen/componentgenerator/ComponentImplementationFactory.java
+++ /dev/null
@@ -1,72 +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.componentgenerator;
-
-import static dagger.internal.codegen.componentgenerator.ComponentGenerator.componentName;
-
-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.Optional;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/** Factory for {@link ComponentImplementation}s. */
-@Singleton
-final class ComponentImplementationFactory {
-  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) {
-    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();
-  }
-}
diff --git a/java/dagger/internal/codegen/componentgenerator/CurrentImplementationSubcomponent.java b/java/dagger/internal/codegen/componentgenerator/CurrentImplementationSubcomponent.java
index 5584372..4a193ce 100644
--- a/java/dagger/internal/codegen/componentgenerator/CurrentImplementationSubcomponent.java
+++ b/java/dagger/internal/codegen/componentgenerator/CurrentImplementationSubcomponent.java
@@ -17,41 +17,64 @@
 package dagger.internal.codegen.componentgenerator;
 
 import dagger.BindsInstance;
+import dagger.Module;
+import dagger.Provides;
 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.ComponentImplementation.ChildComponentImplementationFactory;
+import dagger.internal.codegen.writing.ComponentRequestRepresentations;
 import dagger.internal.codegen.writing.ComponentRequirementExpressions;
 import dagger.internal.codegen.writing.ParentComponent;
 import dagger.internal.codegen.writing.PerComponentImplementation;
 import java.util.Optional;
+import javax.inject.Provider;
 
 /**
  * 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
+@Subcomponent(
+    modules = CurrentImplementationSubcomponent.ChildComponentImplementationFactoryModule.class)
 @PerComponentImplementation
 // This only needs to be public because the type is referenced by generated component.
 public interface CurrentImplementationSubcomponent {
-  ComponentImplementationBuilder componentImplementationBuilder();
+  ComponentImplementation componentImplementation();
+
+  /** A module to bind the {@link ChildComponentImplementationFactory}. */
+  @Module
+  interface ChildComponentImplementationFactoryModule {
+    @Provides
+    static ChildComponentImplementationFactory provideChildComponentImplementationFactory(
+        CurrentImplementationSubcomponent.Builder currentImplementationSubcomponentBuilder,
+        Provider<ComponentImplementation> componentImplementation,
+        Provider<ComponentRequestRepresentations> componentRequestRepresentations,
+        Provider<ComponentRequirementExpressions> componentRequirementExpressions) {
+      return childGraph ->
+          currentImplementationSubcomponentBuilder
+              .bindingGraph(childGraph)
+              .parentImplementation(Optional.of(componentImplementation.get()))
+              .parentRequestRepresentations(Optional.of(componentRequestRepresentations.get()))
+              .parentRequirementExpressions(Optional.of(componentRequirementExpressions.get()))
+              .build()
+              .componentImplementation();
+    }
+  }
 
   /** 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);
+    Builder parentImplementation(
+        @ParentComponent Optional<ComponentImplementation> parentImplementation);
 
     @BindsInstance
-    Builder parentBindingExpressions(
-        @ParentComponent Optional<ComponentBindingExpressions> parentBindingExpressions);
+    Builder parentRequestRepresentations(
+        @ParentComponent Optional<ComponentRequestRepresentations> parentRequestRepresentations);
 
     @BindsInstance
     Builder parentRequirementExpressions(
diff --git a/java/dagger/internal/codegen/componentgenerator/MethodSignature.java b/java/dagger/internal/codegen/componentgenerator/MethodSignature.java
deleted file mode 100644
index 99b05a4..0000000
--- a/java/dagger/internal/codegen/componentgenerator/MethodSignature.java
+++ /dev/null
@@ -1,56 +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.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
index 7919e47..79952c9 100644
--- a/java/dagger/internal/codegen/componentgenerator/TopLevelImplementationComponent.java
+++ b/java/dagger/internal/codegen/componentgenerator/TopLevelImplementationComponent.java
@@ -18,6 +18,7 @@
 
 import dagger.BindsInstance;
 import dagger.Subcomponent;
+import dagger.internal.codegen.binding.BindingGraph;
 import dagger.internal.codegen.writing.ComponentImplementation;
 import dagger.internal.codegen.writing.PerGeneratedFile;
 import dagger.internal.codegen.writing.TopLevel;
@@ -33,10 +34,8 @@
   CurrentImplementationSubcomponent.Builder currentImplementationSubcomponentBuilder();
 
   /** Returns the builder for {@link TopLevelImplementationComponent}. */
-  @Subcomponent.Builder
-  interface Builder {
-    @BindsInstance
-    Builder topLevelComponent(@TopLevel ComponentImplementation topLevelImplementation);
-    TopLevelImplementationComponent build();
+  @Subcomponent.Factory
+  interface Factory {
+    TopLevelImplementationComponent create(@BindsInstance @TopLevel BindingGraph bindingGraph);
   }
 }
diff --git a/java/dagger/internal/codegen/extension/BUILD b/java/dagger/internal/codegen/extension/BUILD
index 468a685..6bcb605 100644
--- a/java/dagger/internal/codegen/extension/BUILD
+++ b/java/dagger/internal/codegen/extension/BUILD
@@ -25,9 +25,9 @@
     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",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/graph",
+        "//third_party/java/jsr305_annotations",
     ],
 )
diff --git a/java/dagger/internal/codegen/javac/BUILD b/java/dagger/internal/codegen/javac/BUILD
index b8cb37c..ad2f91b 100644
--- a/java/dagger/internal/codegen/javac/BUILD
+++ b/java/dagger/internal/codegen/javac/BUILD
@@ -32,6 +32,8 @@
         "//java/dagger/internal/codegen/binding",
         "//java/dagger/internal/codegen/compileroption",
         "//java/dagger/internal/codegen/langmodel",
+        "//java/dagger/internal/codegen/xprocessing",
+        "//third_party/java/guava/collect",
     ],
 )
 
diff --git a/java/dagger/internal/codegen/compileroption/JavacPluginCompilerOptions.java b/java/dagger/internal/codegen/javac/JavacPluginCompilerOptions.java
similarity index 77%
rename from java/dagger/internal/codegen/compileroption/JavacPluginCompilerOptions.java
rename to java/dagger/internal/codegen/javac/JavacPluginCompilerOptions.java
index a86cc1b..c12d865 100644
--- a/java/dagger/internal/codegen/compileroption/JavacPluginCompilerOptions.java
+++ b/java/dagger/internal/codegen/javac/JavacPluginCompilerOptions.java
@@ -14,17 +14,19 @@
  * limitations under the License.
  */
 
-package dagger.internal.codegen.compileroption;
+package dagger.internal.codegen.javac;
 
 import static dagger.internal.codegen.compileroption.ValidationType.NONE;
 import static javax.tools.Diagnostic.Kind.NOTE;
 
+import androidx.room.compiler.processing.XTypeElement;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.compileroption.ValidationType;
 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 {
+final class JavacPluginCompilerOptions extends CompilerOptions {
 
   @Inject
   JavacPluginCompilerOptions() {}
@@ -35,7 +37,12 @@
   }
 
   @Override
-  public boolean fastInit(TypeElement element) {
+  public boolean experimentalMergedMode(XTypeElement element) {
+    return false;
+  }
+
+  @Override
+  public boolean fastInit(XTypeElement element) {
     return false;
   }
 
@@ -65,6 +72,11 @@
   }
 
   @Override
+  public boolean includeStacktraceWithDeferredErrorMessages() {
+    return false;
+  }
+
+  @Override
   public boolean ignorePrivateAndStaticInjectionForComponent() {
     return false;
   }
@@ -95,7 +107,7 @@
   }
 
   @Override
-  public boolean pluginsVisitFullBindingGraphs(TypeElement element) {
+  public boolean pluginsVisitFullBindingGraphs(XTypeElement element) {
     return false;
   }
 
@@ -118,4 +130,9 @@
   public boolean strictMultibindingValidation() {
     return false;
   }
+
+  @Override
+  public boolean strictSuperficialValidation() {
+    return true;
+  }
 }
diff --git a/java/dagger/internal/codegen/javac/JavacPluginModule.java b/java/dagger/internal/codegen/javac/JavacPluginModule.java
index 214191a..aca9f23 100644
--- a/java/dagger/internal/codegen/javac/JavacPluginModule.java
+++ b/java/dagger/internal/codegen/javac/JavacPluginModule.java
@@ -16,6 +16,9 @@
 
 package dagger.internal.codegen.javac;
 
+import androidx.room.compiler.processing.XMessager;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.compat.XConverters;
 import com.sun.tools.javac.model.JavacElements;
 import com.sun.tools.javac.model.JavacTypes;
 import com.sun.tools.javac.util.Context;
@@ -25,68 +28,54 @@
 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;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.util.Elements; // ALLOW_TYPES_ELEMENTS
+import javax.lang.model.util.Types; // ALLOW_TYPES_ELEMENTS
 
 /**
  * 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);
+@Module(includes = JavacPluginModule.BindsModule.class)
+public final class JavacPluginModule {
+  @Module
+  interface BindsModule {
+    @Binds
+    CompilerOptions compilerOptions(JavacPluginCompilerOptions compilerOptions);
+  }
 
-  @Binds
-  abstract Messager messager(NullMessager nullMessager);
+  private final XProcessingEnv processingEnv;
 
-  static final class NullMessager implements Messager {
+  public JavacPluginModule(Context context) {
+    this(JavacElements.instance(context), JavacTypes.instance(context));
+  }
 
-    @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) {}
+  public JavacPluginModule(Elements elements, Types types) {
+    this.processingEnv =
+        XProcessingEnv.create(new JavacPluginProcessingEnvironment(elements, types));
   }
 
   @Provides
-  static DaggerElements daggerElements(Context javaContext) {
-    return new DaggerElements(
-        JavacElements.instance(javaContext), JavacTypes.instance(javaContext));
+  XMessager messager() {
+    return processingEnv.getMessager();
   }
 
   @Provides
-  static DaggerTypes daggerTypes(Context javaContext, DaggerElements elements) {
-    return new DaggerTypes(JavacTypes.instance(javaContext), elements);
+  DaggerElements daggerElements() {
+    ProcessingEnvironment env = XConverters.toJavac(processingEnv);
+    return new DaggerElements(env.getElementUtils(), env.getTypeUtils()); // ALLOW_TYPES_ELEMENTS
   }
 
-  @Binds abstract Types types(DaggerTypes daggerTypes);
+  @Provides
+  DaggerTypes daggerTypes(DaggerElements elements) {
+    ProcessingEnvironment env = XConverters.toJavac(processingEnv);
+    return new DaggerTypes(env.getTypeUtils(), elements); // ALLOW_TYPES_ELEMENTS
+  }
 
-  private JavacPluginModule() {}
+  @Provides
+  XProcessingEnv xProcessingEnv() {
+    return processingEnv;
+  }
 }
diff --git a/java/dagger/internal/codegen/javac/JavacPluginProcessingEnvironment.java b/java/dagger/internal/codegen/javac/JavacPluginProcessingEnvironment.java
new file mode 100644
index 0000000..3f6a575
--- /dev/null
+++ b/java/dagger/internal/codegen/javac/JavacPluginProcessingEnvironment.java
@@ -0,0 +1,141 @@
+/*
+ * 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.javac;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Locale;
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.Messager;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.util.Elements;  // ALLOW_TYPES_ELEMENTS since in interface API
+import javax.lang.model.util.Types;  // ALLOW_TYPES_ELEMENTS since in interface API
+import javax.tools.Diagnostic;
+import javax.tools.FileObject;
+import javax.tools.JavaFileManager.Location;
+import javax.tools.JavaFileObject;
+
+/**
+ * An implementation of {@link ProcessingEnvironment} that runs in a javac plugin environment.
+ *
+ * <p>This environment runs after the classes are already compiled, so parts of the {@link
+ * ProcessingEnvironment} API like {@link Filer}, {@link Messager} don't make sense in this
+ * environment, so they've been replaced with throwing and no-op implementations respectively.
+ */
+final class JavacPluginProcessingEnvironment implements ProcessingEnvironment {
+  private final Elements elements;
+  private final Types types;
+  private final Filer filer = new ThrowingFiler();
+  private final Messager messager = new NoopMessager();
+
+  JavacPluginProcessingEnvironment(Elements elements, Types types) {
+    this.elements = elements;
+    this.types = types;
+  }
+
+  @Override
+  public Elements getElementUtils() {
+    return elements;
+  }
+
+  @Override
+  public Types getTypeUtils() {
+    return types;
+  }
+
+  @Override
+  public Filer getFiler() {
+    return filer;
+  }
+
+  @Override
+  public Locale getLocale() {
+    // Null means there's no locale in effect
+    return null;
+  }
+
+  @Override
+  public Messager getMessager() {
+    return messager;
+  }
+
+  @Override
+  public ImmutableMap<String, String> getOptions() {
+    // TODO(erichang): You can technically parse options out of the context, but it is internal
+    // implementation and unclear that any of the tools will ever be passing an option.
+    return ImmutableMap.of();
+  }
+
+  @Override
+  public SourceVersion getSourceVersion() {
+    // This source version doesn't really matter because it is saying what version generated code
+    // should adhere to, which there shouldn't be any because the Filer doesn't work.
+    return SourceVersion.latestSupported();
+  }
+
+  private static final class ThrowingFiler implements Filer {
+    @Override
+    public JavaFileObject createClassFile(CharSequence name, Element... originatingElements) {
+      throw new UnsupportedOperationException("Cannot use a Filer in this context");
+    }
+
+    @Override
+    public FileObject createResource(
+        Location location,
+        CharSequence pkg,
+        CharSequence relativeName,
+        Element... originatingElements) {
+      throw new UnsupportedOperationException("Cannot use a Filer in this context");
+    }
+
+    @Override
+    public JavaFileObject createSourceFile(CharSequence name, Element... originatingElements) {
+      throw new UnsupportedOperationException("Cannot use a Filer in this context");
+    }
+
+    @Override
+    public FileObject getResource(Location location, CharSequence pkg, CharSequence relativeName) {
+      throw new UnsupportedOperationException("Cannot use a Filer in this context");
+    }
+  }
+
+  private static final class NoopMessager implements Messager {
+    @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) {}
+  }
+}
diff --git a/java/dagger/internal/codegen/javapoet/BUILD b/java/dagger/internal/codegen/javapoet/BUILD
index ddb9f88..4dc4485 100644
--- a/java/dagger/internal/codegen/javapoet/BUILD
+++ b/java/dagger/internal/codegen/javapoet/BUILD
@@ -25,12 +25,12 @@
     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/error_prone:annotations",
-        "@google_bazel_common//third_party/java/javapoet",
-        "@maven//:com_google_auto_auto_common",
+        "//java/dagger/internal/codegen/xprocessing",
+        "//third_party/java/auto:common",
+        "//third_party/java/error_prone:annotations",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/javapoet",
     ],
 )
diff --git a/java/dagger/internal/codegen/javapoet/CodeBlocks.java b/java/dagger/internal/codegen/javapoet/CodeBlocks.java
index 3e9f75d..c0d7a7b 100644
--- a/java/dagger/internal/codegen/javapoet/CodeBlocks.java
+++ b/java/dagger/internal/codegen/javapoet/CodeBlocks.java
@@ -112,6 +112,11 @@
   }
 
   /** Returns {@code expression} cast to a type. */
+  public static CodeBlock cast(CodeBlock expression, ClassName castTo) {
+    return CodeBlock.of("($T) $L", castTo, expression);
+  }
+
+  /** Returns {@code expression} cast to a type. */
   public static CodeBlock cast(CodeBlock expression, Class<?> castTo) {
     return CodeBlock.of("($T) $L", castTo, expression);
   }
diff --git a/java/dagger/internal/codegen/javapoet/Expression.java b/java/dagger/internal/codegen/javapoet/Expression.java
index b79c55c..f7bfbb8 100644
--- a/java/dagger/internal/codegen/javapoet/Expression.java
+++ b/java/dagger/internal/codegen/javapoet/Expression.java
@@ -16,6 +16,9 @@
 
 package dagger.internal.codegen.javapoet;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+
+import androidx.room.compiler.processing.XType;
 import com.google.auto.common.MoreTypes;
 import com.squareup.javapoet.CodeBlock;
 import dagger.internal.codegen.langmodel.DaggerTypes;
@@ -45,6 +48,11 @@
   }
 
   /** Creates a new {@link Expression} with a {@link TypeMirror} and {@link CodeBlock}. */
+  public static Expression create(XType type, CodeBlock expression) {
+    return create(toJavac(type), expression);
+  }
+
+  /** Creates a new {@link Expression} with a {@link TypeMirror} and {@link CodeBlock}. */
   public static Expression create(TypeMirror type, CodeBlock expression) {
     return new Expression(type, expression);
   }
@@ -53,6 +61,14 @@
    * Creates a new {@link Expression} with a {@link TypeMirror}, {@linkplain CodeBlock#of(String,
    * Object[]) format, and arguments}.
    */
+  public static Expression create(XType type, String format, Object... args) {
+    return create(toJavac(type), format, args);
+  }
+
+  /**
+   * Creates a new {@link Expression} with a {@link TypeMirror}, {@linkplain CodeBlock#of(String,
+   * Object[]) format, and arguments}.
+   */
   public static Expression create(TypeMirror type, String format, Object... args) {
     return create(type, CodeBlock.of(format, args));
   }
@@ -60,6 +76,13 @@
   /** Returns a new expression that casts the current expression to {@code newType}. */
   // TODO(ronshapiro): consider overloads that take a Types and Elements and only cast if necessary,
   // or just embedding a Types/Elements instance in an Expression.
+  public Expression castTo(XType newType) {
+    return castTo(toJavac(newType));
+  }
+
+  /** Returns a new expression that casts the current expression to {@code newType}. */
+  // TODO(ronshapiro): consider overloads that take a Types and Elements and only cast if necessary,
+  // or just embedding a Types/Elements instance in an Expression.
   public Expression castTo(TypeMirror newType) {
     return create(newType, "($T) $L", newType, codeBlock);
   }
diff --git a/java/dagger/internal/codegen/javapoet/TypeNames.java b/java/dagger/internal/codegen/javapoet/TypeNames.java
index 71f03f0..ae4145d 100644
--- a/java/dagger/internal/codegen/javapoet/TypeNames.java
+++ b/java/dagger/internal/codegen/javapoet/TypeNames.java
@@ -16,79 +16,152 @@
 
 package dagger.internal.codegen.javapoet;
 
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
+
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.ParameterizedTypeName;
 import com.squareup.javapoet.TypeName;
-import dagger.Lazy;
-import dagger.MembersInjector;
-import dagger.internal.DoubleCheck;
-import dagger.internal.Factory;
-import dagger.internal.InjectedFieldSignature;
-import dagger.internal.InstanceFactory;
-import dagger.internal.MapFactory;
-import dagger.internal.MapProviderFactory;
-import dagger.internal.MembersInjectors;
-import dagger.internal.ProviderOfLazy;
-import dagger.internal.SetFactory;
-import dagger.internal.SingleCheck;
-import dagger.producers.Produced;
-import dagger.producers.Producer;
-import dagger.producers.ProducerModule;
-import dagger.producers.internal.AbstractProducer;
-import dagger.producers.internal.DependencyMethodProducer;
-import dagger.producers.internal.MapOfProducedProducer;
-import dagger.producers.internal.MapOfProducerProducer;
-import dagger.producers.internal.MapProducer;
-import dagger.producers.internal.Producers;
-import dagger.producers.internal.SetOfProducedProducer;
-import dagger.producers.internal.SetProducer;
-import dagger.producers.monitoring.ProducerToken;
-import dagger.producers.monitoring.ProductionComponentMonitor;
-import java.util.List;
-import java.util.Set;
-import javax.inject.Provider;
+import javax.lang.model.type.TypeMirror;
 
 /** Common names and convenience methods for JavaPoet {@link TypeName} usage. */
 public final class TypeNames {
 
-  public static final ClassName ABSTRACT_PRODUCER = ClassName.get(AbstractProducer.class);
-  public static final ClassName DEPENDENCY_METHOD_PRODUCER =
-      ClassName.get(DependencyMethodProducer.class);
-  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);
+  // Dagger Core classnames
+  public static final ClassName ASSISTED = ClassName.get("dagger.assisted", "Assisted");
+  public static final ClassName ASSISTED_FACTORY =
+      ClassName.get("dagger.assisted", "AssistedFactory");
+  public static final ClassName ASSISTED_INJECT =
+      ClassName.get("dagger.assisted", "AssistedInject");
+  public static final ClassName BINDS = ClassName.get("dagger", "Binds");
+  public static final ClassName BINDS_INSTANCE = ClassName.get("dagger", "BindsInstance");
+  public static final ClassName BINDS_OPTIONAL_OF = ClassName.get("dagger", "BindsOptionalOf");
+  public static final ClassName COMPONENT = ClassName.get("dagger", "Component");
+  public static final ClassName COMPONENT_BUILDER = COMPONENT.nestedClass("Builder");
+  public static final ClassName COMPONENT_FACTORY = COMPONENT.nestedClass("Factory");
+  public static final ClassName DAGGER_PROCESSING_OPTIONS =
+      ClassName.get("dagger", "DaggerProcessingOptions");
+  public static final ClassName ELEMENTS_INTO_SET =
+      ClassName.get("dagger.multibindings", "ElementsIntoSet");
+  public static final ClassName INTO_MAP = ClassName.get("dagger.multibindings", "IntoMap");
+  public static final ClassName INTO_SET = ClassName.get("dagger.multibindings", "IntoSet");
+  public static final ClassName MAP_KEY = ClassName.get("dagger", "MapKey");
+  public static final ClassName MODULE = ClassName.get("dagger", "Module");
+  public static final ClassName MULTIBINDS = ClassName.get("dagger.multibindings", "Multibinds");
+  public static final ClassName PROVIDES = ClassName.get("dagger", "Provides");
+  public static final ClassName REUSABLE = ClassName.get("dagger", "Reusable");
+  public static final ClassName SUBCOMPONENT = ClassName.get("dagger", "Subcomponent");
+  public static final ClassName SUBCOMPONENT_BUILDER = SUBCOMPONENT.nestedClass("Builder");
+  public static final ClassName SUBCOMPONENT_FACTORY = SUBCOMPONENT.nestedClass("Factory");
+
+  // Dagger Internal classnames
+  public static final ClassName DELEGATE_FACTORY =
+      ClassName.get("dagger.internal", "DelegateFactory");
+  public static final ClassName DOUBLE_CHECK = ClassName.get("dagger.internal", "DoubleCheck");
+  public static final ClassName FACTORY = ClassName.get("dagger.internal", "Factory");
   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);
-  public static final ClassName LISTENABLE_FUTURE = ClassName.get(ListenableFuture.class);
-  public static final ClassName MAP_FACTORY = ClassName.get(MapFactory.class);
+      ClassName.get("dagger.internal", "InjectedFieldSignature");
+  public static final ClassName INSTANCE_FACTORY =
+      ClassName.get("dagger.internal", "InstanceFactory");
+  public static final ClassName MAP_FACTORY = ClassName.get("dagger.internal", "MapFactory");
+  public static final ClassName MAP_PROVIDER_FACTORY =
+      ClassName.get("dagger.internal", "MapProviderFactory");
+  public static final ClassName MEMBERS_INJECTOR = ClassName.get("dagger", "MembersInjector");
+  public static final ClassName MEMBERS_INJECTORS =
+      ClassName.get("dagger.internal", "MembersInjectors");
+  public static final ClassName PROVIDER = ClassName.get("javax.inject", "Provider");
+  public static final ClassName PROVIDER_OF_LAZY =
+      ClassName.get("dagger.internal", "ProviderOfLazy");
+  public static final ClassName SCOPE_METADATA = ClassName.get("dagger.internal", "ScopeMetadata");
+  public static final ClassName QUALIFIER_METADATA =
+      ClassName.get("dagger.internal", "QualifierMetadata");
+  public static final ClassName SET_FACTORY = ClassName.get("dagger.internal", "SetFactory");
+  public static final ClassName SINGLE_CHECK = ClassName.get("dagger.internal", "SingleCheck");
+  public static final ClassName LAZY = ClassName.get("dagger", "Lazy");
+
+  // Dagger Producers classnames
+  public static final ClassName ABSTRACT_PRODUCER =
+      ClassName.get("dagger.producers.internal", "AbstractProducer");
+  public static final ClassName CANCELLATION_LISTENER =
+      ClassName.get("dagger.producers.internal", "CancellationListener");
+  public static final ClassName CANCELLATION_POLICY =
+      ClassName.get("dagger.producers", "CancellationPolicy");
+  public static final ClassName DELEGATE_PRODUCER =
+      ClassName.get("dagger.producers.internal", "DelegateProducer");
+  public static final ClassName DEPENDENCY_METHOD_PRODUCER =
+      ClassName.get("dagger.producers.internal", "DependencyMethodProducer");
   public static final ClassName MAP_OF_PRODUCED_PRODUCER =
-      ClassName.get(MapOfProducedProducer.class);
+      ClassName.get("dagger.producers.internal", "MapOfProducedProducer");
   public static final ClassName MAP_OF_PRODUCER_PRODUCER =
-      ClassName.get(MapOfProducerProducer.class);
-  public static final ClassName MAP_PRODUCER = ClassName.get(MapProducer.class);
-  public static final ClassName MAP_PROVIDER_FACTORY = ClassName.get(MapProviderFactory.class);
-  public static final ClassName MEMBERS_INJECTOR = ClassName.get(MembersInjector.class);
-  public static final ClassName MEMBERS_INJECTORS = ClassName.get(MembersInjectors.class);
-  public static final ClassName PRODUCER_TOKEN = ClassName.get(ProducerToken.class);
-  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);
+      ClassName.get("dagger.producers.internal", "MapOfProducerProducer");
+  public static final ClassName MAP_PRODUCER =
+      ClassName.get("dagger.producers.internal", "MapProducer");
+  public static final ClassName MONITORS =
+      ClassName.get("dagger.producers.monitoring.internal", "Monitors");
+  public static final ClassName PRODUCED = ClassName.get("dagger.producers", "Produced");
+  public static final ClassName PRODUCER = ClassName.get("dagger.producers", "Producer");
+  public static final ClassName PRODUCERS = ClassName.get("dagger.producers.internal", "Producers");
+  public static final ClassName PRODUCER_MODULE =
+      ClassName.get("dagger.producers", "ProducerModule");
+  public static final ClassName PRODUCES = ClassName.get("dagger.producers", "Produces");
+  public static final ClassName PRODUCTION = ClassName.get("dagger.producers", "Production");
+  public static final ClassName PRODUCTION_COMPONENT =
+      ClassName.get("dagger.producers", "ProductionComponent");
+  public static final ClassName PRODUCTION_COMPONENT_BUILDER =
+      PRODUCTION_COMPONENT.nestedClass("Builder");
+  public static final ClassName PRODUCTION_COMPONENT_FACTORY =
+      PRODUCTION_COMPONENT.nestedClass("Factory");
+  public static final ClassName PRODUCTION_EXECTUTOR_MODULE =
+      ClassName.get("dagger.producers.internal", "ProductionExecutorModule");
+  public static final ClassName PRODUCTION_IMPLEMENTATION =
+      ClassName.get("dagger.producers.internal", "ProductionImplementation");
+  public static final ClassName PRODUCTION_SUBCOMPONENT =
+      ClassName.get("dagger.producers", "ProductionSubcomponent");
+  public static final ClassName PRODUCTION_SUBCOMPONENT_BUILDER =
+      PRODUCTION_SUBCOMPONENT.nestedClass("Builder");
+  public static final ClassName PRODUCTION_SUBCOMPONENT_FACTORY =
+      PRODUCTION_SUBCOMPONENT.nestedClass("Factory");
+  public static final ClassName PRODUCER_TOKEN =
+      ClassName.get("dagger.producers.monitoring", "ProducerToken");
+  public static final ClassName PRODUCTION_COMPONENT_MONITOR =
+      ClassName.get("dagger.producers.monitoring", "ProductionComponentMonitor");
   public static final ClassName PRODUCTION_COMPONENT_MONITOR_FACTORY =
-      ClassName.get(ProductionComponentMonitor.Factory.class);
-  public static final ClassName PROVIDER = ClassName.get(Provider.class);
-  public static final ClassName PROVIDER_OF_LAZY = ClassName.get(ProviderOfLazy.class);
-  public static final ClassName SET = ClassName.get(Set.class);
-  public static final ClassName SET_FACTORY = ClassName.get(SetFactory.class);
+      ClassName.get("dagger.producers.monitoring", "ProductionComponentMonitor", "Factory");
   public static final ClassName SET_OF_PRODUCED_PRODUCER =
-      ClassName.get(SetOfProducedProducer.class);
-  public static final ClassName SET_PRODUCER = ClassName.get(SetProducer.class);
-  public static final ClassName SINGLE_CHECK = ClassName.get(SingleCheck.class);
+      ClassName.get("dagger.producers.internal", "SetOfProducedProducer");
+  public static final ClassName SET_PRODUCER =
+      ClassName.get("dagger.producers.internal", "SetProducer");
+  public static final ClassName PRODUCTION_SCOPE =
+      ClassName.get("dagger.producers", "ProductionScope");
+
+  // Other classnames
+  public static final ClassName EXECUTOR = ClassName.get("java.util.concurrent", "Executor");
+  public static final ClassName ERROR = ClassName.get("java.lang", "Error");
+  public static final ClassName EXCEPTION = ClassName.get("java.lang", "Exception");
+  public static final ClassName RUNTIME_EXCEPTION = ClassName.get("java.lang", "RuntimeException");
+  public static final ClassName MAP = ClassName.get("java.util", "Map");
+  public static final ClassName IMMUTABLE_MAP =
+      ClassName.get("com.google.common.collect", "ImmutableMap");
+  public static final ClassName SINGLETON = ClassName.get("jakarta.inject", "Singleton");
+  public static final ClassName SINGLETON_JAVAX = ClassName.get("javax.inject", "Singleton");
+  public static final ClassName SCOPE = ClassName.get("jakarta.inject", "Scope");
+  public static final ClassName SCOPE_JAVAX = ClassName.get("javax.inject", "Scope");
+  public static final ClassName INJECT = ClassName.get("jakarta.inject", "Inject");
+  public static final ClassName INJECT_JAVAX = ClassName.get("javax.inject", "Inject");
+  public static final ClassName QUALIFIER = ClassName.get("jakarta.inject", "Qualifier");
+  public static final ClassName QUALIFIER_JAVAX = ClassName.get("javax.inject", "Qualifier");
+  public static final ClassName COLLECTION = ClassName.get("java.util", "Collection");
+  public static final ClassName LIST = ClassName.get("java.util", "List");
+  public static final ClassName SET = ClassName.get("java.util", "Set");
+  public static final ClassName IMMUTABLE_SET =
+      ClassName.get("com.google.common.collect", "ImmutableSet");
+  public static final ClassName FUTURES =
+      ClassName.get("com.google.common.util.concurrent", "Futures");
+  public static final ClassName LISTENABLE_FUTURE =
+      ClassName.get("com.google.common.util.concurrent", "ListenableFuture");
+  public static final ClassName GUAVA_OPTIONAL =
+      ClassName.get("com.google.common.base", "Optional");
+  public static final ClassName JDK_OPTIONAL = ClassName.get("java.util", "Optional");
+  public static final ClassName OVERRIDE = ClassName.get("java.lang", "Override");
+  public static final ClassName JVM_STATIC = ClassName.get("kotlin.jvm", "JvmStatic");
 
   /**
    * {@link TypeName#VOID} is lowercase-v {@code void} whereas this represents the class, {@link
@@ -141,8 +214,8 @@
   }
 
   /**
-   * 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.
+   * Returns the {@link TypeName} for the raw type of the given {@link TypeName}. If the argument
+   * isn't a parameterized type, it returns the argument unchanged.
    */
   public static TypeName rawTypeName(TypeName typeName) {
     return (typeName instanceof ParameterizedTypeName)
@@ -150,5 +223,13 @@
         : typeName;
   }
 
+  /**
+   * Returns the {@link TypeName} for the raw type of the given {@link TypeMirror}. If the argument
+   * isn't a parameterized type, it returns the argument unchanged.
+   */
+  public static TypeName rawTypeName(TypeMirror type) {
+    return rawTypeName(TypeName.get(type));
+  }
+
   private TypeNames() {}
 }
diff --git a/java/dagger/internal/codegen/javapoet/TypeSpecs.java b/java/dagger/internal/codegen/javapoet/TypeSpecs.java
index 8ec8747..570f702 100644
--- a/java/dagger/internal/codegen/javapoet/TypeSpecs.java
+++ b/java/dagger/internal/codegen/javapoet/TypeSpecs.java
@@ -16,6 +16,9 @@
 
 package dagger.internal.codegen.javapoet;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.errorprone.annotations.CanIgnoreReturnValue;
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.TypeSpec;
@@ -31,12 +34,28 @@
    * @return {@code typeBuilder}
    */
   @CanIgnoreReturnValue
+  public static TypeSpec.Builder addSupertype(
+      TypeSpec.Builder typeBuilder, XTypeElement supertype) {
+    return addSupertype(typeBuilder, toJavac(supertype));
+  }
+
+  /**
+   * If {@code supertype} is a class, adds it as a superclass for {@code typeBuilder}; if it is an
+   * interface, adds it as a superinterface.
+   *
+   * @return {@code typeBuilder}
+   */
+  @CanIgnoreReturnValue
   public static TypeSpec.Builder addSupertype(TypeSpec.Builder typeBuilder, TypeElement supertype) {
     switch (supertype.getKind()) {
       case CLASS:
-        return typeBuilder.superclass(ClassName.get(supertype));
+        return typeBuilder
+            .superclass(ClassName.get(supertype))
+            .avoidClashesWithNestedClasses(supertype);
       case INTERFACE:
-        return typeBuilder.addSuperinterface(ClassName.get(supertype));
+        return typeBuilder
+            .addSuperinterface(ClassName.get(supertype))
+            .avoidClashesWithNestedClasses(supertype);
       default:
         throw new AssertionError(supertype + " is neither a class nor an interface.");
     }
diff --git a/java/dagger/internal/codegen/kotlin/BUILD b/java/dagger/internal/codegen/kotlin/BUILD
index d1c5458..c2f5f0f 100644
--- a/java/dagger/internal/codegen/kotlin/BUILD
+++ b/java/dagger/internal/codegen/kotlin/BUILD
@@ -29,12 +29,13 @@
         "//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",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:value",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/javapoet",
+        "//third_party/java/jsr305_annotations",
+        "//third_party/java/jsr330_inject",
         "@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
index 5fb49f0..d38eba0 100644
--- a/java/dagger/internal/codegen/kotlin/KotlinMetadata.java
+++ b/java/dagger/internal/codegen/kotlin/KotlinMetadata.java
@@ -34,6 +34,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
 import dagger.internal.codegen.extension.DaggerCollectors;
 import dagger.internal.codegen.langmodel.DaggerElements;
 import java.util.HashMap;
@@ -188,13 +189,12 @@
 
   private static KotlinClassMetadata.Class metadataOf(TypeElement typeElement) {
     Optional<AnnotationMirror> metadataAnnotation =
-        getAnnotationMirror(typeElement, Metadata.class);
+        getAnnotationMirror(typeElement, ClassName.get(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"),
diff --git a/java/dagger/internal/codegen/kotlin/KotlinMetadataUtil.java b/java/dagger/internal/codegen/kotlin/KotlinMetadataUtil.java
index 980ff34..f9ffe44 100644
--- a/java/dagger/internal/codegen/kotlin/KotlinMetadataUtil.java
+++ b/java/dagger/internal/codegen/kotlin/KotlinMetadataUtil.java
@@ -16,9 +16,12 @@
 
 package dagger.internal.codegen.kotlin;
 
-import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
+
 import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.common.base.Preconditions.checkState;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
 import static dagger.internal.codegen.langmodel.DaggerElements.closestEnclosingTypeElement;
+import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotatedAnnotations;
 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;
@@ -26,8 +29,10 @@
 
 import com.google.common.collect.ImmutableCollection;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.squareup.javapoet.ClassName;
 import dagger.internal.codegen.extension.DaggerCollectors;
-import java.lang.annotation.Annotation;
+import dagger.internal.codegen.kotlin.KotlinMetadata.FunctionMetadata;
 import java.util.Optional;
 import javax.inject.Inject;
 import javax.lang.model.element.AnnotationMirror;
@@ -37,7 +42,6 @@
 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. */
@@ -65,11 +69,12 @@
    * 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) {
+      VariableElement fieldElement, ClassName annotationType) {
     return metadataFactory
         .create(fieldElement)
         .getSyntheticAnnotationMethod(fieldElement)
-        .map(methodElement -> getAnnotatedAnnotations(methodElement, annotationType).asList())
+        .map(methodElement ->
+            getAnnotatedAnnotations(methodElement, annotationType).asList())
         .orElse(ImmutableList.of());
   }
 
@@ -163,9 +168,13 @@
   }
 
   /**
-   * Returns {@code true} if the <code>@JvmStatic</code> annotation is present in the given element.
+   * Returns a map mapping all method signatures within the given class element, including methods
+   * that it inherits from its ancestors, to their method names.
    */
-  public static boolean isJvmStaticPresent(ExecutableElement element) {
-    return isAnnotationPresent(element, JvmStatic.class);
+  public ImmutableMap<String, String> getAllMethodNamesBySignature(TypeElement element) {
+    checkState(
+        hasMetadata(element), "Can not call getAllMethodNamesBySignature for non-Kotlin class");
+    return metadataFactory.create(element).classMetadata().functionsBySignature().values().stream()
+        .collect(toImmutableMap(FunctionMetadata::signature, FunctionMetadata::name));
   }
 }
diff --git a/java/dagger/internal/codegen/kythe/BUILD b/java/dagger/internal/codegen/kythe/BUILD
index 9e8dea1..0d77841 100644
--- a/java/dagger/internal/codegen/kythe/BUILD
+++ b/java/dagger/internal/codegen/kythe/BUILD
@@ -28,13 +28,13 @@
         "//java/dagger:core",
         "//java/dagger/internal/codegen/binding",
         "//java/dagger/internal/codegen/javac",
+        "//java/dagger/internal/codegen/javapoet",
         "//java/dagger/internal/codegen/langmodel",
         "//java/dagger/internal/codegen/validation",
-        "//java/dagger/internal/guava:collect",
-        "//java/dagger/producers",
+        "//java/dagger/internal/codegen/xprocessing",
         "//java/dagger/spi",
-        "@google_bazel_common//third_party/java/auto:service",
-        "@maven//:com_google_auto_auto_common",
+        "//third_party/java/auto:service",
+        "//third_party/java/guava/collect",
     ],
 )
 
diff --git a/java/dagger/internal/codegen/kythe/DaggerKythePlugin.java b/java/dagger/internal/codegen/kythe/DaggerKythePlugin.java
index 4c6f85e..a2bc0c2 100644
--- a/java/dagger/internal/codegen/kythe/DaggerKythePlugin.java
+++ b/java/dagger/internal/codegen/kythe/DaggerKythePlugin.java
@@ -19,8 +19,12 @@
 // the regular kythe/java tree.
 package dagger.internal.codegen.kythe;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
 
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.compat.XConverters;
 import com.google.auto.service.AutoService;
 import com.google.common.collect.Iterables;
 import com.google.devtools.kythe.analyzers.base.EntrySet;
@@ -31,8 +35,6 @@
 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;
@@ -41,13 +43,13 @@
 import dagger.internal.codegen.binding.ComponentDescriptorFactory;
 import dagger.internal.codegen.binding.ModuleDescriptor;
 import dagger.internal.codegen.javac.JavacPluginModule;
+import dagger.internal.codegen.javapoet.TypeNames;
 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 dagger.spi.model.BindingGraph;
+import dagger.spi.model.BindingGraph.DependencyEdge;
+import dagger.spi.model.BindingGraph.Edge;
+import dagger.spi.model.BindingGraph.Node;
+import dagger.spi.model.DependencyRequest;
 import java.util.Optional;
 import java.util.logging.Logger;
 import javax.inject.Inject;
@@ -65,14 +67,17 @@
   private FactEmitter emitter;
   @Inject ComponentDescriptorFactory componentDescriptorFactory;
   @Inject BindingGraphFactory bindingGraphFactory;
+  @Inject XProcessingEnv xProcessingEnv;
 
   @Override
   public Void visitClassDef(JCClassDecl tree, Void p) {
     if (tree.sym != null
-        && isAnyAnnotationPresent(tree.sym, Component.class, ProductionComponent.class)) {
+        && isAnyAnnotationPresent(tree.sym, TypeNames.COMPONENT, TypeNames.PRODUCTION_COMPONENT)) {
       addNodesForGraph(
           bindingGraphFactory.create(
-              componentDescriptorFactory.rootComponentDescriptor(tree.sym), false));
+              componentDescriptorFactory.rootComponentDescriptor(
+                  XConverters.toXProcessing(tree.sym, xProcessingEnv)),
+              false));
     }
     return super.visitClassDef(tree, p);
   }
@@ -128,8 +133,8 @@
 
   private void addDependencyEdge(
       DependencyRequest dependency, BindingDeclaration bindingDeclaration) {
-    Element requestElement = dependency.requestElement().get();
-    Element bindingElement = bindingDeclaration.bindingElement().get();
+    Element requestElement = dependency.requestElement().get().java();
+    Element bindingElement = toJavac(bindingDeclaration.bindingElement().get());
     Optional<VName> requestElementNode = jvmNode(requestElement, "request element");
     Optional<VName> bindingElementNode = jvmNode(bindingElement, "binding element");
     emitEdge(requestElementNode, "/inject/satisfiedby", bindingElementNode);
@@ -155,6 +160,10 @@
     graph.subgraphs().forEach(this::addChildComponentEdges);
   }
 
+  private Optional<VName> jvmNode(XElement element, String name) {
+    return jvmNode(toJavac(element), name);
+  }
+
   private Optional<VName> jvmNode(Element element, String name) {
     Optional<VName> jvmNode = kytheGraph.getJvmNode((Symbol) element).map(KytheNode::getVName);
     if (!jvmNode.isPresent()) {
@@ -174,7 +183,7 @@
     if (bindingGraphFactory == null) {
       emitter = entrySets.getEmitter();
       DaggerDaggerKythePlugin_PluginComponent.builder()
-          .context(kytheGraph.getJavaContext())
+          .javacPluginModule(new JavacPluginModule(kytheGraph.getJavaContext()))
           .build()
           .inject(this);
     }
@@ -185,13 +194,5 @@
   @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/langmodel/Accessibility.java b/java/dagger/internal/codegen/langmodel/Accessibility.java
index 62c1fbd..a90e6fd 100644
--- a/java/dagger/internal/codegen/langmodel/Accessibility.java
+++ b/java/dagger/internal/codegen/langmodel/Accessibility.java
@@ -16,12 +16,17 @@
 
 package dagger.internal.codegen.langmodel;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 import static com.google.auto.common.MoreElements.getPackage;
+import static com.google.auto.common.MoreTypes.asElement;
 import static com.google.common.base.Preconditions.checkArgument;
 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.auto.common.MoreElements;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 import java.util.Optional;
 import javax.lang.model.element.Element;
 import javax.lang.model.element.ElementKind;
@@ -35,12 +40,11 @@
 import javax.lang.model.type.NoType;
 import javax.lang.model.type.NullType;
 import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeKind;
 import javax.lang.model.type.TypeMirror;
 import javax.lang.model.type.TypeVariable;
-import javax.lang.model.type.TypeVisitor;
 import javax.lang.model.type.WildcardType;
-import javax.lang.model.util.SimpleElementVisitor6;
-import javax.lang.model.util.SimpleTypeVisitor6;
+import javax.lang.model.util.SimpleElementVisitor8;
 import javax.lang.model.util.SimpleTypeVisitor8;
 
 /**
@@ -61,28 +65,45 @@
 public final class Accessibility {
   /** Returns true if the given type can be referenced from any package. */
   public static boolean isTypePubliclyAccessible(TypeMirror type) {
-    return type.accept(new TypeAccessibilityVisitor(), null);
+    return type.accept(new TypeAccessibilityVisitor(Optional.empty()), null);
+  }
+
+  /** Returns true if the given type can be referenced from code in the given package. */
+  public static boolean isTypeAccessibleFrom(XType type, String packageName) {
+    return isTypeAccessibleFrom(toJavac(type), packageName);
   }
 
   /** Returns true if the given type can be referenced from code in the given package. */
   public static boolean isTypeAccessibleFrom(TypeMirror type, String packageName) {
-    return type.accept(new TypeAccessibilityVisitor(packageName), null);
+    return type.accept(new TypeAccessibilityVisitor(Optional.of(packageName)), null);
   }
 
-  private static boolean isTypeAccessibleFrom(TypeMirror type, Optional<String> packageName) {
-    return type.accept(new TypeAccessibilityVisitor(packageName), null);
+  /**
+   * Returns true if the given type is protected and can be referenced from the given requesting
+   * element.
+   */
+  public static boolean isProtectedMemberOf(DeclaredType type, XTypeElement requestingElement) {
+    return isProtectedAccessibleFromElement(type.asElement(), requestingElement);
   }
 
-  private static final class TypeAccessibilityVisitor extends SimpleTypeVisitor6<Boolean, Void> {
-    final Optional<String> packageName;
-
-    TypeAccessibilityVisitor() {
-      this(Optional.empty());
+  private static Boolean isProtectedAccessibleFromElement(
+      Element element, XTypeElement requestingElement) {
+    if (!element.getModifiers().contains(PROTECTED)) {
+      return false;
     }
-
-    TypeAccessibilityVisitor(String packageName) {
-      this(Optional.of(packageName));
+    if (element.getEnclosingElement().equals(toJavac(requestingElement))) {
+      return true;
     }
+    // Check if the element is protected member of the requesting element's super class.
+    if (requestingElement.getSuperType() != null) {
+      return isProtectedAccessibleFromElement(
+          element, requestingElement.getSuperType().getTypeElement());
+    }
+    return false;
+  }
+
+  private static final class TypeAccessibilityVisitor extends SimpleTypeVisitor8<Boolean, Void> {
+    private final Optional<String> packageName;
 
     TypeAccessibilityVisitor(Optional<String> packageName) {
       this.packageName = packageName;
@@ -103,7 +124,7 @@
         // TODO(gak): investigate this check.  see comment in Binding
         return false;
       }
-      if (!isElementAccessibleFrom(type.asElement(), packageName)) {
+      if (!type.asElement().accept(new ElementAccessibilityVisitor(packageName), null)) {
         return false;
       }
       for (TypeMirror typeArgument : type.getTypeArguments()) {
@@ -156,36 +177,35 @@
 
   /** Returns true if the given element can be referenced from any package. */
   public static boolean isElementPubliclyAccessible(Element element) {
-    return element.accept(new ElementAccessibilityVisitor(), null);
+    return element.accept(new ElementAccessibilityVisitor(Optional.empty()), null);
+  }
+
+  /** Returns true if the given element can be referenced from code in the given package. */
+  // TODO(gak): account for protected
+  // TODO(bcorso): account for kotlin srcs (package-private doesn't exist, internal does exist).
+  public static boolean isElementAccessibleFrom(XElement element, String packageName) {
+    return isElementAccessibleFrom(toJavac(element), packageName);
   }
 
   /** Returns true if the given element can be referenced from code in the given package. */
   // TODO(gak): account for protected
   public static boolean isElementAccessibleFrom(Element element, String packageName) {
-    return element.accept(new ElementAccessibilityVisitor(packageName), null);
+    return element.accept(new ElementAccessibilityVisitor(Optional.of(packageName)), null);
   }
 
-  private static boolean isElementAccessibleFrom(Element element, Optional<String> packageName) {
-    return element.accept(new ElementAccessibilityVisitor(packageName), null);
+  /** Returns true if the given element can be referenced from other code in its own package. */
+  public static boolean isElementAccessibleFromOwnPackage(XElement element) {
+    return isElementAccessibleFromOwnPackage(toJavac(element));
   }
 
   /** Returns true if the given element can be referenced from other code in its own package. */
   public static boolean isElementAccessibleFromOwnPackage(Element element) {
-    return isElementAccessibleFrom(
-        element, MoreElements.getPackage(element).getQualifiedName().toString());
+    return isElementAccessibleFrom(element, getPackage(element).getQualifiedName().toString());
   }
 
   private static final class ElementAccessibilityVisitor
-      extends SimpleElementVisitor6<Boolean, Void> {
-    final Optional<String> packageName;
-
-    ElementAccessibilityVisitor() {
-      this(Optional.empty());
-    }
-
-    ElementAccessibilityVisitor(String packageName) {
-      this(Optional.of(packageName));
-    }
+      extends SimpleElementVisitor8<Boolean, Void> {
+    private final Optional<String> packageName;
 
     ElementAccessibilityVisitor(Optional<String> packageName) {
       this.packageName = packageName;
@@ -211,10 +231,7 @@
     }
 
     boolean accessibleMember(Element element) {
-      if (!element.getEnclosingElement().accept(this, null)) {
-        return false;
-      }
-      return accessibleModifiers(element);
+      return element.getEnclosingElement().accept(this, null) && accessibleModifiers(element);
     }
 
     boolean accessibleModifiers(Element element) {
@@ -222,12 +239,9 @@
         return true;
       } else if (element.getModifiers().contains(PRIVATE)) {
         return false;
-      } else if (packageName.isPresent()
-          && getPackage(element).getQualifiedName().contentEquals(packageName.get())) {
-        return true;
-      } else {
-        return false;
       }
+      return packageName.isPresent()
+          && getPackage(element).getQualifiedName().contentEquals(packageName.get());
     }
 
     @Override
@@ -249,27 +263,18 @@
     }
   }
 
-  private static final TypeVisitor<Boolean, Optional<String>> RAW_TYPE_ACCESSIBILITY_VISITOR =
-      new SimpleTypeVisitor8<Boolean, Optional<String>>() {
-        @Override
-        protected Boolean defaultAction(TypeMirror e, Optional<String> requestingPackage) {
-          return isTypeAccessibleFrom(e, requestingPackage);
-        }
-
-        @Override
-        public Boolean visitDeclared(DeclaredType t, Optional<String> requestingPackage) {
-          return isElementAccessibleFrom(t.asElement(), requestingPackage);
-        }
-      };
-
   /** Returns true if the raw type of {@code type} is accessible from the given package. */
   public static boolean isRawTypeAccessible(TypeMirror type, String requestingPackage) {
-    return type.accept(RAW_TYPE_ACCESSIBILITY_VISITOR, Optional.of(requestingPackage));
+    return type.getKind() == TypeKind.DECLARED
+        ? isElementAccessibleFrom(asElement(type), requestingPackage)
+        : isTypeAccessibleFrom(type, requestingPackage);
   }
 
   /** Returns true if the raw type of {@code type} is accessible from any package. */
   public static boolean isRawTypePubliclyAccessible(TypeMirror type) {
-    return type.accept(RAW_TYPE_ACCESSIBILITY_VISITOR, Optional.empty());
+    return type.getKind() == TypeKind.DECLARED
+        ? isElementPubliclyAccessible(asElement(type))
+        : isTypePubliclyAccessible(type);
   }
 
   private Accessibility() {}
diff --git a/java/dagger/internal/codegen/langmodel/BUILD b/java/dagger/internal/codegen/langmodel/BUILD
index 25f1e62..5e961a1 100644
--- a/java/dagger/internal/codegen/langmodel/BUILD
+++ b/java/dagger/internal/codegen/langmodel/BUILD
@@ -27,11 +27,13 @@
     deps = [
         "//java/dagger:core",
         "//java/dagger/internal/codegen/base:shared",
-        "//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",
+        "//java/dagger/internal/codegen/extension",
+        "//java/dagger/internal/codegen/xprocessing",
+        "//third_party/java/auto:common",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/graph",
+        "//third_party/java/guava/util/concurrent",
+        "//third_party/java/javapoet",
     ],
 )
diff --git a/java/dagger/internal/codegen/langmodel/DaggerElements.java b/java/dagger/internal/codegen/langmodel/DaggerElements.java
index 51c2a60..4d5501e 100644
--- a/java/dagger/internal/codegen/langmodel/DaggerElements.java
+++ b/java/dagger/internal/codegen/langmodel/DaggerElements.java
@@ -16,41 +16,32 @@
 
 package dagger.internal.codegen.langmodel;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 import static com.google.auto.common.MoreElements.asExecutable;
-import static com.google.auto.common.MoreElements.hasModifiers;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.collect.Lists.asList;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
 import static java.util.Comparator.comparing;
-import static java.util.stream.Collectors.toSet;
-import static javax.lang.model.element.Modifier.ABSTRACT;
 
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XMethodElement;
 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;
 import com.squareup.javapoet.ClassName;
 import dagger.Reusable;
 import dagger.internal.codegen.base.ClearableCache;
 import java.io.Writer;
-import java.lang.annotation.Annotation;
-import java.util.Collection;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
-import java.util.Set;
-import java.util.function.Predicate;
 import java.util.stream.Collectors;
-import javax.annotation.processing.ProcessingEnvironment;
 import javax.lang.model.element.AnnotationMirror;
 import javax.lang.model.element.AnnotationValue;
 import javax.lang.model.element.Element;
 import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.ElementVisitor;
 import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.element.Name;
 import javax.lang.model.element.PackageElement;
@@ -63,15 +54,12 @@
 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.SimpleTypeVisitor8;
 import javax.lang.model.util.Types;
 
 /** Extension of {@link Elements} that adds Dagger-specific methods. */
@@ -87,37 +75,32 @@
     this.types = checkNotNull(types);
   }
 
-  public DaggerElements(ProcessingEnvironment processingEnv) {
-    this(processingEnv.getElementUtils(), processingEnv.getTypeUtils());
+  /**
+   * Returns {@code true} if {@code encloser} is equal to or recursively encloses {@code enclosed}.
+   */
+  public static boolean transitivelyEncloses(XElement encloser, XElement enclosed) {
+    return transitivelyEncloses(toJavac(encloser), toJavac(enclosed));
   }
 
   /**
-   * Returns {@code true} if {@code encloser} is equal to {@code enclosed} or recursively encloses
-   * it.
+   * Returns {@code true} if {@code encloser} is equal to or recursively encloses {@code enclosed}.
    */
-  public static boolean elementEncloses(TypeElement encloser, Element enclosed) {
-    return Iterables.contains(GET_ENCLOSED_ELEMENTS.breadthFirst(encloser), enclosed);
+  public static boolean transitivelyEncloses(Element encloser, Element enclosed) {
+    Element current = enclosed;
+    while (current != null) {
+      if (current.equals(encloser)) {
+        return true;
+      }
+      current = current.getEnclosingElement();
+    }
+    return false;
   }
 
-  private static final Traverser<Element> GET_ENCLOSED_ELEMENTS =
-      Traverser.forTree(Element::getEnclosedElements);
-
   public ImmutableSet<ExecutableElement> getLocalAndInheritedMethods(TypeElement type) {
     return getLocalAndInheritedMethodsCache.computeIfAbsent(
         type, k -> MoreElements.getLocalAndInheritedMethods(type, types, elements));
   }
 
-  public ImmutableSet<ExecutableElement> getUnimplementedMethods(TypeElement type) {
-    return FluentIterable.from(getLocalAndInheritedMethods(type))
-        .filter(hasModifiers(ABSTRACT))
-        .toSet();
-  }
-
-  /** Returns the type element for a class. */
-  public TypeElement getTypeElement(Class<?> clazz) {
-    return getTypeElement(clazz.getCanonicalName());
-  }
-
   @Override
   public TypeElement getTypeElement(CharSequence name) {
     return elements.getTypeElement(name);
@@ -130,22 +113,16 @@
 
   /** Returns the argument or the closest enclosing element that is a {@link TypeElement}. */
   public static TypeElement closestEnclosingTypeElement(Element element) {
-    return element.accept(CLOSEST_ENCLOSING_TYPE_ELEMENT, null);
+    Element current = element;
+    while (current != null) {
+      if (MoreElements.isType(current)) {
+        return MoreElements.asType(current);
+      }
+      current = current.getEnclosingElement();
+    }
+    throw new IllegalStateException("There is no enclosing TypeElement for: " + element);
   }
 
-  private static final ElementVisitor<TypeElement, Void> CLOSEST_ENCLOSING_TYPE_ELEMENT =
-      new SimpleElementVisitor8<TypeElement, Void>() {
-        @Override
-        protected TypeElement defaultAction(Element element, Void p) {
-          return element.getEnclosingElement().accept(this, null);
-        }
-
-        @Override
-        public TypeElement visitType(TypeElement type, Void p) {
-          return type;
-        }
-      };
-
   /**
    * Compares elements according to their declaration order among siblings. Only valid to compare
    * elements enclosed by the same parent.
@@ -167,9 +144,9 @@
    * that of {@code annotationClasses}.
    */
   public static boolean isAnyAnnotationPresent(
-      Element element, Iterable<? extends Class<? extends Annotation>> annotationClasses) {
-    for (Class<? extends Annotation> annotation : annotationClasses) {
-      if (MoreElements.isAnnotationPresent(element, annotation)) {
+      Element element, Iterable<ClassName> annotationClasses) {
+    for (ClassName annotation : annotationClasses) {
+      if (isAnnotationPresent(element, annotation)) {
         return true;
       }
     }
@@ -178,53 +155,23 @@
 
   @SafeVarargs
   public static boolean isAnyAnnotationPresent(
-      Element element,
-      Class<? extends Annotation> first,
-      Class<? extends Annotation>... otherAnnotations) {
+      Element element, ClassName first, ClassName... otherAnnotations) {
     return isAnyAnnotationPresent(element, asList(first, otherAnnotations));
   }
 
   /**
-   * Returns {@code true} iff the given element has an {@link AnnotationMirror} whose {@linkplain
-   * AnnotationMirror#getAnnotationType() annotation type} is equivalent to {@code annotationType}.
+   * Returns {@code true} iff the given element has an {@link AnnotationMirror} whose {@link
+   * AnnotationMirror#getAnnotationType() annotation type} has the same canonical name as that of
+   * {@code annotationClass}. This method is a safer alternative to calling {@link
+   * Element#getAnnotation} and checking for {@code null} as it avoids any interaction with
+   * annotation proxies.
    */
-  public static boolean isAnnotationPresent(Element element, TypeMirror annotationType) {
-    return element.getAnnotationMirrors().stream()
-        .map(AnnotationMirror::getAnnotationType)
-        .anyMatch(candidate -> MoreTypes.equivalence().equivalent(candidate, annotationType));
+  public static boolean isAnnotationPresent(Element element, ClassName annotationName) {
+    return getAnnotationMirror(element, annotationName).isPresent();
   }
 
-  /**
-   * Returns the annotation present on {@code element} whose type is {@code first} or within {@code
-   * rest}, checking each annotation type in order.
-   */
-  @SafeVarargs
-  public static Optional<AnnotationMirror> getAnyAnnotation(
-      Element element, Class<? extends Annotation> first, Class<? extends Annotation>... rest) {
-    return getAnyAnnotation(element, asList(first, rest));
-  }
-
-  /**
-   * Returns the annotation present on {@code element} whose type is in {@code annotations},
-   * checking each annotation type in order.
-   */
-  public static Optional<AnnotationMirror> getAnyAnnotation(
-      Element element, Collection<? extends Class<? extends Annotation>> annotations) {
-    return element.getAnnotationMirrors().stream()
-        .filter(hasAnnotationTypeIn(annotations))
-        .map((AnnotationMirror a) -> a) // Avoid returning Optional<? extends AnnotationMirror>.
-        .findFirst();
-  }
-
-  /** Returns the annotations present on {@code element} of all types. */
-  @SafeVarargs
-  public static ImmutableSet<AnnotationMirror> getAllAnnotations(
-      Element element, Class<? extends Annotation> first, Class<? extends Annotation>... rest) {
-    return ImmutableSet.copyOf(
-        Iterables.filter(
-            element.getAnnotationMirrors(), hasAnnotationTypeIn(asList(first, rest))::test));
-  }
-
+  // Note: This is similar to auto-common's MoreElements except using ClassName rather than Class.
+  // TODO(bcorso): Contribute a String version to auto-common's MoreElements?
   /**
    * Returns an {@link AnnotationMirror} for the annotation of type {@code annotationClass} on
    * {@code element}, or {@link Optional#empty()} if no such annotation exists. This method is a
@@ -232,25 +179,23 @@
    * annotation proxies.
    */
   public static Optional<AnnotationMirror> getAnnotationMirror(
-      Element element, Class<? extends Annotation> annotationClass) {
-    return Optional.ofNullable(MoreElements.getAnnotationMirror(element, annotationClass).orNull());
-  }
-
-  private static Predicate<AnnotationMirror> hasAnnotationTypeIn(
-      Collection<? extends Class<? extends Annotation>> annotations) {
-    Set<String> annotationClassNames =
-        annotations.stream().map(Class::getCanonicalName).collect(toSet());
-    return annotation ->
-        annotationClassNames.contains(
-            MoreTypes.asTypeElement(annotation.getAnnotationType()).getQualifiedName().toString());
-  }
-
-  public static ImmutableSet<String> suppressedWarnings(Element element) {
-    SuppressWarnings suppressedWarnings = element.getAnnotation(SuppressWarnings.class);
-    if (suppressedWarnings == null) {
-      return ImmutableSet.of();
+      Element element, ClassName annotationName) {
+    String annotationClassName = annotationName.canonicalName();
+    for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
+      TypeElement annotationTypeElement =
+          MoreElements.asType(annotationMirror.getAnnotationType().asElement());
+      if (annotationTypeElement.getQualifiedName().contentEquals(annotationClassName)) {
+        return Optional.of(annotationMirror);
+      }
     }
-    return ImmutableSet.copyOf(suppressedWarnings.value());
+    return Optional.empty();
+  }
+
+  public static ImmutableSet<? extends AnnotationMirror> getAnnotatedAnnotations(
+      Element element, ClassName annotationName) {
+    return element.getAnnotationMirrors().stream()
+        .filter(input -> isAnnotationPresent(input.getAnnotationType().asElement(), annotationName))
+        .collect(toImmutableSet());
   }
 
   /**
@@ -275,6 +220,20 @@
    * href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3.3">JVM
    * specification, section 4.3.3</a>.
    */
+  // TODO(bcorso): Expose getMethodDescriptor() method in XProcessing instead.
+  public static String getMethodDescriptor(XMethodElement element) {
+    return getMethodDescriptor(toJavac(element));
+  }
+
+  /**
+   * 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());
   }
@@ -283,8 +242,8 @@
     return t.accept(JVM_DESCRIPTOR_TYPE_VISITOR, null);
   }
 
-  private static final AbstractTypeVisitor8<String, Void> JVM_DESCRIPTOR_TYPE_VISITOR =
-      new AbstractTypeVisitor8<String, Void>() {
+  private static final SimpleTypeVisitor8<String, Void> JVM_DESCRIPTOR_TYPE_VISITOR =
+      new SimpleTypeVisitor8<String, Void>() {
 
         @Override
         public String visitArray(ArrayType arrayType, Void v) {
@@ -326,11 +285,6 @@
         }
 
         @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:
@@ -361,12 +315,7 @@
         }
 
         @Override
-        public String visitUnion(UnionType unionType, Void v) {
-          return visitUnknown(unionType, null);
-        }
-
-        @Override
-        public String visitUnknown(TypeMirror typeMirror, Void v) {
+        public String defaultAction(TypeMirror typeMirror, Void v) {
           throw new IllegalArgumentException("Unsupported type: " + typeMirror);
         }
 
@@ -408,18 +357,6 @@
         }
       };
 
-  /**
-   * Invokes {@link Elements#getTypeElement(CharSequence)}, throwing {@link TypeNotPresentException}
-   * if it is not accessible in the current compilation.
-   */
-  public TypeElement checkTypePresent(String typeName) {
-    TypeElement type = elements.getTypeElement(typeName);
-    if (type == null) {
-      throw new TypeNotPresentException(typeName, null);
-    }
-    return type;
-  }
-
   @Override
   public PackageElement getPackageElement(CharSequence name) {
     return elements.getPackageElement(name);
@@ -491,8 +428,8 @@
   }
 
   @Override
-  public Name getName(CharSequence cs) {
-    return elements.getName(cs);
+  public Name getName(CharSequence cs) { // SUPPRESS_GET_NAME_CHECK: This is not xprocessing usage.
+    return elements.getName(cs); // SUPPRESS_GET_NAME_CHECK: This is not xprocessing usage.
   }
 
   @Override
diff --git a/java/dagger/internal/codegen/langmodel/DaggerTypes.java b/java/dagger/internal/codegen/langmodel/DaggerTypes.java
index fb291db..855e739 100644
--- a/java/dagger/internal/codegen/langmodel/DaggerTypes.java
+++ b/java/dagger/internal/codegen/langmodel/DaggerTypes.java
@@ -16,23 +16,28 @@
 
 package dagger.internal.codegen.langmodel;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+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.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
 
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.auto.common.MoreElements;
 import com.google.auto.common.MoreTypes;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.graph.Traverser;
 import com.google.common.util.concurrent.FluentFuture;
 import com.google.common.util.concurrent.ListenableFuture;
+import com.squareup.javapoet.ArrayTypeName;
 import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.TypeName;
 import java.util.List;
 import java.util.Optional;
 import java.util.function.Predicate;
-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.ArrayType;
 import javax.lang.model.type.DeclaredType;
@@ -43,7 +48,6 @@
 import javax.lang.model.type.PrimitiveType;
 import javax.lang.model.type.TypeKind;
 import javax.lang.model.type.TypeMirror;
-import javax.lang.model.type.TypeVariable;
 import javax.lang.model.type.WildcardType;
 import javax.lang.model.util.SimpleTypeVisitor8;
 import javax.lang.model.util.Types;
@@ -53,12 +57,94 @@
   private final Types types;
   private final DaggerElements elements;
 
-  @Inject
   public DaggerTypes(Types types, DaggerElements elements) {
     this.types = checkNotNull(types);
     this.elements = checkNotNull(elements);
   }
 
+  // Note: This is similar to auto-common's MoreTypes except using ClassName rather than Class.
+  // TODO(bcorso): Contribute a String version to auto-common's MoreTypes?
+  /**
+   * Returns true if the raw type underlying the given {@link TypeMirror} represents the same raw
+   * type as the given {@link Class} and throws an IllegalArgumentException if the {@link
+   * TypeMirror} does not represent a type that can be referenced by a {@link Class}
+   */
+  public static boolean isTypeOf(final TypeName typeName, TypeMirror type) {
+    checkNotNull(typeName);
+    return type.accept(new IsTypeOf(typeName), null);
+  }
+
+  private static final class IsTypeOf extends SimpleTypeVisitor8<Boolean, Void> {
+    private final TypeName typeName;
+
+    IsTypeOf(TypeName typeName) {
+      this.typeName = typeName;
+    }
+
+    @Override
+    protected Boolean defaultAction(TypeMirror type, Void ignored) {
+      throw new IllegalArgumentException(type + " cannot be represented as a Class<?>.");
+    }
+
+    @Override
+    public Boolean visitNoType(NoType noType, Void p) {
+      if (noType.getKind().equals(TypeKind.VOID)) {
+        return typeName.equals(TypeName.VOID);
+      }
+      throw new IllegalArgumentException(noType + " cannot be represented as a Class<?>.");
+    }
+
+    @Override
+    public Boolean visitError(ErrorType errorType, Void p) {
+      return false;
+    }
+
+    @Override
+    public Boolean visitPrimitive(PrimitiveType type, Void p) {
+      switch (type.getKind()) {
+        case BOOLEAN:
+          return typeName.equals(TypeName.BOOLEAN);
+        case BYTE:
+          return typeName.equals(TypeName.BYTE);
+        case CHAR:
+          return typeName.equals(TypeName.CHAR);
+        case DOUBLE:
+          return typeName.equals(TypeName.DOUBLE);
+        case FLOAT:
+          return typeName.equals(TypeName.FLOAT);
+        case INT:
+          return typeName.equals(TypeName.INT);
+        case LONG:
+          return typeName.equals(TypeName.LONG);
+        case SHORT:
+          return typeName.equals(TypeName.SHORT);
+        default:
+          throw new IllegalArgumentException(type + " cannot be represented as a Class<?>.");
+      }
+    }
+
+    @Override
+    public Boolean visitArray(ArrayType array, Void p) {
+      return (typeName instanceof ArrayTypeName)
+          && isTypeOf(((ArrayTypeName) typeName).componentType, array.getComponentType());
+    }
+
+    @Override
+    public Boolean visitDeclared(DeclaredType type, Void ignored) {
+      TypeElement typeElement = MoreElements.asType(type.asElement());
+      return (typeName instanceof ClassName)
+          && typeElement.getQualifiedName().contentEquals(((ClassName) typeName).canonicalName());
+    }
+  }
+
+  /**
+   * Returns the non-{@link Object} superclass of the type with the proper type parameters. An empty
+   * {@link Optional} is returned if there is no non-{@link Object} superclass.
+   */
+  public Optional<DeclaredType> nonObjectSuperclass(XType type) {
+    return isDeclared(type) ? nonObjectSuperclass(asDeclared(toJavac(type))) : Optional.empty();
+  }
+
   /**
    * Returns the non-{@link Object} superclass of the type with the proper type parameters. An empty
    * {@link Optional} is returned if there is no non-{@link Object} superclass.
@@ -83,22 +169,24 @@
    * @throws IllegalArgumentException if {@code type} is not a declared type or has zero or more
    *     than one type arguments.
    */
-  public static TypeMirror unwrapType(TypeMirror type) {
-    TypeMirror unwrapped = unwrapTypeOrDefault(type, null);
+  public static XType unwrapType(XType type) {
+    XType unwrapped = unwrapTypeOrDefault(type, null);
     checkArgument(unwrapped != null, "%s is a raw type", type);
     return unwrapped;
   }
 
   /**
-   * Returns {@code type}'s single type argument, if one exists, or {@link Object} if not.
+   * Returns {@code type}'s single type argument.
    *
    * <p>For example, if {@code type} is {@code List<Number>} this will return {@code Number}.
    *
-   * @throws IllegalArgumentException if {@code type} is not a declared type or has more than one
-   *     type argument.
+   * @throws IllegalArgumentException if {@code type} is not a declared type or has zero or more
+   *     than one type arguments.
    */
-  public TypeMirror unwrapTypeOrObject(TypeMirror type) {
-    return unwrapTypeOrDefault(type, elements.getTypeElement(Object.class).asType());
+  public static TypeMirror unwrapType(TypeMirror type) {
+    TypeMirror unwrapped = unwrapTypeOrDefault(type, null);
+    checkArgument(unwrapped != null, "%s is a raw type", type);
+    return unwrapped;
   }
 
   private static TypeMirror unwrapTypeOrDefault(TypeMirror type, TypeMirror defaultType) {
@@ -111,14 +199,48 @@
     return getOnlyElement(declaredType.getTypeArguments(), defaultType);
   }
 
+  private static XType unwrapTypeOrDefault(XType type, XType defaultType) {
+    // Check the type parameters of the element's XType since the input XType could be raw.
+    checkArgument(isDeclared(type));
+    XTypeElement typeElement = type.getTypeElement();
+    checkArgument(
+        typeElement.getType().getTypeArguments().size() == 1,
+        "%s does not have exactly 1 type parameter. Found: %s",
+        typeElement.getQualifiedName(),
+        typeElement.getType().getTypeArguments());
+    return getOnlyElement(type.getTypeArguments(), defaultType);
+  }
+
+  /**
+   * Returns {@code type}'s single type argument, if one exists, or {@link Object} if not.
+   *
+   * <p>For example, if {@code type} is {@code List<Number>} this will return {@code Number}.
+   *
+   * @throws IllegalArgumentException if {@code type} is not a declared type or has more than one
+   *     type argument.
+   */
+  public TypeMirror unwrapTypeOrObject(TypeMirror type) {
+    return unwrapTypeOrDefault(type, elements.getTypeElement(TypeName.OBJECT).asType());
+  }
+
   /**
    * Returns {@code type} wrapped in {@code wrappingClass}.
    *
    * <p>For example, if {@code type} is {@code List<Number>} and {@code wrappingClass} is {@code
    * Set.class}, this will return {@code Set<List<Number>>}.
    */
-  public DeclaredType wrapType(TypeMirror type, Class<?> wrappingClass) {
-    return types.getDeclaredType(elements.getTypeElement(wrappingClass), type);
+  public DeclaredType wrapType(XType type, ClassName wrappingClassName) {
+    return wrapType(toJavac(type), wrappingClassName);
+  }
+
+  /**
+   * Returns {@code type} wrapped in {@code wrappingClass}.
+   *
+   * <p>For example, if {@code type} is {@code List<Number>} and {@code wrappingClass} is {@code
+   * Set.class}, this will return {@code Set<List<Number>>}.
+   */
+  public DeclaredType wrapType(TypeMirror type, ClassName wrappingClassName) {
+    return types.getDeclaredType(elements.getTypeElement(wrappingClassName.canonicalName()), type);
   }
 
   /**
@@ -132,9 +254,9 @@
    *
    * @throws IllegalArgumentException if {@code} has more than one type argument.
    */
-  public DeclaredType rewrapType(TypeMirror type, Class<?> wrappingClass) {
+  public DeclaredType rewrapType(TypeMirror type, ClassName wrappingClassName) {
     List<? extends TypeMirror> typeArguments = MoreTypes.asDeclared(type).getTypeArguments();
-    TypeElement wrappingType = elements.getTypeElement(wrappingClass);
+    TypeElement wrappingType = elements.getTypeElement(wrappingClassName.canonicalName());
     switch (typeArguments.size()) {
       case 0:
         return getDeclaredType(wrappingType);
@@ -146,17 +268,16 @@
   }
 
   /**
-   * Returns a publicly accessible type based on {@code type}:
+   * Returns an accessible type in {@code requestingClass}'s package based on {@code type}:
    *
    * <ul>
-   *   <li>If {@code type} is publicly accessible, returns it.
-   *   <li>If not, but {@code type}'s raw type is publicly accessible, returns the raw type.
+   *   <li>If {@code type} is accessible from the package, returns it.
+   *   <li>If not, but {@code type}'s raw type is accessible from the package, returns the raw type.
    *   <li>Otherwise returns {@link Object}.
    * </ul>
    */
-  public TypeMirror publiclyAccessibleType(TypeMirror type) {
-    return accessibleType(
-        type, Accessibility::isTypePubliclyAccessible, Accessibility::isRawTypePubliclyAccessible);
+  public TypeMirror accessibleType(XType type, ClassName requestingClass) {
+    return accessibleType(toJavac(type), requestingClass);
   }
 
   /**
@@ -185,7 +306,7 @@
         && rawTypeAccessibilityPredicate.test(type)) {
       return getDeclaredType(MoreTypes.asTypeElement(type));
     } else {
-      return elements.getTypeElement(Object.class).asType();
+      return elements.getTypeElement(TypeName.OBJECT).asType();
     }
   }
 
@@ -220,44 +341,14 @@
   private static final ImmutableSet<Class<?>> FUTURE_TYPES =
       ImmutableSet.of(ListenableFuture.class, FluentFuture.class);
 
+  public static boolean isFutureType(XType type) {
+    return isFutureType(toJavac(type));
+  }
+
   public static boolean isFutureType(TypeMirror type) {
     return FUTURE_TYPES.stream().anyMatch(t -> MoreTypes.isTypeOf(t, type));
   }
 
-  public static boolean hasTypeVariable(TypeMirror type) {
-    return type.accept(
-        new SimpleTypeVisitor8<Boolean, Void>() {
-          @Override
-          public Boolean visitArray(ArrayType arrayType, Void p) {
-            return arrayType.getComponentType().accept(this, p);
-          }
-
-          @Override
-          public Boolean visitDeclared(DeclaredType declaredType, Void p) {
-            return declaredType.getTypeArguments().stream().anyMatch(type -> type.accept(this, p));
-          }
-
-          @Override
-          public Boolean visitTypeVariable(TypeVariable t, Void aVoid) {
-            return true;
-          }
-
-          @Override
-          protected Boolean defaultAction(TypeMirror e, Void aVoid) {
-            return false;
-          }
-        },
-        null);
-  }
-
-  /**
-   * Resolves the type of the given executable element as a member of the given type. This may
-   * resolve type variables to concrete types, etc.
-   */
-  public ExecutableType resolveExecutableType(ExecutableElement element, TypeMirror containerType) {
-    return MoreTypes.asExecutable(asMemberOf(MoreTypes.asDeclared(containerType), element));
-  }
-
   // Implementation of Types methods, delegating to types.
 
   @Override
@@ -270,6 +361,10 @@
     return types.isSameType(t1, t2);
   }
 
+  public boolean isSubtype(XType t1, XType t2) {
+    return isSubtype(toJavac(t1), toJavac(t2));
+  }
+
   @Override
   public boolean isSubtype(TypeMirror t1, TypeMirror t2) {
     return types.isSubtype(t1, t2);
diff --git a/java/dagger/internal/codegen/validation/AnyBindingMethodValidator.java b/java/dagger/internal/codegen/validation/AnyBindingMethodValidator.java
index 140afd2..896ba15 100644
--- a/java/dagger/internal/codegen/validation/AnyBindingMethodValidator.java
+++ b/java/dagger/internal/codegen/validation/AnyBindingMethodValidator.java
@@ -16,33 +16,32 @@
 
 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 dagger.internal.codegen.xprocessing.XElements.getSimpleName;
+import static dagger.internal.codegen.xprocessing.XElements.hasAnyAnnotation;
 import static java.util.stream.Collectors.joining;
 
+import androidx.room.compiler.processing.XExecutableElement;
+import androidx.room.compiler.processing.XMethodElement;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
 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<>();
+  private final ImmutableMap<ClassName, BindingMethodValidator> validators;
+  private final Map<XMethodElement, ValidationReport> reports = new HashMap<>();
 
   @Inject
-  AnyBindingMethodValidator(
-      ImmutableMap<Class<? extends Annotation>, BindingMethodValidator> validators) {
+  AnyBindingMethodValidator(ImmutableMap<ClassName, BindingMethodValidator> validators) {
     this.validators = validators;
   }
 
@@ -52,7 +51,7 @@
   }
 
   /** Returns the binding method annotations considered by this validator. */
-  ImmutableSet<Class<? extends Annotation>> methodAnnotations() {
+  ImmutableSet<ClassName> methodAnnotations() {
     return validators.keySet();
   }
 
@@ -60,8 +59,8 @@
    * Returns {@code true} if {@code method} is annotated with at least one of {@link
    * #methodAnnotations()}.
    */
-  boolean isBindingMethod(ExecutableElement method) {
-    return isAnyAnnotationPresent(method, methodAnnotations());
+  boolean isBindingMethod(XExecutableElement method) {
+    return hasAnyAnnotation(method, methodAnnotations());
   }
 
   /**
@@ -77,25 +76,22 @@
    * @throws IllegalArgumentException if {@code method} is not annotated by any {@linkplain
    *     #methodAnnotations() binding method annotation}
    */
-  ValidationReport<ExecutableElement> validate(ExecutableElement method) {
+  ValidationReport validate(XMethodElement method) {
     return reentrantComputeIfAbsent(reports, method, this::validateUncached);
   }
 
   /**
-   * Returns {@code true} if {@code method} was already {@linkplain #validate(ExecutableElement)
+   * Returns {@code true} if {@code method} was already {@linkplain #validate(XMethodElement)
    * validated}.
    */
-  boolean wasAlreadyValidated(ExecutableElement method) {
+  boolean wasAlreadyValidated(XMethodElement 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());
+  private ValidationReport validateUncached(XMethodElement method) {
+    ValidationReport.Builder report = ValidationReport.about(method);
+    ImmutableSet<ClassName> bindingMethodAnnotations =
+        methodAnnotations().stream().filter(method::hasAnnotation).collect(toImmutableSet());
     switch (bindingMethodAnnotations.size()) {
       case 0:
         throw new IllegalArgumentException(
@@ -110,8 +106,8 @@
         report.addError(
             String.format(
                 "%s is annotated with more than one of (%s)",
-                method.getSimpleName(),
-                methodAnnotations().stream().map(Class::getCanonicalName).collect(joining(", "))),
+                getSimpleName(method),
+                methodAnnotations().stream().map(ClassName::canonicalName).collect(joining(", "))),
             method);
         break;
     }
diff --git a/java/dagger/internal/codegen/validation/BUILD b/java/dagger/internal/codegen/validation/BUILD
index 602157b..d41215a 100644
--- a/java/dagger/internal/codegen/validation/BUILD
+++ b/java/dagger/internal/codegen/validation/BUILD
@@ -33,18 +33,19 @@
         "//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/internal/codegen/xprocessing",
         "//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",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:value",
+        "//third_party/java/checker_framework_annotations",
+        "//third_party/java/error_prone:annotations",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/cache",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/graph",
+        "//third_party/java/guava/util/concurrent",
+        "//third_party/java/javapoet",
+        "//third_party/java/jsr330_inject",
+        "@maven//:org_jetbrains_kotlin_kotlin_stdlib_jdk8",
     ],
 )
diff --git a/java/dagger/internal/codegen/validation/BindingElementValidator.java b/java/dagger/internal/codegen/validation/BindingElementValidator.java
index b8f9912..d4b405e 100644
--- a/java/dagger/internal/codegen/validation/BindingElementValidator.java
+++ b/java/dagger/internal/codegen/validation/BindingElementValidator.java
@@ -16,77 +16,73 @@
 
 package dagger.internal.codegen.validation;
 
-import static com.google.auto.common.MoreTypes.asTypeElement;
+import static androidx.room.compiler.processing.XTypeKt.isArray;
+import static androidx.room.compiler.processing.XTypeKt.isVoid;
 import static com.google.common.base.Verify.verifyNotNull;
-import static dagger.internal.codegen.base.Scopes.scopesOf;
+import static com.google.common.collect.Iterables.getOnlyElement;
 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 static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
+import static dagger.internal.codegen.xprocessing.XTypes.isPrimitive;
+import static dagger.internal.codegen.xprocessing.XTypes.isTypeVariable;
 
-import com.google.common.collect.ImmutableCollection;
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.common.collect.ImmutableSet;
 import com.google.errorprone.annotations.FormatMethod;
-import dagger.MapKey;
-import dagger.Provides;
+import com.squareup.javapoet.ClassName;
 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 dagger.internal.codegen.javapoet.TypeNames;
+import dagger.internal.codegen.xprocessing.XElements;
+import dagger.spi.model.Key;
+import dagger.spi.model.Scope;
 import java.util.Formatter;
 import java.util.HashMap;
 import java.util.Map;
 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;
-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;
+public abstract class BindingElementValidator<E extends XElement> {
+  private static final ImmutableSet<ClassName> MULTIBINDING_ANNOTATIONS =
+      ImmutableSet.of(TypeNames.INTO_SET, TypeNames.ELEMENTS_INTO_SET, TypeNames.INTO_MAP);
+
+  // TODO(bcorso): Inject this directly into InjectionAnnotations instead of using field injection.
+  @Inject XProcessingEnv processingEnv;
+
   private final AllowsMultibindings allowsMultibindings;
   private final AllowsScoping allowsScoping;
-  private final Map<E, ValidationReport<E>> cache = new HashMap<>();
+  private final Map<E, ValidationReport> 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
-   */
+  /** Creates a validator object. */
+  // TODO(bcorso): Consider reworking BindingElementValidator and all subclasses to use composition
+  // rather than inheritance. The web of inheritance makes it difficult to track what implementation
+  // of a method is actually being used.
   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) {
+  final ValidationReport validate(E element) {
     return reentrantComputeIfAbsent(cache, element, this::validateUncached);
   }
 
-  private ValidationReport<E> validateUncached(E element) {
+  private ValidationReport validateUncached(E element) {
     return elementValidator(element).validate();
   }
 
@@ -118,7 +114,7 @@
 
   /**
    * The error message when a the type for a binding element with {@link
-   * ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES} is a not set type.
+   * dagger.multibindings.ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES} is a not set type.
    */
   protected String elementsIntoSetNotASetMessage() {
     return bindingElements(
@@ -127,7 +123,7 @@
 
   /**
    * The error message when a the type for a binding element with {@link
-   * ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES} is a raw set.
+   * dagger.multibindings.ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES} is a raw set.
    */
   protected String elementsIntoSetRawSetMessage() {
     return bindingElements(
@@ -139,9 +135,9 @@
 
   /** Validator for a single binding element. */
   protected abstract class ElementValidator {
-    protected final E element;
-    protected final ValidationReport.Builder<E> report;
-    private final ImmutableCollection<? extends AnnotationMirror> qualifiers;
+    private final E element;
+    protected final ValidationReport.Builder report;
+    private final ImmutableSet<XAnnotation> qualifiers;
 
     protected ElementValidator(E element) {
       this.element = element;
@@ -150,7 +146,7 @@
     }
 
     /** Checks the element for validity. */
-    private ValidationReport<E> validate() {
+    private ValidationReport validate() {
       checkType();
       checkQualifiers();
       checkMapKeys();
@@ -169,8 +165,8 @@
      * 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();
+    // TODO(dpb): should this be an ImmutableList<XType>, with this class checking the size?
+    protected abstract Optional<XType> bindingElementType();
 
     /**
      * Adds an error if the {@link #bindingElementType() binding element type} is not appropriate.
@@ -180,8 +176,8 @@
      * <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}
+     * <p>If the element has {@link dagger.multibindings.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)) {
@@ -199,7 +195,7 @@
 
         case SET:
         case MAP:
-          bindingElementType().ifPresent(type -> checkKeyType(type));
+          bindingElementType().ifPresent(this::checkKeyType);
           break;
 
         case SET_VALUES:
@@ -210,14 +206,13 @@
     /**
      * 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)) {
+    protected void checkKeyType(XType keyType) {
+      if (isVoid(keyType)) {
         report.addError(bindingElements("must %s a value (not void)", bindingElementTypeVerb()));
-      } else if (!(kind.isPrimitive()
-          || kind.equals(DECLARED)
-          || kind.equals(ARRAY)
-          || kind.equals(TYPEVAR))) {
+      } else if (!(isPrimitive(keyType)
+          || isDeclared(keyType)
+          || isArray(keyType)
+          || isTypeVariable(keyType))) {
         report.addError(badTypeMessage());
       }
     }
@@ -225,9 +220,9 @@
     /** Adds errors for unqualified assisted types. */
     private void checkAssistedType() {
       if (qualifiers.isEmpty()
-              && bindingElementType().isPresent()
-              && bindingElementType().get().getKind() == DECLARED) {
-        TypeElement keyElement = asTypeElement(bindingElementType().get());
+          && bindingElementType().isPresent()
+          && isDeclared(bindingElementType().get())) {
+        XTypeElement keyElement = bindingElementType().get().getTypeElement();
         if (isAssistedInjectionType(keyElement)) {
           report.addError("Dagger does not support providing @AssistedInject types.", keyElement);
         }
@@ -238,16 +233,17 @@
     }
 
     /**
-     * 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}.
+     * Adds an error if the type for an element with {@link
+     * dagger.multibindings.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));
+      bindingElementType().ifPresent(this::checkSetValuesType);
     }
 
     /** Adds an error if {@code type} is not a {@code Set<T>} for a reasonable {@code T}. */
-    protected final void checkSetValuesType(TypeMirror type) {
+    protected final void checkSetValuesType(XType type) {
       if (!SetType.isSet(type)) {
         report.addError(elementsIntoSetNotASetMessage());
       } else {
@@ -255,7 +251,10 @@
         if (setType.isRawType()) {
           report.addError(elementsIntoSetRawSetMessage());
         } else {
-          checkKeyType(setType.elementType());
+          // TODO(bcorso): Use setType.elementType() once setType is fully converted to XProcessing.
+          // However, currently SetType returns TypeMirror instead of XType and we have no
+          // conversion from TypeMirror to XType, so we just get the type ourselves.
+          checkKeyType(getOnlyElement(type.getTypeArguments()));
         }
       }
     }
@@ -265,7 +264,7 @@
      */
     private void checkQualifiers() {
       if (qualifiers.size() > 1) {
-        for (AnnotationMirror qualifier : qualifiers) {
+        for (XAnnotation qualifier : qualifiers) {
           report.addError(
               bindingElements("may not use more than one @Qualifier"),
               element,
@@ -275,14 +274,15 @@
     }
 
     /**
-     * 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.
+     * Adds an error if an {@link dagger.multibindings.IntoMap @IntoMap} element doesn't have
+     * exactly one {@link dagger.MapKey @MapKey} annotation, or if an element that is {@link
+     * dagger.multibindings.IntoMap @IntoMap} has any.
      */
     private void checkMapKeys() {
       if (!allowsMultibindings.allowsMultibindings()) {
         return;
       }
-      ImmutableSet<? extends AnnotationMirror> mapKeys = getMapKeys(element);
+      ImmutableSet<XAnnotation> mapKeys = getMapKeys(element);
       if (ContributionType.fromBindingElement(element).equals(ContributionType.MAP)) {
         switch (mapKeys.size()) {
           case 0:
@@ -306,17 +306,17 @@
      *   <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.
+     *   <li>the element has a multibinding annotation and its {@link dagger.Provides} or {@link
+     *       dagger.producers.Produces} annotation has a {@code type} parameter.
      * </ul>
      */
     private void checkMultibindings() {
-      ImmutableSet<AnnotationMirror> multibindingAnnotations =
-          MultibindingAnnotations.forElement(element);
+      ImmutableSet<XAnnotation> multibindingAnnotations =
+          XElements.getAllAnnotations(element, MULTIBINDING_ANNOTATIONS);
 
       switch (allowsMultibindings) {
         case NO_MULTIBINDINGS:
-          for (AnnotationMirror annotation : multibindingAnnotations) {
+          for (XAnnotation annotation : multibindingAnnotations) {
             report.addError(
                 bindingElements("cannot have multibinding annotations"),
                 element,
@@ -326,7 +326,7 @@
 
         case ALLOWS_MULTIBINDINGS:
           if (multibindingAnnotations.size() > 1) {
-            for (AnnotationMirror annotation : multibindingAnnotations) {
+            for (XAnnotation annotation : multibindingAnnotations) {
               report.addError(
                   bindingElements("cannot have more than one multibinding annotation"),
                   element,
@@ -335,20 +335,6 @@
           }
           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);
-        }
-      }
     }
 
     /**
@@ -356,7 +342,7 @@
      * one {@linkplain Scope scope} annotation.
      */
     private void checkScopes() {
-      ImmutableSet<Scope> scopes = scopesOf(element);
+      ImmutableSet<Scope> scopes = injectionAnnotations.getScopes(element);
       String error = null;
       switch (allowsScoping) {
         case ALLOWS_SCOPING:
@@ -371,7 +357,7 @@
       }
       verifyNotNull(error);
       for (Scope scope : scopes) {
-        report.addError(error, element, scope.scopeAnnotation());
+        report.addError(error, element, scope.scopeAnnotation().xprocessing());
       }
     }
 
diff --git a/java/dagger/internal/codegen/validation/BindingGraphValidator.java b/java/dagger/internal/codegen/validation/BindingGraphValidator.java
index 99e86e7..09477d8 100644
--- a/java/dagger/internal/codegen/validation/BindingGraphValidator.java
+++ b/java/dagger/internal/codegen/validation/BindingGraphValidator.java
@@ -16,41 +16,32 @@
 
 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 androidx.room.compiler.processing.XTypeElement;
 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 dagger.spi.model.BindingGraph;
 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 ValidationBindingGraphPlugins validationPlugins;
+  private final ExternalBindingGraphPlugins externalPlugins;
   private final CompilerOptions compilerOptions;
 
   @Inject
   BindingGraphValidator(
-      @Validation ImmutableSet<BindingGraphPlugin> validationPlugins,
-      ImmutableSet<BindingGraphPlugin> externalPlugins,
-      DiagnosticReporterFactory diagnosticReporterFactory,
+      ValidationBindingGraphPlugins validationPlugins,
+      ExternalBindingGraphPlugins externalPlugins,
       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) {
+  public boolean shouldDoFullBindingGraphValidation(XTypeElement component) {
     return requiresFullBindingGraphValidation()
         || compilerOptions.pluginsVisitFullBindingGraphs(component);
   }
@@ -61,47 +52,29 @@
 
   /** Returns {@code true} if no errors are reported for {@code graph}. */
   public boolean isValid(BindingGraph graph) {
-    return validate(graph) && visitPlugins(graph);
+    return visitValidationPlugins(graph) && visitExternalPlugins(graph);
   }
 
   /** Returns {@code true} if validation plugins report no errors. */
-  private boolean validate(BindingGraph graph) {
+  private boolean visitValidationPlugins(BindingGraph graph) {
     if (graph.isFullBindingGraph() && !requiresFullBindingGraphValidation()) {
       return true;
     }
 
-    boolean errorsAsWarnings =
-        graph.isFullBindingGraph()
-        && compilerOptions.fullBindingGraphValidationType().equals(ValidationType.WARNING);
-
-    return runPlugins(validationPlugins, graph, errorsAsWarnings);
+    return validationPlugins.visit(graph);
   }
 
   /** Returns {@code true} if external plugins report no errors. */
-  private boolean visitPlugins(BindingGraph graph) {
-    TypeElement component = graph.rootComponentNode().componentPath().currentComponent();
+  private boolean visitExternalPlugins(BindingGraph graph) {
     if (graph.isFullBindingGraph()
         // TODO(b/135938915): Consider not visiting plugins if only
         // fullBindingGraphValidation is enabled.
         && !requiresFullBindingGraphValidation()
-        && !compilerOptions.pluginsVisitFullBindingGraphs(component)) {
+        && !compilerOptions.pluginsVisitFullBindingGraphs(
+            graph.rootComponentNode().componentPath().currentComponent().xprocessing())) {
       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;
+    return externalPlugins.visit(graph);
   }
 }
diff --git a/java/dagger/internal/codegen/validation/BindingMethodProcessingStep.java b/java/dagger/internal/codegen/validation/BindingMethodProcessingStep.java
index 10aec06..03aff8a 100644
--- a/java/dagger/internal/codegen/validation/BindingMethodProcessingStep.java
+++ b/java/dagger/internal/codegen/validation/BindingMethodProcessingStep.java
@@ -18,37 +18,32 @@
 
 import static com.google.common.base.Preconditions.checkArgument;
 
-import com.google.auto.common.MoreElements;
+import androidx.room.compiler.processing.XMessager;
+import androidx.room.compiler.processing.XMethodElement;
 import com.google.common.collect.ImmutableSet;
-import java.lang.annotation.Annotation;
-import java.util.Set;
-import javax.annotation.processing.Messager;
+import com.squareup.javapoet.ClassName;
 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> {
+public final class BindingMethodProcessingStep extends TypeCheckingProcessingStep<XMethodElement> {
 
-  private final Messager messager;
+  private final XMessager messager;
   private final AnyBindingMethodValidator anyBindingMethodValidator;
 
   @Inject
   BindingMethodProcessingStep(
-      Messager messager, AnyBindingMethodValidator anyBindingMethodValidator) {
-    super(MoreElements::asExecutable);
+      XMessager messager, AnyBindingMethodValidator anyBindingMethodValidator) {
     this.messager = messager;
     this.anyBindingMethodValidator = anyBindingMethodValidator;
   }
 
   @Override
-  public Set<? extends Class<? extends Annotation>> annotations() {
+  public ImmutableSet<ClassName> annotationClassNames() {
     return anyBindingMethodValidator.methodAnnotations();
   }
 
   @Override
-  protected void process(
-      ExecutableElement method, ImmutableSet<Class<? extends Annotation>> annotations) {
+  protected void process(XMethodElement method, ImmutableSet<ClassName> annotations) {
     checkArgument(
         anyBindingMethodValidator.isBindingMethod(method),
         "%s is not annotated with any of %s",
diff --git a/java/dagger/internal/codegen/validation/BindingMethodValidator.java b/java/dagger/internal/codegen/validation/BindingMethodValidator.java
index 81349b9..1542b45 100644
--- a/java/dagger/internal/codegen/validation/BindingMethodValidator.java
+++ b/java/dagger/internal/codegen/validation/BindingMethodValidator.java
@@ -16,34 +16,31 @@
 
 package dagger.internal.codegen.validation;
 
-import static com.google.auto.common.MoreElements.asType;
-import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
+import static dagger.internal.codegen.xprocessing.XElements.hasAnyAnnotation;
+import static dagger.internal.codegen.xprocessing.XMethodElements.getEnclosingTypeElement;
+import static dagger.internal.codegen.xprocessing.XMethodElements.hasTypeParameters;
 import static java.util.stream.Collectors.joining;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-import static javax.lang.model.element.Modifier.PRIVATE;
 
+import androidx.room.compiler.processing.XExecutableElement;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
+import androidx.room.compiler.processing.XVariableElement;
 import com.google.common.collect.ImmutableSet;
 import com.google.errorprone.annotations.FormatMethod;
+import com.squareup.javapoet.ClassName;
 import dagger.internal.codegen.binding.InjectionAnnotations;
-import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
-import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.javapoet.TypeNames;
 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> {
+abstract class BindingMethodValidator extends BindingElementValidator<XMethodElement> {
 
-  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 ClassName methodAnnotation;
+  private final ImmutableSet<ClassName> enclosingElementAnnotations;
   private final Abstractness abstractness;
   private final ExceptionSuperclass exceptionSuperclass;
 
@@ -55,21 +52,17 @@
    *     with this annotation
    */
   protected BindingMethodValidator(
-      DaggerElements elements,
       DaggerTypes types,
-      KotlinMetadataUtil metadataUtil,
       DependencyRequestValidator dependencyRequestValidator,
-      Class<? extends Annotation> methodAnnotation,
-      Class<? extends Annotation> enclosingElementAnnotation,
+      ClassName methodAnnotation,
+      ClassName enclosingElementAnnotation,
       Abstractness abstractness,
       ExceptionSuperclass exceptionSuperclass,
       AllowsMultibindings allowsMultibindings,
       AllowsScoping allowsScoping,
       InjectionAnnotations injectionAnnotations) {
     this(
-        elements,
         types,
-        metadataUtil,
         methodAnnotation,
         ImmutableSet.of(enclosingElementAnnotation),
         dependencyRequestValidator,
@@ -88,21 +81,17 @@
    *     annotated with one of these annotations
    */
   protected BindingMethodValidator(
-      DaggerElements elements,
       DaggerTypes types,
-      KotlinMetadataUtil metadataUtil,
-      Class<? extends Annotation> methodAnnotation,
-      Iterable<? extends Class<? extends Annotation>> enclosingElementAnnotations,
+      ClassName methodAnnotation,
+      Iterable<ClassName> enclosingElementAnnotations,
       DependencyRequestValidator dependencyRequestValidator,
       Abstractness abstractness,
       ExceptionSuperclass exceptionSuperclass,
       AllowsMultibindings allowsMultibindings,
       AllowsScoping allowsScoping,
       InjectionAnnotations injectionAnnotations) {
-    super(methodAnnotation, allowsMultibindings, allowsScoping, injectionAnnotations);
-    this.elements = elements;
+    super(allowsMultibindings, allowsScoping, injectionAnnotations);
     this.types = types;
-    this.metadataUtil = metadataUtil;
     this.methodAnnotation = methodAnnotation;
     this.enclosingElementAnnotations = ImmutableSet.copyOf(enclosingElementAnnotations);
     this.dependencyRequestValidator = dependencyRequestValidator;
@@ -111,7 +100,7 @@
   }
 
   /** The annotation that identifies binding methods validated by this object. */
-  final Class<? extends Annotation> methodAnnotation() {
+  final ClassName methodAnnotation() {
     return methodAnnotation;
   }
 
@@ -127,7 +116,7 @@
 
   @Override
   protected final String bindingElements() {
-    return String.format("@%s methods", methodAnnotation.getSimpleName());
+    return String.format("@%s methods", methodAnnotation.simpleName());
   }
 
   @Override
@@ -137,13 +126,16 @@
 
   /** Abstract validator for individual binding method elements. */
   protected abstract class MethodValidator extends ElementValidator {
-    protected MethodValidator(ExecutableElement element) {
-      super(element);
+    private final XMethodElement method;
+
+    protected MethodValidator(XMethodElement method) {
+      super(method);
+      this.method = method;
     }
 
     @Override
-    protected final Optional<TypeMirror> bindingElementType() {
-      return Optional.of(element.getReturnType());
+    protected final Optional<XType> bindingElementType() {
+      return Optional.of(method.getReturnType());
     }
 
     @Override
@@ -165,38 +157,38 @@
      * {@link #enclosingElementAnnotations}.
      */
     private void checkEnclosingElement() {
-      TypeElement enclosingElement = asType(element.getEnclosingElement());
-      if (metadataUtil.isCompanionObjectClass(enclosingElement)) {
+      XTypeElement enclosingTypeElement = getEnclosingTypeElement(method);
+      if (enclosingTypeElement.isCompanionObject()) {
         // Binding method is in companion object, use companion object's enclosing class instead.
-        enclosingElement = asType(enclosingElement.getEnclosingElement());
+        enclosingTypeElement = enclosingTypeElement.getEnclosingTypeElement();
       }
-      if (!isAnyAnnotationPresent(enclosingElement, enclosingElementAnnotations)) {
+      if (!hasAnyAnnotation(enclosingTypeElement, enclosingElementAnnotations)) {
         report.addError(
             bindingMethods(
                 "can only be present within a @%s",
                 enclosingElementAnnotations.stream()
-                    .map(Class::getSimpleName)
+                    .map(ClassName::simpleName)
                     .collect(joining(" or @"))));
       }
     }
 
     /** Adds an error if the method is generic. */
     private void checkTypeParameters() {
-      if (!element.getTypeParameters().isEmpty()) {
+      if (hasTypeParameters(method)) {
         report.addError(bindingMethods("may not have type parameters"));
       }
     }
 
     /** Adds an error if the method is private. */
     private void checkNotPrivate() {
-      if (element.getModifiers().contains(PRIVATE)) {
+      if (method.isPrivate()) {
         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);
+      boolean isAbstract = method.isAbstract();
       switch (abstractness) {
         case MUST_BE_ABSTRACT:
           if (!isAbstract) {
@@ -216,12 +208,12 @@
      * subtype of {@link Exception}.
      */
     private void checkThrows() {
-      exceptionSuperclass.checkThrows(BindingMethodValidator.this, element, report);
+      exceptionSuperclass.checkThrows(BindingMethodValidator.this, method, report);
     }
 
     /** Adds errors for the method parameters. */
     protected void checkParameters() {
-      for (VariableElement parameter : element.getParameters()) {
+      for (XVariableElement parameter : method.getParameters()) {
         checkParameter(parameter);
       }
     }
@@ -230,8 +222,8 @@
      * 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());
+    protected void checkParameter(XVariableElement parameter) {
+      dependencyRequestValidator.validateDependencyRequest(report, parameter, parameter.getType());
     }
   }
 
@@ -256,8 +248,8 @@
       @Override
       protected void checkThrows(
           BindingMethodValidator validator,
-          ExecutableElement element,
-          ValidationReport.Builder<ExecutableElement> report) {
+          XExecutableElement element,
+          ValidationReport.Builder report) {
         if (!element.getThrownTypes().isEmpty()) {
           report.addError(validator.bindingMethods("may not throw"));
           return;
@@ -266,7 +258,7 @@
     },
 
     /** Methods may throw checked or unchecked exceptions or errors. */
-    EXCEPTION(Exception.class) {
+    EXCEPTION(TypeNames.EXCEPTION) {
       @Override
       protected String errorMessage(BindingMethodValidator validator) {
         return validator.bindingMethods(
@@ -275,7 +267,7 @@
     },
 
     /** Methods may throw unchecked exceptions or errors. */
-    RUNTIME_EXCEPTION(RuntimeException.class) {
+    RUNTIME_EXCEPTION(TypeNames.RUNTIME_EXCEPTION) {
       @Override
       protected String errorMessage(BindingMethodValidator validator) {
         return validator.bindingMethods("may only throw unchecked exceptions");
@@ -283,13 +275,14 @@
     },
     ;
 
-    private final Class<? extends Exception> superclass;
+    @SuppressWarnings("Immutable")
+    private final ClassName superclass;
 
     ExceptionSuperclass() {
       this(null);
     }
 
-    ExceptionSuperclass(Class<? extends Exception> superclass) {
+    ExceptionSuperclass(ClassName superclass) {
       this.superclass = superclass;
     }
 
@@ -301,11 +294,11 @@
      */
     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()) {
+        XExecutableElement element,
+        ValidationReport.Builder report) {
+      XType exceptionSupertype = validator.processingEnv.findType(superclass);
+      XType errorType = validator.processingEnv.findType(TypeNames.ERROR);
+      for (XType thrownType : element.getThrownTypes()) {
         if (!validator.types.isSubtype(thrownType, exceptionSupertype)
             && !validator.types.isSubtype(thrownType, errorType)) {
           report.addError(errorMessage(validator));
diff --git a/java/dagger/internal/codegen/validation/BindingMethodValidatorsModule.java b/java/dagger/internal/codegen/validation/BindingMethodValidatorsModule.java
index 08afbc8..3b196a6 100644
--- a/java/dagger/internal/codegen/validation/BindingMethodValidatorsModule.java
+++ b/java/dagger/internal/codegen/validation/BindingMethodValidatorsModule.java
@@ -19,11 +19,11 @@
 import static com.google.common.collect.Maps.uniqueIndex;
 
 import com.google.common.collect.ImmutableMap;
+import com.squareup.javapoet.ClassName;
 import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
 import dagger.multibindings.IntoSet;
-import java.lang.annotation.Annotation;
 import java.util.Set;
 
 /**
@@ -33,7 +33,7 @@
 @Module
 public interface BindingMethodValidatorsModule {
   @Provides
-  static ImmutableMap<Class<? extends Annotation>, BindingMethodValidator> indexValidators(
+  static ImmutableMap<ClassName, BindingMethodValidator> indexValidators(
       Set<BindingMethodValidator> validators) {
     return uniqueIndex(validators, BindingMethodValidator::methodAnnotation);
   }
diff --git a/java/dagger/internal/codegen/validation/BindsInstanceElementValidator.java b/java/dagger/internal/codegen/validation/BindsInstanceElementValidator.java
index 283af7d..c22f8db 100644
--- a/java/dagger/internal/codegen/validation/BindsInstanceElementValidator.java
+++ b/java/dagger/internal/codegen/validation/BindsInstanceElementValidator.java
@@ -16,14 +16,13 @@
 
 package dagger.internal.codegen.validation;
 
-import dagger.BindsInstance;
+import androidx.room.compiler.processing.XElement;
 import dagger.internal.codegen.binding.InjectionAnnotations;
-import javax.lang.model.element.Element;
 
-abstract class BindsInstanceElementValidator<E extends Element> extends BindingElementValidator<E> {
+abstract class BindsInstanceElementValidator<E extends XElement>
+    extends BindingElementValidator<E> {
   BindsInstanceElementValidator(InjectionAnnotations injectionAnnotations) {
     super(
-        BindsInstance.class,
         AllowsMultibindings.NO_MULTIBINDINGS,
         AllowsScoping.NO_SCOPING,
         injectionAnnotations);
diff --git a/java/dagger/internal/codegen/validation/BindsInstanceMethodValidator.java b/java/dagger/internal/codegen/validation/BindsInstanceMethodValidator.java
index 6d144bd..234a553 100644
--- a/java/dagger/internal/codegen/validation/BindsInstanceMethodValidator.java
+++ b/java/dagger/internal/codegen/validation/BindsInstanceMethodValidator.java
@@ -19,48 +19,56 @@
 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 static dagger.internal.codegen.xprocessing.XMethodElements.getEnclosingTypeElement;
 
-import com.google.auto.common.MoreElements;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
+import androidx.room.compiler.processing.XVariableElement;
+import dagger.internal.codegen.base.DaggerSuperficialValidation;
 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> {
+final class BindsInstanceMethodValidator extends BindsInstanceElementValidator<XMethodElement> {
+  private final DaggerSuperficialValidation superficialValidation;
+
   @Inject
-  BindsInstanceMethodValidator(InjectionAnnotations injectionAnnotations) {
+  BindsInstanceMethodValidator(
+      InjectionAnnotations injectionAnnotations,
+      DaggerSuperficialValidation superficialValidation) {
     super(injectionAnnotations);
+    this.superficialValidation = superficialValidation;
   }
 
   @Override
-  protected ElementValidator elementValidator(ExecutableElement element) {
-    return new Validator(element);
+  protected ElementValidator elementValidator(XMethodElement method) {
+    return new Validator(method);
   }
 
   private class Validator extends ElementValidator {
-    Validator(ExecutableElement element) {
-      super(element);
+    private final XMethodElement method;
+
+    Validator(XMethodElement method) {
+      super(method);
+      this.method = method;
     }
 
     @Override
     protected void checkAdditionalProperties() {
-      if (!element.getModifiers().contains(ABSTRACT)) {
+      if (!method.isAbstract()) {
         report.addError("@BindsInstance methods must be abstract");
       }
-      if (element.getParameters().size() != 1) {
+      if (method.getParameters().size() != 1) {
         report.addError(
             "@BindsInstance methods should have exactly one parameter for the bound type");
       }
-      TypeElement enclosingType = MoreElements.asType(element.getEnclosingElement());
-      moduleAnnotation(enclosingType)
+      XTypeElement enclosingTypeElement = getEnclosingTypeElement(method);
+      moduleAnnotation(enclosingTypeElement, superficialValidation)
           .ifPresent(moduleAnnotation -> report.addError(didYouMeanBinds(moduleAnnotation)));
-      anyComponentAnnotation(enclosingType)
+      anyComponentAnnotation(enclosingTypeElement, superficialValidation)
           .ifPresent(
               componentAnnotation ->
                   report.addError(
@@ -71,11 +79,10 @@
     }
 
     @Override
-    protected Optional<TypeMirror> bindingElementType() {
-      List<? extends VariableElement> parameters =
-          MoreElements.asExecutable(element).getParameters();
+    protected Optional<XType> bindingElementType() {
+      List<? extends XVariableElement> parameters = method.getParameters();
       return parameters.size() == 1
-          ? Optional.of(getOnlyElement(parameters).asType())
+          ? Optional.of(getOnlyElement(parameters).getType())
           : Optional.empty();
     }
   }
@@ -83,6 +90,6 @@
   private static String didYouMeanBinds(ModuleAnnotation moduleAnnotation) {
     return String.format(
         "@BindsInstance methods should not be included in @%ss. Did you mean @Binds?",
-        moduleAnnotation.annotationName());
+        moduleAnnotation.simpleName());
   }
 }
diff --git a/java/dagger/internal/codegen/validation/BindsInstanceParameterValidator.java b/java/dagger/internal/codegen/validation/BindsInstanceParameterValidator.java
index 24d65a9..b071aa7 100644
--- a/java/dagger/internal/codegen/validation/BindsInstanceParameterValidator.java
+++ b/java/dagger/internal/codegen/validation/BindsInstanceParameterValidator.java
@@ -16,53 +16,46 @@
 
 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 static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
+import static dagger.internal.codegen.xprocessing.XTypes.isTypeVariable;
 
-import com.google.auto.common.MoreElements;
+import androidx.room.compiler.processing.XExecutableParameterElement;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XType;
 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> {
+final class BindsInstanceParameterValidator
+    extends BindsInstanceElementValidator<XExecutableParameterElement> {
   @Inject
   BindsInstanceParameterValidator(InjectionAnnotations injectionAnnotations) {
     super(injectionAnnotations);
   }
 
   @Override
-  protected ElementValidator elementValidator(VariableElement element) {
-    return new Validator(element);
+  protected ElementValidator elementValidator(XExecutableParameterElement parameter) {
+    return new Validator(parameter);
   }
 
   private class Validator extends ElementValidator {
-    Validator(VariableElement element) {
-      super(element);
+    private final XExecutableParameterElement parameter;
+
+    Validator(XExecutableParameterElement parameter) {
+      super(parameter);
+      this.parameter = parameter;
     }
 
     @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)) {
+      if (!parameter.getEnclosingMethodElement().isAbstract()) {
         report.addError("@BindsInstance parameters may only be used in abstract methods");
       }
 
-      TypeKind returnKind = method.getReturnType().getKind();
-      if (!(returnKind.equals(DECLARED) || returnKind.equals(TYPEVAR))) {
+      // The above check should rule out constructors since constructors cannot be abstract, so we
+      // know the XExecutableElement enclosing the parameter has to be an XMethodElement.
+      XMethodElement method = (XMethodElement) parameter.getEnclosingMethodElement();
+      if (!(isDeclared(method.getReturnType()) || isTypeVariable(method.getReturnType()))) {
         report.addError(
             "@BindsInstance parameters may not be used in methods with a void, array or primitive "
                 + "return type");
@@ -70,8 +63,8 @@
     }
 
     @Override
-    protected Optional<TypeMirror> bindingElementType() {
-      return Optional.of(element.asType());
+    protected Optional<XType> bindingElementType() {
+      return Optional.of(parameter.getType());
     }
   }
 }
diff --git a/java/dagger/internal/codegen/validation/BindsInstanceProcessingStep.java b/java/dagger/internal/codegen/validation/BindsInstanceProcessingStep.java
index 0e79b91..b4132df 100644
--- a/java/dagger/internal/codegen/validation/BindsInstanceProcessingStep.java
+++ b/java/dagger/internal/codegen/validation/BindsInstanceProcessingStep.java
@@ -16,51 +16,50 @@
 
 package dagger.internal.codegen.validation;
 
-import com.google.auto.common.MoreElements;
+import static androidx.room.compiler.processing.XElementKt.isMethod;
+import static androidx.room.compiler.processing.XElementKt.isMethodParameter;
+
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XExecutableParameterElement;
+import androidx.room.compiler.processing.XMessager;
+import androidx.room.compiler.processing.XMethodElement;
 import com.google.common.collect.ImmutableSet;
-import dagger.BindsInstance;
-import java.lang.annotation.Annotation;
-import java.util.Set;
-import javax.annotation.processing.Messager;
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.javapoet.TypeNames;
 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> {
+public final class BindsInstanceProcessingStep extends TypeCheckingProcessingStep<XElement> {
   private final BindsInstanceMethodValidator methodValidator;
   private final BindsInstanceParameterValidator parameterValidator;
-  private final Messager messager;
+  private final XMessager messager;
 
   @Inject
   BindsInstanceProcessingStep(
       BindsInstanceMethodValidator methodValidator,
       BindsInstanceParameterValidator parameterValidator,
-      Messager messager) {
-    super(element -> element);
+      XMessager messager) {
     this.methodValidator = methodValidator;
     this.parameterValidator = parameterValidator;
     this.messager = messager;
   }
 
   @Override
-  public Set<? extends Class<? extends Annotation>> annotations() {
-    return ImmutableSet.of(BindsInstance.class);
+  public ImmutableSet<ClassName> annotationClassNames() {
+    return ImmutableSet.of(TypeNames.BINDS_INSTANCE);
   }
 
   @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);
+  protected void process(XElement element, ImmutableSet<ClassName> annotations) {
+    if (isMethod(element)) {
+      methodValidator.validate((XMethodElement) element).printMessagesTo(messager);
+    } else if (isMethodParameter(element)) {
+      parameterValidator.validate((XExecutableParameterElement) element).printMessagesTo(messager);
+    } else {
+      throw new AssertionError(element);
     }
   }
 }
diff --git a/java/dagger/internal/codegen/validation/BindsMethodValidator.java b/java/dagger/internal/codegen/validation/BindsMethodValidator.java
index 6d2dd18..20f9eb5 100644
--- a/java/dagger/internal/codegen/validation/BindsMethodValidator.java
+++ b/java/dagger/internal/codegen/validation/BindsMethodValidator.java
@@ -20,67 +20,63 @@
 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 static dagger.internal.codegen.xprocessing.XTypes.isPrimitive;
 
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XVariableElement;
 import com.google.common.collect.ImmutableSet;
-import dagger.Binds;
-import dagger.Module;
 import dagger.internal.codegen.base.ContributionType;
+import dagger.internal.codegen.base.DaggerSuperficialValidation;
 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.javapoet.TypeNames;
 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. */
+/** A validator for {@link dagger.Binds} methods. */
 final class BindsMethodValidator extends BindingMethodValidator {
-  private final DaggerTypes types;
   private final BindsTypeChecker bindsTypeChecker;
+  private final DaggerSuperficialValidation superficialValidation;
 
   @Inject
   BindsMethodValidator(
-      DaggerElements elements,
       DaggerTypes types,
-      KotlinMetadataUtil kotlinMetadataUtil,
       BindsTypeChecker bindsTypeChecker,
       DependencyRequestValidator dependencyRequestValidator,
+      DaggerSuperficialValidation superficialValidation,
       InjectionAnnotations injectionAnnotations) {
     super(
-        elements,
         types,
-        kotlinMetadataUtil,
-        Binds.class,
-        ImmutableSet.of(Module.class, ProducerModule.class),
+        TypeNames.BINDS,
+        ImmutableSet.of(TypeNames.MODULE, TypeNames.PRODUCER_MODULE),
         dependencyRequestValidator,
         MUST_BE_ABSTRACT,
         NO_EXCEPTIONS,
         ALLOWS_MULTIBINDINGS,
         ALLOWS_SCOPING,
         injectionAnnotations);
-    this.types = types;
     this.bindsTypeChecker = bindsTypeChecker;
+    this.superficialValidation = superficialValidation;
   }
 
   @Override
-  protected ElementValidator elementValidator(ExecutableElement element) {
-    return new Validator(element);
+  protected ElementValidator elementValidator(XMethodElement method) {
+    return new Validator(method);
   }
 
   private class Validator extends MethodValidator {
-    Validator(ExecutableElement element) {
-      super(element);
+    private final XMethodElement method;
+
+    Validator(XMethodElement method) {
+      super(method);
+      this.method = method;
     }
 
     @Override
     protected void checkParameters() {
-      if (element.getParameters().size() != 1) {
+      if (method.getParameters().size() != 1) {
         report.addError(
             bindingMethods(
                 "must have exactly one parameter, whose type is assignable to the return type"));
@@ -90,24 +86,24 @@
     }
 
     @Override
-    protected void checkParameter(VariableElement parameter) {
+    protected void checkParameter(XVariableElement 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)) {
+      XType returnType = boxIfNecessary(method.getReturnType());
+      XType parameterType = parameter.getType();
+      ContributionType contributionType = ContributionType.fromBindingElement(method);
+      if (contributionType.equals(ContributionType.SET_VALUES) && !SetType.isSet(returnType)) {
         report.addError(
             "@Binds @ElementsIntoSet methods must return a Set and take a Set parameter");
       }
 
-      if (!bindsTypeChecker.isAssignable(rightHandSide, leftHandSide, contributionType)) {
+      if (!bindsTypeChecker.isAssignable(parameterType, returnType, 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);
+        superficialValidation.validateTypeHierarchyOf("return type", method, returnType);
+        superficialValidation.validateTypeHierarchyOf("parameter", parameter, parameterType);
         // 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>)
@@ -115,11 +111,8 @@
       }
     }
 
-    private TypeMirror boxIfNecessary(TypeMirror maybePrimitive) {
-      if (maybePrimitive.getKind().isPrimitive()) {
-        return types.boxedClass(MoreTypes.asPrimitiveType(maybePrimitive)).asType();
-      }
-      return maybePrimitive;
+    private XType boxIfNecessary(XType maybePrimitive) {
+      return isPrimitive(maybePrimitive) ? maybePrimitive.boxed() : maybePrimitive;
     }
   }
 }
diff --git a/java/dagger/internal/codegen/validation/BindsOptionalOfMethodValidator.java b/java/dagger/internal/codegen/validation/BindsOptionalOfMethodValidator.java
index cb776e1..fff851f 100644
--- a/java/dagger/internal/codegen/validation/BindsOptionalOfMethodValidator.java
+++ b/java/dagger/internal/codegen/validation/BindsOptionalOfMethodValidator.java
@@ -16,7 +16,6 @@
 
 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;
@@ -24,64 +23,55 @@
 import static dagger.internal.codegen.validation.BindingMethodValidator.Abstractness.MUST_BE_ABSTRACT;
 import static dagger.internal.codegen.validation.BindingMethodValidator.ExceptionSuperclass.NO_EXCEPTIONS;
 
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XType;
 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.javapoet.TypeNames;
 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. */
+/** A validator for {@link dagger.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),
+        TypeNames.BINDS_OPTIONAL_OF,
+        ImmutableSet.of(TypeNames.MODULE, TypeNames.PRODUCER_MODULE),
         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);
+  protected ElementValidator elementValidator(XMethodElement method) {
+    return new Validator(method);
   }
 
   private class Validator extends MethodValidator {
-    Validator(ExecutableElement element) {
-      super(element);
+    private final XMethodElement method;
+
+    Validator(XMethodElement method) {
+      super(method);
+      this.method = method;
     }
 
     @Override
-    protected void checkKeyType(TypeMirror keyType) {
+    protected void checkKeyType(XType keyType) {
       super.checkKeyType(keyType);
       if (isValidImplicitProvisionKey(
-              injectionAnnotations.getQualifiers(element).stream().findFirst(), keyType, types)
-          && !injectedConstructors(asTypeElement(keyType)).isEmpty()) {
+              injectionAnnotations.getQualifiers(method).stream().findFirst(), keyType)
+          && !injectedConstructors(keyType.getTypeElement()).isEmpty()) {
         report.addError(
             "@BindsOptionalOf methods cannot return unqualified types that have an @Inject-"
                 + "annotated constructor because those are always present");
@@ -90,7 +80,7 @@
 
     @Override
     protected void checkParameters() {
-      if (!element.getParameters().isEmpty()) {
+      if (!method.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
index a6d9a3f..f1d0438 100644
--- a/java/dagger/internal/codegen/validation/ComponentCreatorValidator.java
+++ b/java/dagger/internal/codegen/validation/ComponentCreatorValidator.java
@@ -16,53 +16,50 @@
 
 package dagger.internal.codegen.validation;
 
-import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static androidx.room.compiler.processing.XTypeKt.isVoid;
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.base.ComponentCreatorAnnotation.getCreatorAnnotations;
 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 static dagger.internal.codegen.xprocessing.XMethodElements.hasTypeParameters;
+import static dagger.internal.codegen.xprocessing.XTypeElements.getAllUnimplementedMethods;
+import static dagger.internal.codegen.xprocessing.XTypeElements.hasTypeParameters;
+import static dagger.internal.codegen.xprocessing.XTypes.isPrimitive;
+import static javax.lang.model.SourceVersion.isKeyword;
 
-import com.google.auto.common.MoreElements;
+import androidx.room.compiler.processing.XConstructorElement;
+import androidx.room.compiler.processing.XExecutableParameterElement;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 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.base.ComponentCreatorAnnotation;
 import dagger.internal.codegen.binding.ErrorMessages;
 import dagger.internal.codegen.binding.ErrorMessages.ComponentCreatorMessages;
-import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
 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 Map<XTypeElement, ValidationReport> reports = new HashMap<>();
   private final DaggerTypes types;
-  private final Map<TypeElement, ValidationReport<TypeElement>> reports = new HashMap<>();
+  private final KotlinMetadataUtil metadataUtil;
 
   @Inject
-  ComponentCreatorValidator(DaggerElements elements, DaggerTypes types) {
-    this.elements = elements;
+  ComponentCreatorValidator(DaggerTypes types, KotlinMetadataUtil metadataUtil) {
     this.types = types;
+    this.metadataUtil = metadataUtil;
   }
 
   @Override
@@ -71,12 +68,12 @@
   }
 
   /** Validates that the given {@code type} is potentially a valid component creator type. */
-  public ValidationReport<TypeElement> validate(TypeElement type) {
+  public ValidationReport validate(XTypeElement type) {
     return reentrantComputeIfAbsent(reports, type, this::validateUncached);
   }
 
-  private ValidationReport<TypeElement> validateUncached(TypeElement type) {
-    ValidationReport.Builder<TypeElement> report = ValidationReport.about(type);
+  private ValidationReport validateUncached(XTypeElement type) {
+    ValidationReport.Builder report = ValidationReport.about(type);
 
     ImmutableSet<ComponentCreatorAnnotation> creatorAnnotations = getCreatorAnnotations(type);
     if (!validateOnlyOneCreatorAnnotation(creatorAnnotations, report)) {
@@ -93,7 +90,7 @@
 
   private boolean validateOnlyOneCreatorAnnotation(
       ImmutableSet<ComponentCreatorAnnotation> creatorAnnotations,
-      ValidationReport.Builder<?> report) {
+      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) {
@@ -109,30 +106,29 @@
   }
 
   /**
-   * Validator for a single {@link TypeElement} that is annotated with a {@code Builder} or {@code
+   * Validator for a single {@link XTypeElement} 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 XTypeElement creator;
+    private final ValidationReport.Builder report;
     private final ComponentCreatorAnnotation annotation;
     private final ComponentCreatorMessages messages;
 
     private ElementValidator(
-        TypeElement type,
-        ValidationReport.Builder<TypeElement> report,
+        XTypeElement creator,
+        ValidationReport.Builder report,
         ComponentCreatorAnnotation annotation) {
-      this.type = type;
-      this.component = type.getEnclosingElement();
+      this.creator = creator;
       this.report = report;
       this.annotation = annotation;
       this.messages = ErrorMessages.creatorMessagesFor(annotation);
     }
 
     /** Validates the creator type. */
-    final ValidationReport<TypeElement> validate() {
-      if (!isAnnotationPresent(component, annotation.componentAnnotation())) {
+    final ValidationReport validate() {
+      XTypeElement enclosingType = creator.getEnclosingTypeElement();
+      if (enclosingType == null || !enclosingType.hasAnnotation(annotation.componentAnnotation())) {
         report.addError(messages.mustBeInComponent());
       }
 
@@ -156,29 +152,26 @@
 
     /** 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());
+      if (creator.isClass()) {
+        validateConstructor();
+        return true;
       }
+      if (creator.isInterface()) {
+        return true;
+      }
+      report.addError(messages.mustBeClassOrInterface());
       return false;
     }
 
     private void validateConstructor() {
-      List<? extends Element> allElements = type.getEnclosedElements();
-      List<ExecutableElement> constructors = ElementFilter.constructorsIn(allElements);
+      List<XConstructorElement> constructors = creator.getConstructors();
 
       boolean valid = true;
       if (constructors.size() != 1) {
         valid = false;
       } else {
-        ExecutableElement constructor = getOnlyElement(constructors);
-        valid =
-            constructor.getParameters().isEmpty() && !constructor.getModifiers().contains(PRIVATE);
+        XConstructorElement constructor = getOnlyElement(constructors);
+        valid = constructor.getParameters().isEmpty() && !constructor.isPrivate();
       }
 
       if (!valid) {
@@ -188,26 +181,26 @@
 
     /** Validates basic requirements about the type that are common to both creator kinds. */
     private void validateTypeRequirements() {
-      if (!type.getTypeParameters().isEmpty()) {
+      if (hasTypeParameters(creator)) {
         report.addError(messages.generics());
       }
 
-      Set<Modifier> modifiers = type.getModifiers();
-      if (modifiers.contains(PRIVATE)) {
+      if (creator.isPrivate()) {
         report.addError(messages.isPrivate());
       }
-      if (!modifiers.contains(STATIC)) {
+      if (!creator.isStatic()) {
         report.addError(messages.mustBeStatic());
       }
       // Note: Must be abstract, so no need to check for final.
-      if (!modifiers.contains(ABSTRACT)) {
+      if (!creator.isAbstract()) {
         report.addError(messages.mustBeAbstract());
       }
     }
 
     private void validateBuilder() {
-      ExecutableElement buildMethod = null;
-      for (ExecutableElement method : elements.getUnimplementedMethods(type)) {
+      validateClassMethodName();
+      XMethodElement buildMethod = null;
+      for (XMethodElement method : getAllUnimplementedMethods(creator)) {
         switch (method.getParameters().size()) {
           case 0: // If this is potentially a build() method, validate it returns the correct type.
             if (validateFactoryMethodReturnType(method)) {
@@ -244,9 +237,24 @@
       }
     }
 
-    private void validateSetterMethod(ExecutableElement method) {
-      TypeMirror returnType = types.resolveExecutableType(method, type.asType()).getReturnType();
-      if (returnType.getKind() != TypeKind.VOID && !types.isSubtype(type.asType(), returnType)) {
+    private void validateClassMethodName() {
+      // Only Kotlin class can have method name the same as a Java reserved keyword, so only check
+      // the method name if this class is a Kotlin class.
+      if (metadataUtil.hasMetadata(toJavac(creator))) {
+        metadataUtil
+            .getAllMethodNamesBySignature(toJavac(creator))
+            .forEach(
+                (signature, name) -> {
+                  if (isKeyword(name)) {
+                    report.addError("Can not use a Java keyword as method name: " + signature);
+                  }
+                });
+      }
+    }
+
+    private void validateSetterMethod(XMethodElement method) {
+      XType returnType = method.asMemberOf(creator.getType()).getReturnType();
+      if (!isVoid(returnType) && !types.isSubtype(creator.getType(), returnType)) {
         error(
             method,
             messages.setterMethodsMustReturnVoidOrBuilder(),
@@ -255,10 +263,10 @@
 
       validateNotGeneric(method);
 
-      VariableElement parameter = method.getParameters().get(0);
+      XExecutableParameterElement parameter = method.getParameters().get(0);
 
-      boolean methodIsBindsInstance = isAnnotationPresent(method, BindsInstance.class);
-      boolean parameterIsBindsInstance = isAnnotationPresent(parameter, BindsInstance.class);
+      boolean methodIsBindsInstance = method.hasAnnotation(TypeNames.BINDS_INSTANCE);
+      boolean parameterIsBindsInstance = parameter.hasAnnotation(TypeNames.BINDS_INSTANCE);
       boolean bindsInstance = methodIsBindsInstance || parameterIsBindsInstance;
 
       if (methodIsBindsInstance && parameterIsBindsInstance) {
@@ -268,7 +276,7 @@
             messages.inheritedBindsInstanceNotAllowedOnBothSetterMethodAndParameter());
       }
 
-      if (!bindsInstance && parameter.asType().getKind().isPrimitive()) {
+      if (!bindsInstance && isPrimitive(parameter.getType())) {
         error(
             method,
             messages.nonBindsInstanceParametersMayNotBePrimitives(),
@@ -277,8 +285,7 @@
     }
 
     private void validateFactory() {
-      ImmutableList<ExecutableElement> abstractMethods =
-          elements.getUnimplementedMethods(type).asList();
+      ImmutableList<XMethodElement> abstractMethods = getAllUnimplementedMethods(creator);
       switch (abstractMethods.size()) {
         case 0:
           report.addError(messages.missingFactoryMethod());
@@ -298,7 +305,7 @@
     }
 
     /** Validates that the given {@code method} is a valid component factory method. */
-    private void validateFactoryMethod(ExecutableElement method) {
+    private void validateFactoryMethod(XMethodElement method) {
       validateNotGeneric(method);
 
       if (!validateFactoryMethodReturnType(method)) {
@@ -307,9 +314,9 @@
         return;
       }
 
-      for (VariableElement parameter : method.getParameters()) {
-        if (!isAnnotationPresent(parameter, BindsInstance.class)
-            && parameter.asType().getKind().isPrimitive()) {
+      for (XExecutableParameterElement parameter : method.getParameters()) {
+        if (!parameter.hasAnnotation(TypeNames.BINDS_INSTANCE)
+            && isPrimitive(parameter.getType())) {
           error(
               method,
               messages.nonBindsInstanceParametersMayNotBePrimitives(),
@@ -322,10 +329,10 @@
      * 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)) {
+    private boolean validateFactoryMethodReturnType(XMethodElement method) {
+      XTypeElement component = creator.getEnclosingTypeElement();
+      XType returnType = method.asMemberOf(creator.getType()).getReturnType();
+      if (!types.isSubtype(component.getType(), returnType)) {
         error(
             method,
             messages.factoryMethodMustReturnComponentType(),
@@ -333,7 +340,7 @@
         return false;
       }
 
-      if (isAnnotationPresent(method, BindsInstance.class)) {
+      if (method.hasAnnotation(TypeNames.BINDS_INSTANCE)) {
         error(
             method,
             messages.factoryMethodMayNotBeAnnotatedWithBindsInstance(),
@@ -341,14 +348,16 @@
         return false;
       }
 
-      TypeElement componentType = MoreElements.asType(component);
-      if (!types.isSameType(componentType.asType(), returnType)) {
-        ImmutableSet<ExecutableElement> methodsOnlyInComponent =
-            methodsOnlyInComponent(componentType);
-        if (!methodsOnlyInComponent.isEmpty()) {
+      if (!returnType.isSameType(component.getType())) {
+        // 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.
+        ImmutableSet<XMethodElement> declaredMethods =
+            ImmutableSet.copyOf(component.getDeclaredMethods());
+        if (!declaredMethods.isEmpty()) {
           report.addWarning(
               messages.factoryMethodReturnsSupertypeWithMissingMethods(
-                  componentType, type, returnType, method, methodsOnlyInComponent),
+                  component, creator, returnType, method, declaredMethods),
               method);
         }
       }
@@ -374,11 +383,8 @@
      * 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)) {
+        XMethodElement method, String enclosedError, String inheritedError, Object... extraArgs) {
+      if (method.getEnclosingElement().equals(creator)) {
         report.addError(String.format(enclosedError, extraArgs), method);
       } else {
         report.addError(String.format(inheritedError, ObjectArrays.concat(extraArgs, method)));
@@ -386,23 +392,13 @@
     }
 
     /** Validates that the given {@code method} is not generic. * */
-    private void validateNotGeneric(ExecutableElement method) {
-      if (!method.getTypeParameters().isEmpty()) {
+    private void validateNotGeneric(XMethodElement method) {
+      if (hasTypeParameters(method)) {
         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
index 28b1c2b..dc7eac6 100644
--- a/java/dagger/internal/codegen/validation/ComponentDescriptorValidator.java
+++ b/java/dagger/internal/codegen/validation/ComponentDescriptorValidator.java
@@ -16,7 +16,9 @@
 
 package dagger.internal.codegen.validation;
 
-import static com.google.auto.common.MoreTypes.asDeclared;
+import static androidx.room.compiler.processing.XElementKt.isMethod;
+import static androidx.room.compiler.processing.XElementKt.isMethodParameter;
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Predicates.in;
 import static com.google.common.collect.Collections2.transform;
@@ -24,22 +26,30 @@
 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 dagger.internal.codegen.xprocessing.XElements.asMethod;
+import static dagger.internal.codegen.xprocessing.XElements.asMethodParameter;
+import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
 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 androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XExecutableParameterElement;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XMethodType;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 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 com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.base.DaggerSuperficialValidation;
 import dagger.internal.codegen.binding.ComponentCreatorDescriptor;
 import dagger.internal.codegen.binding.ComponentDescriptor;
 import dagger.internal.codegen.binding.ComponentRequirement;
@@ -47,14 +57,13 @@
 import dagger.internal.codegen.binding.ContributionBinding;
 import dagger.internal.codegen.binding.ErrorMessages;
 import dagger.internal.codegen.binding.ErrorMessages.ComponentCreatorMessages;
+import dagger.internal.codegen.binding.InjectionAnnotations;
 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 dagger.internal.codegen.javapoet.TypeNames;
+import dagger.spi.model.Scope;
 import java.util.ArrayDeque;
 import java.util.Collection;
 import java.util.Deque;
@@ -65,14 +74,6 @@
 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;
 
 /**
@@ -88,30 +89,27 @@
 // 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;
+  private final InjectionAnnotations injectionAnnotations;
+  private final DaggerSuperficialValidation superficialValidation;
 
   @Inject
   ComponentDescriptorValidator(
-      DaggerElements elements,
-      DaggerTypes types,
       CompilerOptions compilerOptions,
       MethodSignatureFormatter methodSignatureFormatter,
       ComponentHierarchyValidator componentHierarchyValidator,
-      KotlinMetadataUtil metadataUtil) {
-    this.elements = elements;
-    this.types = types;
+      InjectionAnnotations injectionAnnotations,
+      DaggerSuperficialValidation superficialValidation) {
     this.compilerOptions = compilerOptions;
     this.methodSignatureFormatter = methodSignatureFormatter;
     this.componentHierarchyValidator = componentHierarchyValidator;
-    this.metadataUtil = metadataUtil;
+    this.injectionAnnotations = injectionAnnotations;
+    this.superficialValidation = superficialValidation;
   }
 
-  public ValidationReport<TypeElement> validate(ComponentDescriptor component) {
+  public ValidationReport validate(ComponentDescriptor component) {
     ComponentValidation validation = new ComponentValidation(component);
     validation.visitComponent(component);
     validation.report(component).addSubreport(componentHierarchyValidator.validate(component));
@@ -120,23 +118,21 @@
 
   private final class ComponentValidation {
     final ComponentDescriptor rootComponent;
-    final Map<ComponentDescriptor, ValidationReport.Builder<TypeElement>> reports =
-        new LinkedHashMap<>();
+    final Map<ComponentDescriptor, ValidationReport.Builder> 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());
+    ValidationReport buildReport() {
+      ValidationReport.Builder 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) {
+    private ValidationReport.Builder report(ComponentDescriptor component) {
       return reentrantComputeIfAbsent(
           reports, component, descriptor -> ValidationReport.about(descriptor.typeElement()));
     }
@@ -166,7 +162,9 @@
 
     /** Recursive method to validate that component dependencies do not form a cycle. */
     private void validateComponentDependencyHierarchy(
-        ComponentDescriptor component, TypeElement dependency, Deque<TypeElement> dependencyStack) {
+        ComponentDescriptor component,
+        XTypeElement dependency,
+        Deque<XTypeElement> dependencyStack) {
       if (dependencyStack.contains(dependency)) {
         // Current component has already appeared in the component chain.
         StringBuilder message = new StringBuilder();
@@ -183,16 +181,14 @@
           // Always validate direct component dependencies referenced by this component regardless
           // of the flag value
           || dependencyStack.isEmpty()) {
-        rootComponentAnnotation(dependency)
+        rootComponentAnnotation(dependency, superficialValidation)
             .ifPresent(
                 componentAnnotation -> {
                   dependencyStack.push(dependency);
-
-                  for (TypeElement nextDependency : componentAnnotation.dependencies()) {
+                  for (XTypeElement nextDependency : componentAnnotation.dependencies()) {
                     validateComponentDependencyHierarchy(
                         component, nextDependency, dependencyStack);
                   }
-
                   dependencyStack.pop();
                 });
       }
@@ -203,19 +199,18 @@
      * singleton components have no scoped dependencies.
      */
     private void validateDependencyScopes(ComponentDescriptor component) {
-      ImmutableSet<Scope> scopes = component.scopes();
-      ImmutableSet<TypeElement> scopedDependencies =
+      ImmutableSet<ClassName> scopes =
+          component.scopes().stream().map(Scope::className).collect(toImmutableSet());
+      ImmutableSet<XTypeElement> scopedDependencies =
           scopedTypesIn(
-              component
-                  .dependencies()
-                  .stream()
+              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)) {
+            && (scopes.contains(TypeNames.SINGLETON)
+                || scopes.contains(TypeNames.SINGLETON_JAVAX))) {
           // Singleton is a special-case representing the longest lifetime, and therefore
           // @Singleton components may not depend on scoped components
           if (!scopedDependencies.isEmpty()) {
@@ -249,7 +244,7 @@
 
     private void validateModules(ComponentDescriptor component) {
       for (ModuleDescriptor module : component.modules()) {
-        if (module.moduleElement().getModifiers().contains(Modifier.ABSTRACT)) {
+        if (module.moduleElement().isAbstract()) {
           for (ContributionBinding binding : module.bindings()) {
             if (binding.requiresModuleInstance()) {
               report(component).addError(abstractModuleHasInstanceBindingMethodsError(module));
@@ -300,9 +295,9 @@
           Sets.difference(
               creatorModuleAndDependencyRequirements, componentModuleAndDependencyRequirements);
 
-      DeclaredType container = asDeclared(creator.typeElement().asType());
+      XType container = creator.typeElement().getType();
       if (!inapplicableRequirementsOnCreator.isEmpty()) {
-        Collection<Element> excessElements =
+        Collection<XElement> excessElements =
             Multimaps.filterKeys(
                     creator.unvalidatedRequirementElements(), in(inapplicableRequirementsOnCreator))
                 .values();
@@ -318,7 +313,7 @@
       Set<ComponentRequirement> mustBePassed =
           Sets.filter(
               componentModuleAndDependencyRequirements,
-              input -> input.nullPolicy(elements, metadataUtil).equals(NullPolicy.THROW));
+              input -> input.nullPolicy().equals(NullPolicy.THROW));
       // Component requirements that the creator must be able to set, but can't
       Set<ComponentRequirement> missingRequirements =
           Sets.difference(mustBePassed, creatorModuleAndDependencyRequirements);
@@ -333,19 +328,20 @@
       }
 
       // Validate that declared creator requirements (modules, dependencies) have unique types.
-      ImmutableSetMultimap<Wrapper<TypeMirror>, Element> declaredRequirementsByType =
+      ImmutableSetMultimap<TypeName, XElement> declaredRequirementsByType =
           Multimaps.filterKeys(
                   creator.unvalidatedRequirementElements(),
                   creatorModuleAndDependencyRequirements::contains)
-              .entries().stream()
+              .entries()
+              .stream()
               .collect(
-                  toImmutableSetMultimap(entry -> entry.getKey().wrappedType(), Entry::getValue));
+                  toImmutableSetMultimap(
+                      entry -> entry.getKey().type().getTypeName(), Entry::getValue));
       declaredRequirementsByType
           .asMap()
           .forEach(
-              (typeWrapper, elementsForType) -> {
+              (type, 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.
@@ -365,40 +361,39 @@
       // for subcomponents.
     }
 
-    private String formatElement(Element element, DeclaredType container) {
+    private String formatElement(XElement element, XType 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();
+      if (isMethod(element)) {
+        return methodSignatureFormatter.format(asMethod(element), Optional.of(container));
+      } else if (isMethodParameter(element)) {
+        return formatParameter(asMethodParameter(element), container);
       }
+      // This method shouldn't be called with any other type of element.
+      throw new AssertionError();
     }
 
-    private String formatParameter(VariableElement parameter, DeclaredType container) {
+    private String formatParameter(XExecutableParameterElement parameter, XType 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);
+      parameter.getAllAnnotations().stream()
+          .map(XAnnotation::getQualifiedName)
+          .forEach(joiner::add);
+      XType parameterType = resolveParameterType(parameter, container);
       return joiner
-          .add(stripCommonTypePrefixes(parameterType.toString()))
-          .add(parameter.getSimpleName())
+          .add(stripCommonTypePrefixes(parameterType.getTypeName().toString()))
+          .add(getSimpleName(parameter))
           .toString();
     }
 
-    private TypeMirror resolveParameterType(VariableElement parameter, DeclaredType container) {
-      ExecutableElement method =
-          MoreElements.asExecutable(parameter.getEnclosingElement());
+    private XType resolveParameterType(XExecutableParameterElement parameter, XType container) {
+      checkArgument(isMethod(parameter.getEnclosingMethodElement()));
+      XMethodElement method = asMethod(parameter.getEnclosingMethodElement());
       int parameterIndex = method.getParameters().indexOf(parameter);
 
-      ExecutableType methodType = MoreTypes.asExecutable(types.asMemberOf(container, method));
+      XMethodType methodType = method.asMemberOf(container);
       return methodType.getParameterTypes().get(parameterIndex);
     }
 
@@ -413,10 +408,10 @@
      */
     private void validateDependencyScopeHierarchy(
         ComponentDescriptor component,
-        TypeElement dependency,
+        XTypeElement dependency,
         Deque<ImmutableSet<Scope>> scopeStack,
-        Deque<TypeElement> scopedDependencyStack) {
-      ImmutableSet<Scope> scopes = scopesOf(dependency);
+        Deque<XTypeElement> scopedDependencyStack) {
+      ImmutableSet<Scope> scopes = injectionAnnotations.getScopes(dependency);
       if (stackOverlaps(scopeStack, scopes)) {
         scopedDependencyStack.push(dependency);
         // Current scope has already appeared in the component chain.
@@ -436,22 +431,19 @@
           // of the flag value
           || scopedDependencyStack.isEmpty()) {
         // TODO(beder): transitively check scopes of production components too.
-        rootComponentAnnotation(dependency)
+        rootComponentAnnotation(dependency, superficialValidation)
             .filter(componentAnnotation -> !componentAnnotation.isProduction())
             .ifPresent(
                 componentAnnotation -> {
-                  ImmutableSet<TypeElement> scopedDependencies =
+                  ImmutableSet<XTypeElement> scopedDependencies =
                       scopedTypesIn(componentAnnotation.dependencies());
                   if (!scopedDependencies.isEmpty()) {
                     // empty can be ignored (base-case)
                     scopeStack.push(scopes);
                     scopedDependencyStack.push(dependency);
-                    for (TypeElement scopedDependency : scopedDependencies) {
+                    for (XTypeElement scopedDependency : scopedDependencies) {
                       validateDependencyScopeHierarchy(
-                          component,
-                          scopedDependency,
-                          scopeStack,
-                          scopedDependencyStack);
+                          component, scopedDependency, scopeStack, scopedDependencyStack);
                     }
                     scopedDependencyStack.pop();
                     scopeStack.pop();
@@ -470,10 +462,10 @@
     }
 
     /** 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) {
+    private void appendIndentedComponentsList(StringBuilder message, Iterable<XTypeElement> types) {
+      for (XTypeElement scopedComponent : types) {
         message.append(INDENT);
-        for (Scope scope : scopesOf(scopedComponent)) {
+        for (Scope scope : injectionAnnotations.getScopes(scopedComponent)) {
           message.append(getReadableSource(scope)).append(' ');
         }
         message
@@ -486,8 +478,10 @@
      * 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());
+    private ImmutableSet<XTypeElement> scopedTypesIn(Collection<XTypeElement> types) {
+      return types.stream()
+          .filter(type -> !injectionAnnotations.getScopes(type).isEmpty())
+          .collect(toImmutableSet());
     }
   }
 }
diff --git a/java/dagger/internal/codegen/validation/ComponentHierarchyValidator.java b/java/dagger/internal/codegen/validation/ComponentHierarchyValidator.java
index 1861636..f67c9e3 100644
--- a/java/dagger/internal/codegen/validation/ComponentHierarchyValidator.java
+++ b/java/dagger/internal/codegen/validation/ComponentHierarchyValidator.java
@@ -21,13 +21,13 @@
 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 static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
 
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XExecutableParameterElement;
+import androidx.room.compiler.processing.XTypeElement;
 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;
@@ -37,32 +37,35 @@
 import com.google.common.collect.Multimaps;
 import com.google.common.collect.SetMultimap;
 import com.google.common.collect.Sets;
+import dagger.internal.codegen.base.ModuleKind;
 import dagger.internal.codegen.binding.ComponentDescriptor;
 import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.binding.InjectionAnnotations;
 import dagger.internal.codegen.binding.ModuleDescriptor;
-import dagger.internal.codegen.binding.ModuleKind;
 import dagger.internal.codegen.compileroption.CompilerOptions;
-import dagger.model.Scope;
+import dagger.spi.model.Scope;
 import java.util.Collection;
 import java.util.Formatter;
 import java.util.Map;
+import java.util.Optional;
 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;
+  private final InjectionAnnotations injectionAnnotations;
 
   @Inject
-  ComponentHierarchyValidator(CompilerOptions compilerOptions) {
+  ComponentHierarchyValidator(
+      CompilerOptions compilerOptions, InjectionAnnotations injectionAnnotations) {
     this.compilerOptions = compilerOptions;
+    this.injectionAnnotations = injectionAnnotations;
   }
 
-  ValidationReport<TypeElement> validate(ComponentDescriptor componentDescriptor) {
-    ValidationReport.Builder<TypeElement> report =
-        ValidationReport.about(componentDescriptor.typeElement());
+  ValidationReport validate(ComponentDescriptor componentDescriptor) {
+    ValidationReport.Builder report = ValidationReport.about(componentDescriptor.typeElement());
     validateSubcomponentMethods(
         report,
         componentDescriptor,
@@ -78,9 +81,9 @@
   }
 
   private void validateSubcomponentMethods(
-      ValidationReport.Builder<?> report,
+      ValidationReport.Builder report,
       ComponentDescriptor componentDescriptor,
-      ImmutableMap<TypeElement, TypeElement> existingModuleToOwners) {
+      ImmutableMap<XTypeElement, XTypeElement> existingModuleToOwners) {
     componentDescriptor
         .childComponentsDeclaredByFactoryMethods()
         .forEach(
@@ -97,7 +100,7 @@
               validateSubcomponentMethods(
                   report,
                   childComponent,
-                  new ImmutableMap.Builder<TypeElement, TypeElement>()
+                  new ImmutableMap.Builder<XTypeElement, XTypeElement>()
                       .putAll(existingModuleToOwners)
                       .putAll(
                           Maps.toMap(
@@ -109,21 +112,21 @@
   }
 
   private void validateFactoryMethodParameters(
-      ValidationReport.Builder<?> report,
+      ValidationReport.Builder report,
       ComponentMethodDescriptor subcomponentMethodDescriptor,
-      ImmutableMap<TypeElement, TypeElement> existingModuleToOwners) {
-    for (VariableElement factoryMethodParameter :
+      ImmutableMap<XTypeElement, XTypeElement> existingModuleToOwners) {
+    for (XExecutableParameterElement factoryMethodParameter :
         subcomponentMethodDescriptor.methodElement().getParameters()) {
-      TypeElement moduleType = MoreTypes.asTypeElement(factoryMethodParameter.asType());
-      TypeElement originatingComponent = existingModuleToOwners.get(moduleType);
-      if (originatingComponent != null) {
+      XTypeElement moduleType = factoryMethodParameter.getType().getTypeElement();
+      if (existingModuleToOwners.containsKey(moduleType)) {
         /* 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()),
+                getSimpleName(moduleType),
+                existingModuleToOwners.get(moduleType).getQualifiedName()),
             factoryMethodParameter);
       }
     }
@@ -133,7 +136,7 @@
    * Checks that components do not have any scopes that are also applied on any of their ancestors.
    */
   private void validateScopeHierarchy(
-      ValidationReport.Builder<TypeElement> report,
+      ValidationReport.Builder report,
       ComponentDescriptor subject,
       SetMultimap<ComponentDescriptor, Scope> scopesByComponent) {
     scopesByComponent.putAll(subject, subject.scopes());
@@ -172,7 +175,7 @@
   }
 
   private void validateProductionModuleUniqueness(
-      ValidationReport.Builder<TypeElement> report,
+      ValidationReport.Builder report,
       ComponentDescriptor componentDescriptor,
       SetMultimap<ComponentDescriptor, ModuleDescriptor> producerModulesByComponent) {
     ImmutableSet<ModuleDescriptor> producerModules =
@@ -209,7 +212,7 @@
   }
 
   private void validateRepeatedScopedDeclarations(
-      ValidationReport.Builder<TypeElement> report,
+      ValidationReport.Builder report,
       ComponentDescriptor component,
       // TODO(ronshapiro): optimize ModuleDescriptor.hashCode()/equals. Otherwise this could be
       // quite costly
@@ -264,10 +267,10 @@
   }
 
   private ImmutableSet<Scope> moduleScopes(ModuleDescriptor module) {
-    return FluentIterable.concat(module.allBindingDeclarations())
-        .transform(declaration -> uniqueScopeOf(declaration.bindingElement().get()))
+    return module.allBindingDeclarations().stream()
+        .map(declaration -> injectionAnnotations.getScope(declaration.bindingElement().get()))
         .filter(scope -> scope.isPresent() && !scope.get().isReusable())
-        .transform(scope -> scope.get())
-        .toSet();
+        .map(Optional::get)
+        .collect(toImmutableSet());
   }
 }
diff --git a/java/dagger/internal/codegen/validation/ComponentValidator.java b/java/dagger/internal/codegen/validation/ComponentValidator.java
index 72a44f2..9f2119a 100644
--- a/java/dagger/internal/codegen/validation/ComponentValidator.java
+++ b/java/dagger/internal/codegen/validation/ComponentValidator.java
@@ -16,38 +16,43 @@
 
 package dagger.internal.codegen.validation;
 
-import static com.google.auto.common.MoreElements.asType;
-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 androidx.room.compiler.processing.XTypeKt.isVoid;
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static androidx.room.compiler.processing.compat.XConverters.toXProcessing;
 import static com.google.common.base.Verify.verify;
+import static com.google.common.collect.Iterables.consumingIterable;
 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.ComponentAnnotation.subcomponentAnnotation;
+import static dagger.internal.codegen.base.ComponentCreatorAnnotation.creatorAnnotationsFor;
+import static dagger.internal.codegen.base.ComponentCreatorAnnotation.productionCreatorAnnotations;
+import static dagger.internal.codegen.base.ComponentCreatorAnnotation.subcomponentCreatorAnnotations;
+import static dagger.internal.codegen.base.ComponentKind.annotationsFor;
 import static dagger.internal.codegen.base.ModuleAnnotation.moduleAnnotation;
+import static dagger.internal.codegen.base.ModuleAnnotation.moduleAnnotations;
 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 dagger.internal.codegen.xprocessing.XElements.asMethod;
+import static dagger.internal.codegen.xprocessing.XElements.asTypeElement;
+import static dagger.internal.codegen.xprocessing.XElements.getAnyAnnotation;
+import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
+import static dagger.internal.codegen.xprocessing.XTypeElements.getAllUnimplementedMethods;
+import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
 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 androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XExecutableParameterElement;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XMethodType;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
@@ -55,76 +60,75 @@
 import com.google.common.collect.Maps;
 import com.google.common.collect.SetMultimap;
 import com.google.common.collect.Sets;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.TypeName;
 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.base.ComponentKind;
+import dagger.internal.codegen.base.DaggerSuperficialValidation;
+import dagger.internal.codegen.base.ModuleKind;
 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.javapoet.TypeNames;
+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.producers.CancellationPolicy;
-import dagger.producers.ProductionComponent;
-import java.lang.annotation.Annotation;
+import dagger.spi.model.DependencyRequest;
+import dagger.spi.model.Key;
+import java.util.ArrayDeque;
 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.Queue;
 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;
+import javax.lang.model.SourceVersion;
 
 /**
  * Performs superficial validation of the contract of the {@link Component} and {@link
- * ProductionComponent} annotations.
+ * dagger.producers.ProductionComponent} annotations.
  */
 @Singleton
 public final class ComponentValidator implements ClearableCache {
+  private final XProcessingEnv processingEnv;
   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<>();
+  private final DaggerSuperficialValidation superficialValidation;
+  private final Map<XTypeElement, ValidationReport> reports = new HashMap<>();
+  private final KotlinMetadataUtil metadataUtil;
 
   @Inject
   ComponentValidator(
+      XProcessingEnv processingEnv,
       DaggerElements elements,
-      DaggerTypes types,
       ModuleValidator moduleValidator,
       ComponentCreatorValidator creatorValidator,
       DependencyRequestValidator dependencyRequestValidator,
       MembersInjectionValidator membersInjectionValidator,
       MethodSignatureFormatter methodSignatureFormatter,
-      DependencyRequestFactory dependencyRequestFactory) {
+      DependencyRequestFactory dependencyRequestFactory,
+      DaggerSuperficialValidation superficialValidation,
+      KotlinMetadataUtil metadataUtil) {
+    this.processingEnv = processingEnv;
     this.elements = elements;
-    this.types = types;
     this.moduleValidator = moduleValidator;
     this.creatorValidator = creatorValidator;
     this.dependencyRequestValidator = dependencyRequestValidator;
     this.membersInjectionValidator = membersInjectionValidator;
     this.methodSignatureFormatter = methodSignatureFormatter;
     this.dependencyRequestFactory = dependencyRequestFactory;
+    this.superficialValidation = superficialValidation;
+    this.metadataUtil = metadataUtil;
   }
 
   @Override
@@ -133,24 +137,24 @@
   }
 
   /** Validates the given component. */
-  public ValidationReport<TypeElement> validate(TypeElement component) {
+  public ValidationReport validate(XTypeElement component) {
     return reentrantComputeIfAbsent(reports, component, this::validateUncached);
   }
 
-  private ValidationReport<TypeElement> validateUncached(TypeElement component) {
+  private ValidationReport validateUncached(XTypeElement component) {
     return new ElementValidator(component).validateElement();
   }
 
   private class ElementValidator {
-    private final TypeElement component;
-    private final ValidationReport.Builder<TypeElement> report;
+    private final XTypeElement component;
+    private final ValidationReport.Builder report;
     private final ImmutableSet<ComponentKind> componentKinds;
 
     // Populated by ComponentMethodValidators
-    private final SetMultimap<Element, ExecutableElement> referencedSubcomponents =
+    private final SetMultimap<XTypeElement, XMethodElement> referencedSubcomponents =
         LinkedHashMultimap.create();
 
-    ElementValidator(TypeElement component) {
+    ElementValidator(XTypeElement component) {
       this.component = component;
       this.report = ValidationReport.about(component);
       this.componentKinds = ComponentKind.getComponentKinds(component);
@@ -161,14 +165,10 @@
     }
 
     private ComponentAnnotation componentAnnotation() {
-      return anyComponentAnnotation(component).get();
+      return anyComponentAnnotation(component, superficialValidation).get();
     }
 
-    private DeclaredType componentType() {
-      return asDeclared(component.asType());
-    }
-
-    ValidationReport<TypeElement> validateElement() {
+    ValidationReport validateElement() {
       if (componentKinds.size() > 1) {
         return moreThanOneComponentAnnotation();
       }
@@ -187,7 +187,7 @@
       return report.build();
     }
 
-    private ValidationReport<TypeElement> moreThanOneComponentAnnotation() {
+    private ValidationReport moreThanOneComponentAnnotation() {
       String error =
           "Components may not be annotated with more than one component annotation: found "
               + annotationsFor(componentKinds);
@@ -196,8 +196,7 @@
     }
 
     private void validateUseOfCancellationPolicy() {
-      if (isAnnotationPresent(component, CancellationPolicy.class)
-          && !componentKind().isProducer()) {
+      if (component.hasAnnotation(TypeNames.CANCELLATION_POLICY) && !componentKind().isProducer()) {
         report.addError(
             "@CancellationPolicy may only be applied to production components and subcomponents",
             component);
@@ -205,23 +204,19 @@
     }
 
     private void validateIsAbstractType() {
-      if (!component.getKind().equals(INTERFACE)
-          && !(component.getKind().equals(CLASS) && component.getModifiers().contains(ABSTRACT))) {
+      if (!component.isInterface() && !(component.isClass() && component.isAbstract())) {
         report.addError(
             String.format(
                 "@%s may only be applied to an interface or abstract class",
-                componentKind().annotation().getSimpleName()),
+                componentKind().annotation().simpleName()),
             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))));
+      ImmutableSet<XTypeElement> creators =
+          enclosedAnnotatedTypes(component, creatorAnnotationsFor(componentAnnotation()));
+      creators.forEach(creator -> report.addSubreport(creatorValidator.validate(creator)));
       if (creators.size() > 1) {
         report.addError(
             String.format(
@@ -231,32 +226,44 @@
     }
 
     private void validateNoReusableAnnotation() {
-      Optional<AnnotationMirror> reusableAnnotation =
-          getAnnotationMirror(component, Reusable.class);
-      if (reusableAnnotation.isPresent()) {
+      if (component.hasAnnotation(TypeNames.REUSABLE)) {
         report.addError(
             "@Reusable cannot be applied to components or subcomponents",
             component,
-            reusableAnnotation.get());
+            component.getAnnotation(TypeNames.REUSABLE));
       }
     }
 
     private void validateComponentMethods() {
-      elements.getUnimplementedMethods(component).stream()
+      validateClassMethodName();
+      getAllUnimplementedMethods(component).stream()
           .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;
+    private void validateClassMethodName() {
+      if (metadataUtil.hasMetadata(toJavac(component))) {
+        metadataUtil
+            .getAllMethodNamesBySignature(toJavac(component))
+            .forEach(
+                (signature, name) -> {
+                  if (SourceVersion.isKeyword(name)) {
+                    report.addError("Can not use a Java keyword as method name: " + signature);
+                  }
+                });
+      }
+    }
 
-      ComponentMethodValidator(ExecutableElement method) {
+    private class ComponentMethodValidator {
+      private final XMethodElement method;
+      private final XMethodType resolvedMethod;
+      private final List<XType> parameterTypes;
+      private final List<XExecutableParameterElement> parameters;
+      private final XType returnType;
+
+      ComponentMethodValidator(XMethodElement method) {
         this.method = method;
-        this.resolvedMethod = asExecutable(types.asMemberOf(componentType(), method));
+        this.resolvedMethod = method.asMemberOf(component.getType());
         this.parameterTypes = resolvedMethod.getParameterTypes();
         this.parameters = method.getParameters();
         this.returnType = resolvedMethod.getReturnType();
@@ -268,7 +275,7 @@
         // 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();
+        Optional<ComponentAnnotation> subcomponentAnnotation = legalSubcomponentAnnotation();
         if (subcomponentAnnotation.isPresent()) {
           validateSubcomponentFactoryMethod(subcomponentAnnotation.get());
         } else if (subcomponentCreatorAnnotation().isPresent()) {
@@ -290,20 +297,25 @@
       }
 
       private void validateNoTypeVariables() {
-        if (!resolvedMethod.getTypeVariables().isEmpty()) {
+        if (!resolvedMethod.getTypeVariableNames().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<ComponentAnnotation> legalSubcomponentAnnotation() {
+        return Optional.ofNullable(returnType.getTypeElement())
+            .flatMap(element -> subcomponentAnnotation(element, superficialValidation))
+            // TODO(bcorso): Consider failing on illegal subcomponents rather than just filtering.
+            .filter(annotation -> legalSubcomponentAnnotations().contains(annotation.className()));
       }
 
-      private Optional<AnnotationMirror> subcomponentCreatorAnnotation() {
+      private ImmutableSet<ClassName> legalSubcomponentAnnotations() {
+        return componentKind().legalSubcomponentKinds().stream()
+            .map(ComponentKind::annotation)
+            .collect(toImmutableSet());
+      }
+
+      private Optional<XAnnotation> subcomponentCreatorAnnotation() {
         return checkForAnnotations(
             returnType,
             componentAnnotation().isProduction()
@@ -311,64 +323,46 @@
                 : subcomponentCreatorAnnotations());
       }
 
-      private void validateSubcomponentFactoryMethod(AnnotationMirror subcomponentAnnotation) {
-        referencedSubcomponents.put(MoreTypes.asElement(returnType), method);
+      private void validateSubcomponentFactoryMethod(ComponentAnnotation subcomponentAnnotation) {
+        referencedSubcomponents.put(returnType.getTypeElement(), method);
 
-        ComponentKind subcomponentKind =
-            ComponentKind.forAnnotatedElement(MoreTypes.asTypeElement(returnType)).get();
-        ImmutableSet<TypeElement> moduleTypes =
-            ComponentAnnotation.componentAnnotation(subcomponentAnnotation).modules();
+        ImmutableSet<ClassName> legalModuleAnnotations =
+            ComponentKind.forAnnotatedElement(returnType.getTypeElement())
+                .get()
+                .legalModuleKinds()
+                .stream()
+                .map(ModuleKind::annotation)
+                .collect(toImmutableSet());
+        ImmutableSet<XTypeElement> moduleTypes = 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);
+        ImmutableSet<XTypeElement> transitiveModules = getTransitiveModules(moduleTypes);
 
-        Set<TypeElement> variableTypes = Sets.newHashSet();
-
+        Set<XTypeElement> referencedModules = 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())) {
+          XExecutableParameterElement parameter = parameters.get(i);
+          XType parameterType = parameterTypes.get(i);
+          if (checkForAnnotations(parameterType, legalModuleAnnotations).isPresent()) {
+            XTypeElement module = parameterType.getTypeElement();
+            if (referencedModules.contains(module)) {
               report.addError(
                   String.format(
-                      "A module may only occur once an an argument in a Subcomponent factory "
+                      "A module may only occur once as an argument in a Subcomponent factory "
                           + "method, but %s was already passed.",
-                      moduleType.get().getQualifiedName()),
+                      module.getQualifiedName()),
                   parameter);
             }
-            if (!transitiveModules.contains(moduleType.get())) {
+            if (!transitiveModules.contains(module)) {
               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()),
+                      module.getQualifiedName(), returnType.getTypeElement().getQualifiedName()),
                   method);
             }
-            variableTypes.add(moduleType.get());
+            referencedModules.add(module);
           } else {
             report.addError(
                 String.format(
@@ -379,14 +373,52 @@
         }
       }
 
+      /**
+       * Returns the full set of modules transitively included from the given seed modules, which
+       * includes all transitive {@link Module#includes} and all transitive super classes. If a
+       * module is malformed and a type listed in {@link Module#includes} is not annotated with
+       * {@link Module}, it is ignored.
+       */
+      private ImmutableSet<XTypeElement> getTransitiveModules(
+          Collection<XTypeElement> seedModules) {
+        Set<XTypeElement> processedElements = Sets.newLinkedHashSet();
+        Queue<XTypeElement> moduleQueue = new ArrayDeque<>(seedModules);
+        ImmutableSet.Builder<XTypeElement> moduleElements = ImmutableSet.builder();
+        for (XTypeElement moduleElement : consumingIterable(moduleQueue)) {
+          if (processedElements.add(moduleElement)) {
+            moduleAnnotation(moduleElement, superficialValidation)
+                .ifPresent(
+                    moduleAnnotation -> {
+                      moduleElements.add(moduleElement);
+                      moduleQueue.addAll(moduleAnnotation.includes());
+                      moduleQueue.addAll(includesFromSuperclasses(moduleElement));
+                    });
+          }
+        }
+        return moduleElements.build();
+      }
+
+      /** Returns {@link Module#includes()} from all transitive super classes. */
+      private ImmutableSet<XTypeElement> includesFromSuperclasses(XTypeElement element) {
+        ImmutableSet.Builder<XTypeElement> builder = ImmutableSet.builder();
+        XType superclass = element.getSuperType();
+        while (superclass != null && !TypeName.OBJECT.equals(superclass.getTypeName())) {
+          element = superclass.getTypeElement();
+          moduleAnnotation(element, superficialValidation)
+              .ifPresent(moduleAnnotation -> builder.addAll(moduleAnnotation.includes()));
+          superclass = element.getSuperType();
+        }
+        return builder.build();
+      }
+
       private void validateSubcomponentCreatorMethod() {
-        referencedSubcomponents.put(MoreTypes.asElement(returnType).getEnclosingElement(), method);
+        referencedSubcomponents.put(returnType.getTypeElement().getEnclosingTypeElement(), method);
 
         if (!parameters.isEmpty()) {
           report.addError(builderMethodRequiresNoArgs(), method);
         }
 
-        TypeElement creatorElement = MoreTypes.asTypeElement(returnType);
+        XTypeElement creatorElement = returnType.getTypeElement();
         // 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.
@@ -398,10 +430,10 @@
       }
 
       private void validateMembersInjectionMethod() {
-        TypeMirror parameterType = getOnlyElement(parameterTypes);
+        XType parameterType = getOnlyElement(parameterTypes);
         report.addSubreport(
             membersInjectionValidator.validateMembersInjectionMethod(method, parameterType));
-        if (!(returnType.getKind().equals(VOID) || types.isSameType(returnType, parameterType))) {
+        if (!(isVoid(returnType) || returnType.isSameType(parameterType))) {
           report.addError(
               "Members injection methods may only return the injected type or void.", method);
         }
@@ -418,38 +450,40 @@
     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();
+      SetMultimap<String, XMethodElement> entryPoints = HashMultimap.create();
 
-      methodsIn(elements.getAllMembers(component)).stream()
-          .filter(
-              method ->
-                  isEntryPoint(method, asExecutable(types.asMemberOf(componentType(), method))))
+      // TODO(b/201729320): There's a bug in auto-common's MoreElements#overrides(), b/201729320,
+      // which prevents us from using XTypeElement#getAllMethods() here (since that method relies on
+      // MoreElements#overrides() under the hood).
+      //
+      // There's two options here.
+      //    1. Fix the bug in auto-common and update XProcessing's auto-common dependency
+      //    2. Add a new method in XProcessing which relies on Elements#overrides(), which does not
+      //       have this issue. However, this approach risks causing issues for EJC (Eclipse) users.
+      methodsIn(elements.getAllMembers(toJavac(component))).stream()
+          .map(method -> asMethod(toXProcessing(method, processingEnv)))
+          .filter(method -> isEntryPoint(method, method.asMemberOf(component.getType())))
           .forEach(
-              method ->
-                  addMethodUnlessOverridden(
-                      method, entryPointMethods.get(method.getSimpleName().toString())));
+              method -> addMethodUnlessOverridden(method, entryPoints.get(getSimpleName(method))));
 
-      for (Set<ExecutableElement> methods : asMap(entryPointMethods).values()) {
-        if (distinctKeys(methods).size() > 1) {
-          reportConflictingEntryPoints(methods);
-        }
-      }
+      asMap(entryPoints).values().stream()
+          .filter(methods -> distinctKeys(methods).size() > 1)
+          .forEach(this::reportConflictingEntryPoints);
     }
 
-    private void reportConflictingEntryPoints(Collection<ExecutableElement> methods) {
+    private void reportConflictingEntryPoints(Collection<XMethodElement> methods) {
       verify(
-          methods.stream().map(ExecutableElement::getEnclosingElement).distinct().count()
+          methods.stream().map(XMethodElement::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())
+          .typedFormatter(component.getType())
           .formatIndentedList(
               message,
               ImmutableList.sortedCopyOf(
-                  comparing(
-                      method -> asType(method.getEnclosingElement()).getQualifiedName().toString()),
+                  comparing(method -> method.getEnclosingElement().getClassName().canonicalName()),
                   methods),
               1);
       report.addError(message.toString());
@@ -465,8 +499,12 @@
     }
 
     private void validateComponentDependencies() {
-      for (TypeMirror type : componentAnnotation().dependencyTypes()) {
-        type.accept(CHECK_DEPENDENCY_TYPES, report);
+      for (XType type : componentAnnotation().dependencyTypes()) {
+        if (!isDeclared(type)) {
+          report.addError(type + " is not a valid component dependency type");
+        } else if (type.getTypeElement().hasAnyAnnotation(moduleAnnotations())) {
+          report.addError(type + " is a module, which cannot be a component dependency");
+        }
       }
     }
 
@@ -481,35 +519,34 @@
 
     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);
-      }
+      referencedSubcomponents
+          .keySet()
+          .forEach(subcomponent -> report.addSubreport(validate(subcomponent)));
     }
 
-    private ImmutableSet<Key> distinctKeys(Set<ExecutableElement> methods) {
+    private ImmutableSet<Key> distinctKeys(Set<XMethodElement> 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()
+    private DependencyRequest dependencyRequest(XMethodElement method) {
+      XMethodType methodType = method.asMemberOf(component.getType());
+      return componentKind().isProducer()
           ? dependencyRequestFactory.forComponentProductionMethod(method, methodType)
           : dependencyRequestFactory.forComponentProvisionMethod(method, methodType);
     }
   }
 
-  private static boolean isEntryPoint(ExecutableElement method, ExecutableType methodType) {
-    return method.getModifiers().contains(ABSTRACT)
+  private static boolean isEntryPoint(XMethodElement method, XMethodType methodType) {
+    return method.isAbstract()
         && method.getParameters().isEmpty()
-        && !methodType.getReturnType().getKind().equals(VOID)
-        && methodType.getTypeVariables().isEmpty();
+        && !isVoid(methodType.getReturnType())
+        && methodType.getTypeVariableNames().isEmpty();
   }
 
-  private void addMethodUnlessOverridden(ExecutableElement method, Set<ExecutableElement> methods) {
+  private void addMethodUnlessOverridden(XMethodElement method, Set<XMethodElement> methods) {
     if (methods.stream().noneMatch(existingMethod -> overridesAsDeclared(existingMethod, method))) {
       methods.removeIf(existingMethod -> overridesAsDeclared(method, existingMethod));
       methods.add(method);
@@ -521,36 +558,15 @@
    * 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 boolean overridesAsDeclared(XMethodElement overrider, XMethodElement overridden) {
+    return elements.overrides(
+        toJavac(overrider),
+        toJavac(overridden),
+        toJavac(asTypeElement(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);
+  private static Optional<XAnnotation> checkForAnnotations(XType type, Set<ClassName> annotations) {
+    return Optional.ofNullable(type.getTypeElement())
+        .flatMap(typeElement -> getAnyAnnotation(typeElement, annotations));
   }
 }
diff --git a/java/dagger/internal/codegen/validation/CompositeBindingGraphPlugin.java b/java/dagger/internal/codegen/validation/CompositeBindingGraphPlugin.java
index a9c650a..63bf32b 100644
--- a/java/dagger/internal/codegen/validation/CompositeBindingGraphPlugin.java
+++ b/java/dagger/internal/codegen/validation/CompositeBindingGraphPlugin.java
@@ -22,17 +22,17 @@
 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 static dagger.internal.codegen.langmodel.DaggerElements.transitivelyEncloses;
 
 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 dagger.spi.model.BindingGraph;
+import dagger.spi.model.BindingGraph.ChildFactoryMethodEdge;
+import dagger.spi.model.BindingGraph.ComponentNode;
+import dagger.spi.model.BindingGraph.DependencyEdge;
+import dagger.spi.model.BindingGraph.MaybeBinding;
+import dagger.spi.model.BindingGraphPlugin;
+import dagger.spi.model.DiagnosticReporter;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
@@ -216,16 +216,18 @@
         String message) {
       // TODO(erichang): This repeats some of the logic in DiagnosticReporterImpl. Remove when
       // merged.
-      if (elementEncloses(
-          graph.rootComponentNode().componentPath().currentComponent(),
-          childFactoryMethodEdge.factoryMethod())) {
+      if (transitivelyEncloses(
+          graph.rootComponentNode().componentPath().currentComponent().java(),
+          childFactoryMethodEdge.factoryMethod().java())) {
         // 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));
+                "[%s] %s",
+                elementToString(childFactoryMethodEdge.factoryMethod().java()),
+                message));
       }
     }
 
diff --git a/java/dagger/internal/codegen/validation/DependencyRequestValidator.java b/java/dagger/internal/codegen/validation/DependencyRequestValidator.java
index f280497..45b7763 100644
--- a/java/dagger/internal/codegen/validation/DependencyRequestValidator.java
+++ b/java/dagger/internal/codegen/validation/DependencyRequestValidator.java
@@ -16,55 +16,54 @@
 
 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 androidx.room.compiler.processing.XElementKt.isField;
+import static androidx.room.compiler.processing.XElementKt.isTypeElement;
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 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 static dagger.internal.codegen.xprocessing.XElements.asField;
+import static dagger.internal.codegen.xprocessing.XElements.asTypeElement;
+import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
+import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
+import static dagger.internal.codegen.xprocessing.XTypes.isTypeOf;
+import static dagger.internal.codegen.xprocessing.XTypes.isWildcard;
 
-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 androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XFieldElement;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
+import androidx.room.compiler.processing.XVariableElement;
+import com.google.common.collect.ImmutableSet;
 import dagger.internal.codegen.base.FrameworkTypes;
 import dagger.internal.codegen.base.RequestKinds;
 import dagger.internal.codegen.binding.InjectionAnnotations;
+import dagger.internal.codegen.javapoet.TypeNames;
 import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.model.RequestKind;
+import dagger.spi.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 XProcessingEnv processingEnv;
   private final MembersInjectionValidator membersInjectionValidator;
   private final InjectionAnnotations injectionAnnotations;
   private final KotlinMetadataUtil metadataUtil;
-  private final DaggerElements elements;
 
   @Inject
   DependencyRequestValidator(
+      XProcessingEnv processingEnv,
       MembersInjectionValidator membersInjectionValidator,
       InjectionAnnotations injectionAnnotations,
-      KotlinMetadataUtil metadataUtil,
-      DaggerElements elements) {
+      KotlinMetadataUtil metadataUtil) {
+    this.processingEnv = processingEnv;
     this.membersInjectionValidator = membersInjectionValidator;
     this.injectionAnnotations = injectionAnnotations;
     this.metadataUtil = metadataUtil;
-    this.elements = elements;
   }
 
   /**
@@ -72,15 +71,16 @@
    * non-instance request with a wildcard type.
    */
   void validateDependencyRequest(
-      ValidationReport.Builder<?> report, Element requestElement, TypeMirror requestType) {
-    if (MoreElements.isAnnotationPresent(requestElement, Assisted.class)) {
+      ValidationReport.Builder report, XElement requestElement, XType requestType) {
+    if (requestElement.hasAnnotation(TypeNames.ASSISTED)) {
       // Don't validate assisted parameters. These are not dependency requests.
       return;
     }
     if (missingQualifierMetadata(requestElement)) {
       report.addError(
-          "Unable to read annotations on an injected Kotlin property. The Dagger compiler must"
-              + " also be applied to any project containing @Inject properties.",
+          "Unable to read annotations on an injected Kotlin property. "
+          + "The Dagger compiler must also be applied to any project containing @Inject "
+          + "properties.",
           requestElement);
 
       // Skip any further validation if we don't have valid metadata for a type that needs it.
@@ -91,36 +91,36 @@
   }
 
   /** Returns {@code true} if a kotlin inject field is missing metadata about its qualifiers. */
-  private boolean missingQualifierMetadata(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()))));
-      return !membersInjector.isPresent();
+  private boolean missingQualifierMetadata(XElement requestElement) {
+    if (isField(requestElement)) {
+      XFieldElement fieldElement = asField(requestElement);
+      // static/top-level injected fields are not supported,
+      // so no need to get qualifier from kotlin metadata
+      if ((!fieldElement.isStatic() || !isTypeElement(fieldElement.getEnclosingElement()))
+          && metadataUtil.hasMetadata(toJavac(fieldElement))
+          && metadataUtil.isMissingSyntheticPropertyForAnnotations(toJavac(fieldElement))) {
+        Optional<XTypeElement> membersInjector =
+            Optional.ofNullable(
+                processingEnv.findTypeElement(
+                    membersInjectorNameForType(asTypeElement(fieldElement.getEnclosingElement()))));
+        return !membersInjector.isPresent();
+      }
     }
     return false;
   }
 
   private final class Validator {
-    private final ValidationReport.Builder<?> report;
-    private final Element requestElement;
-    private final TypeMirror requestType;
-    private final TypeMirror keyType;
-    private final RequestKind requestKind;
-    private final ImmutableCollection<? extends AnnotationMirror> qualifiers;
+    private final ValidationReport.Builder report;
+    private final XElement requestElement;
+    private final XType requestType;
+    private final XType keyType;
+    private final ImmutableSet<XAnnotation> qualifiers;
 
-
-    Validator(ValidationReport.Builder<?> report, Element requestElement, TypeMirror requestType) {
+    Validator(ValidationReport.Builder report, XElement requestElement, XType requestType) {
       this.report = report;
       this.requestElement = requestElement;
       this.requestType = requestType;
       this.keyType = extractKeyType(requestType);
-      this.requestKind = RequestKinds.getRequestKind(requestType);
       this.qualifiers = injectionAnnotations.getQualifiers(requestElement);
     }
 
@@ -131,7 +131,7 @@
 
     private void checkQualifiers() {
       if (qualifiers.size() > 1) {
-        for (AnnotationMirror qualifier : qualifiers) {
+        for (XAnnotation qualifier : qualifiers) {
           report.addError(
               "A single dependency request may not use more than one @Qualifier",
               requestElement,
@@ -141,8 +141,8 @@
     }
 
     private void checkType() {
-      if (qualifiers.isEmpty() && keyType.getKind() == TypeKind.DECLARED) {
-        TypeElement typeElement = asTypeElement(keyType);
+      if (qualifiers.isEmpty() && isDeclared(keyType)) {
+        XTypeElement typeElement = keyType.getTypeElement();
         if (isAssistedInjectionType(typeElement)) {
           report.addError(
               "Dagger does not support injecting @AssistedInject type, "
@@ -150,15 +150,17 @@
                   + ". Did you mean to inject its assisted factory type instead?",
               requestElement);
         }
-        if (requestKind != RequestKind.INSTANCE && isAssistedFactoryType(typeElement)) {
+        RequestKind requestKind = RequestKinds.getRequestKind(requestType);
+        if (!(requestKind == RequestKind.INSTANCE || requestKind == RequestKind.PROVIDER)
+            && isAssistedFactoryType(typeElement)) {
           report.addError(
-              "Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, "
+              "Dagger does not support injecting Lazy<T>, Producer<T>, "
                   + "or Produced<T> when T is an @AssistedFactory-annotated type such as "
                   + keyType,
               requestElement);
         }
       }
-      if (keyType.getKind().equals(WILDCARD)) {
+      if (isWildcard(keyType)) {
         // TODO(ronshapiro): Explore creating this message using RequestKinds.
         report.addError(
             "Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, "
@@ -166,14 +168,13 @@
                 + keyType,
             requestElement);
       }
-      if (MoreTypes.isType(keyType) && MoreTypes.isTypeOf(MembersInjector.class, keyType)) {
-        DeclaredType membersInjectorType = MoreTypes.asDeclared(keyType);
-        if (membersInjectorType.getTypeArguments().isEmpty()) {
+      if (isTypeOf(keyType, TypeNames.MEMBERS_INJECTOR)) {
+        if (keyType.getTypeArguments().isEmpty()) {
           report.addError("Cannot inject a raw MembersInjector", requestElement);
         } else {
           report.addSubreport(
               membersInjectionValidator.validateMembersInjectionRequest(
-                  requestElement, membersInjectorType.getTypeArguments().get(0)));
+                  requestElement, keyType.getTypeArguments().get(0)));
         }
       }
     }
@@ -186,13 +187,13 @@
    * <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();
+  void checkNotProducer(ValidationReport.Builder report, XVariableElement requestElement) {
+    XType requestType = requestElement.getType();
     if (FrameworkTypes.isProducerType(requestType)) {
       report.addError(
           String.format(
               "%s may only be injected in @Produces methods",
-              MoreTypes.asTypeElement(requestType).getSimpleName()),
+              getSimpleName(requestType.getTypeElement())),
           requestElement);
     }
   }
diff --git a/java/dagger/internal/codegen/validation/DiagnosticMessageGenerator.java b/java/dagger/internal/codegen/validation/DiagnosticMessageGenerator.java
index 7d7b9e3..dca74a6 100644
--- a/java/dagger/internal/codegen/validation/DiagnosticMessageGenerator.java
+++ b/java/dagger/internal/codegen/validation/DiagnosticMessageGenerator.java
@@ -48,12 +48,14 @@
 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 dagger.spi.model.Binding;
+import dagger.spi.model.BindingGraph;
+import dagger.spi.model.BindingGraph.DependencyEdge;
+import dagger.spi.model.BindingGraph.Edge;
+import dagger.spi.model.BindingGraph.MaybeBinding;
+import dagger.spi.model.BindingGraph.Node;
+import dagger.spi.model.ComponentPath;
+import dagger.spi.model.DaggerElement;
 import java.util.Comparator;
 import java.util.Set;
 import java.util.function.Function;
@@ -149,7 +151,7 @@
       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);
+      Binding binding = (Binding) source(dependencyEdge);
       entryPoints = graph.entryPointEdgesDependingOnBinding(binding);
       dependencyTrace =
           ImmutableList.<DependencyEdge>builder()
@@ -178,7 +180,15 @@
         appendComponentPathUnlessAtRoot(message, source(getLast(dependencyTrace)));
       }
     }
+    message.append(getRequestsNotInTrace(dependencyTrace, requests, entryPoints));
+    return message.toString();
+  }
 
+  public String getRequestsNotInTrace(
+      ImmutableList<DependencyEdge> dependencyTrace,
+      ImmutableSet<DependencyEdge> requests,
+      ImmutableSet<DependencyEdge> entryPoints) {
+    StringBuilder message = new StringBuilder();
     // Print any dependency requests that aren't shown as part of the dependency trace.
     ImmutableSet<Element> requestsToPrint =
         requests.stream()
@@ -189,6 +199,7 @@
                         || (!request.isEntryPoint() && !isTracedRequest(dependencyTrace, request)))
             .map(request -> request.dependencyRequest().requestElement())
             .flatMap(presentValues())
+            .map(DaggerElement::java)
             .collect(toImmutableSet());
     if (!requestsToPrint.isEmpty()) {
       message
@@ -229,14 +240,14 @@
       new Formatter<DependencyEdge>() {
         @Override
         public String format(DependencyEdge object) {
-          Element requestElement = object.dependencyRequest().requestElement().get();
+          Element requestElement = object.dependencyRequest().requestElement().get().java();
           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())) {
+              || !requestElement.getEnclosingElement().equals(componentPath.rootComponent().java())) {
             element.append(String.format(" [%s]", componentPath));
           }
           return element.toString();
@@ -252,9 +263,9 @@
    * 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.
+  // TODO(ronshapiro): Adding a DependencyPath type to dagger.spi.model could be useful, i.e.
   // bindingGraph.shortestPathFromEntryPoint(DependencyEdge, MaybeBindingNode)
-  ImmutableList<DependencyEdge> dependencyTrace(
+  public 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.
@@ -299,7 +310,7 @@
   }
 
   /** Returns all the nonsynthetic dependency requests for a binding. */
-  ImmutableSet<DependencyEdge> requests(MaybeBinding binding) {
+  public ImmutableSet<DependencyEdge> requests(MaybeBinding binding) {
     return graph.network().inEdges(binding).stream()
         .flatMap(instancesOf(DependencyEdge.class))
         .filter(edge -> edge.dependencyRequest().requestElement().isPresent())
@@ -353,12 +364,12 @@
   }
 
   TypeElement componentContainingEntryPoint(DependencyEdge entryPoint) {
-    return source(entryPoint).componentPath().currentComponent();
+    return source(entryPoint).componentPath().currentComponent().java();
   }
 
   TypeElement typeDeclaringEntryPoint(DependencyEdge entryPoint) {
     return MoreElements.asType(
-        entryPoint.dependencyRequest().requestElement().get().getEnclosingElement());
+        entryPoint.dependencyRequest().requestElement().get().java().getEnclosingElement());
   }
 
   /**
@@ -368,7 +379,7 @@
   Comparator<DependencyEdge> requestEnclosingTypeName() {
     return comparing(
         edge ->
-            closestEnclosingTypeElement(edge.dependencyRequest().requestElement().get())
+            closestEnclosingTypeElement(edge.dependencyRequest().requestElement().get().java())
                 .getQualifiedName()
                 .toString());
   }
@@ -380,7 +391,8 @@
    * <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);
+    return comparing(
+        edge -> edge.dependencyRequest().requestElement().get().java(), DECLARATION_ORDER);
   }
 
   private Node source(Edge edge) {
diff --git a/java/dagger/internal/codegen/validation/DiagnosticReporterFactory.java b/java/dagger/internal/codegen/validation/DiagnosticReporterFactory.java
index 35ae7c7..32cbf18 100644
--- a/java/dagger/internal/codegen/validation/DiagnosticReporterFactory.java
+++ b/java/dagger/internal/codegen/validation/DiagnosticReporterFactory.java
@@ -18,22 +18,21 @@
 
 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 dagger.internal.codegen.langmodel.DaggerElements.transitivelyEncloses;
 import static javax.tools.Diagnostic.Kind.ERROR;
 
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XMessager;
+import androidx.room.compiler.processing.XTypeElement;
 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 dagger.spi.model.BindingGraph;
+import dagger.spi.model.BindingGraph.ChildFactoryMethodEdge;
+import dagger.spi.model.BindingGraph.ComponentNode;
+import dagger.spi.model.BindingGraph.DependencyEdge;
+import dagger.spi.model.BindingGraph.MaybeBinding;
+import dagger.spi.model.DiagnosticReporter;
 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;
 
@@ -41,20 +40,20 @@
 // 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 XMessager messager;
   private final DiagnosticMessageGenerator.Factory diagnosticMessageGeneratorFactory;
 
   @Inject
   DiagnosticReporterFactory(
-      Messager messager, DiagnosticMessageGenerator.Factory diagnosticMessageGeneratorFactory) {
+      XMessager 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);
+      BindingGraph graph, String pluginName, boolean reportErrorsAsWarnings) {
+    return new DiagnosticReporterImpl(graph, pluginName, reportErrorsAsWarnings);
   }
 
   /**
@@ -63,7 +62,7 @@
    */
   final class DiagnosticReporterImpl implements DiagnosticReporter {
     private final String plugin;
-    private final TypeElement rootComponent;
+    private final XTypeElement rootComponent;
     private final boolean reportErrorsAsWarnings;
     private final ImmutableSet.Builder<Diagnostic.Kind> reportedDiagnosticKinds =
         ImmutableSet.builder();
@@ -72,7 +71,8 @@
     DiagnosticReporterImpl(BindingGraph graph, String plugin, boolean reportErrorsAsWarnings) {
       this.plugin = plugin;
       this.reportErrorsAsWarnings = reportErrorsAsWarnings;
-      this.rootComponent = graph.rootComponentNode().componentPath().currentComponent();
+      this.rootComponent =
+          graph.rootComponentNode().componentPath().currentComponent().xprocessing();
       this.diagnosticMessageGenerator = diagnosticMessageGeneratorFactory.create(graph);
     }
 
@@ -145,7 +145,7 @@
         Diagnostic.Kind diagnosticKind,
         ChildFactoryMethodEdge childFactoryMethodEdge,
         String message) {
-      printMessage(diagnosticKind, message, childFactoryMethodEdge.factoryMethod());
+      printMessage(diagnosticKind, message, childFactoryMethodEdge.factoryMethod().xprocessing());
     }
 
     @Override
@@ -163,10 +163,10 @@
       return String.format(messageFormat, asList(firstArg, moreArgs).toArray());
     }
 
-    void printMessage(
+    private void printMessage(
         Diagnostic.Kind diagnosticKind,
         CharSequence message,
-        @NullableDecl Element elementToReport) {
+        @NullableDecl XElement elementToReport) {
       if (diagnosticKind.equals(ERROR) && reportErrorsAsWarnings) {
         diagnosticKind = Diagnostic.Kind.WARNING;
       }
@@ -174,14 +174,16 @@
       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;
+      if (elementToReport == null) {
+        messager.printMessage(diagnosticKind, fullMessage.append(message).toString());
+      } else {
+        if (!transitivelyEncloses(rootComponent, elementToReport)) {
+          appendBracketPrefix(fullMessage, elementToString(elementToReport));
+          elementToReport = rootComponent;
+        }
+        messager.printMessage(
+            diagnosticKind, fullMessage.append(message).toString(), elementToReport);
       }
-
-      messager.printMessage(diagnosticKind, fullMessage.append(message), elementToReport);
     }
 
     private void appendBracketPrefix(StringBuilder message, String prefix) {
diff --git a/java/dagger/internal/codegen/validation/ExternalBindingGraphConverter.java b/java/dagger/internal/codegen/validation/ExternalBindingGraphConverter.java
new file mode 100644
index 0000000..e2618cc
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/ExternalBindingGraphConverter.java
@@ -0,0 +1,432 @@
+/*
+ * 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.validation;
+
+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.ImmutableMap;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.graph.EndpointPair;
+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 com.google.errorprone.annotations.FormatMethod;
+import dagger.model.Binding;
+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.MissingBinding;
+import dagger.model.BindingGraph.Node;
+import dagger.model.BindingGraph.SubcomponentCreatorBindingEdge;
+import dagger.model.BindingKind;
+import dagger.model.ComponentPath;
+import dagger.model.DependencyRequest;
+import dagger.model.Key;
+import dagger.model.Key.MultibindingContributionIdentifier;
+import dagger.model.RequestKind;
+import dagger.model.Scope;
+import dagger.spi.DiagnosticReporter;
+import dagger.spi.model.DaggerAnnotation;
+import dagger.spi.model.DaggerElement;
+import dagger.spi.model.DaggerTypeElement;
+import java.util.Optional;
+import javax.tools.Diagnostic;
+
+/** A Utility class for converting to the {@link BindingGraph} used by external plugins. */
+public final class ExternalBindingGraphConverter {
+  private ExternalBindingGraphConverter() {}
+
+  /** Returns a {@link DiagnosticReporter} from a {@link dagger.spi.DiagnosticReporter}. */
+  public static DiagnosticReporter fromSpiModel(dagger.spi.model.DiagnosticReporter reporter) {
+    return DiagnosticReporterImpl.create(reporter);
+  }
+
+  /** Returns a {@link BindingGraph} from a {@link dagger.spi.model.BindingGraph}. */
+  public static BindingGraph fromSpiModel(dagger.spi.model.BindingGraph graph) {
+    return BindingGraphImpl.create(graph);
+  }
+
+  private static ImmutableNetwork<Node, Edge> fromSpiModel(
+      Network<dagger.spi.model.BindingGraph.Node, dagger.spi.model.BindingGraph.Edge> spiNetwork) {
+    MutableNetwork<Node, Edge> network =
+        NetworkBuilder.directed().allowsParallelEdges(true).allowsSelfLoops(true).build();
+
+    ImmutableMap<dagger.spi.model.BindingGraph.Node, Node> fromSpiNodes =
+        spiNetwork.nodes().stream()
+            .collect(
+                toImmutableMap(
+                    spiNode -> spiNode,
+                    ExternalBindingGraphConverter::fromSpiModel));
+
+    for (Node node : fromSpiNodes.values()) {
+      network.addNode(node);
+    }
+    for (dagger.spi.model.BindingGraph.Edge edge : spiNetwork.edges()) {
+      EndpointPair<dagger.spi.model.BindingGraph.Node> edgePair = spiNetwork.incidentNodes(edge);
+      network.addEdge(
+          fromSpiNodes.get(edgePair.source()),
+          fromSpiNodes.get(edgePair.target()),
+          fromSpiModel(edge));
+    }
+    return ImmutableNetwork.copyOf(network);
+  }
+
+  private static Node fromSpiModel(dagger.spi.model.BindingGraph.Node node) {
+    if (node instanceof dagger.spi.model.Binding) {
+      return BindingNodeImpl.create((dagger.spi.model.Binding) node);
+    } else if (node instanceof dagger.spi.model.BindingGraph.ComponentNode) {
+      return ComponentNodeImpl.create((dagger.spi.model.BindingGraph.ComponentNode) node);
+    } else if (node instanceof dagger.spi.model.BindingGraph.MissingBinding) {
+      return MissingBindingImpl.create((dagger.spi.model.BindingGraph.MissingBinding) node);
+    } else {
+      throw new IllegalStateException("Unhandled node type: " + node.getClass());
+    }
+  }
+
+  private static Edge fromSpiModel(dagger.spi.model.BindingGraph.Edge edge) {
+    if (edge instanceof dagger.spi.model.BindingGraph.DependencyEdge) {
+      return DependencyEdgeImpl.create((dagger.spi.model.BindingGraph.DependencyEdge) edge);
+    } else if (edge instanceof dagger.spi.model.BindingGraph.ChildFactoryMethodEdge) {
+      return ChildFactoryMethodEdgeImpl.create(
+          (dagger.spi.model.BindingGraph.ChildFactoryMethodEdge) edge);
+    } else if (edge instanceof dagger.spi.model.BindingGraph.SubcomponentCreatorBindingEdge) {
+      return SubcomponentCreatorBindingEdgeImpl.create(
+          (dagger.spi.model.BindingGraph.SubcomponentCreatorBindingEdge) edge);
+    } else {
+      throw new IllegalStateException("Unhandled edge type: " + edge.getClass());
+    }
+  }
+
+  private static MultibindingContributionIdentifier fromSpiModel(
+      dagger.spi.model.Key.MultibindingContributionIdentifier identifier) {
+    return new MultibindingContributionIdentifier(identifier.bindingElement(), identifier.module());
+  }
+
+  private static Key fromSpiModel(dagger.spi.model.Key key) {
+    return Key.builder(key.type().java())
+        .qualifier(key.qualifier().map(DaggerAnnotation::java))
+        .multibindingContributionIdentifier(
+            key.multibindingContributionIdentifier().isPresent()
+                ? Optional.of(fromSpiModel(key.multibindingContributionIdentifier().get()))
+                : Optional.empty())
+        .build();
+  }
+
+  private static BindingKind fromSpiModel(dagger.spi.model.BindingKind bindingKind) {
+    return BindingKind.valueOf(bindingKind.name());
+  }
+
+  private static RequestKind fromSpiModel(dagger.spi.model.RequestKind requestKind) {
+    return RequestKind.valueOf(requestKind.name());
+  }
+
+  private static DependencyRequest fromSpiModel(dagger.spi.model.DependencyRequest request) {
+    DependencyRequest.Builder builder =
+        DependencyRequest.builder()
+            .kind(fromSpiModel(request.kind()))
+            .key(fromSpiModel(request.key()))
+            .isNullable(request.isNullable());
+
+    request.requestElement().ifPresent(e -> builder.requestElement(e.java()));
+    return builder.build();
+  }
+
+  private static Scope fromSpiModel(dagger.spi.model.Scope scope) {
+    return Scope.scope(scope.scopeAnnotation().java());
+  }
+
+  private static ComponentPath fromSpiModel(dagger.spi.model.ComponentPath path) {
+    return ComponentPath.create(
+        path.components().stream().map(DaggerTypeElement::java).collect(toImmutableList()));
+  }
+
+  private static dagger.spi.model.BindingGraph.ComponentNode toSpiModel(
+      ComponentNode componentNode) {
+    return ((ComponentNodeImpl) componentNode).spiDelegate();
+  }
+
+  private static dagger.spi.model.BindingGraph.MaybeBinding toSpiModel(MaybeBinding maybeBinding) {
+    if (maybeBinding instanceof MissingBindingImpl) {
+      return ((MissingBindingImpl) maybeBinding).spiDelegate();
+    } else if (maybeBinding instanceof BindingNodeImpl) {
+      return ((BindingNodeImpl) maybeBinding).spiDelegate();
+    } else {
+      throw new IllegalStateException("Unhandled binding type: " + maybeBinding.getClass());
+    }
+  }
+
+  private static dagger.spi.model.BindingGraph.DependencyEdge toSpiModel(
+      DependencyEdge dependencyEdge) {
+    return ((DependencyEdgeImpl) dependencyEdge).spiDelegate();
+  }
+
+  private static dagger.spi.model.BindingGraph.ChildFactoryMethodEdge toSpiModel(
+      ChildFactoryMethodEdge childFactoryMethodEdge) {
+    return ((ChildFactoryMethodEdgeImpl) childFactoryMethodEdge).spiDelegate();
+  }
+
+  @AutoValue
+  abstract static class ComponentNodeImpl implements ComponentNode {
+    static ComponentNode create(dagger.spi.model.BindingGraph.ComponentNode componentNode) {
+      return new AutoValue_ExternalBindingGraphConverter_ComponentNodeImpl(
+          fromSpiModel(componentNode.componentPath()),
+          componentNode.isSubcomponent(),
+          componentNode.isRealComponent(),
+          componentNode.entryPoints().stream()
+              .map(ExternalBindingGraphConverter::fromSpiModel)
+              .collect(toImmutableSet()),
+          componentNode.scopes().stream()
+              .map(ExternalBindingGraphConverter::fromSpiModel)
+              .collect(toImmutableSet()),
+          componentNode);
+    }
+
+    abstract dagger.spi.model.BindingGraph.ComponentNode spiDelegate();
+
+    @Override
+    public final String toString() {
+      return spiDelegate().toString();
+    }
+  }
+
+  @AutoValue
+  abstract static class BindingNodeImpl implements Binding {
+    static Binding create(dagger.spi.model.Binding binding) {
+      return new AutoValue_ExternalBindingGraphConverter_BindingNodeImpl(
+          fromSpiModel(binding.key()),
+          fromSpiModel(binding.componentPath()),
+          binding.dependencies().stream()
+              .map(ExternalBindingGraphConverter::fromSpiModel)
+              .collect(toImmutableSet()),
+          binding.bindingElement().map(DaggerElement::java),
+          binding.contributingModule().map(DaggerTypeElement::java),
+          binding.requiresModuleInstance(),
+          binding.scope().map(ExternalBindingGraphConverter::fromSpiModel),
+          binding.isNullable(),
+          binding.isProduction(),
+          fromSpiModel(binding.kind()),
+          binding);
+    }
+
+    abstract dagger.spi.model.Binding spiDelegate();
+
+    @Override
+    public final String toString() {
+      return spiDelegate().toString();
+    }
+  }
+
+  @AutoValue
+  abstract static class MissingBindingImpl extends MissingBinding {
+    static MissingBinding create(dagger.spi.model.BindingGraph.MissingBinding missingBinding) {
+      return new AutoValue_ExternalBindingGraphConverter_MissingBindingImpl(
+          fromSpiModel(missingBinding.componentPath()),
+          fromSpiModel(missingBinding.key()),
+          missingBinding);
+    }
+
+    abstract dagger.spi.model.BindingGraph.MissingBinding spiDelegate();
+
+    @Memoized
+    @Override
+    public abstract int hashCode();
+
+    @Override
+    public abstract boolean equals(Object o);
+  }
+
+  @AutoValue
+  abstract static class DependencyEdgeImpl implements DependencyEdge {
+    static DependencyEdge create(dagger.spi.model.BindingGraph.DependencyEdge dependencyEdge) {
+      return new AutoValue_ExternalBindingGraphConverter_DependencyEdgeImpl(
+          fromSpiModel(dependencyEdge.dependencyRequest()),
+          dependencyEdge.isEntryPoint(),
+          dependencyEdge);
+    }
+
+    abstract dagger.spi.model.BindingGraph.DependencyEdge spiDelegate();
+
+    @Override
+    public final String toString() {
+      return spiDelegate().toString();
+    }
+  }
+
+  @AutoValue
+  abstract static class ChildFactoryMethodEdgeImpl implements ChildFactoryMethodEdge {
+    static ChildFactoryMethodEdge create(
+        dagger.spi.model.BindingGraph.ChildFactoryMethodEdge childFactoryMethodEdge) {
+      return new AutoValue_ExternalBindingGraphConverter_ChildFactoryMethodEdgeImpl(
+          childFactoryMethodEdge.factoryMethod().java(), childFactoryMethodEdge);
+    }
+
+    abstract dagger.spi.model.BindingGraph.ChildFactoryMethodEdge spiDelegate();
+
+    @Override
+    public final String toString() {
+      return spiDelegate().toString();
+    }
+  }
+
+  @AutoValue
+  abstract static class SubcomponentCreatorBindingEdgeImpl
+      implements SubcomponentCreatorBindingEdge {
+    static SubcomponentCreatorBindingEdge create(
+        dagger.spi.model.BindingGraph.SubcomponentCreatorBindingEdge
+            subcomponentCreatorBindingEdge) {
+      return new AutoValue_ExternalBindingGraphConverter_SubcomponentCreatorBindingEdgeImpl(
+          subcomponentCreatorBindingEdge.declaringModules().stream()
+              .map(DaggerTypeElement::java)
+              .collect(toImmutableSet()),
+          subcomponentCreatorBindingEdge);
+    }
+
+    abstract dagger.spi.model.BindingGraph.SubcomponentCreatorBindingEdge spiDelegate();
+
+    @Override
+    public final String toString() {
+      return spiDelegate().toString();
+    }
+  }
+
+  @AutoValue
+  abstract static class BindingGraphImpl extends BindingGraph {
+    static BindingGraph create(dagger.spi.model.BindingGraph bindingGraph) {
+      BindingGraphImpl bindingGraphImpl =
+          new AutoValue_ExternalBindingGraphConverter_BindingGraphImpl(
+              fromSpiModel(bindingGraph.network()), bindingGraph.isFullBindingGraph());
+
+      bindingGraphImpl.componentNodesByPath =
+          bindingGraphImpl.componentNodes().stream()
+              .collect(toImmutableMap(ComponentNode::componentPath, node -> node));
+
+      return bindingGraphImpl;
+    }
+
+    private ImmutableMap<ComponentPath, ComponentNode> componentNodesByPath;
+
+    // This overrides dagger.model.BindingGraph with a more efficient implementation.
+    @Override
+    public Optional<ComponentNode> componentNode(ComponentPath componentPath) {
+      return componentNodesByPath.containsKey(componentPath)
+          ? Optional.of(componentNodesByPath.get(componentPath))
+          : Optional.empty();
+    }
+
+    // This overrides dagger.model.BindingGraph to memoize the output.
+    @Override
+    @Memoized
+    public ImmutableSetMultimap<Class<? extends Node>, ? extends Node> nodesByClass() {
+      return super.nodesByClass();
+    }
+  }
+
+  private static final class DiagnosticReporterImpl implements DiagnosticReporter {
+    static DiagnosticReporterImpl create(dagger.spi.model.DiagnosticReporter reporter) {
+      return new DiagnosticReporterImpl(reporter);
+    }
+
+    private final dagger.spi.model.DiagnosticReporter delegate;
+
+    DiagnosticReporterImpl(dagger.spi.model.DiagnosticReporter delegate) {
+      this.delegate = delegate;
+    }
+
+    @Override
+    public void reportComponent(
+        Diagnostic.Kind diagnosticKind, ComponentNode componentNode, String message) {
+      delegate.reportComponent(diagnosticKind, toSpiModel(componentNode), message);
+    }
+
+    @Override
+    @FormatMethod
+    public void reportComponent(
+        Diagnostic.Kind diagnosticKind,
+        ComponentNode componentNode,
+        String messageFormat,
+        Object firstArg,
+        Object... moreArgs) {
+      delegate.reportComponent(
+          diagnosticKind, toSpiModel(componentNode), messageFormat, firstArg, moreArgs);
+    }
+
+    @Override
+    public void reportBinding(
+        Diagnostic.Kind diagnosticKind, MaybeBinding binding, String message) {
+      delegate.reportBinding(diagnosticKind, toSpiModel(binding), message);
+    }
+
+    @Override
+    @FormatMethod
+    public void reportBinding(
+        Diagnostic.Kind diagnosticKind,
+        MaybeBinding binding,
+        String messageFormat,
+        Object firstArg,
+        Object... moreArgs) {
+      delegate.reportBinding(
+          diagnosticKind, toSpiModel(binding), messageFormat, firstArg, moreArgs);
+    }
+
+    @Override
+    public void reportDependency(
+        Diagnostic.Kind diagnosticKind, DependencyEdge dependencyEdge, String message) {
+      delegate.reportDependency(diagnosticKind, toSpiModel(dependencyEdge), message);
+    }
+
+    @Override
+    @FormatMethod
+    public void reportDependency(
+        Diagnostic.Kind diagnosticKind,
+        DependencyEdge dependencyEdge,
+        String messageFormat,
+        Object firstArg,
+        Object... moreArgs) {
+      delegate.reportDependency(
+          diagnosticKind, toSpiModel(dependencyEdge), messageFormat, firstArg, moreArgs);
+    }
+
+    @Override
+    public void reportSubcomponentFactoryMethod(
+        Diagnostic.Kind diagnosticKind,
+        ChildFactoryMethodEdge childFactoryMethodEdge,
+        String message) {
+      delegate.reportSubcomponentFactoryMethod(
+          diagnosticKind, toSpiModel(childFactoryMethodEdge), message);
+    }
+
+    @Override
+    @FormatMethod
+    public void reportSubcomponentFactoryMethod(
+        Diagnostic.Kind diagnosticKind,
+        ChildFactoryMethodEdge childFactoryMethodEdge,
+        String messageFormat,
+        Object firstArg,
+        Object... moreArgs) {
+      delegate.reportSubcomponentFactoryMethod(
+          diagnosticKind, toSpiModel(childFactoryMethodEdge), messageFormat, firstArg, moreArgs);
+    }
+  }
+}
diff --git a/java/dagger/internal/codegen/validation/BindingGraphPlugins.java b/java/dagger/internal/codegen/validation/ExternalBindingGraphPlugins.java
similarity index 60%
rename from java/dagger/internal/codegen/validation/BindingGraphPlugins.java
rename to java/dagger/internal/codegen/validation/ExternalBindingGraphPlugins.java
index 16ef78f..9021790 100644
--- a/java/dagger/internal/codegen/validation/BindingGraphPlugins.java
+++ b/java/dagger/internal/codegen/validation/ExternalBindingGraphPlugins.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Dagger Authors.
+ * 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.
@@ -17,36 +17,42 @@
 package dagger.internal.codegen.validation;
 
 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static javax.tools.Diagnostic.Kind.ERROR;
 
+import androidx.room.compiler.processing.XFiler;
+import androidx.room.compiler.processing.compat.XConverters;
 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.internal.codegen.validation.DiagnosticReporterFactory.DiagnosticReporterImpl;
+import dagger.model.BindingGraph;
 import dagger.spi.BindingGraphPlugin;
+import dagger.spi.DiagnosticReporter;
 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 {
+public final class ExternalBindingGraphPlugins {
   private final ImmutableSet<BindingGraphPlugin> plugins;
-  private final Filer filer;
+  private final DiagnosticReporterFactory diagnosticReporterFactory;
+  private final XFiler 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,
+  ExternalBindingGraphPlugins(
+      ImmutableSet<BindingGraphPlugin> plugins,
+      DiagnosticReporterFactory diagnosticReporterFactory,
+      XFiler filer,
       DaggerTypes types,
       DaggerElements elements,
       @ProcessingOptions Map<String, String> processingOptions) {
-    this.plugins = Sets.union(validationPlugins, externalPlugins).immutableCopy();
+    this.plugins = plugins;
+    this.diagnosticReporterFactory = diagnosticReporterFactory;
     this.filer = filer;
     this.types = types;
     this.elements = elements;
@@ -67,7 +73,7 @@
   }
 
   private void initializePlugin(BindingGraphPlugin plugin) {
-    plugin.initFiler(filer);
+    plugin.initFiler(XConverters.toJavac(filer));
     plugin.initTypes(types);
     plugin.initElements(elements);
     Set<String> supportedOptions = plugin.supportedOptions();
@@ -75,4 +81,21 @@
       plugin.initOptions(Maps.filterKeys(processingOptions, supportedOptions::contains));
     }
   }
+
+  /** Returns {@code false} if any of the plugins reported an error. */
+  boolean visit(dagger.spi.model.BindingGraph spiGraph) {
+    BindingGraph graph = ExternalBindingGraphConverter.fromSpiModel(spiGraph);
+    boolean isClean = true;
+    for (BindingGraphPlugin plugin : plugins) {
+      DiagnosticReporterImpl spiReporter =
+          diagnosticReporterFactory.reporter(
+              spiGraph, plugin.pluginName(), /* reportErrorsAsWarnings= */ false);
+      DiagnosticReporter reporter = ExternalBindingGraphConverter.fromSpiModel(spiReporter);
+      plugin.visitGraph(graph, reporter);
+      if (spiReporter.reportedDiagnosticKinds().contains(ERROR)) {
+        isClean = false;
+      }
+    }
+    return isClean;
+  }
 }
diff --git a/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java b/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java
index 9787f4f..7aa95ae 100644
--- a/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java
+++ b/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java
@@ -16,6 +16,9 @@
 
 package dagger.internal.codegen.validation;
 
+import static androidx.room.compiler.processing.XElementKt.isTypeElement;
+import static androidx.room.compiler.processing.compat.XConverters.toXProcessing;
+import static com.google.auto.common.MoreTypes.asTypeElement;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
@@ -24,17 +27,23 @@
 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 static dagger.internal.codegen.extension.DaggerCollectors.toOptional;
 import static dagger.internal.codegen.langmodel.DaggerTypes.unwrapType;
+import static dagger.internal.codegen.xprocessing.XElements.asTypeElement;
+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 com.google.common.collect.Iterables;
+import androidx.room.compiler.processing.XConstructorElement;
+import androidx.room.compiler.processing.XFieldElement;
+import androidx.room.compiler.processing.XMessager;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 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.base.SourceFileGenerationException;
 import dagger.internal.codegen.base.SourceFileGenerator;
@@ -45,20 +54,18 @@
 import dagger.internal.codegen.binding.MembersInjectionBinding;
 import dagger.internal.codegen.binding.ProvisionBinding;
 import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.javapoet.TypeNames;
 import dagger.internal.codegen.langmodel.DaggerElements;
 import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.Key;
+import dagger.spi.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 java.util.stream.Stream;
 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;
@@ -71,9 +78,10 @@
  */
 @Singleton
 final class InjectBindingRegistryImpl implements InjectBindingRegistry {
+  private final XProcessingEnv processingEnv;
   private final DaggerElements elements;
   private final DaggerTypes types;
-  private final Messager messager;
+  private final XMessager messager;
   private final InjectValidator injectValidator;
   private final InjectValidator injectValidatorWhenGeneratingCode;
   private final KeyFactory keyFactory;
@@ -81,12 +89,12 @@
   private final CompilerOptions compilerOptions;
 
   final class BindingsCollection<B extends Binding> {
-    private final Class<?> factoryClass;
+    private final ClassName 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) {
+    BindingsCollection(ClassName factoryClass) {
       this.factoryClass = factoryClass;
     }
 
@@ -95,7 +103,11 @@
           binding != null;
           binding = bindingsRequiringGeneration.poll()) {
         checkState(!binding.unresolved().isPresent());
-        if (injectValidatorWhenGeneratingCode.isValidType(binding.key().type())) {
+        TypeMirror type = binding.key().type().java();
+        if (!type.getKind().equals(DECLARED)
+            || injectValidatorWhenGeneratingCode
+                .validate(toXProcessing(asTypeElement(type), processingEnv))
+                .isClean()) {
           generator.generate(binding);
         }
         materializedBindingKeys.add(binding.key());
@@ -134,8 +146,8 @@
               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.
+                  factoryClass.simpleName(),
+                  types.erasure(binding.key().type().java()))); // erasure to strip <T> from msgs.
         }
       }
     }
@@ -153,7 +165,7 @@
       // 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()) {
+          || binding.bindingTypeElement().get().getType().getTypeArguments().isEmpty()) {
         Key key = binding.key();
         Binding previousValue = bindingsByKey.put(key, binding);
         checkState(previousValue == null || binding.equals(previousValue),
@@ -164,19 +176,21 @@
   }
 
   private final BindingsCollection<ProvisionBinding> provisionBindings =
-      new BindingsCollection<>(Provider.class);
+      new BindingsCollection<>(TypeNames.PROVIDER);
   private final BindingsCollection<MembersInjectionBinding> membersInjectionBindings =
-      new BindingsCollection<>(MembersInjector.class);
+      new BindingsCollection<>(TypeNames.MEMBERS_INJECTOR);
 
   @Inject
   InjectBindingRegistryImpl(
+      XProcessingEnv processingEnv,
       DaggerElements elements,
       DaggerTypes types,
-      Messager messager,
+      XMessager messager,
       InjectValidator injectValidator,
       KeyFactory keyFactory,
       BindingFactory bindingFactory,
       CompilerOptions compilerOptions) {
+    this.processingEnv = processingEnv;
     this.elements = elements;
     this.types = types;
     this.messager = messager;
@@ -187,7 +201,6 @@
     this.compilerOptions = compilerOptions;
   }
 
-
   // TODO(dpb): make the SourceFileGenerators fields so they don't have to be passed in
   @Override
   public void generateSourcesForRequiredBindings(
@@ -234,29 +247,32 @@
   }
 
   @Override
-  public Optional<ProvisionBinding> tryRegisterConstructor(ExecutableElement constructorElement) {
+  public Optional<ProvisionBinding> tryRegisterInjectConstructor(
+      XConstructorElement constructorElement) {
     return tryRegisterConstructor(constructorElement, Optional.empty(), false);
   }
 
   @CanIgnoreReturnValue
   private Optional<ProvisionBinding> tryRegisterConstructor(
-      ExecutableElement constructorElement,
-      Optional<TypeMirror> resolvedType,
+      XConstructorElement constructorElement,
+      Optional<XType> resolvedType,
       boolean warnIfNotAlreadyGenerated) {
-    TypeElement typeElement = MoreElements.asType(constructorElement.getEnclosingElement());
-    DeclaredType type = MoreTypes.asDeclared(typeElement.asType());
+    XTypeElement typeElement = constructorElement.getEnclosingElement();
+
+    // Validating here shouldn't have a performance penalty because the validator caches its reports
+    ValidationReport report = injectValidator.validate(typeElement);
+    report.printMessagesTo(messager);
+    if (!report.isClean()) {
+      return Optional.empty();
+    }
+
+    XType type = typeElement.getType();
     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()) {
-      return Optional.empty();
-    }
-
     ProvisionBinding binding = bindingFactory.injectionBinding(constructorElement, resolvedType);
     registerBinding(binding, warnIfNotAlreadyGenerated);
     if (!binding.injectionSites().isEmpty()) {
@@ -266,29 +282,50 @@
   }
 
   @Override
-  public Optional<MembersInjectionBinding> tryRegisterMembersInjectedType(TypeElement typeElement) {
-    return tryRegisterMembersInjectedType(typeElement, Optional.empty(), false);
+  public Optional<MembersInjectionBinding> tryRegisterInjectField(XFieldElement fieldElement) {
+    // TODO(b/204116636): Add a test for this once we're able to test kotlin sources.
+    // TODO(b/204208307): Add validation for KAPT to test if this came from a top-level field.
+    if (!isTypeElement(fieldElement.getEnclosingElement())) {
+      messager.printMessage(
+          Kind.ERROR,
+          "@Inject fields must be enclosed in a type.",
+          fieldElement);
+    }
+    return tryRegisterMembersInjectedType(
+        asTypeElement(fieldElement.getEnclosingElement()), Optional.empty(), false);
+  }
+
+  @Override
+  public Optional<MembersInjectionBinding> tryRegisterInjectMethod(XMethodElement methodElement) {
+    // TODO(b/204116636): Add a test for this once we're able to test kotlin sources.
+    // TODO(b/204208307): Add validation for KAPT to test if this came from a top-level method.
+    if (!isTypeElement(methodElement.getEnclosingElement())) {
+      messager.printMessage(
+          Kind.ERROR,
+          "@Inject methods must be enclosed in a type.",
+          methodElement);
+    }
+    return tryRegisterMembersInjectedType(
+        asTypeElement(methodElement.getEnclosingElement()), Optional.empty(), false);
   }
 
   @CanIgnoreReturnValue
   private Optional<MembersInjectionBinding> tryRegisterMembersInjectedType(
-      TypeElement typeElement,
-      Optional<TypeMirror> resolvedType,
-      boolean warnIfNotAlreadyGenerated) {
-    DeclaredType type = MoreTypes.asDeclared(typeElement.asType());
+      XTypeElement typeElement, Optional<XType> resolvedType, boolean warnIfNotAlreadyGenerated) {
+    // Validating here shouldn't have a performance penalty because the validator caches its reports
+    ValidationReport report = injectValidator.validateForMembersInjection(typeElement);
+    report.printMessagesTo(messager);
+    if (!report.isClean()) {
+      return Optional.empty();
+    }
+
+    XType type = typeElement.getType();
     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()) {
-      return Optional.empty();
-    }
-
     MembersInjectionBinding binding = bindingFactory.membersInjectionBinding(type, resolvedType);
     registerBinding(binding, warnIfNotAlreadyGenerated);
     for (Optional<DeclaredType> supertype = types.nonObjectSuperclass(type);
@@ -303,7 +340,7 @@
   @Override
   public Optional<ProvisionBinding> getOrFindProvisionBinding(Key key) {
     checkNotNull(key);
-    if (!isValidImplicitProvisionKey(key, types)) {
+    if (!isValidImplicitProvisionKey(key)) {
       return Optional.empty();
     }
     ProvisionBinding binding = provisionBindings.getBinding(key);
@@ -311,24 +348,21 @@
       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);
+    XType type = key.type().xprocessing();
+    XTypeElement element = type.getTypeElement();
+
+    ValidationReport report = injectValidator.validate(element);
+    report.printMessagesTo(messager);
+    if (!report.isClean()) {
+      return Optional.empty();
     }
+
+    return Stream.concat(
+            injectedConstructors(element).stream(),
+            assistedInjectedConstructors(element).stream())
+        // We're guaranteed that there's at most 1 @Inject constructors from above validation.
+        .collect(toOptional())
+        .flatMap(constructor -> tryRegisterConstructor(constructor, Optional.of(type), true));
   }
 
   @CanIgnoreReturnValue
@@ -341,10 +375,8 @@
     if (binding != null) {
       return Optional.of(binding);
     }
-    Optional<MembersInjectionBinding> newBinding =
-        tryRegisterMembersInjectedType(
-            MoreTypes.asTypeElement(key.type()), Optional.of(key.type()), true);
-    return newBinding;
+    return tryRegisterMembersInjectedType(
+        key.type().xprocessing().getTypeElement(), Optional.of(key.type().xprocessing()), true);
   }
 
   @Override
@@ -352,7 +384,7 @@
     if (!isValidMembersInjectionKey(key)) {
       return Optional.empty();
     }
-    Key membersInjectionKey = keyFactory.forMembersInjectedType(unwrapType(key.type()));
+    Key membersInjectionKey = keyFactory.forMembersInjectedType(unwrapType(key.type().java()));
     return getOrFindMembersInjectionBinding(membersInjectionKey)
         .map(binding -> bindingFactory.membersInjectorBinding(key, binding));
   }
diff --git a/java/dagger/internal/codegen/validation/InjectValidator.java b/java/dagger/internal/codegen/validation/InjectValidator.java
index 240f9d0..4758d32 100644
--- a/java/dagger/internal/codegen/validation/InjectValidator.java
+++ b/java/dagger/internal/codegen/validation/InjectValidator.java
@@ -16,45 +16,43 @@
 
 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 androidx.room.compiler.processing.compat.XConverters.toXProcessing;
+import static com.google.common.collect.Iterables.getOnlyElement;
 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 static dagger.internal.codegen.binding.SourceFiles.factoryNameForElement;
+import static dagger.internal.codegen.binding.SourceFiles.membersInjectorNameForType;
+import static dagger.internal.codegen.xprocessing.XElements.closestEnclosingTypeElement;
+import static dagger.internal.codegen.xprocessing.XElements.getAnyAnnotation;
+import static dagger.internal.codegen.xprocessing.XMethodElements.hasTypeParameters;
 
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XConstructorElement;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XExecutableParameterElement;
+import androidx.room.compiler.processing.XFieldElement;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
+import androidx.room.compiler.processing.XVariableElement;
 import com.google.common.collect.ImmutableSet;
-import dagger.assisted.AssistedInject;
+import com.squareup.javapoet.ClassName;
 import dagger.internal.codegen.base.ClearableCache;
+import dagger.internal.codegen.base.DaggerSuperficialValidation;
 import dagger.internal.codegen.binding.InjectionAnnotations;
 import dagger.internal.codegen.compileroption.CompilerOptions;
-import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
+import dagger.internal.codegen.javapoet.TypeNames;
 import dagger.internal.codegen.langmodel.Accessibility;
-import dagger.internal.codegen.langmodel.DaggerElements;
 import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.Scope;
+import dagger.internal.codegen.xprocessing.XAnnotations;
+import dagger.spi.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;
 
@@ -64,53 +62,55 @@
  */
 @Singleton
 public final class InjectValidator implements ClearableCache {
+  private final XProcessingEnv processingEnv;
   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<>();
+  private final DaggerSuperficialValidation superficialValidation;
+  private final Map<XTypeElement, ValidationReport> provisionReports = new HashMap<>();
+  private final Map<XTypeElement, ValidationReport> membersInjectionReports = new HashMap<>();
 
   @Inject
   InjectValidator(
+      XProcessingEnv processingEnv,
       DaggerTypes types,
-      DaggerElements elements,
       DependencyRequestValidator dependencyRequestValidator,
       CompilerOptions compilerOptions,
       InjectionAnnotations injectionAnnotations,
-      KotlinMetadataUtil metadataUtil) {
+      DaggerSuperficialValidation superficialValidation) {
     this(
+        processingEnv,
         types,
-        elements,
         compilerOptions,
         dependencyRequestValidator,
         Optional.empty(),
         injectionAnnotations,
-        metadataUtil);
+        superficialValidation);
   }
 
   private InjectValidator(
+      XProcessingEnv processingEnv,
       DaggerTypes types,
-      DaggerElements elements,
       CompilerOptions compilerOptions,
       DependencyRequestValidator dependencyRequestValidator,
       Optional<Kind> privateAndStaticInjectionDiagnosticKind,
       InjectionAnnotations injectionAnnotations,
-      KotlinMetadataUtil metadataUtil) {
+      DaggerSuperficialValidation superficialValidation) {
+    this.processingEnv = processingEnv;
     this.types = types;
-    this.elements = elements;
     this.compilerOptions = compilerOptions;
     this.dependencyRequestValidator = dependencyRequestValidator;
     this.privateAndStaticInjectionDiagnosticKind = privateAndStaticInjectionDiagnosticKind;
     this.injectionAnnotations = injectionAnnotations;
-    this.metadataUtil = metadataUtil;
+    this.superficialValidation = superficialValidation;
   }
 
   @Override
   public void clearCache() {
-    reports.clear();
+    provisionReports.clear();
+    membersInjectionReports.clear();
   }
 
   /**
@@ -122,60 +122,104 @@
     return compilerOptions.ignorePrivateAndStaticInjectionForComponent()
         ? this
         : new InjectValidator(
+            processingEnv,
             types,
-            elements,
             compilerOptions,
             dependencyRequestValidator,
             Optional.of(Diagnostic.Kind.ERROR),
             injectionAnnotations,
-            metadataUtil);
+            superficialValidation);
   }
 
-  public ValidationReport<TypeElement> validateConstructor(ExecutableElement constructorElement) {
-    return reentrantComputeIfAbsent(reports, constructorElement, this::validateConstructorUncached);
+  public ValidationReport validate(XTypeElement typeElement) {
+    return reentrantComputeIfAbsent(provisionReports, typeElement, this::validateUncached);
   }
 
-  private ValidationReport<TypeElement> validateConstructorUncached(
-      ExecutableElement constructorElement) {
-    ValidationReport.Builder<TypeElement> builder =
-        ValidationReport.about(asType(constructorElement.getEnclosingElement()));
+  private ValidationReport validateUncached(XTypeElement typeElement) {
+    ValidationReport.Builder builder = ValidationReport.about(typeElement);
+    builder.addSubreport(validateForMembersInjectionInternal(typeElement));
 
-    if (isAnnotationPresent(constructorElement, Inject.class)
-        && isAnnotationPresent(constructorElement, AssistedInject.class)) {
+    ImmutableSet<XConstructorElement> injectConstructors =
+        ImmutableSet.<XConstructorElement>builder()
+            .addAll(injectedConstructors(typeElement))
+            .addAll(assistedInjectedConstructors(typeElement))
+            .build();
+
+    switch (injectConstructors.size()) {
+      case 0:
+        break; // Nothing to validate.
+      case 1:
+        builder.addSubreport(validateConstructor(getOnlyElement(injectConstructors)));
+        break;
+      default:
+        builder.addError(
+            String.format(
+                "Type %s may only contain one injected constructor. Found: %s",
+                typeElement,
+                injectConstructors),
+            typeElement);
+    }
+
+    return builder.build();
+  }
+
+  private ValidationReport validateConstructor(XConstructorElement constructorElement) {
+    superficialValidation.validateTypeOf(constructorElement);
+    ValidationReport.Builder builder =
+        ValidationReport.about(constructorElement.getEnclosingElement());
+
+    if (InjectionAnnotations.hasInjectAnnotation(constructorElement)
+        && constructorElement.hasAnnotation(TypeNames.ASSISTED_INJECT)) {
       builder.addError("Constructors cannot be annotated with both @Inject and @AssistedInject");
     }
 
-    Class<?> injectAnnotation =
-        isAnnotationPresent(constructorElement, Inject.class) ? Inject.class : AssistedInject.class;
+    ClassName injectAnnotation =
+        getAnyAnnotation(
+            constructorElement,
+            TypeNames.INJECT,
+            TypeNames.INJECT_JAVAX,
+            TypeNames.ASSISTED_INJECT).map(XAnnotations::getClassName).get();
 
-    if (constructorElement.getModifiers().contains(PRIVATE)) {
+    if (constructorElement.isPrivate()) {
       builder.addError(
           "Dagger does not support injection into private constructors", constructorElement);
     }
 
-    for (AnnotationMirror qualifier : injectionAnnotations.getQualifiers(constructorElement)) {
-      builder.addError(
+    // If this type has already been processed in a previous round or compilation unit then there
+    // is no reason to recheck for invalid scope annotations since it's already been checked.
+    // This allows us to skip superficial validation of constructor annotations in subsequent
+    // compilations where the annotation types may no longer be on the classpath.
+    if (!processedInPreviousRoundOrCompilationUnit(constructorElement)) {
+      superficialValidation.validateAnnotationsOf(constructorElement);
+      for (XAnnotation qualifier : injectionAnnotations.getQualifiers(constructorElement)) {
+        builder.addError(
+            String.format(
+                "@Qualifier annotations are not allowed on @%s constructors",
+                injectAnnotation.simpleName()),
+            constructorElement,
+            qualifier);
+      }
+
+      String scopeErrorMsg =
           String.format(
-              "@Qualifier annotations are not allowed on @%s constructors",
-              injectAnnotation.getSimpleName()),
-          constructorElement,
-          qualifier);
+              "@Scope annotations are not allowed on @%s constructors",
+              injectAnnotation.simpleName());
+
+      if (injectAnnotation.equals(TypeNames.INJECT)
+          || injectAnnotation.equals(TypeNames.INJECT_JAVAX)) {
+        scopeErrorMsg += "; annotate the class instead";
+      }
+
+      for (Scope scope : injectionAnnotations.getScopes(constructorElement)) {
+        builder.addError(
+            scopeErrorMsg,
+            constructorElement,
+            toXProcessing(scope.scopeAnnotation().java(), processingEnv));
+      }
     }
 
-    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()) {
+    for (XExecutableParameterElement parameter : constructorElement.getParameters()) {
+      superficialValidation.validateTypeOf(parameter);
       validateDependencyRequest(builder, parameter);
     }
 
@@ -183,81 +227,68 @@
       builder.addItem(
           String.format(
               "Dagger does not support checked exceptions on @%s constructors",
-              injectAnnotation.getSimpleName()),
+              injectAnnotation.simpleName()),
           privateMemberDiagnosticKind(),
           constructorElement);
     }
 
     checkInjectIntoPrivateClass(constructorElement, builder);
 
-    TypeElement enclosingElement =
-        MoreElements.asType(constructorElement.getEnclosingElement());
-
-    Set<Modifier> typeModifiers = enclosingElement.getModifiers();
-    if (typeModifiers.contains(ABSTRACT)) {
+    XTypeElement enclosingElement = constructorElement.getEnclosingElement();
+    if (enclosingElement.isAbstract()) {
       builder.addError(
           String.format(
               "@%s is nonsense on the constructor of an abstract class",
-              injectAnnotation.getSimpleName()),
+              injectAnnotation.simpleName()),
           constructorElement);
     }
 
-    if (enclosingElement.getNestingKind().isNested()
-        && !typeModifiers.contains(STATIC)) {
+    if (enclosingElement.isNested() && !enclosingElement.isStatic()) {
       builder.addError(
           String.format(
               "@%s constructors are invalid on inner classes. "
                   + "Did you mean to make the class static?",
-              injectAnnotation.getSimpleName()),
+              injectAnnotation.simpleName()),
           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) {
+    // Note: superficial validation of the annotations is done as part of getting the scopes.
+    ImmutableSet<Scope> scopes =
+        injectionAnnotations.getScopes(constructorElement.getEnclosingElement());
+    if (injectAnnotation.equals(TypeNames.ASSISTED_INJECT)) {
       for (Scope scope : scopes) {
         builder.addError(
             "A type with an @AssistedInject-annotated constructor cannot be scoped",
             enclosingElement,
-            scope.scopeAnnotation());
+            toXProcessing(scope.scopeAnnotation().java(), processingEnv));
       }
     } else if (scopes.size() > 1) {
       for (Scope scope : scopes) {
         builder.addError(
             "A single binding may not declare more than one @Scope",
             enclosingElement,
-            scope.scopeAnnotation());
+            toXProcessing(scope.scopeAnnotation().java(), processingEnv));
       }
     }
 
     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)) {
+  private ValidationReport validateField(XFieldElement fieldElement) {
+    superficialValidation.validateTypeOf(fieldElement);
+    ValidationReport.Builder builder = ValidationReport.about(fieldElement);
+    if (fieldElement.isFinal()) {
       builder.addError("@Inject fields may not be final", fieldElement);
     }
 
-    if (modifiers.contains(PRIVATE)) {
+    if (fieldElement.isPrivate()) {
       builder.addItem(
           "Dagger does not support injection into private fields",
           privateMemberDiagnosticKind(),
           fieldElement);
     }
 
-    if (modifiers.contains(STATIC)) {
+    if (fieldElement.isStatic()) {
       builder.addItem(
           "Dagger does not support injection into static fields",
           staticMemberDiagnosticKind(),
@@ -269,37 +300,40 @@
     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)) {
+  private ValidationReport validateMethod(XMethodElement methodElement) {
+    superficialValidation.validateTypeOf(methodElement);
+    ValidationReport.Builder builder = ValidationReport.about(methodElement);
+    if (methodElement.isAbstract()) {
       builder.addError("Methods with @Inject may not be abstract", methodElement);
     }
 
-    if (modifiers.contains(PRIVATE)) {
+    if (methodElement.isPrivate()) {
       builder.addItem(
           "Dagger does not support injection into private methods",
           privateMemberDiagnosticKind(),
           methodElement);
     }
 
-    if (modifiers.contains(STATIC)) {
+    if (methodElement.isStatic()) {
       builder.addItem(
           "Dagger does not support injection into static methods",
           staticMemberDiagnosticKind(),
           methodElement);
     }
 
-    if (!methodElement.getTypeParameters().isEmpty()) {
+    // No need to resolve type parameters since we're only checking existence.
+    if (hasTypeParameters(methodElement)) {
       builder.addError("Methods with @Inject may not declare type parameters", methodElement);
     }
 
+    // No need to resolve thrown types since we're only checking existence.
     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()) {
+    for (XExecutableParameterElement parameter : methodElement.getParameters()) {
+      superficialValidation.validateTypeOf(parameter);
       validateDependencyRequest(builder, parameter);
     }
 
@@ -307,29 +341,41 @@
   }
 
   private void validateDependencyRequest(
-      ValidationReport.Builder<?> builder, VariableElement parameter) {
-    dependencyRequestValidator.validateDependencyRequest(builder, parameter, parameter.asType());
+      ValidationReport.Builder builder, XVariableElement parameter) {
+    dependencyRequestValidator.validateDependencyRequest(builder, parameter, parameter.getType());
     dependencyRequestValidator.checkNotProducer(builder, parameter);
   }
 
-  public ValidationReport<TypeElement> validateMembersInjectionType(TypeElement typeElement) {
+  public ValidationReport validateForMembersInjection(XTypeElement typeElement) {
+    return !processedInPreviousRoundOrCompilationUnit(typeElement)
+        ? validate(typeElement) // validate everything
+        : validateForMembersInjectionInternal(typeElement); // validate only inject members
+  }
+
+  private ValidationReport validateForMembersInjectionInternal(XTypeElement typeElement) {
+    return reentrantComputeIfAbsent(
+        membersInjectionReports, typeElement, this::validateForMembersInjectionInternalUncached);
+  }
+
+  private ValidationReport validateForMembersInjectionInternalUncached(XTypeElement typeElement) {
+    superficialValidation.validateTypeOf(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);
+    ValidationReport.Builder builder = ValidationReport.about(typeElement);
     boolean hasInjectedMembers = false;
-    for (VariableElement element : ElementFilter.fieldsIn(typeElement.getEnclosedElements())) {
-      if (MoreElements.isAnnotationPresent(element, Inject.class)) {
+    for (XFieldElement field : typeElement.getDeclaredFields()) {
+      if (InjectionAnnotations.hasInjectAnnotation(field)) {
         hasInjectedMembers = true;
-        ValidationReport<VariableElement> report = validateField(element);
+        ValidationReport report = validateField(field);
         if (!report.isClean()) {
           builder.addSubreport(report);
         }
       }
     }
-    for (ExecutableElement element : ElementFilter.methodsIn(typeElement.getEnclosedElements())) {
-      if (MoreElements.isAnnotationPresent(element, Inject.class)) {
+    for (XMethodElement method : typeElement.getDeclaredMethods()) {
+      if (InjectionAnnotations.hasInjectAnnotation(method)) {
         hasInjectedMembers = true;
-        ValidationReport<ExecutableElement> report = validateMethod(element);
+        ValidationReport report = validateMethod(method);
         if (!report.isClean()) {
           builder.addSubreport(report);
         }
@@ -340,9 +386,10 @@
       checkInjectIntoPrivateClass(typeElement, builder);
       checkInjectIntoKotlinObject(typeElement, builder);
     }
-    TypeMirror superclass = typeElement.getSuperclass();
-    if (!superclass.getKind().equals(TypeKind.NONE)) {
-      ValidationReport<TypeElement> report = validateType(MoreTypes.asTypeElement(superclass));
+    if (typeElement.getSuperType() != null) {
+      superficialValidation.validateSuperTypeOf(typeElement);
+      ValidationReport report =
+          validateForMembersInjection(typeElement.getSuperType().getTypeElement());
       if (!report.isClean()) {
         builder.addSubreport(report);
       }
@@ -350,50 +397,17 @@
     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 boolean throwsCheckedExceptions(XConstructorElement constructorElement) {
+    XType runtimeException = processingEnv.findType(TypeNames.RUNTIME_EXCEPTION);
+    XType error = processingEnv.findType(TypeNames.ERROR);
+    superficialValidation.validateThrownTypesOf(constructorElement);
+    return !constructorElement.getThrownTypes().stream()
+        .allMatch(type -> types.isSubtype(type, runtimeException) || types.isSubtype(type, error));
   }
 
-  private void checkInjectIntoPrivateClass(
-      Element element, ValidationReport.Builder<TypeElement> builder) {
-    if (!Accessibility.isElementAccessibleFromOwnPackage(
-        DaggerElements.closestEnclosingTypeElement(element))) {
+  private void checkInjectIntoPrivateClass(XElement element, ValidationReport.Builder builder) {
+    if (!Accessibility.isElementAccessibleFromOwnPackage(closestEnclosingTypeElement(element))) {
       builder.addItem(
           "Dagger does not support injection into private classes",
           privateMemberDiagnosticKind(),
@@ -401,9 +415,8 @@
     }
   }
 
-  private void checkInjectIntoKotlinObject(
-      TypeElement element, ValidationReport.Builder<TypeElement> builder) {
-    if (metadataUtil.isObjectClass(element) || metadataUtil.isCompanionObjectClass(element)) {
+  private void checkInjectIntoKotlinObject(XTypeElement element, ValidationReport.Builder builder) {
+    if (element.isKotlinObject() || element.isCompanionObject()) {
       builder.addError("Dagger does not support injection into Kotlin objects", element);
     }
   }
@@ -417,4 +430,12 @@
     return privateAndStaticInjectionDiagnosticKind.orElse(
         compilerOptions.staticMemberValidationKind());
   }
+
+  private boolean processedInPreviousRoundOrCompilationUnit(XConstructorElement injectConstructor) {
+    return processingEnv.findTypeElement(factoryNameForElement(injectConstructor)) != null;
+  }
+
+  private boolean processedInPreviousRoundOrCompilationUnit(XTypeElement membersInjectedType) {
+    return processingEnv.findTypeElement(membersInjectorNameForType(membersInjectedType)) != null;
+  }
 }
diff --git a/java/dagger/internal/codegen/validation/MapKeyValidator.java b/java/dagger/internal/codegen/validation/MapKeyValidator.java
index 6aa5147..1ec6495 100644
--- a/java/dagger/internal/codegen/validation/MapKeyValidator.java
+++ b/java/dagger/internal/codegen/validation/MapKeyValidator.java
@@ -16,20 +16,17 @@
 
 package dagger.internal.codegen.validation;
 
-import static javax.lang.model.util.ElementFilter.methodsIn;
-
+import androidx.room.compiler.processing.XAnnotationKt;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XTypeElement;
+import androidx.room.compiler.processing.XTypeKt;
 import dagger.MapKey;
+import dagger.internal.codegen.javapoet.TypeNames;
 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.
- */
+/** 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;
@@ -39,22 +36,26 @@
     this.elements = elements;
   }
 
-  public ValidationReport<Element> validate(Element element) {
-    ValidationReport.Builder<Element> builder = ValidationReport.about(element);
-    List<ExecutableElement> members = methodsIn(((TypeElement) element).getEnclosedElements());
+  public ValidationReport validate(XTypeElement element) {
+    ValidationReport.Builder builder = ValidationReport.about(element);
+    List<XMethodElement> members = element.getDeclaredMethods();
     if (members.isEmpty()) {
       builder.addError("Map key annotations must have members", element);
-    } else if (element.getAnnotation(MapKey.class).unwrapValue()) {
+    } else if (XAnnotationKt.get(
+        element.getAnnotation(TypeNames.MAP_KEY), "unwrapValue", Boolean.class)) {
       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) {
+      } else if (XTypeKt.isArray(members.get(0).getReturnType())) {
         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>");
+              + "dependency for the annotation, "
+              + "\"com.google.auto.value:auto-value-annotations:<current version>\", "
+              + "and the annotation processor, "
+              + "\"com.google.auto.value:auto-value:<current version>\"");
     }
     return builder.build();
   }
diff --git a/java/dagger/internal/codegen/validation/MembersInjectionValidator.java b/java/dagger/internal/codegen/validation/MembersInjectionValidator.java
index afa6270..0ff11b8 100644
--- a/java/dagger/internal/codegen/validation/MembersInjectionValidator.java
+++ b/java/dagger/internal/codegen/validation/MembersInjectionValidator.java
@@ -16,20 +16,19 @@
 
 package dagger.internal.codegen.validation;
 
+import static androidx.room.compiler.processing.XTypeKt.isArray;
 import static com.google.common.base.Preconditions.checkArgument;
+import static dagger.internal.codegen.xprocessing.XTypes.asArray;
+import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
+import static dagger.internal.codegen.xprocessing.XTypes.isPrimitive;
+import static dagger.internal.codegen.xprocessing.XTypes.isRawParameterizedType;
 
-import com.google.auto.common.MoreElements;
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XType;
 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
@@ -44,11 +43,11 @@
   }
 
   /** 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);
+  ValidationReport validateMembersInjectionRequest(
+      XElement requestElement, XType membersInjectedType) {
+    ValidationReport.Builder report = ValidationReport.about(requestElement);
     checkQualifiers(report, requestElement);
-    membersInjectedType.accept(VALIDATE_MEMBERS_INJECTED_TYPE, report);
+    checkMembersInjectedType(report, membersInjectedType);
     return report.build();
   }
 
@@ -57,96 +56,61 @@
    *
    * @throws IllegalArgumentException if the method doesn't have exactly one parameter
    */
-  ValidationReport<ExecutableElement> validateMembersInjectionMethod(
-      ExecutableElement method, TypeMirror membersInjectedType) {
+  ValidationReport validateMembersInjectionMethod(
+      XMethodElement method, XType membersInjectedType) {
     checkArgument(
         method.getParameters().size() == 1, "expected a method with one parameter: %s", method);
 
-    ValidationReport.Builder<ExecutableElement> report = ValidationReport.about(method);
+    ValidationReport.Builder report = ValidationReport.about(method);
     checkQualifiers(report, method);
     checkQualifiers(report, method.getParameters().get(0));
-    membersInjectedType.accept(VALIDATE_MEMBERS_INJECTED_TYPE, report);
+    checkMembersInjectedType(report, membersInjectedType);
     return report.build();
   }
 
-  private void checkQualifiers(ValidationReport.Builder<?> report, Element element) {
-    for (AnnotationMirror qualifier : injectionAnnotations.getQualifiers(element)) {
+  private void checkQualifiers(ValidationReport.Builder report, XElement element) {
+    for (XAnnotation 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;
-            }
+  private void checkMembersInjectedType(ValidationReport.Builder report, XType type) {
+    // Only declared types can be members-injected.
+    if (!isDeclared(type)) {
+      report.addError("Cannot inject members into " + type);
+      return;
+    }
 
-            @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;
-            }
-          };
+    // 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 (isRawParameterizedType(type)) {
+      report.addError("Cannot inject members into raw type " + type);
+      return;
+    }
+
+    // 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.
+    if (!type.getTypeArguments().stream().allMatch(this::isResolvableTypeArgument)) {
+      report.addError("Cannot inject members into types with unbounded type arguments: " + type);
+    }
+  }
 
   // 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;
-                    }
+  private boolean isResolvableTypeArgument(XType type) {
+    return isDeclared(type)
+        || (isArray(type) && isResolvableArrayComponentType(asArray(type).getComponentType()));
+  }
 
-                    @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;
-        }
-      };
+  private boolean isResolvableArrayComponentType(XType type) {
+    if (isDeclared(type)) {
+      return type.getTypeArguments().stream().allMatch(this::isResolvableTypeArgument);
+    } else if (isArray(type)) {
+      return isResolvableArrayComponentType(asArray(type).getComponentType());
+    }
+    return isPrimitive(type);
+  }
 }
diff --git a/java/dagger/internal/codegen/validation/ModuleValidator.java b/java/dagger/internal/codegen/validation/ModuleValidator.java
index 02ac06a..4d3ea3b 100644
--- a/java/dagger/internal/codegen/validation/ModuleValidator.java
+++ b/java/dagger/internal/codegen/validation/ModuleValidator.java
@@ -16,35 +16,32 @@
 
 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.ComponentCreatorAnnotation.getCreatorAnnotations;
 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.DaggerCollectors.toOptional;
 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 dagger.internal.codegen.xprocessing.XAnnotations.getClassName;
+import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
+import static dagger.internal.codegen.xprocessing.XElements.hasAnyAnnotation;
+import static dagger.internal.codegen.xprocessing.XTypeElements.hasTypeParameters;
+import static dagger.internal.codegen.xprocessing.XTypeElements.isEffectivelyPrivate;
+import static dagger.internal.codegen.xprocessing.XTypeElements.isEffectivelyPublic;
+import static dagger.internal.codegen.xprocessing.XTypes.areEquivalentTypes;
+import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
 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 static kotlin.streams.jdk8.StreamsKt.asStream;
 
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
-import com.google.auto.common.Visibility;
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XAnnotationValue;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableListMultimap;
@@ -53,22 +50,19 @@
 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 com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.TypeName;
+import dagger.internal.codegen.base.ComponentCreatorAnnotation;
+import dagger.internal.codegen.base.DaggerSuperficialValidation;
+import dagger.internal.codegen.base.ModuleKind;
 import dagger.internal.codegen.binding.BindingGraphFactory;
-import dagger.internal.codegen.binding.ComponentCreatorAnnotation;
 import dagger.internal.codegen.binding.ComponentDescriptorFactory;
+import dagger.internal.codegen.binding.InjectionAnnotations;
 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 dagger.internal.codegen.javapoet.TypeNames;
+import dagger.internal.codegen.xprocessing.XElements;
+import dagger.spi.model.BindingGraph;
+import dagger.spi.model.Scope;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.EnumSet;
@@ -76,34 +70,25 @@
 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. */
+/**
+ * A {@linkplain ValidationReport validator} for {@link dagger.Module}s or {@link
+ * dagger.producers.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 =
+  private static final ImmutableSet<ClassName> SUBCOMPONENT_TYPES =
+      ImmutableSet.of(TypeNames.SUBCOMPONENT, TypeNames.PRODUCTION_SUBCOMPONENT);
+  private static final ImmutableSet<ClassName> SUBCOMPONENT_CREATOR_TYPES =
       ImmutableSet.of(
-          Subcomponent.Builder.class,
-          Subcomponent.Factory.class,
-          ProductionSubcomponent.Builder.class,
-          ProductionSubcomponent.Factory.class);
+          TypeNames.SUBCOMPONENT_BUILDER,
+          TypeNames.SUBCOMPONENT_FACTORY,
+          TypeNames.PRODUCTION_SUBCOMPONENT_BUILDER,
+          TypeNames.PRODUCTION_SUBCOMPONENT_FACTORY);
   private static final Optional<Class<?>> ANDROID_PROCESSOR;
   private static final String CONTRIBUTES_ANDROID_INJECTOR_NAME =
       "dagger.android.ContributesAndroidInjector";
@@ -119,35 +104,35 @@
     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<>();
+  private final InjectionAnnotations injectionAnnotations;
+  private final DaggerSuperficialValidation superficialValidation;
+  private final XProcessingEnv processingEnv;
+  private final Map<XTypeElement, ValidationReport> cache = new HashMap<>();
+  private final Set<XTypeElement> 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;
+      InjectionAnnotations injectionAnnotations,
+      DaggerSuperficialValidation superficialValidation,
+      XProcessingEnv processingEnv) {
     this.anyBindingMethodValidator = anyBindingMethodValidator;
     this.methodSignatureFormatter = methodSignatureFormatter;
     this.componentDescriptorFactory = componentDescriptorFactory;
     this.bindingGraphFactory = bindingGraphFactory;
     this.bindingGraphValidator = bindingGraphValidator;
-    this.metadataUtil = metadataUtil;
+    this.injectionAnnotations = injectionAnnotations;
+    this.superficialValidation = superficialValidation;
+    this.processingEnv = processingEnv;
   }
 
   /**
@@ -156,49 +141,44 @@
    * 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}.
+   * <p>This logic depends on this method being called before {@linkplain #validate(XTypeElement)
+   * validating} any module or {@linkplain #validateReferencedModules(XTypeElement, ModuleKind, Set,
+   * DiagnosticReporter.Builder) component}.
    */
-  public void addKnownModules(Collection<TypeElement> modules) {
+  public void addKnownModules(Collection<XTypeElement> modules) {
     knownModules.addAll(modules);
   }
 
   /** Returns a validation report for a module type. */
-  public ValidationReport<TypeElement> validate(TypeElement module) {
+  public ValidationReport validate(XTypeElement module) {
     return validate(module, new HashSet<>());
   }
 
-  private ValidationReport<TypeElement> validate(
-      TypeElement module, Set<TypeElement> visitedModules) {
+  private ValidationReport validate(XTypeElement module, Set<XTypeElement> 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);
+  private ValidationReport validateUncached(XTypeElement module, Set<XTypeElement> visitedModules) {
+    ValidationReport.Builder 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) {
+    Optional<XType> contributesAndroidInjector =
+        Optional.ofNullable(processingEnv.findTypeElement(CONTRIBUTES_ANDROID_INJECTOR_NAME))
+            .map(XTypeElement::getType);
+    List<XMethodElement> moduleMethods = module.getDeclaredMethods();
+    List<XMethodElement> bindingMethods = new ArrayList<>();
+    for (XMethodElement moduleMethod : moduleMethods) {
       if (anyBindingMethodValidator.isBindingMethod(moduleMethod)) {
         builder.addSubreport(anyBindingMethodValidator.validate(moduleMethod));
         bindingMethods.add(moduleMethod);
       }
 
-      for (AnnotationMirror annotation : moduleMethod.getAnnotationMirrors()) {
+      for (XAnnotation annotation : moduleMethod.getAllAnnotations()) {
         if (!ANDROID_PROCESSOR.isPresent()
-            && MoreTypes.equivalence()
-                .equivalent(contributesAndroidInjector, annotation.getAnnotationType())) {
+            && contributesAndroidInjector.isPresent()
+            && areEquivalentTypes(contributesAndroidInjector.get(), annotation.getType())) {
           builder.addSubreport(
               ValidationReport.about(moduleMethod)
                   .addError(
@@ -219,30 +199,31 @@
       builder.addError(
           String.format(
               "A @%s may not contain both non-static and abstract binding methods",
-              moduleKind.annotation().getSimpleName()));
+              moduleKind.annotation().simpleName()));
     }
 
     validateModuleVisibility(module, moduleKind, builder);
 
-    ImmutableListMultimap<Name, ExecutableElement> bindingMethodsByName =
-        Multimaps.index(bindingMethods, ExecutableElement::getSimpleName);
+    ImmutableListMultimap<String, XMethodElement> bindingMethodsByName =
+        Multimaps.index(bindingMethods, XElements::getSimpleName);
 
     validateMethodsWithSameName(builder, bindingMethodsByName);
-    if (module.getKind() != ElementKind.INTERFACE) {
+    if (!module.isInterface()) {
       validateBindingMethodOverrides(
           module,
           builder,
-          Multimaps.index(moduleMethods, ExecutableElement::getSimpleName),
+          Multimaps.index(moduleMethods, XElements::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);
-    }
+    validateSelfCycles(module, moduleKind, builder);
+    module.getEnclosedTypeElements().stream()
+        .filter(XTypeElement::isCompanionObject)
+        .collect(toOptional())
+        .ifPresent(companionModule -> validateCompanionModule(companionModule, builder));
 
     if (builder.build().isClean()
         && bindingGraphValidator.shouldDoFullBindingGraphValidation(module)) {
@@ -253,89 +234,74 @@
   }
 
   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;
-                }
+      XTypeElement subject, ModuleKind moduleKind, ValidationReport.Builder builder) {
+    XAnnotation moduleAnnotation = moduleKind.getModuleAnnotation(subject);
+    for (XAnnotationValue subcomponentValue :
+        moduleAnnotation.getAsAnnotationValueList("subcomponents")) {
+      XType type = subcomponentValue.asType();
+      if (!isDeclared(type)) {
+        builder.addError(
+            type + " is not a valid subcomponent type",
+            subject,
+            moduleAnnotation,
+            subcomponentValue);
+        continue;
+      }
 
-                @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);
+      XTypeElement subcomponentElement = type.getTypeElement();
+      if (hasAnyAnnotation(subcomponentElement, SUBCOMPONENT_TYPES)) {
+        validateSubcomponentHasBuilder(subject, subcomponentElement, moduleAnnotation, builder);
+      } else {
+        builder.addError(
+            hasAnyAnnotation(subcomponentElement, SUBCOMPONENT_CREATOR_TYPES)
+                ? moduleSubcomponentsIncludesCreator(subcomponentElement)
+                : moduleSubcomponentsIncludesNonSubcomponent(subcomponentElement),
+            subject,
+            moduleAnnotation,
+            subcomponentValue);
+      }
     }
   }
 
-  private static String moduleSubcomponentsIncludesNonSubcomponent(TypeElement notSubcomponent) {
+  private static String moduleSubcomponentsIncludesNonSubcomponent(XTypeElement notSubcomponent) {
     return notSubcomponent.getQualifiedName()
         + " is not a @Subcomponent or @ProductionSubcomponent";
   }
 
-  private static String moduleSubcomponentsIncludesCreator(
-      TypeElement moduleSubcomponentsAttribute) {
-    TypeElement subcomponentType =
-        MoreElements.asType(moduleSubcomponentsAttribute.getEnclosingElement());
+  private String moduleSubcomponentsIncludesCreator(XTypeElement moduleSubcomponentsAttribute) {
+    XTypeElement subcomponentType = moduleSubcomponentsAttribute.getEnclosingTypeElement();
     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(),
+        subcomponentAnnotation(subcomponentType, superficialValidation).get().simpleName(),
         creatorAnnotation.creatorKind().typeName(),
         subcomponentType.getQualifiedName());
   }
 
-  private static void validateSubcomponentHasBuilder(
-      TypeElement subcomponentAttribute,
-      AnnotationMirror moduleAnnotation,
-      ValidationReport.Builder<TypeElement> builder) {
+  private void validateSubcomponentHasBuilder(
+      XTypeElement subject,
+      XTypeElement subcomponentAttribute,
+      XAnnotation moduleAnnotation,
+      ValidationReport.Builder builder) {
     if (getSubcomponentCreator(subcomponentAttribute).isPresent()) {
       return;
     }
     builder.addError(
         moduleSubcomponentsDoesntHaveCreator(subcomponentAttribute, moduleAnnotation),
-        builder.getSubject(),
+        subject,
         moduleAnnotation);
   }
 
-  private static String moduleSubcomponentsDoesntHaveCreator(
-      TypeElement subcomponent, AnnotationMirror moduleAnnotation) {
+  private String moduleSubcomponentsDoesntHaveCreator(
+      XTypeElement subcomponent, XAnnotation 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));
+        subcomponentAnnotation(subcomponent, superficialValidation).get().simpleName(),
+        getClassName(moduleAnnotation).simpleName());
   }
 
   enum ModuleMethodKind {
@@ -344,10 +310,10 @@
     STATIC_BINDING,
     ;
 
-    static ModuleMethodKind ofMethod(ExecutableElement moduleMethod) {
-      if (moduleMethod.getModifiers().contains(STATIC)) {
+    static ModuleMethodKind ofMethod(XMethodElement moduleMethod) {
+      if (moduleMethod.isStatic()) {
         return STATIC_BINDING;
-      } else if (moduleMethod.getModifiers().contains(ABSTRACT)) {
+      } else if (moduleMethod.isAbstract()) {
         return ABSTRACT_DECLARATION;
       } else {
         return INSTANCE_BINDING;
@@ -355,38 +321,34 @@
     }
   }
 
-  private void validateModifiers(
-      TypeElement subject, ValidationReport.Builder<TypeElement> builder) {
+  private void validateModifiers(XTypeElement subject, ValidationReport.Builder 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)) {
+    if (hasTypeParameters(subject) && !subject.isAbstract()) {
       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);
-        }
-      }
-    }
+      ValidationReport.Builder builder, ListMultimap<String, XMethodElement> bindingMethodsByName) {
+    bindingMethodsByName.asMap().values().stream()
+        .filter(methods -> methods.size() > 1)
+        .flatMap(Collection::stream)
+        .forEach(
+            duplicateMethod -> {
+              builder.addError(
+                  "Cannot have more than one binding method with the same name in a single module",
+                  duplicateMethod);
+            });
   }
 
   private void validateReferencedModules(
-      TypeElement subject,
+      XTypeElement subject,
       ModuleKind moduleKind,
-      Set<TypeElement> visitedModules,
-      ValidationReport.Builder<TypeElement> builder) {
+      Set<XTypeElement> visitedModules,
+      ValidationReport.Builder builder) {
     // Validate that all the modules we include are valid for inclusion.
-    AnnotationMirror mirror = moduleKind.getModuleAnnotation(subject);
+    XAnnotation mirror = moduleKind.getModuleAnnotation(subject);
     builder.addSubreport(
         validateReferencedModules(
             subject, mirror, moduleKind.legalIncludedModuleKinds(), visitedModules));
@@ -407,80 +369,87 @@
    *     {@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,
+  ValidationReport validateReferencedModules(
+      XTypeElement annotatedType,
+      XAnnotation 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());
+      Set<XTypeElement> visitedModules) {
+    superficialValidation.validateAnnotationOf(annotatedType, annotation);
 
-    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;
-                }
+    ValidationReport.Builder subreport = ValidationReport.about(annotatedType);
+    // TODO(bcorso): Consider creating a DiagnosticLocation object to encapsulate the location in a
+    // single object to avoid duplication across all reported errors
+    for (XAnnotationValue includedModule : getModules(annotation)) {
+      XType type = includedModule.asType();
+      if (!isDeclared(type)) {
+        subreport.addError(
+            String.format("%s is not a valid module type.", type),
+            annotatedType,
+            annotation,
+            includedModule);
+        continue;
+      }
 
-                @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;
-                }
+      XTypeElement module = type.getTypeElement();
+      if (hasTypeParameters(module)) {
+        subreport.addError(
+            String.format(
+                "%s is listed as a module, but has type parameters", module.getQualifiedName()),
+            annotatedType,
+            annotation,
+            includedModule);
+      }
 
-                @FormatMethod
-                private void reportError(String format, Object... args) {
-                  subreport.addError(
-                      String.format(format, args), annotatedType, annotation, includedModule);
-                }
-              },
-              null);
+      ImmutableSet<ClassName> validModuleAnnotations =
+          validModuleKinds.stream().map(ModuleKind::annotation).collect(toImmutableSet());
+      if (!hasAnyAnnotation(module, validModuleAnnotations)) {
+        subreport.addError(
+            String.format(
+                "%s is listed as a module, but is not annotated with %s",
+                module.getQualifiedName(),
+                (validModuleAnnotations.size() > 1 ? "one of " : "")
+                    + validModuleAnnotations.stream()
+                        .map(otherClass -> "@" + otherClass.simpleName())
+                        .collect(joining(", "))),
+            annotatedType,
+            annotation,
+            includedModule);
+      } else if (knownModules.contains(module) && !validate(module, visitedModules).isClean()) {
+        subreport.addError(
+            String.format("%s has errors", module.getQualifiedName()),
+            annotatedType,
+            annotation,
+            includedModule);
+      }
+      if (module.isCompanionObject()) {
+        subreport.addError(
+            String.format(
+                "%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()),
+            annotatedType,
+            annotation,
+            includedModule);
+      }
     }
     return subreport.build();
   }
 
-  private static ImmutableList<AnnotationValue> getModules(AnnotationMirror annotation) {
+  private static ImmutableList<XAnnotationValue> getModules(XAnnotation annotation) {
     if (isModuleAnnotation(annotation)) {
-      return moduleAnnotation(annotation).includesAsAnnotationValues();
+      return ImmutableList.copyOf(annotation.getAsAnnotationValueList("includes"));
     }
     if (isComponentAnnotation(annotation)) {
-      return componentAnnotation(annotation).moduleValues();
+      return ImmutableList.copyOf(annotation.getAsAnnotationValueList("modules"));
     }
     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) {
+      XTypeElement subject,
+      ValidationReport.Builder builder,
+      ImmutableListMultimap<String, XMethodElement> moduleMethodsByName,
+      ImmutableListMultimap<String, XMethodElement> bindingMethodsByName) {
     // For every binding method, confirm it overrides nothing *and* nothing overrides it.
     // Consider the following hierarchy:
     // class Parent {
@@ -496,22 +465,22 @@
     // 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 =
+    XTypeElement currentClass = subject;
+    XType objectType = processingEnv.findType(TypeName.OBJECT);
+    // We keep track of visited methods so we don't spam with multiple failures.
+    Set<XMethodElement> visitedMethods = Sets.newHashSet();
+    ListMultimap<String, XMethodElement> 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();
+    while (!currentClass.getSuperType().isSameType(objectType)) {
+      currentClass = currentClass.getSuperType().getTypeElement();
+      List<XMethodElement> superclassMethods = currentClass.getDeclaredMethods();
+      for (XMethodElement superclassMethod : superclassMethods) {
+        String name = getSimpleName(superclassMethod);
         // 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)) {
+        for (XMethodElement bindingMethod : bindingMethodsByName.get(name)) {
+          if (visitedMethods.add(bindingMethod)
+              && bindingMethod.overrides(superclassMethod, subject)) {
             builder.addError(
                 String.format(
                     "Binding methods may not override another method. Overrides: %s",
@@ -521,9 +490,8 @@
         }
         // 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)) {
+          for (XMethodElement method : allMethodsByName.get(name)) {
+            if (visitedMethods.add(method) && method.overrides(superclassMethod, subject)) {
               builder.addError(
                   String.format(
                       "Binding methods may not be overridden in modules. Overrides: %s",
@@ -532,54 +500,41 @@
             }
           }
         }
-        allMethodsByName.put(superclassMethod.getSimpleName(), superclassMethod);
+        // TODO(b/202521399): Add a test for cases that add to this map.
+        allMethodsByName.put(getSimpleName(superclassMethod), 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)) {
+      XTypeElement moduleElement, ModuleKind moduleKind, ValidationReport.Builder reportBuilder) {
+    if (moduleElement.isPrivate()) {
       reportBuilder.addError("Modules cannot be private.", moduleElement);
-    } else if (moduleEffectiveVisibility.equals(PRIVATE)) {
+    } else if (isEffectivelyPrivate(moduleElement)) {
       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);
-          }
-        }
+    if (isEffectivelyPublic(moduleElement)) {
+      ImmutableSet<XTypeElement> invalidVisibilityIncludes =
+          getModuleIncludesWithInvalidVisibility(moduleKind.getModuleAnnotation(moduleElement));
+      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))
+  private ImmutableSet<XTypeElement> getModuleIncludesWithInvalidVisibility(
+      XAnnotation moduleAnnotation) {
+    return moduleAnnotation.getAnnotationValue("includes").asTypeList().stream()
+        .map(XType::getTypeElement)
+        .filter(include -> !isEffectivelyPublic(include))
         .filter(this::requiresModuleInstance)
         .collect(toImmutableSet());
   }
@@ -590,103 +545,82 @@
    * {@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 boolean requiresModuleInstance(XTypeElement module) {
+    // Note: We use XTypeElement#getAllMethods() rather than XTypeElement#getDeclaredMethods() here
+    // because 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 !(module.isKotlinObject() || module.isCompanionObject())
+        && !asStream(module.getAllMethods())
+            .filter(anyBindingMethodValidator::isBindingMethod)
+            .allMatch(method -> method.isAbstract() || method.isStatic());
   }
 
   private void validateNoScopeAnnotationsOnModuleElement(
-      TypeElement module, ModuleKind moduleKind, ValidationReport.Builder<TypeElement> report) {
-    for (AnnotationMirror scope : getAnnotatedAnnotations(module, Scope.class)) {
+      XTypeElement module, ModuleKind moduleKind, ValidationReport.Builder report) {
+    for (Scope scope : injectionAnnotations.getScopes(module)) {
       report.addError(
           String.format(
               "@%ss cannot be scoped. Did you mean to scope a method instead?",
-              moduleKind.annotation().getSimpleName()),
+              moduleKind.annotation().simpleName()),
           module,
-          scope);
+          scope.scopeAnnotation().xprocessing());
     }
   }
 
   private void validateSelfCycles(
-      TypeElement module, ValidationReport.Builder<TypeElement> builder) {
-    ModuleAnnotation moduleAnnotation = moduleAnnotation(module).get();
-    moduleAnnotation
-        .includesAsAnnotationValues()
+      XTypeElement module, ModuleKind moduleKind, ValidationReport.Builder builder) {
+    XAnnotation moduleAnnotation = moduleKind.getModuleAnnotation(module);
+    moduleAnnotation.getAsAnnotationValueList("includes").stream()
+        .filter(includedModule -> areEquivalentTypes(module.getType(), includedModule.asType()))
         .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));
+            includedModule ->
+                builder.addError(
+                    String.format(
+                        "@%s cannot include themselves.", moduleKind.annotation().simpleName()),
+                    module,
+                    moduleAnnotation,
+                    includedModule));
   }
 
   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);
+      XTypeElement companionModule, ValidationReport.Builder builder) {
+    List<XMethodElement> companionBindingMethods = new ArrayList<>();
+    for (XMethodElement companionMethod : companionModule.getDeclaredMethods()) {
+      if (anyBindingMethodValidator.isBindingMethod(companionMethod)) {
+        builder.addSubreport(anyBindingMethodValidator.validate(companionMethod));
+        companionBindingMethods.add(companionMethod);
       }
 
       // 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)) {
+      if (companionMethod.hasAnnotation(TypeNames.OVERRIDE)) {
         builder.addError(
-            "Binding method in companion object may not override another method.",
-            companionModuleMethod);
+            "Binding method in companion object may not override another method.", companionMethod);
       }
 
       // 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);
+    ImmutableListMultimap<String, XMethodElement> bindingMethodsByName =
+        Multimaps.index(companionBindingMethods, XElements::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)) {
+    if (!companionBindingMethods.isEmpty() && companionModule.isPrivate()) {
       builder.addError(
           "A Companion Module with binding methods cannot be private.", companionModule);
     }
   }
 
-  private void validateModuleBindings(
-      TypeElement module, ValidationReport.Builder<TypeElement> report) {
+  private void validateModuleBindings(XTypeElement module, ValidationReport.Builder report) {
     BindingGraph bindingGraph =
-        bindingGraphFactory.create(
-                componentDescriptorFactory.moduleComponentDescriptor(module), true)
+        bindingGraphFactory
+            .create(componentDescriptorFactory.moduleComponentDescriptor(module), true)
             .topLevelBindingGraph();
     if (!bindingGraphValidator.isValid(bindingGraph)) {
       // Since the validator uses a DiagnosticReporter to report errors, the ValdiationReport won't
diff --git a/java/dagger/internal/codegen/validation/MonitoringModuleGenerator.java b/java/dagger/internal/codegen/validation/MonitoringModuleGenerator.java
index 1c2eb1c..fbbd18f 100644
--- a/java/dagger/internal/codegen/validation/MonitoringModuleGenerator.java
+++ b/java/dagger/internal/codegen/validation/MonitoringModuleGenerator.java
@@ -26,40 +26,36 @@
 import static javax.lang.model.element.Modifier.PRIVATE;
 import static javax.lang.model.element.Modifier.STATIC;
 
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XFiler;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.common.collect.ImmutableList;
-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.javapoet.TypeNames;
 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 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> {
+final class MonitoringModuleGenerator extends SourceFileGenerator<XTypeElement> {
 
   @Inject
-  MonitoringModuleGenerator(Filer filer, DaggerElements elements, SourceVersion sourceVersion) {
+  MonitoringModuleGenerator(XFiler filer, DaggerElements elements, SourceVersion sourceVersion) {
     super(filer, elements, sourceVersion);
   }
 
   @Override
-  public Element originatingElement(TypeElement componentElement) {
+  public XElement originatingElement(XTypeElement componentElement) {
     return componentElement;
   }
 
   @Override
-  public ImmutableList<TypeSpec.Builder> topLevelTypes(TypeElement componentElement) {
+  public ImmutableList<TypeSpec.Builder> topLevelTypes(XTypeElement componentElement) {
     return ImmutableList.of(
         classBuilder(SourceFiles.generatedMonitoringModuleName(componentElement))
             .addAnnotation(Module.class)
@@ -81,17 +77,16 @@
         .build();
   }
 
-  private MethodSpec monitor(TypeElement componentElement) {
+  private MethodSpec monitor(XTypeElement componentElement) {
     return methodBuilder("monitor")
-        .returns(ProductionComponentMonitor.class)
+        .returns(TypeNames.PRODUCTION_COMPONENT_MONITOR)
         .addModifiers(STATIC)
-        .addAnnotation(Provides.class)
-        .addAnnotation(ProductionScope.class)
-        .addParameter(providerOf(ClassName.get(componentElement.asType())), "component")
-        .addParameter(
-            providerOf(setOf(PRODUCTION_COMPONENT_MONITOR_FACTORY)), "factories")
+        .addAnnotation(TypeNames.PROVIDES)
+        .addAnnotation(TypeNames.PRODUCTION_SCOPE)
+        .addParameter(providerOf(componentElement.getType().getTypeName()), "component")
+        .addParameter(providerOf(setOf(PRODUCTION_COMPONENT_MONITOR_FACTORY)), "factories")
         .addStatement(
-            "return $T.createMonitorForComponent(component, factories)", Monitors.class)
+            "return $T.createMonitorForComponent(component, factories)", TypeNames.MONITORS)
         .build();
   }
 }
diff --git a/java/dagger/internal/codegen/validation/MonitoringModuleProcessingStep.java b/java/dagger/internal/codegen/validation/MonitoringModuleProcessingStep.java
index 5d4c64e..e7ba8fd 100644
--- a/java/dagger/internal/codegen/validation/MonitoringModuleProcessingStep.java
+++ b/java/dagger/internal/codegen/validation/MonitoringModuleProcessingStep.java
@@ -16,40 +16,35 @@
 
 package dagger.internal.codegen.validation;
 
-import com.google.auto.common.MoreElements;
+import androidx.room.compiler.processing.XMessager;
+import androidx.room.compiler.processing.XTypeElement;
 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 com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.javapoet.TypeNames;
 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}.
+ * dagger.producers.ProductionComponent} or {@link dagger.producers.ProductionSubcomponent}.
  */
-public final class MonitoringModuleProcessingStep extends TypeCheckingProcessingStep<TypeElement> {
-  private final Messager messager;
+public final class MonitoringModuleProcessingStep extends TypeCheckingProcessingStep<XTypeElement> {
+  private final XMessager messager;
   private final MonitoringModuleGenerator monitoringModuleGenerator;
 
   @Inject
   MonitoringModuleProcessingStep(
-      Messager messager, MonitoringModuleGenerator monitoringModuleGenerator) {
-    super(MoreElements::asType);
+      XMessager messager, MonitoringModuleGenerator monitoringModuleGenerator) {
     this.messager = messager;
     this.monitoringModuleGenerator = monitoringModuleGenerator;
   }
 
   @Override
-  public Set<? extends Class<? extends Annotation>> annotations() {
-    return ImmutableSet.of(ProductionComponent.class, ProductionSubcomponent.class);
+  public ImmutableSet<ClassName> annotationClassNames() {
+    return ImmutableSet.of(TypeNames.PRODUCTION_COMPONENT, TypeNames.PRODUCTION_SUBCOMPONENT);
   }
 
   @Override
-  protected void process(
-      TypeElement element, ImmutableSet<Class<? extends Annotation>> annotations) {
-      monitoringModuleGenerator.generate(MoreElements.asType(element), messager);
+  protected void process(XTypeElement productionComponent, ImmutableSet<ClassName> annotations) {
+    monitoringModuleGenerator.generate(productionComponent, messager);
   }
 }
diff --git a/java/dagger/internal/codegen/validation/MultibindingAnnotationsProcessingStep.java b/java/dagger/internal/codegen/validation/MultibindingAnnotationsProcessingStep.java
index fd75eac..4771de2 100644
--- a/java/dagger/internal/codegen/validation/MultibindingAnnotationsProcessingStep.java
+++ b/java/dagger/internal/codegen/validation/MultibindingAnnotationsProcessingStep.java
@@ -16,45 +16,39 @@
 
 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 androidx.room.compiler.processing.XExecutableElement;
+import androidx.room.compiler.processing.XMessager;
 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 com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.javapoet.TypeNames;
 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.
+ * Processing step that verifies that {@link dagger.multibindings.IntoSet}, {@link
+ * dagger.multibindings.ElementsIntoSet} and {@link dagger.multibindings.IntoMap} are not present on
+ * non-binding methods.
  */
 public final class MultibindingAnnotationsProcessingStep
-    extends TypeCheckingProcessingStep<ExecutableElement> {
+    extends TypeCheckingProcessingStep<XExecutableElement> {
   private final AnyBindingMethodValidator anyBindingMethodValidator;
-  private final Messager messager;
+  private final XMessager messager;
 
   @Inject
   MultibindingAnnotationsProcessingStep(
-      AnyBindingMethodValidator anyBindingMethodValidator, Messager messager) {
-    super(MoreElements::asExecutable);
+      AnyBindingMethodValidator anyBindingMethodValidator, XMessager messager) {
     this.anyBindingMethodValidator = anyBindingMethodValidator;
     this.messager = messager;
   }
 
   @Override
-  public Set<? extends Class<? extends Annotation>> annotations() {
-    return ImmutableSet.of(IntoSet.class, ElementsIntoSet.class, IntoMap.class);
+  public ImmutableSet<ClassName> annotationClassNames() {
+    return ImmutableSet.of(TypeNames.INTO_SET, TypeNames.ELEMENTS_INTO_SET, TypeNames.INTO_MAP);
   }
 
   @Override
-  protected void process(
-      ExecutableElement method, ImmutableSet<Class<? extends Annotation>> annotations) {
+  protected void process(XExecutableElement method, ImmutableSet<ClassName> annotations) {
     if (!anyBindingMethodValidator.isBindingMethod(method)) {
       annotations.forEach(
           annotation ->
@@ -62,7 +56,7 @@
                   ERROR,
                   "Multibinding annotations may only be on @Provides, @Produces, or @Binds methods",
                   method,
-                  getAnnotationMirror(method, annotation).get()));
+                  method.getAnnotation(annotation)));
     }
   }
 }
diff --git a/java/dagger/internal/codegen/validation/MultibindsMethodValidator.java b/java/dagger/internal/codegen/validation/MultibindsMethodValidator.java
index 8829667..de54141 100644
--- a/java/dagger/internal/codegen/validation/MultibindsMethodValidator.java
+++ b/java/dagger/internal/codegen/validation/MultibindsMethodValidator.java
@@ -21,39 +21,31 @@
 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 static dagger.internal.codegen.xprocessing.XTypes.isWildcard;
 
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XType;
 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.javapoet.TypeNames;
 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. */
+/** A validator for {@link dagger.multibindings.Multibinds} methods. */
 class MultibindsMethodValidator extends BindingMethodValidator {
 
-  /** Creates a validator for {@link Multibinds @Multibinds} methods. */
+  /** Creates a validator for {@link dagger.multibindings.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),
+        TypeNames.MULTIBINDS,
+        ImmutableSet.of(TypeNames.MODULE, TypeNames.PRODUCER_MODULE),
         dependencyRequestValidator,
         MUST_BE_ABSTRACT,
         NO_EXCEPTIONS,
@@ -63,18 +55,21 @@
   }
 
   @Override
-  protected ElementValidator elementValidator(ExecutableElement element) {
-    return new Validator(element);
+  protected ElementValidator elementValidator(XMethodElement method) {
+    return new Validator(method);
   }
 
   private class Validator extends MethodValidator {
-    Validator(ExecutableElement element) {
-      super(element);
+    private final XMethodElement method;
+
+    Validator(XMethodElement method) {
+      super(method);
+      this.method = method;
     }
 
     @Override
     protected void checkParameters() {
-      if (!element.getParameters().isEmpty()) {
+      if (!method.getParameters().isEmpty()) {
         report.addError(bindingMethods("cannot have parameters"));
       }
     }
@@ -82,29 +77,28 @@
     /** 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())) {
+      if (!isPlainMap(method.getReturnType()) && !isPlainSet(method.getReturnType())) {
         report.addError(bindingMethods("must return Map<K, V> or Set<T>"));
       }
     }
 
-    private boolean isPlainMap(TypeMirror returnType) {
+    private boolean isPlainMap(XType returnType) {
       if (!MapType.isMap(returnType)) {
         return false;
       }
       MapType mapType = MapType.from(returnType);
       return !mapType.isRawType()
-          && MoreTypes.isType(mapType.valueType()) // No wildcards.
+          && !isWildcard(mapType.valueType())
           && !isFrameworkType(mapType.valueType());
     }
 
-    private boolean isPlainSet(TypeMirror returnType) {
+    private boolean isPlainSet(XType returnType) {
       if (!SetType.isSet(returnType)) {
         return false;
       }
       SetType setType = SetType.from(returnType);
       return !setType.isRawType()
-          && MoreTypes.isType(setType.elementType()) // No wildcards.
+          && !isWildcard(setType.elementType())
           && !isFrameworkType(setType.elementType());
     }
   }
diff --git a/java/dagger/internal/codegen/validation/ProducesMethodValidator.java b/java/dagger/internal/codegen/validation/ProducesMethodValidator.java
index 1606ebe..ed71086 100644
--- a/java/dagger/internal/codegen/validation/ProducesMethodValidator.java
+++ b/java/dagger/internal/codegen/validation/ProducesMethodValidator.java
@@ -17,45 +17,36 @@
 package dagger.internal.codegen.validation;
 
 import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.internal.codegen.binding.ConfigurationAnnotations.getNullableAnnotation;
 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 static dagger.internal.codegen.xprocessing.XTypes.isTypeOf;
 
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XType;
 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.javapoet.TypeNames;
 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. */
+/** A validator for {@link dagger.producers.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,
+        TypeNames.PRODUCES,
+        TypeNames.PRODUCER_MODULE,
         MUST_BE_CONCRETE,
         EXCEPTION,
         ALLOWS_MULTIBINDINGS,
@@ -75,13 +66,16 @@
   }
 
   @Override
-  protected ElementValidator elementValidator(ExecutableElement element) {
-    return new Validator(element);
+  protected ElementValidator elementValidator(XMethodElement method) {
+    return new Validator(method);
   }
 
   private class Validator extends MethodValidator {
-    Validator(ExecutableElement element) {
-      super(element);
+    private final XMethodElement method;
+
+    Validator(XMethodElement method) {
+      super(method);
+      this.method = method;
     }
 
     @Override
@@ -89,10 +83,12 @@
       checkNullable();
     }
 
-    /** Adds a warning if a {@link Produces @Produces} method is declared nullable. */
+    /**
+     * Adds a warning if a {@link dagger.producers.Produces @Produces} method is declared nullable.
+     */
     // TODO(beder): Properly handle nullable with producer methods.
     private void checkNullable() {
-      if (ConfigurationAnnotations.getNullableType(element).isPresent()) {
+      if (getNullableAnnotation(method).isPresent()) {
         report.addWarning("@Nullable on @Produces methods does not do anything");
       }
     }
@@ -103,35 +99,28 @@
      * <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());
-      }
+    protected void checkKeyType(XType keyType) {
+      unwrapListenableFuture(keyType).ifPresent(super::checkKeyType);
     }
 
     /**
      * {@inheritDoc}
      *
-     * <p>Allows an {@link ElementsIntoSet @ElementsIntoSet} or {@code SET_VALUES} method to return
-     * a {@link ListenableFuture} of a {@link Set} as well.
+     * <p>Allows an {@link dagger.multibindings.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());
-      }
+      unwrapListenableFuture(method.getReturnType()).ifPresent(this::checkSetValuesType);
     }
 
-    private Optional<TypeMirror> unwrapListenableFuture(TypeMirror type) {
-      if (MoreTypes.isType(type) && MoreTypes.isTypeOf(ListenableFuture.class, type)) {
-        DeclaredType declaredType = MoreTypes.asDeclared(type);
-        if (declaredType.getTypeArguments().isEmpty()) {
+    private Optional<XType> unwrapListenableFuture(XType type) {
+      if (isTypeOf(type, TypeNames.LISTENABLE_FUTURE)) {
+        if (type.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(getOnlyElement(type.getTypeArguments()));
         }
       }
       return Optional.of(type);
diff --git a/java/dagger/internal/codegen/validation/ProvidesMethodValidator.java b/java/dagger/internal/codegen/validation/ProvidesMethodValidator.java
index 6b4f303..5099323 100644
--- a/java/dagger/internal/codegen/validation/ProvidesMethodValidator.java
+++ b/java/dagger/internal/codegen/validation/ProvidesMethodValidator.java
@@ -21,36 +21,28 @@
 import static dagger.internal.codegen.validation.BindingMethodValidator.Abstractness.MUST_BE_CONCRETE;
 import static dagger.internal.codegen.validation.BindingMethodValidator.ExceptionSuperclass.RUNTIME_EXCEPTION;
 
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XVariableElement;
 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.javapoet.TypeNames;
 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. */
+/** A validator for {@link dagger.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),
+        TypeNames.PROVIDES,
+        ImmutableSet.of(TypeNames.MODULE, TypeNames.PRODUCER_MODULE),
         dependencyRequestValidator,
         MUST_BE_CONCRETE,
         RUNTIME_EXCEPTION,
@@ -61,22 +53,18 @@
   }
 
   @Override
-  protected ElementValidator elementValidator(ExecutableElement element) {
-    return new Validator(element);
+  protected ElementValidator elementValidator(XMethodElement method) {
+    return new Validator(method);
   }
 
   private class Validator extends MethodValidator {
-    Validator(ExecutableElement element) {
-      super(element);
+    Validator(XMethodElement method) {
+      super(method);
     }
 
+    /** Adds an error if a {@link dagger.Provides @Provides} method depends on a producer type. */
     @Override
-    protected void checkAdditionalMethodProperties() {
-    }
-
-    /** Adds an error if a {@link Provides @Provides} method depends on a producer type. */
-    @Override
-    protected void checkParameter(VariableElement parameter) {
+    protected void checkParameter(XVariableElement parameter) {
       super.checkParameter(parameter);
       dependencyRequestValidator.checkNotProducer(report, parameter);
     }
diff --git a/java/dagger/internal/codegen/validation/SuperficialValidator.java b/java/dagger/internal/codegen/validation/SuperficialValidator.java
new file mode 100644
index 0000000..6684407
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/SuperficialValidator.java
@@ -0,0 +1,69 @@
+/*
+ * 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.validation;
+
+import static dagger.internal.codegen.xprocessing.XElements.closestEnclosingTypeElement;
+
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XTypeElement;
+import dagger.internal.codegen.base.ClearableCache;
+import dagger.internal.codegen.base.DaggerSuperficialValidation;
+import dagger.internal.codegen.base.DaggerSuperficialValidation.ValidationException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** Validates enclosing type elements in a round. */
+@Singleton
+public final class SuperficialValidator implements ClearableCache {
+
+  private final DaggerSuperficialValidation superficialValidation;
+  private final Map<XTypeElement, Optional<ValidationException>> validationExceptions =
+      new HashMap<>();
+
+  @Inject
+  SuperficialValidator(DaggerSuperficialValidation superficialValidation) {
+    this.superficialValidation = superficialValidation;
+  }
+
+  public void throwIfNearestEnclosingTypeNotValid(XElement element) {
+    Optional<ValidationException> validationException =
+        validationExceptions.computeIfAbsent(
+            closestEnclosingTypeElement(element),
+            this::validationExceptionsUncached);
+
+    if (validationException.isPresent()) {
+      throw validationException.get();
+    }
+  }
+
+  private Optional<ValidationException> validationExceptionsUncached(XTypeElement element) {
+    try {
+      superficialValidation.validateElement(element);
+    } catch (ValidationException validationException) {
+      return Optional.of(validationException);
+    }
+    return Optional.empty();
+  }
+
+  @Override
+  public void clearCache() {
+    validationExceptions.clear();
+  }
+}
diff --git a/java/dagger/internal/codegen/validation/TypeCheckingProcessingStep.java b/java/dagger/internal/codegen/validation/TypeCheckingProcessingStep.java
index 57867f1..5b14135 100644
--- a/java/dagger/internal/codegen/validation/TypeCheckingProcessingStep.java
+++ b/java/dagger/internal/codegen/validation/TypeCheckingProcessingStep.java
@@ -16,52 +16,175 @@
 
 package dagger.internal.codegen.validation;
 
-import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Throwables.getStackTraceAsString;
+import static com.google.common.collect.Sets.difference;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static javax.tools.Diagnostic.Kind.ERROR;
 
-import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XMessager;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XProcessingStep;
+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 java.lang.annotation.Annotation;
-import java.util.function.Function;
-import javax.lang.model.element.Element;
+import com.google.common.collect.Maps;
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.base.DaggerSuperficialValidation.ValidationException;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.inject.Inject;
 
 /**
- * A {@link ProcessingStep} that processes one element at a time and defers any for which {@link
+ * A {@link XProcessingStep} 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;
+public abstract class TypeCheckingProcessingStep<E extends XElement> implements XProcessingStep {
 
-  protected TypeCheckingProcessingStep(Function<Element, E> downcaster) {
-    this.downcaster = checkNotNull(downcaster);
-  }
+  private final List<String> lastDeferredErrorMessages = new ArrayList<>();
+  @Inject XMessager messager;
+  @Inject CompilerOptions compilerOptions;
+  @Inject SuperficialValidator superficialValidator;
 
   @Override
-  public ImmutableSet<Element> process(
-      SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
-    ImmutableSet.Builder<Element> deferredElements = ImmutableSet.builder();
-    ImmutableSetMultimap.copyOf(elementsByAnnotation)
-        .inverse()
-        .asMap()
+  public final ImmutableSet<String> annotations() {
+    return annotationClassNames().stream().map(ClassName::canonicalName).collect(toImmutableSet());
+  }
+
+  @SuppressWarnings("unchecked") // Subclass must ensure all annotated targets are of valid type.
+  @Override
+  public ImmutableSet<XElement> process(
+      XProcessingEnv env, Map<String, ? extends Set<? extends XElement>> elementsByAnnotation) {
+    // We only really care about the deferred error messages from the final round of processing.
+    // Thus, we can clear the values stored from the previous processing round since that clearly
+    // wasn't the final round, and we replace it with any deferred error messages from this round.
+    lastDeferredErrorMessages.clear();
+    ImmutableSet.Builder<XElement> deferredElements = ImmutableSet.builder();
+    inverse(elementsByAnnotation)
         .forEach(
             (element, annotations) -> {
               try {
-                process(downcaster.apply(element), ImmutableSet.copyOf(annotations));
+                // The XBasicAnnotationProcessor only validates the element itself. However, we
+                // validate the enclosing type here to keep the previous behavior of
+                // BasicAnnotationProcessor, since Dagger still relies on this behavior.
+                // TODO(b/201479062): It's inefficient to require validation of the entire enclosing
+                //  type, we should try to remove this and handle any additional validation into the
+                //  steps that need it.
+                superficialValidator.throwIfNearestEnclosingTypeNotValid(element);
+                process((E) element, annotations);
               } catch (TypeNotPresentException e) {
+                // TODO(bcorso): We should be able to remove this once we replace all calls to
+                // SuperficialValidation with DaggerSuperficialValidation.
                 deferredElements.add(element);
+                cacheErrorMessage(typeNotPresentErrorMessage(element, e), e);
+              } catch (ValidationException.UnexpectedException unexpectedException) {
+                // Rethrow since the exception was created from an unexpected throwable so
+                // deferring to another round is unlikely to help.
+                throw unexpectedException;
+              } catch (ValidationException.KnownErrorType e) {
+                deferredElements.add(element);
+                cacheErrorMessage(knownErrorTypeErrorMessage(element, e), e);
+              } catch (ValidationException.UnknownErrorType e) {
+                deferredElements.add(element);
+                cacheErrorMessage(unknownErrorTypeErrorMessage(element, e), e);
               }
             });
     return deferredElements.build();
   }
 
+  @Override
+  public void processOver(
+      XProcessingEnv env, Map<String, ? extends Set<? extends XElement>> elementsByAnnotation) {
+    // We avoid doing any actual processing here since this is run in the same round as the last
+    // call to process(). Instead, we just report the last deferred error messages, if any.
+    lastDeferredErrorMessages.forEach(errorMessage -> messager.printMessage(ERROR, errorMessage));
+    lastDeferredErrorMessages.clear();
+  }
+
+  private void cacheErrorMessage(String errorMessage, Exception exception) {
+    lastDeferredErrorMessages.add(
+        compilerOptions.includeStacktraceWithDeferredErrorMessages()
+            ? String.format("%s\n\n%s", errorMessage, getStackTraceAsString(exception))
+            : errorMessage);
+  }
+
+  private String typeNotPresentErrorMessage(XElement element, TypeNotPresentException exception) {
+    return String.format(
+        "%1$s was unable to process '%2$s' because '%3$s' could not be resolved."
+            + "\n"
+            + "\nIf type '%3$s' is a generated type, check above for compilation errors that may "
+            + "have prevented the type from being generated. Otherwise, ensure that type '%3$s' is "
+            + "on your classpath.",
+        this.getClass().getSimpleName(),
+        element,
+        exception.typeName());
+  }
+
+  private String knownErrorTypeErrorMessage(
+      XElement element, ValidationException.KnownErrorType exception) {
+    return String.format(
+        "%1$s was unable to process '%2$s' because '%3$s' could not be resolved."
+            + "\n"
+            + "\nDependency trace:"
+            + "\n    => %4$s"
+            + "\n"
+            + "\nIf type '%3$s' is a generated type, check above for compilation errors that may "
+            + "have prevented the type from being generated. Otherwise, ensure that type '%3$s' is "
+            + "on your classpath.",
+        this.getClass().getSimpleName(),
+        element,
+        exception.getErrorTypeName(),
+        exception.getTrace());
+  }
+
+  private String unknownErrorTypeErrorMessage(
+      XElement element, ValidationException.UnknownErrorType exception) {
+    return String.format(
+        "%1$s was unable to process '%2$s' because one of its dependencies could not be resolved."
+            + "\n"
+            + "\nDependency trace:"
+            + "\n    => %3$s"
+            + "\n"
+            + "\nIf the dependency is a generated type, check above for compilation errors that may"
+            + " have prevented the type from being generated. Otherwise, ensure that the dependency"
+            + " is on your classpath.",
+        this.getClass().getSimpleName(), element, exception.getTrace());
+  }
+
   /**
    * 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
+   * @param annotations the subset of {@link XProcessingStep#annotations()} that annotate {@code
    *     element}
    */
-  protected abstract void process(E element, ImmutableSet<Class<? extends Annotation>> annotations);
+  protected abstract void process(E element, ImmutableSet<ClassName> annotations);
+
+  private ImmutableMap<XElement, ImmutableSet<ClassName>> inverse(
+      Map<String, ? extends Set<? extends XElement>> elementsByAnnotation) {
+    ImmutableMap<String, ClassName> annotationClassNames =
+        annotationClassNames().stream()
+            .collect(toImmutableMap(ClassName::canonicalName, className -> className));
+    checkState(
+        annotationClassNames.keySet().containsAll(elementsByAnnotation.keySet()),
+        "Unexpected annotations for %s: %s",
+        this.getClass().getCanonicalName(),
+        difference(elementsByAnnotation.keySet(), annotationClassNames.keySet()));
+
+    ImmutableSetMultimap.Builder<XElement, ClassName> builder = ImmutableSetMultimap.builder();
+    elementsByAnnotation.forEach(
+        (annotationName, elementSet) ->
+            elementSet.forEach(
+                element -> builder.put(element, annotationClassNames.get(annotationName))));
+
+    return ImmutableMap.copyOf(Maps.transformValues(builder.build().asMap(), ImmutableSet::copyOf));
+  }
+
+  /** Returns the set of annotations processed by this processing step. */
+  protected abstract Set<ClassName> annotationClassNames();
 }
diff --git a/java/dagger/internal/codegen/validation/TypeHierarchyValidator.java b/java/dagger/internal/codegen/validation/TypeHierarchyValidator.java
deleted file mode 100644
index 5fa0270..0000000
--- a/java/dagger/internal/codegen/validation/TypeHierarchyValidator.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
index 620b0f0..6d73151 100644
--- a/java/dagger/internal/codegen/validation/Validation.java
+++ b/java/dagger/internal/codegen/validation/Validation.java
@@ -22,8 +22,8 @@
 import javax.inject.Qualifier;
 
 /**
- * Qualifier annotation for the {@link dagger.spi.BindingGraphPlugin}s that are used to implement
- * core Dagger validation.
+ * Qualifier annotation for the {@link dagger.spi.model.BindingGraphPlugin}s that are used to
+ * implement core Dagger validation.
  */
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
diff --git a/java/dagger/internal/codegen/validation/ValidationBindingGraphPlugins.java b/java/dagger/internal/codegen/validation/ValidationBindingGraphPlugins.java
new file mode 100644
index 0000000..0a43d87
--- /dev/null
+++ b/java/dagger/internal/codegen/validation/ValidationBindingGraphPlugins.java
@@ -0,0 +1,106 @@
+/*
+ * 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.validation;
+
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import androidx.room.compiler.processing.XFiler;
+import androidx.room.compiler.processing.compat.XConverters;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.compileroption.ProcessingOptions;
+import dagger.internal.codegen.compileroption.ValidationType;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.validation.DiagnosticReporterFactory.DiagnosticReporterImpl;
+import dagger.spi.model.BindingGraph;
+import dagger.spi.model.BindingGraphPlugin;
+import java.util.Map;
+import java.util.Set;
+import javax.inject.Inject;
+
+/** Initializes {@link BindingGraphPlugin}s. */
+public final class ValidationBindingGraphPlugins {
+  private final ImmutableSet<BindingGraphPlugin> plugins;
+  private final DiagnosticReporterFactory diagnosticReporterFactory;
+  private final XFiler filer;
+  private final DaggerTypes types;
+  private final DaggerElements elements;
+  private final CompilerOptions compilerOptions;
+  private final Map<String, String> processingOptions;
+
+  @Inject
+  ValidationBindingGraphPlugins(
+      @Validation ImmutableSet<BindingGraphPlugin> plugins,
+      DiagnosticReporterFactory diagnosticReporterFactory,
+      XFiler filer,
+      DaggerTypes types,
+      DaggerElements elements,
+      CompilerOptions compilerOptions,
+      @ProcessingOptions Map<String, String> processingOptions) {
+    this.plugins = plugins;
+    this.diagnosticReporterFactory = diagnosticReporterFactory;
+    this.filer = filer;
+    this.types = types;
+    this.elements = elements;
+    this.compilerOptions = compilerOptions;
+    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(XConverters.toJavac(filer));
+    plugin.initTypes(types);
+    plugin.initElements(elements);
+    Set<String> supportedOptions = plugin.supportedOptions();
+    if (!supportedOptions.isEmpty()) {
+      plugin.initOptions(Maps.filterKeys(processingOptions, supportedOptions::contains));
+    }
+  }
+
+  /** Returns {@code false} if any of the plugins reported an error. */
+  boolean visit(BindingGraph graph) {
+    boolean errorsAsWarnings =
+        graph.isFullBindingGraph()
+            && compilerOptions.fullBindingGraphValidationType().equals(ValidationType.WARNING);
+
+    boolean isClean = true;
+    for (BindingGraphPlugin plugin : plugins) {
+      DiagnosticReporterImpl reporter =
+          diagnosticReporterFactory.reporter(graph, plugin.pluginName(), errorsAsWarnings);
+      plugin.visitGraph(graph, reporter);
+      if (reporter.reportedDiagnosticKinds().contains(ERROR)) {
+        isClean = false;
+      }
+    }
+    return isClean;
+  }
+}
diff --git a/java/dagger/internal/codegen/validation/ValidationReport.java b/java/dagger/internal/codegen/validation/ValidationReport.java
index 7f37375..f9049cd 100644
--- a/java/dagger/internal/codegen/validation/ValidationReport.java
+++ b/java/dagger/internal/codegen/validation/ValidationReport.java
@@ -18,38 +18,39 @@
 
 import static dagger.internal.codegen.base.ElementFormatter.elementToString;
 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.langmodel.DaggerElements.transitivelyEncloses;
 import static javax.tools.Diagnostic.Kind.ERROR;
 import static javax.tools.Diagnostic.Kind.NOTE;
 import static javax.tools.Diagnostic.Kind.WARNING;
 
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XAnnotationValue;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XMessager;
 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 =
+public final class ValidationReport {
+  private static final Traverser<ValidationReport> SUBREPORTS =
       Traverser.forTree(report -> report.subreports);
 
-  private final T subject;
+  private final XElement subject;
   private final ImmutableSet<Item> items;
-  private final ImmutableSet<ValidationReport<?>> subreports;
+  private final ImmutableSet<ValidationReport> subreports;
   private final boolean markedDirty;
   private boolean hasPrintedErrors;
 
   private ValidationReport(
-      T subject,
+      XElement subject,
       ImmutableSet<Item> items,
-      ImmutableSet<ValidationReport<?>> subreports,
+      ImmutableSet<ValidationReport> subreports,
       boolean markedDirty) {
     this.subject = subject;
     this.items = items;
@@ -81,7 +82,7 @@
           break;
       }
     }
-    for (ValidationReport<?> subreport : subreports) {
+    for (ValidationReport subreport : subreports) {
       if (!subreport.isClean()) {
         return false;
       }
@@ -90,20 +91,20 @@
   }
 
   /**
-   * 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.
+   * 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) {
+  public void printMessagesTo(XMessager 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 (transitivelyEncloses(subject, item.element())) {
         if (item.annotation().isPresent()) {
           if (item.annotationValue().isPresent()) {
             messager.printMessage(
@@ -124,143 +125,132 @@
         messager.printMessage(item.kind(), message, subject);
       }
     }
-    for (ValidationReport<?> subreport : subreports) {
+    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 abstract XElement element();
+    public abstract Optional<XAnnotation> annotation();
+    abstract Optional<XAnnotationValue> annotationValue();
   }
 
-  public static <T extends Element> Builder<T> about(T subject) {
-    return new Builder<>(subject);
+  public static Builder about(XElement subject) {
+    return new Builder(subject);
   }
 
   /** A {@link ValidationReport} builder. */
   @CanIgnoreReturnValue
-  public static final class Builder<T extends Element> {
-    private final T subject;
+  public static final class Builder {
+    private final XElement subject;
     private final ImmutableSet.Builder<Item> items = ImmutableSet.builder();
-    private final ImmutableSet.Builder<ValidationReport<?>> subreports = ImmutableSet.builder();
+    private final ImmutableSet.Builder<ValidationReport> subreports = ImmutableSet.builder();
     private boolean markedDirty;
 
-    private Builder(T subject) {
+    private Builder(XElement subject) {
       this.subject = subject;
     }
 
-    @CheckReturnValue
-    T getSubject() {
-      return subject;
-    }
-
-    Builder<T> addItems(Iterable<Item> newItems) {
+    Builder addItems(Iterable<Item> newItems) {
       items.addAll(newItems);
       return this;
     }
 
-    public Builder<T> addError(String message) {
+    public Builder addError(String message) {
       return addError(message, subject);
     }
 
-    public Builder<T> addError(String message, Element element) {
+    public Builder addError(String message, XElement element) {
       return addItem(message, ERROR, element);
     }
 
-    public Builder<T> addError(String message, Element element, AnnotationMirror annotation) {
+    public Builder addError(String message, XElement element, XAnnotation annotation) {
       return addItem(message, ERROR, element, annotation);
     }
 
-    public Builder<T> addError(
+    public Builder addError(
         String message,
-        Element element,
-        AnnotationMirror annotation,
-        AnnotationValue annotationValue) {
+        XElement element,
+        XAnnotation annotation,
+        XAnnotationValue annotationValue) {
       return addItem(message, ERROR, element, annotation, annotationValue);
     }
 
-    Builder<T> addWarning(String message) {
+    Builder addWarning(String message) {
       return addWarning(message, subject);
     }
 
-    Builder<T> addWarning(String message, Element element) {
+    Builder addWarning(String message, XElement element) {
       return addItem(message, WARNING, element);
     }
 
-    Builder<T> addWarning(String message, Element element, AnnotationMirror annotation) {
+    Builder addWarning(String message, XElement element, XAnnotation annotation) {
       return addItem(message, WARNING, element, annotation);
     }
 
-    Builder<T> addWarning(
+    Builder addWarning(
         String message,
-        Element element,
-        AnnotationMirror annotation,
-        AnnotationValue annotationValue) {
+        XElement element,
+        XAnnotation annotation,
+        XAnnotationValue annotationValue) {
       return addItem(message, WARNING, element, annotation, annotationValue);
     }
 
-    Builder<T> addNote(String message) {
+    Builder addNote(String message) {
       return addNote(message, subject);
     }
 
-    Builder<T> addNote(String message, Element element) {
+    Builder addNote(String message, XElement element) {
       return addItem(message, NOTE, element);
     }
 
-    Builder<T> addNote(String message, Element element, AnnotationMirror annotation) {
+    Builder addNote(String message, XElement element, XAnnotation annotation) {
       return addItem(message, NOTE, element, annotation);
     }
 
-    Builder<T> addNote(
+    Builder addNote(
         String message,
-        Element element,
-        AnnotationMirror annotation,
-        AnnotationValue annotationValue) {
+        XElement element,
+        XAnnotation annotation,
+        XAnnotationValue annotationValue) {
       return addItem(message, NOTE, element, annotation, annotationValue);
     }
 
-    Builder<T> addItem(String message, Kind kind, Element element) {
+    Builder addItem(String message, Kind kind, XElement element) {
       return addItem(message, kind, element, Optional.empty(), Optional.empty());
     }
 
-    Builder<T> addItem(String message, Kind kind, Element element, AnnotationMirror annotation) {
+    Builder addItem(String message, Kind kind, XElement element, XAnnotation annotation) {
       return addItem(message, kind, element, Optional.of(annotation), Optional.empty());
     }
 
-    Builder<T> addItem(
+    Builder addItem(
         String message,
         Kind kind,
-        Element element,
-        AnnotationMirror annotation,
-        AnnotationValue annotationValue) {
+        XElement element,
+        XAnnotation annotation,
+        XAnnotationValue annotationValue) {
       return addItem(message, kind, element, Optional.of(annotation), Optional.of(annotationValue));
     }
 
-    private Builder<T> addItem(
+    private Builder addItem(
         String message,
         Kind kind,
-        Element element,
-        Optional<AnnotationMirror> annotation,
-        Optional<AnnotationValue> annotationValue) {
+        XElement element,
+        Optional<XAnnotation> annotation,
+        Optional<XAnnotationValue> annotationValue) {
       items.add(
-          new AutoValue_ValidationReport_Item(message, kind, element, annotation, annotationValue));
+          new AutoValue_ValidationReport_Item(
+              message,
+              kind,
+              element,
+              annotation,
+              annotationValue));
       return this;
     }
 
@@ -272,14 +262,14 @@
       this.markedDirty = true;
     }
 
-    public Builder<T> addSubreport(ValidationReport<?> subreport) {
+    public Builder addSubreport(ValidationReport subreport) {
       subreports.add(subreport);
       return this;
     }
 
     @CheckReturnValue
-    public ValidationReport<T> build() {
-      return new ValidationReport<>(subject, items.build(), subreports.build(), markedDirty);
+    public ValidationReport 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
index fa3a16c..a0699f1 100644
--- a/java/dagger/internal/codegen/writing/AnnotationCreatorGenerator.java
+++ b/java/dagger/internal/codegen/writing/AnnotationCreatorGenerator.java
@@ -22,13 +22,17 @@
 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 dagger.internal.codegen.xprocessing.XElements.getSimpleName;
 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 androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XFiler;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XTypeElement;
+import androidx.room.compiler.processing.compat.XConverters;
 import com.google.common.collect.ImmutableList;
 import com.google.errorprone.annotations.CanIgnoreReturnValue;
 import com.squareup.javapoet.ClassName;
@@ -40,15 +44,8 @@
 import dagger.internal.codegen.langmodel.DaggerElements;
 import java.util.LinkedHashSet;
 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
@@ -77,47 +74,49 @@
  *   }
  * </pre>
  */
-public class AnnotationCreatorGenerator extends SourceFileGenerator<TypeElement> {
+public class AnnotationCreatorGenerator extends SourceFileGenerator<XTypeElement> {
   private static final ClassName AUTO_ANNOTATION =
       ClassName.get("com.google.auto.value", "AutoAnnotation");
 
   @Inject
-  AnnotationCreatorGenerator(Filer filer, DaggerElements elements, SourceVersion sourceVersion) {
+  AnnotationCreatorGenerator(XFiler filer, DaggerElements elements, SourceVersion sourceVersion) {
     super(filer, elements, sourceVersion);
   }
 
   @Override
-  public Element originatingElement(TypeElement annotationType) {
+  public XElement originatingElement(XTypeElement annotationType) {
     return annotationType;
   }
 
   @Override
-  public ImmutableList<TypeSpec.Builder> topLevelTypes(TypeElement annotationType) {
-    ClassName generatedTypeName = getAnnotationCreatorClassName(annotationType);
+  public ImmutableList<TypeSpec.Builder> topLevelTypes(XTypeElement annotationType) {
+    ClassName generatedTypeName =
+        getAnnotationCreatorClassName(XConverters.toJavac(annotationType));
     TypeSpec.Builder annotationCreatorBuilder =
         classBuilder(generatedTypeName)
             .addModifiers(PUBLIC, FINAL)
             .addMethod(constructorBuilder().addModifiers(PRIVATE).build());
 
-    for (TypeElement annotationElement : annotationsToCreate(annotationType)) {
+    for (XTypeElement annotationElement : annotationsToCreate(annotationType)) {
       annotationCreatorBuilder.addMethod(buildCreateMethod(generatedTypeName, annotationElement));
     }
 
     return ImmutableList.of(annotationCreatorBuilder);
   }
 
-  private MethodSpec buildCreateMethod(ClassName generatedTypeName, TypeElement annotationElement) {
-    String createMethodName = createMethodName(annotationElement);
+  private MethodSpec buildCreateMethod(
+      ClassName generatedTypeName, XTypeElement annotationElement) {
+    String createMethodName = createMethodName(XConverters.toJavac(annotationElement));
     MethodSpec.Builder createMethod =
         methodBuilder(createMethodName)
             .addAnnotation(AUTO_ANNOTATION)
             .addModifiers(PUBLIC, STATIC)
-            .returns(TypeName.get(annotationElement.asType()));
+            .returns(annotationElement.getType().getTypeName());
 
     ImmutableList.Builder<CodeBlock> parameters = ImmutableList.builder();
-    for (ExecutableElement annotationMember : methodsIn(annotationElement.getEnclosedElements())) {
-      String parameterName = annotationMember.getSimpleName().toString();
-      TypeName parameterType = TypeName.get(annotationMember.getReturnType());
+    for (XMethodElement annotationMember : annotationElement.getDeclaredMethods()) {
+      String parameterName = getSimpleName(annotationMember);
+      TypeName parameterType = annotationMember.getReturnType().getTypeName();
       createMethod.addParameter(parameterType, parameterName);
       parameters.add(CodeBlock.of("$L", parameterName));
     }
@@ -134,30 +133,23 @@
    * Returns the annotation types for which {@code @AutoAnnotation static Foo createFoo(…)} methods
    * should be written.
    */
-  protected Set<TypeElement> annotationsToCreate(TypeElement annotationElement) {
+  protected Set<XTypeElement> annotationsToCreate(XTypeElement annotationElement) {
     return nestedAnnotationElements(annotationElement, new LinkedHashSet<>());
   }
 
   @CanIgnoreReturnValue
-  private static Set<TypeElement> nestedAnnotationElements(
-      TypeElement annotationElement, Set<TypeElement> annotationElements) {
+  private static Set<XTypeElement> nestedAnnotationElements(
+      XTypeElement annotationElement, Set<XTypeElement> annotationElements) {
     if (annotationElements.add(annotationElement)) {
-      for (ExecutableElement method : methodsIn(annotationElement.getEnclosedElements())) {
-        TRAVERSE_NESTED_ANNOTATIONS.visit(method.getReturnType(), annotationElements);
+      for (XMethodElement method : annotationElement.getDeclaredMethods()) {
+        XTypeElement returnType = method.getReturnType().getTypeElement();
+        // Return type may be null if it doesn't return a type or type is not known
+        if (returnType != null && returnType.isAnnotationClass()) {
+          // Ignore the return value since this method is just an accumulator method.
+          nestedAnnotationElements(returnType, 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
index b1237ca..7256fd6 100644
--- a/java/dagger/internal/codegen/writing/AnonymousProviderCreationExpression.java
+++ b/java/dagger/internal/codegen/writing/AnonymousProviderCreationExpression.java
@@ -16,12 +16,16 @@
 
 package dagger.internal.codegen.writing;
 
+import static com.google.common.base.Preconditions.checkNotNull;
 import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
 import static dagger.internal.codegen.javapoet.CodeBlocks.anonymousProvider;
-import static dagger.model.RequestKind.INSTANCE;
+import static dagger.spi.model.RequestKind.INSTANCE;
 
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
 import dagger.internal.codegen.binding.BindingRequest;
 import dagger.internal.codegen.binding.ContributionBinding;
 import dagger.internal.codegen.javapoet.Expression;
@@ -34,27 +38,33 @@
 final class AnonymousProviderCreationExpression
     implements FrameworkInstanceCreationExpression {
   private final ContributionBinding binding;
-  private final ComponentBindingExpressions componentBindingExpressions;
+  private final ComponentRequestRepresentations componentRequestRepresentations;
   private final ClassName requestingClass;
 
+  @AssistedInject
   AnonymousProviderCreationExpression(
-      ContributionBinding binding,
-      ComponentBindingExpressions componentBindingExpressions,
-      ClassName requestingClass) {
-    this.binding = binding;
-    this.componentBindingExpressions = componentBindingExpressions;
-    this.requestingClass = requestingClass;
+      @Assisted ContributionBinding binding,
+      ComponentRequestRepresentations componentRequestRepresentations,
+      ComponentImplementation componentImplementation) {
+    this.binding = checkNotNull(binding);
+    this.componentRequestRepresentations = componentRequestRepresentations;
+    this.requestingClass = componentImplementation.name();
   }
 
   @Override
   public CodeBlock creationExpression() {
     BindingRequest instanceExpressionRequest = bindingRequest(binding.key(), INSTANCE);
     Expression instanceExpression =
-        componentBindingExpressions.getDependencyExpression(
+        componentRequestRepresentations.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);
   }
+
+  @AssistedFactory
+  static interface Factory {
+    AnonymousProviderCreationExpression create(ContributionBinding binding);
+  }
 }
diff --git a/java/dagger/internal/codegen/writing/AssistedFactoryBindingExpression.java b/java/dagger/internal/codegen/writing/AssistedFactoryBindingExpression.java
deleted file mode 100644
index d90ab71..0000000
--- a/java/dagger/internal/codegen/writing/AssistedFactoryBindingExpression.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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);
-
-    // 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/AssistedFactoryRequestRepresentation.java b/java/dagger/internal/codegen/writing/AssistedFactoryRequestRepresentation.java
new file mode 100644
index 0000000..485774e
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/AssistedFactoryRequestRepresentation.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.binding.AssistedInjectionAnnotations.assistedFactoryMethod;
+import static dagger.internal.codegen.writing.AssistedInjectionParameters.assistedFactoryParameterSpecs;
+import static dagger.internal.codegen.xprocessing.XElements.asTypeElement;
+import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
+
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeSpec;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import dagger.internal.codegen.binding.Binding;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.xprocessing.MethodSpecs;
+import dagger.spi.model.DependencyRequest;
+import java.util.Optional;
+
+/**
+ * A {@link dagger.internal.codegen.writing.RequestRepresentation} for {@link
+ * dagger.assisted.AssistedFactory} methods.
+ */
+final class AssistedFactoryRequestRepresentation extends RequestRepresentation {
+  private final ProvisionBinding binding;
+  private final BindingGraph graph;
+  private final SimpleMethodRequestRepresentation.Factory simpleMethodRequestRepresentationFactory;
+  private final ComponentImplementation componentImplementation;
+
+  @AssistedInject
+  AssistedFactoryRequestRepresentation(
+      @Assisted ProvisionBinding binding,
+      BindingGraph graph,
+      ComponentImplementation componentImplementation,
+      SimpleMethodRequestRepresentation.Factory simpleMethodRequestRepresentationFactory) {
+    this.binding = checkNotNull(binding);
+    this.graph = graph;
+    this.componentImplementation = componentImplementation;
+    this.simpleMethodRequestRepresentationFactory = simpleMethodRequestRepresentationFactory;
+  }
+
+  @Override
+  Expression getDependencyExpression(ClassName requestingClass) {
+    // An assisted factory binding should have a single request for an assisted injection type.
+    DependencyRequest assistedInjectionRequest = getOnlyElement(binding.provisionDependencies());
+    // Get corresponding assisted injection binding.
+    Optional<Binding> localBinding = graph.localContributionBinding(assistedInjectionRequest.key());
+    checkArgument(
+        localBinding.isPresent(),
+        "assisted factory should have a dependency on an assisted injection binding");
+    Expression assistedInjectionExpression =
+        simpleMethodRequestRepresentationFactory
+            .create((ProvisionBinding) localBinding.get())
+            .getDependencyExpression(requestingClass.peerClass(""));
+    return Expression.create(
+        assistedInjectionExpression.type(),
+        CodeBlock.of("$L", anonymousfactoryImpl(localBinding.get(), assistedInjectionExpression)));
+  }
+
+  private TypeSpec anonymousfactoryImpl(
+      Binding assistedBinding, Expression assistedInjectionExpression) {
+    XTypeElement factory = asTypeElement(binding.bindingElement().get());
+    XType factoryType = binding.key().type().xprocessing();
+    XMethodElement factoryMethod = assistedFactoryMethod(factory);
+
+    // We can't use MethodSpec.overriding directly because we need to control the parameter names.
+    MethodSpec factoryOverride = MethodSpecs.overriding(factoryMethod, factoryType).build();
+    TypeSpec.Builder builder =
+        TypeSpec.anonymousClassBuilder("")
+            .addMethod(
+                MethodSpec.methodBuilder(getSimpleName(factoryMethod))
+                    .addModifiers(factoryOverride.modifiers)
+                    .addTypeVariables(factoryOverride.typeVariables)
+                    .returns(factoryOverride.returnType)
+                    .addAnnotations(factoryOverride.annotations)
+                    .addExceptions(factoryOverride.exceptions)
+                    .addParameters(
+                        assistedFactoryParameterSpecs(
+                            binding, componentImplementation.shardImplementation(assistedBinding)))
+                    .addStatement("return $L", assistedInjectionExpression.codeBlock())
+                    .build());
+
+    if (factory.isInterface()) {
+      builder.addSuperinterface(factoryType.getTypeName());
+    } else {
+      builder.superclass(factoryType.getTypeName());
+    }
+
+    return builder.build();
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    AssistedFactoryRequestRepresentation create(ProvisionBinding binding);
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/AssistedInjectionParameters.java b/java/dagger/internal/codegen/writing/AssistedInjectionParameters.java
new file mode 100644
index 0000000..9e55e03
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/AssistedInjectionParameters.java
@@ -0,0 +1,102 @@
+/*
+ * 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 androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static com.google.common.base.Preconditions.checkArgument;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.xprocessing.XElements.asConstructor;
+import static dagger.internal.codegen.xprocessing.XElements.asTypeElement;
+
+import androidx.room.compiler.processing.XConstructorElement;
+import androidx.room.compiler.processing.XConstructorType;
+import androidx.room.compiler.processing.XMethodType;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
+import androidx.room.compiler.processing.XVariableElement;
+import com.google.common.collect.ImmutableList;
+import com.squareup.javapoet.ParameterSpec;
+import dagger.internal.codegen.binding.AssistedInjectionAnnotations;
+import dagger.internal.codegen.binding.AssistedInjectionAnnotations.AssistedFactoryMetadata;
+import dagger.internal.codegen.binding.Binding;
+import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
+import dagger.spi.model.BindingKind;
+import java.util.List;
+
+/** Utility class for generating unique assisted parameter names for a component shard. */
+final class AssistedInjectionParameters {
+  /**
+   * 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, ShardImplementation shardImplementation) {
+    checkArgument(binding.kind() == BindingKind.ASSISTED_FACTORY);
+    XTypeElement factory = asTypeElement(binding.bindingElement().get());
+    AssistedFactoryMetadata metadata = AssistedFactoryMetadata.create(factory.getType());
+    XMethodType factoryMethodType =
+        metadata.factoryMethod().asMemberOf(binding.key().type().xprocessing());
+    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(),
+        shardImplementation);
+  }
+
+  /**
+   * 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, ShardImplementation shardImplementation) {
+    checkArgument(binding.kind() == BindingKind.ASSISTED_INJECTION);
+    XConstructorElement constructor = asConstructor(binding.bindingElement().get());
+    XConstructorType constructorType = constructor.asMemberOf(binding.key().type().xprocessing());
+    return assistedParameterSpecs(
+        constructor.getParameters(), constructorType.getParameterTypes(), shardImplementation);
+  }
+
+  private static ImmutableList<ParameterSpec> assistedParameterSpecs(
+      List<? extends XVariableElement> paramElements,
+      List<XType> paramTypes,
+      ShardImplementation shardImplementation) {
+    ImmutableList.Builder<ParameterSpec> assistedParameterSpecs = ImmutableList.builder();
+    for (int i = 0; i < paramElements.size(); i++) {
+      XVariableElement paramElement = paramElements.get(i);
+      XType paramType = paramTypes.get(i);
+      if (AssistedInjectionAnnotations.isAssistedParameter(paramElement)) {
+        assistedParameterSpecs.add(
+            ParameterSpec.builder(
+                    paramType.getTypeName(),
+                    shardImplementation.getUniqueFieldNameForAssistedParam(toJavac(paramElement)))
+                .build());
+      }
+    }
+    return assistedParameterSpecs.build();
+  }
+
+  private AssistedInjectionParameters() {}
+}
diff --git a/java/dagger/internal/codegen/writing/BUILD b/java/dagger/internal/codegen/writing/BUILD
index dcb88b3..c99a253 100644
--- a/java/dagger/internal/codegen/writing/BUILD
+++ b/java/dagger/internal/codegen/writing/BUILD
@@ -33,15 +33,16 @@
         "//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/codegen/xprocessing",
         "//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",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:value",
+        "//third_party/java/error_prone:annotations",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/util/concurrent",
+        "//third_party/java/javapoet",
+        "//third_party/java/jsr330_inject",
     ],
 )
diff --git a/java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/BuildModule.java b/java/dagger/internal/codegen/writing/BindingRepresentation.java
similarity index 61%
rename from java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/BuildModule.java
rename to java/dagger/internal/codegen/writing/BindingRepresentation.java
index f6317c2..cf01c52 100644
--- a/java/dagger/example/gradle/android/simple/app/src/main/java/dagger/example/gradle/android/simple/BuildModule.java
+++ b/java/dagger/internal/codegen/writing/BindingRepresentation.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Dagger Authors.
+ * 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.
@@ -14,18 +14,11 @@
  * limitations under the License.
  */
 
-package dagger.example.gradle.android.simple;
+package dagger.internal.codegen.writing;
 
-import static android.os.Build.MODEL;
+import dagger.internal.codegen.binding.BindingRequest;
 
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-final class BuildModule {
-  @Provides
-  @Model
-  static String provideModel() {
-    return MODEL;
-  }
+/** A factory of code expressions to satisfy all kinds of requests for a binding in a component. */
+interface BindingRepresentation {
+  RequestRepresentation getRequestRepresentation(BindingRequest request);
 }
diff --git a/java/dagger/internal/codegen/writing/BindingRepresentations.java b/java/dagger/internal/codegen/writing/BindingRepresentations.java
new file mode 100644
index 0000000..3558474
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/BindingRepresentations.java
@@ -0,0 +1,38 @@
+/*
+ * 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.writing;
+
+import static dagger.internal.codegen.javapoet.TypeNames.DOUBLE_CHECK;
+import static dagger.internal.codegen.javapoet.TypeNames.SINGLE_CHECK;
+
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.binding.Binding;
+import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+
+/** Holds common methods for BindingRepresentations. */
+final class BindingRepresentations {
+  static FrameworkInstanceCreationExpression scope(
+      Binding binding, FrameworkInstanceCreationExpression unscoped) {
+    return () ->
+        CodeBlock.of(
+            "$T.provider($L)",
+            binding.scope().get().isReusable() ? SINGLE_CHECK : DOUBLE_CHECK,
+            unscoped.creationExpression());
+  }
+
+  private BindingRepresentations() {}
+}
diff --git a/java/dagger/internal/codegen/writing/ComponentBindingExpressions.java b/java/dagger/internal/codegen/writing/ComponentBindingExpressions.java
deleted file mode 100644
index dc15c99..0000000
--- a/java/dagger/internal/codegen/writing/ComponentBindingExpressions.java
+++ /dev/null
@@ -1,707 +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.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());
-    return MethodSpec.overriding(
-            componentMethod.methodElement(),
-            MoreTypes.asDeclared(graph.componentTypeElement().asType()),
-            types)
-        .addCode(
-            getBindingExpression(request)
-                .getComponentMethodImplementation(componentMethod, componentImplementation))
-        .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 (isFastInit()
-        && 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.
-   */
-  private BindingExpression instanceBindingExpression(ContributionBinding binding) {
-    Optional<BindingExpression> maybeDirectInstanceExpression =
-        unscopedDirectInstanceExpression(binding);
-    if (maybeDirectInstanceExpression.isPresent()) {
-      // If this is the case where we don't need to use Provider#get() because there's no caching
-      // and it isn't an assisted factory, or because we're in fastInit mode (since fastInit avoids
-      // using Providers), we can try to use the direct expression, possibly wrapped in a method
-      // if necessary (e.g. it has dependencies).
-      if ((!needsCaching(binding) && binding.kind() != BindingKind.ASSISTED_FACTORY)
-          || isFastInit()) {
-        BindingExpression directInstanceExpression = maybeDirectInstanceExpression.get();
-        // While this can't require caching in default mode, if we're in fastInit mode and we need
-        // caching we also need to wrap it in a method.
-        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 !isFastInit()
-        || binding.kind().equals(MULTIBOUND_MAP)
-        || binding.kind().equals(MULTIBOUND_SET);
-  }
-
-  /**
-   * 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 (isFastInit()) {
-      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;
-  }
-
-  private boolean isFastInit() {
-    return compilerOptions.fastInit(
-        topLevelComponentImplementation.componentDescriptor().typeElement());
-  }
-}
diff --git a/java/dagger/internal/codegen/componentgenerator/ComponentCreatorImplementationFactory.java b/java/dagger/internal/codegen/writing/ComponentCreatorImplementationFactory.java
similarity index 79%
rename from java/dagger/internal/codegen/componentgenerator/ComponentCreatorImplementationFactory.java
rename to java/dagger/internal/codegen/writing/ComponentCreatorImplementationFactory.java
index 66270c6..313b379 100644
--- a/java/dagger/internal/codegen/componentgenerator/ComponentCreatorImplementationFactory.java
+++ b/java/dagger/internal/codegen/writing/ComponentCreatorImplementationFactory.java
@@ -14,13 +14,12 @@
  * limitations under the License.
  */
 
-package dagger.internal.codegen.componentgenerator;
+package dagger.internal.codegen.writing;
 
-import static com.google.auto.common.MoreTypes.asDeclared;
+import static androidx.room.compiler.processing.XTypeKt.isVoid;
 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;
@@ -32,11 +31,12 @@
 import static javax.lang.model.element.Modifier.PUBLIC;
 import static javax.lang.model.element.Modifier.STATIC;
 
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XType;
 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;
@@ -50,40 +50,25 @@
 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 dagger.internal.codegen.xprocessing.MethodSpecs;
+import dagger.internal.codegen.xprocessing.XElements;
 import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Stream;
 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;
   }
 
@@ -98,34 +83,28 @@
 
     Builder builder =
         creatorDescriptor.isPresent()
-            ? new BuilderForCreatorDescriptor(componentImplementation, creatorDescriptor.get())
-            : new BuilderForGeneratedRootComponentBuilder(componentImplementation);
+            ? new BuilderForCreatorDescriptor(creatorDescriptor.get())
+            : new BuilderForGeneratedRootComponentBuilder();
     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 final TypeSpec.Builder classBuilder =
+        classBuilder(componentImplementation.getCreatorName());
+    private final UniqueNameSet fieldNames = new UniqueNameSet();
     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();
+      this.fields = addFields();
       addSetterMethods();
       addFactoryMethod();
-      return ComponentCreatorImplementation.create(classBuilder.build(), className, fields);
+      return ComponentCreatorImplementation.create(
+          classBuilder.build(), componentImplementation.getCreatorName(), fields);
     }
 
     /** Returns the descriptor for the component. */
@@ -168,10 +147,7 @@
 
     private void setModifiers() {
       visibility().ifPresent(classBuilder::addModifiers);
-      if (!componentImplementation.isNested()) {
-        classBuilder.addModifiers(STATIC);
-      }
-      classBuilder.addModifiers(FINAL);
+      classBuilder.addModifiers(STATIC, FINAL);
     }
 
     /** Returns the visibility modifier the generated class should have, if any. */
@@ -181,17 +157,28 @@
     protected abstract void setSupertype();
 
     /** Adds a constructor for the creator type, if needed. */
-    protected abstract void addConstructor();
+    protected void addConstructor() {
+      MethodSpec.Builder constructor = MethodSpec.constructorBuilder().addModifiers(PRIVATE);
+      componentImplementation
+          .creatorComponentFields()
+          .forEach(
+              field -> {
+                fieldNames.claim(field.name);
+                classBuilder.addField(field);
+                constructor.addParameter(field.type, field.name);
+                constructor.addStatement("this.$1N = $1N", field);
+              });
+      classBuilder.addMethod(constructor.build());
+    }
 
     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()),
+                          requirement.type().getTypeName(),
                           fieldNames.getUniqueName(requirement.variableName()),
                           PRIVATE)
                       .build());
@@ -220,7 +207,8 @@
           // 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())) {
+          if (isElementAccessibleFrom(
+              requirement.typeElement(), componentImplementation.name().packageName())) {
             return Optional.of(noopSetterMethod(requirement));
           } else {
             return Optional.empty();
@@ -237,7 +225,7 @@
       method.addStatement(
           "this.$N = $L",
           fields.get(requirement),
-          requirement.nullPolicy(elements, metadataUtil).equals(NullPolicy.ALLOW)
+          requirement.nullPolicy().equals(NullPolicy.ALLOW)
               ? CodeBlock.of("$N", parameter)
               : CodeBlock.of("$T.checkNotNull($N)", Preconditions.class, parameter));
       return maybeReturnThis(method);
@@ -262,7 +250,7 @@
               UnsupportedOperationException.class,
               String.class,
               "%s cannot be set because it is inherited from the enclosing component",
-              TypeNames.rawTypeName(TypeName.get(requirement.type())))
+              TypeNames.rawTypeName(requirement.type().getTypeName()))
           .build();
     }
 
@@ -284,7 +272,7 @@
     MethodSpec factoryMethod() {
       MethodSpec.Builder factoryMethod = factoryMethodBuilder();
       factoryMethod
-          .returns(ClassName.get(componentDescriptor().typeElement()))
+          .returns(componentDescriptor().typeElement().getClassName())
           .addModifiers(PUBLIC);
 
       ImmutableMap<ComponentRequirement, String> factoryMethodParameters =
@@ -310,7 +298,7 @@
 
     private void addNullHandlingForField(
         ComponentRequirement requirement, FieldSpec field, MethodSpec.Builder factoryMethod) {
-      switch (requirement.nullPolicy(elements, metadataUtil)) {
+      switch (requirement.nullPolicy()) {
         case NEW:
           checkState(requirement.kind().isModule());
           factoryMethod
@@ -334,7 +322,7 @@
 
     private void addNullHandlingForParameter(
         ComponentRequirement requirement, String parameter, MethodSpec.Builder factoryMethod) {
-      if (!requirement.nullPolicy(elements, metadataUtil).equals(NullPolicy.ALLOW)) {
+      if (!requirement.nullPolicy().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);
@@ -346,23 +334,27 @@
 
     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);
-                }
-              })
+      return Stream.concat(
+              componentImplementation.creatorComponentFields().stream()
+                  .map(field -> CodeBlock.of("$N", field)),
+              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);
+      return moduleProxies.newModuleInstance(
+          requirement.typeElement(), componentImplementation.getCreatorName());
     }
   }
 
@@ -370,10 +362,7 @@
   private final class BuilderForCreatorDescriptor extends Builder {
     final ComponentCreatorDescriptor creatorDescriptor;
 
-    BuilderForCreatorDescriptor(
-        ComponentImplementation componentImplementation,
-        ComponentCreatorDescriptor creatorDescriptor) {
-      super(componentImplementation);
+    BuilderForCreatorDescriptor(ComponentCreatorDescriptor creatorDescriptor) {
       this.creatorDescriptor = creatorDescriptor;
     }
 
@@ -389,12 +378,14 @@
 
     @Override
     protected void setSupertype() {
-      addSupertype(classBuilder, creatorDescriptor.typeElement());
+      addSupertype(super.classBuilder, creatorDescriptor.typeElement());
     }
 
     @Override
     protected void addConstructor() {
-      // Just use the implicit no-arg public constructor.
+      if (!componentImplementation.creatorComponentFields().isEmpty()) {
+        super.addConstructor();
+      }
     }
 
     @Override
@@ -405,18 +396,16 @@
     @Override
     protected ImmutableMap<ComponentRequirement, String> factoryMethodParameters() {
       return ImmutableMap.copyOf(
-          Maps.transformValues(
-              creatorDescriptor.factoryParameters(),
-              element -> element.getSimpleName().toString()));
+          Maps.transformValues(creatorDescriptor.factoryParameters(), XElements::getSimpleName));
     }
 
-    private DeclaredType creatorType() {
-      return asDeclared(creatorDescriptor.typeElement().asType());
+    private XType creatorType() {
+      return creatorDescriptor.typeElement().getType();
     }
 
     @Override
     protected MethodSpec.Builder factoryMethodBuilder() {
-      return MethodSpec.overriding(creatorDescriptor.factoryMethod(), creatorType(), types);
+      return MethodSpecs.overriding(creatorDescriptor.factoryMethod(), creatorType());
     }
 
     private RequirementStatus requirementStatus(ComponentRequirement requirement) {
@@ -447,11 +436,11 @@
 
     @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)) {
+      XMethodElement supertypeMethod = creatorDescriptor.setterMethods().get(requirement);
+      MethodSpec.Builder method = MethodSpecs.overriding(supertypeMethod, creatorType());
+      if (!isVoid(supertypeMethod.getReturnType())) {
         // Take advantage of covariant returns so that we don't have to worry about type variables
-        method.returns(className);
+        method.returns(componentImplementation.getCreatorName());
       }
       return method;
     }
@@ -462,9 +451,6 @@
    * 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() {
@@ -478,11 +464,9 @@
 
     @Override
     protected Optional<Modifier> visibility() {
-      return componentImplementation
-          .componentDescriptor()
-          .typeElement()
-          .getModifiers()
-          .contains(PUBLIC) ? Optional.of(PUBLIC) : Optional.empty();
+      return componentImplementation.componentDescriptor().typeElement().isPublic()
+          ? Optional.of(PUBLIC)
+          : Optional.empty();
     }
 
     @Override
@@ -491,11 +475,6 @@
     }
 
     @Override
-    protected void addConstructor() {
-      classBuilder.addMethod(constructorBuilder().addModifiers(PRIVATE).build());
-    }
-
-    @Override
     protected ImmutableSet<ComponentRequirement> setterMethods() {
       return componentDescriptor().dependenciesAndConcreteModules();
     }
@@ -512,11 +491,11 @@
 
     @Override
     protected MethodSpec.Builder setterMethodBuilder(ComponentRequirement requirement) {
-      String name = simpleVariableName(requirement.typeElement());
+      String name = simpleVariableName(requirement.typeElement().getClassName());
       return methodBuilder(name)
           .addModifiers(PUBLIC)
-          .addParameter(TypeName.get(requirement.type()), name)
-          .returns(className);
+          .addParameter(requirement.type().getTypeName(), name)
+          .returns(componentImplementation.getCreatorName());
     }
   }
 
diff --git a/java/dagger/internal/codegen/writing/ComponentImplementation.java b/java/dagger/internal/codegen/writing/ComponentImplementation.java
index a09620e..f458a9a 100644
--- a/java/dagger/internal/codegen/writing/ComponentImplementation.java
+++ b/java/dagger/internal/codegen/writing/ComponentImplementation.java
@@ -16,68 +16,129 @@
 
 package dagger.internal.codegen.writing;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static com.google.auto.common.MoreTypes.asDeclared;
 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.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
 import static com.squareup.javapoet.TypeSpec.classBuilder;
-import static dagger.internal.codegen.binding.ComponentCreatorKind.BUILDER;
+import static dagger.internal.codegen.base.ComponentCreatorKind.BUILDER;
+import static dagger.internal.codegen.binding.SourceFiles.simpleVariableName;
+import static dagger.internal.codegen.extension.DaggerStreams.instancesOf;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
+import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings;
+import static dagger.internal.codegen.javapoet.CodeBlocks.parameterNames;
+import static dagger.internal.codegen.langmodel.Accessibility.isProtectedMemberOf;
 import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+import static dagger.internal.codegen.writing.ComponentImplementation.MethodSpecKind.COMPONENT_METHOD;
+import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
+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 static javax.tools.Diagnostic.Kind.ERROR;
 
+import androidx.room.compiler.processing.XMessager;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
+import androidx.room.compiler.processing.compat.XConverters;
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.base.Function;
 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.Iterables;
 import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 import com.google.common.collect.MultimapBuilder;
-import com.squareup.javapoet.AnnotationSpec;
+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.ComponentCreatorKind;
 import dagger.internal.codegen.base.UniqueNameSet;
+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.ComponentCreatorDescriptor;
-import dagger.internal.codegen.binding.ComponentCreatorKind;
 import dagger.internal.codegen.binding.ComponentDescriptor;
+import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
 import dagger.internal.codegen.binding.ComponentRequirement;
 import dagger.internal.codegen.binding.KeyVariableNamer;
+import dagger.internal.codegen.binding.MethodSignature;
 import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.internal.codegen.javapoet.TypeNames;
 import dagger.internal.codegen.javapoet.TypeSpecs;
-import dagger.model.Key;
-import dagger.model.RequestKind;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.spi.model.BindingGraph.Node;
+import dagger.spi.model.Key;
+import dagger.spi.model.RequestKind;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
-import java.util.LinkedHashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Provider;
+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.TypeMirror;
 
 /** The implementation of a component type. */
+@PerComponentImplementation
 public final class ComponentImplementation {
+  /** A factory for creating a {@link ComponentImplementation}. */
+  public interface ChildComponentImplementationFactory {
+    /** Creates a {@link ComponentImplementation} for the given {@code childGraph}. */
+    ComponentImplementation create(BindingGraph childGraph);
+  }
+
+  /** Compiler Modes. */
+  public enum CompilerMode {
+    DEFAULT,
+    FAST_INIT,
+    EXPERIMENTAL_MERGED_MODE;
+
+    public boolean isFastInit() {
+      return this == CompilerMode.FAST_INIT;
+    }
+
+    public boolean isExperimentalMergedMode() {
+      return this == CompilerMode.EXPERIMENTAL_MERGED_MODE;
+    }
+  }
+
   /** A type of field that this component can contain. */
   public enum FieldSpecKind {
     /** A field for a component shard. */
-    COMPONENT_SHARD,
+    COMPONENT_SHARD_FIELD,
 
     /** 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,
 
@@ -114,8 +175,7 @@
      * The {@link dagger.producers.internal.CancellationListener#onProducerFutureCancelled(boolean)}
      * method for a production component.
      */
-    CANCELLATION_LISTENER_METHOD,
-    ;
+    CANCELLATION_LISTENER_METHOD
   }
 
   /** A type of nested class that this component can contain. */
@@ -129,134 +189,237 @@
     /** A provider class for a component provision. */
     COMPONENT_PROVISION_FACTORY,
 
+    /** A class for a component shard. */
+    COMPONENT_SHARD_TYPE,
+
     /** 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);
+  /**
+   * Returns the {@link ShardImplementation} for each binding in this graph.
+   *
+   * <p>Each shard contains approximately {@link CompilerOptions#keysPerComponentShard()} bindings.
+   *
+   * <p>If more than 1 shard is needed, we iterate the strongly connected nodes to make sure of two
+   * things: 1) bindings are put in shards in reverse topological order (i.e., bindings in Shard{i}
+   * do not depend on bindings in Shard{i+j}) and 2) bindings belonging to the same cycle are put in
+   * the same shard. These two guarantees allow us to initialize each shard in a well defined order.
+   */
+  private static ImmutableMap<Binding, ShardImplementation> createShardsByBinding(
+      ShardImplementation componentShard, BindingGraph graph, CompilerOptions compilerOptions) {
+    ImmutableList<ImmutableList<Binding>> partitions = bindingPartitions(graph, compilerOptions);
+    ImmutableMap.Builder<Binding, ShardImplementation> builder = ImmutableMap.builder();
+    for (int i = 0; i < partitions.size(); i++) {
+      ShardImplementation shard = i == 0 ? componentShard : componentShard.createShard("Shard" + i);
+      partitions.get(i).forEach(binding -> builder.put(binding, shard));
     }
-    return shardsByKey.get(key);
+    return builder.build();
   }
 
-  /** Returns a reference to this compenent when called from a class nested in this component. */
-  public CodeBlock externalReferenceBlock() {
-    return externalReferenceBlock;
+  private static ImmutableList<ImmutableList<Binding>> bindingPartitions(
+      BindingGraph graph, CompilerOptions compilerOptions) {
+    int bindingsPerShard = compilerOptions.keysPerComponentShard(graph.componentTypeElement());
+    int maxPartitions = (graph.localBindingNodes().size() / bindingsPerShard) + 1;
+    if (maxPartitions <= 1) {
+      return ImmutableList.of(
+          graph.localBindingNodes().stream().map(BindingNode::delegate).collect(toImmutableList()));
+    }
+
+    // Iterate through all SCCs in order until all bindings local to this component are partitioned.
+    List<Binding> currPartition = new ArrayList<>(bindingsPerShard);
+    ImmutableList.Builder<ImmutableList<Binding>> partitions =
+        ImmutableList.builderWithExpectedSize(maxPartitions);
+    for (ImmutableSet<Node> nodes : graph.topLevelBindingGraph().stronglyConnectedNodes()) {
+      nodes.stream()
+          .flatMap(instancesOf(BindingNode.class))
+          .filter(bindingNode -> bindingNode.componentPath().equals(graph.componentPath()))
+          .map(BindingNode::delegate)
+          .forEach(currPartition::add);
+      if (currPartition.size() >= bindingsPerShard) {
+        partitions.add(ImmutableList.copyOf(currPartition));
+        currPartition = new ArrayList<>(bindingsPerShard);
+      }
+    }
+    if (!currPartition.isEmpty()) {
+      partitions.add(ImmutableList.copyOf(currPartition));
+    }
+    return partitions.build();
   }
 
-  // TODO(ronshapiro): see if we can remove this method and instead inject it in the objects that
-  // need it.
+  /** The boolean parameter of the onProducerFutureCancelled method. */
+  public static final ParameterSpec MAY_INTERRUPT_IF_RUNNING_PARAM =
+      ParameterSpec.builder(boolean.class, "mayInterruptIfRunning").build();
+
+  private static final String CANCELLATION_LISTENER_METHOD_NAME = "onProducerFutureCancelled";
+
+  /**
+   * How many statements per {@code initialize()} or {@code onProducerFutureCancelled()} method
+   * before they get partitioned.
+   */
+  private static final int STATEMENTS_PER_METHOD = 100;
+
+  private final ShardImplementation componentShard;
+  private final ImmutableMap<Binding, ShardImplementation> shardsByBinding;
+  private final Map<ShardImplementation, FieldSpec> shardFieldsByImplementation = new HashMap<>();
+  private final List<CodeBlock> shardInitializations = new ArrayList<>();
+  private final List<CodeBlock> shardCancellations = new ArrayList<>();
+  private final Optional<ComponentImplementation> parent;
+  private final ChildComponentImplementationFactory childComponentImplementationFactory;
+  private final Provider<ComponentRequestRepresentations> bindingExpressionsProvider;
+  private final Provider<ComponentCreatorImplementationFactory>
+      componentCreatorImplementationFactoryProvider;
+  private final BindingGraph graph;
+  private final ComponentNames componentNames;
+  private final DaggerElements elements;
+  private final DaggerTypes types;
+  private final ImmutableMap<ComponentImplementation, FieldSpec> componentFieldsByImplementation;
+  private final XMessager messager;
+  private final CompilerMode compilerMode;
+
+  @Inject
+  ComponentImplementation(
+      @ParentComponent Optional<ComponentImplementation> parent,
+      ChildComponentImplementationFactory childComponentImplementationFactory,
+      // Inject as Provider<> to prevent a cycle.
+      Provider<ComponentRequestRepresentations> bindingExpressionsProvider,
+      Provider<ComponentCreatorImplementationFactory> componentCreatorImplementationFactoryProvider,
+      BindingGraph graph,
+      ComponentNames componentNames,
+      CompilerOptions compilerOptions,
+      DaggerElements elements,
+      DaggerTypes types,
+      XMessager messager) {
+    this.parent = parent;
+    this.childComponentImplementationFactory = childComponentImplementationFactory;
+    this.bindingExpressionsProvider = bindingExpressionsProvider;
+    this.componentCreatorImplementationFactoryProvider =
+        componentCreatorImplementationFactoryProvider;
+    this.graph = graph;
+    this.componentNames = componentNames;
+    this.elements = elements;
+    this.types = types;
+
+    // The first group of keys belong to the component itself. We call this the componentShard.
+    this.componentShard = new ShardImplementation(componentNames.get(graph.componentPath()));
+
+    // Claim the method names for all local and inherited methods on the component type.
+    elements
+        .getLocalAndInheritedMethods(toJavac(graph.componentTypeElement()))
+        .forEach(method -> componentShard.componentMethodNames.claim(method.getSimpleName()));
+
+    // Create the shards for this component, indexed by binding.
+    this.shardsByBinding = createShardsByBinding(componentShard, graph, compilerOptions);
+
+    // Create and claim the fields for this and all ancestor components stored as fields.
+    this.componentFieldsByImplementation =
+        createComponentFieldsByImplementation(this, compilerOptions);
+    this.messager = messager;
+    XTypeElement typeElement = rootComponentImplementation().componentDescriptor().typeElement();
+    this.compilerMode =
+        compilerOptions.fastInit(typeElement)
+            ? CompilerMode.FAST_INIT
+            : (compilerOptions.experimentalMergedMode(typeElement)
+                ? CompilerMode.EXPERIMENTAL_MERGED_MODE
+                : CompilerMode.DEFAULT);
+  }
+
+  /**
+   * Returns the shard for a given {@link Binding}.
+   *
+   * <p>Each set of {@link CompilerOptions#keysPerShard()} will get its own shard instance.
+   */
+  public ShardImplementation shardImplementation(Binding binding) {
+    checkState(shardsByBinding.containsKey(binding), "No shard in %s for: %s", name(), binding);
+    return shardsByBinding.get(binding);
+  }
+
+  /** Returns the root {@link ComponentImplementation}. */
+  ComponentImplementation rootComponentImplementation() {
+    return parent.map(ComponentImplementation::rootComponentImplementation).orElse(this);
+  }
+
+  /** Returns a reference to this implementation when called from a different class. */
+  public CodeBlock componentFieldReference() {
+    // TODO(bcorso): This currently relies on all requesting classes having a reference to the
+    // component with the same name, which is kind of sketchy. Try to think of a better way that
+    // can accomodate the component missing in some classes if it's not used.
+    return CodeBlock.of("$N", componentFieldsByImplementation.get(this));
+  }
+
+  /** Returns the fields for all components in the component path. */
+  public ImmutableList<FieldSpec> componentFields() {
+    return ImmutableList.copyOf(componentFieldsByImplementation.values());
+  }
+
+  /** Returns the fields for all components in the component path except the current component. */
+  public ImmutableList<FieldSpec> creatorComponentFields() {
+    return componentFieldsByImplementation.entrySet().stream()
+        .filter(entry -> !this.equals(entry.getKey()))
+        .map(Map.Entry::getValue)
+        .collect(toImmutableList());
+  }
+
+  private static ImmutableMap<ComponentImplementation, FieldSpec>
+      createComponentFieldsByImplementation(
+          ComponentImplementation componentImplementation, CompilerOptions compilerOptions) {
+    checkArgument(
+        componentImplementation.componentShard != null,
+        "The component shard must be set before computing the component fields.");
+    ImmutableList.Builder<ComponentImplementation> builder = ImmutableList.builder();
+    for (ComponentImplementation curr = componentImplementation;
+        curr != null;
+        curr = curr.parent.orElse(null)) {
+      builder.add(curr);
+    }
+    // For better readability when adding these fields/parameters to generated code, we collect the
+    // component implementations in reverse order so that parents appear before children.
+    return builder.build().reverse().stream()
+        .collect(
+            toImmutableMap(
+                componentImpl -> componentImpl,
+                componentImpl -> {
+                  ClassName component =
+                      componentImpl.graph.componentPath().currentComponent().className();
+                  ClassName fieldType = componentImpl.name();
+                  String fieldName =
+                      componentImpl.isNested()
+                          ? simpleVariableName(componentImpl.name())
+                          : simpleVariableName(component);
+                  FieldSpec.Builder field = FieldSpec.builder(fieldType, fieldName, PRIVATE, FINAL);
+                  componentImplementation.componentShard.componentFieldNames.claim(fieldName);
+
+                  return field.build();
+                }));
+  }
+  /** Returns the shard representing the {@link ComponentImplementation} itself. */
+  public ShardImplementation getComponentShard() {
+    return componentShard;
+  }
+
   /** Returns the binding graph for the component being generated. */
   public BindingGraph graph() {
-    return graph;
+    return componentShard.graph();
   }
 
   /** Returns the descriptor for the component being generated. */
   public ComponentDescriptor componentDescriptor() {
-    return graph.componentDescriptor();
+    return componentShard.componentDescriptor();
   }
 
   /** Returns the name of the component. */
   public ClassName name() {
-    return name;
+    return componentShard.name;
+  }
+
+  /** Returns if the current compile mode is fast init. */
+  public CompilerMode compilerMode() {
+    return compilerMode;
   }
 
   /** 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);
+  private boolean isNested() {
+    return name().enclosingClassName() != null;
   }
 
   /**
@@ -264,172 +427,666 @@
    * 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());
+    return componentNames.getCreatorName(graph.componentPath());
   }
 
-  /** 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());
-    // TODO(erichang): Hacky fix to shorten the suffix if we're too deeply
-    // nested to save on file name length. 2 chosen arbitrarily.
-    String suffix = name.simpleNames().size() > 2 ? "I" : "Impl";
-    return name.nestedClass(subcomponentNames.get(childDescriptor) + suffix);
+  /** Generates the component and returns the resulting {@link TypeSpec}. */
+  public TypeSpec generate() {
+    return componentShard.generate();
   }
 
   /**
-   * 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.
+   * The implementation of a shard.
    *
-   * <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.
+   * <p>The purpose of a shard is to allow a component implemenation to be split into multiple
+   * classes, where each class owns the creation logic for a set of keys. Sharding is useful for
+   * large component implementations, where a single component implementation class can reach size
+   * limitations, such as the constant pool size.
+   *
+   * <p>When generating the actual sources, the creation logic within the first instance of {@link
+   * ShardImplementation} will go into the component implementation class itself (e.g. {@code
+   * MySubcomponentImpl}). Each subsequent instance of {@link ShardImplementation} will generate a
+   * nested "shard" class within the component implementation (e.g. {@code
+   * MySubcomponentImpl.Shard1}, {@code MySubcomponentImpl.Shard2}, etc).
    */
-  // 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);
-  }
+  public final class ShardImplementation {
+    private final ClassName name;
+    private final UniqueNameSet componentFieldNames = new UniqueNameSet();
+    private final UniqueNameSet componentMethodNames = new UniqueNameSet();
+    private final UniqueNameSet componentClassNames = new UniqueNameSet();
+    private final UniqueNameSet assistedParamNames = new UniqueNameSet();
+    private final List<CodeBlock> initializations = new ArrayList<>();
+    private final Map<Key, CodeBlock> cancellations = new LinkedHashMap<>();
+    private final Map<VariableElement, String> uniqueAssistedName = new LinkedHashMap<>();
+    private final List<CodeBlock> componentRequirementInitializations = new ArrayList<>();
+    private final ImmutableMap<ComponentRequirement, ParameterSpec> constructorParameters;
+    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 boolean initialized = false; // This is used for initializing assistedParamNames.
 
-  /**
-   * 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);
-  }
+    private ShardImplementation(ClassName name) {
+      this.name = name;
+      if (graph.componentDescriptor().isProduction()) {
+        claimMethodName(CANCELLATION_LISTENER_METHOD_NAME);
+      }
 
-  /** 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);
+      // Build the map of constructor parameters for this shard and claim the field names to prevent
+      // collisions between the constructor parameters and fields.
+      constructorParameters =
+          constructorRequirements(graph).stream()
+              .collect(
+                  toImmutableMap(
+                      requirement -> requirement,
+                      requirement ->
+                          ParameterSpec.builder(
+                                  requirement.type().getTypeName(),
+                                  getUniqueFieldName(requirement.variableName() + "Param"))
+                              .build()));
     }
-    return graph.componentTypeElement().getModifiers().contains(PUBLIC)
-        // TODO(ronshapiro): perhaps all generated components should be non-public?
-        ? ImmutableSet.of(PUBLIC, FINAL)
-        : ImmutableSet.of(FINAL);
+
+    private ShardImplementation createShard(String shardName) {
+      checkState(isComponentShard(), "Only the componentShard can create other shards.");
+      return new ShardImplementation(name.nestedClass(shardName));
+    }
+
+    /** Returns the {@link ComponentImplementation} that owns this shard. */
+    public ComponentImplementation getComponentImplementation() {
+      return ComponentImplementation.this;
+    }
+
+    /**
+     * Returns {@code true} if this shard represents the component implementation rather than a
+     * separate {@code Shard} class.
+     */
+    public boolean isComponentShard() {
+      return this == componentShard;
+    }
+
+    /** Returns the fields for all components in the component path by component implementation. */
+    public ImmutableMap<ComponentImplementation, FieldSpec> componentFieldsByImplementation() {
+      return componentFieldsByImplementation;
+    }
+
+    /** Returns a reference to this implementation when called from a different class. */
+    public CodeBlock shardFieldReference() {
+      if (!isComponentShard() && !shardFieldsByImplementation.containsKey(this)) {
+        // Add the shard if this is the first time it's requested by something.
+        String shardFieldName =
+            componentShard.getUniqueFieldName(UPPER_CAMEL.to(LOWER_CAMEL, name.simpleName()));
+        FieldSpec shardField = FieldSpec.builder(name, shardFieldName, PRIVATE).build();
+
+        shardFieldsByImplementation.put(this, shardField);
+      }
+      // TODO(bcorso): This currently relies on all requesting classes having a reference to the
+      // component with the same name, which is kind of sketchy. Try to think of a better way that
+      // can accomodate the component missing in some classes if it's not used.
+      return isComponentShard()
+          ? componentFieldReference()
+          : CodeBlock.of("$L.$N", componentFieldReference(), shardFieldsByImplementation.get(this));
+    }
+
+    // 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 the name of the creator implementation class for the given subcomponent creator
+     * {@link Key}.
+     */
+    ClassName getSubcomponentCreatorSimpleName(Key creatorKey) {
+      return componentNames.getSubcomponentCreatorName(graph.componentPath(), creatorKey);
+    }
+
+    /**
+     * Returns an accessible type for this shard implementation, returns Object if the type is not
+     * accessible.
+     *
+     * <p>This method checks accessibility for public types and package private types, and it also
+     * checks protected types' accessibility.
+     */
+    TypeMirror accessibleType(TypeMirror type) {
+      // Returns the original type if the type is accessible from this shard, or returns original
+      // type's raw type if only its raw type is accessible. Otherwise, returns Object.
+      TypeMirror castedType = types.accessibleType(type, name());
+      // Previous check marks protected type as inaccessible, so a second check is needed to check
+      // if the type is protected type and accessible.
+      if (TypeName.get(castedType).equals(TypeName.OBJECT) && isTypeAccessible(type)) {
+        castedType = type;
+      }
+      return castedType;
+    }
+
+    /**
+     * Returns {@code true} if {@code type} is accessible from the generated component.
+     *
+     * <p>This method checks accessibility for public types and package private types, and it also
+     * checks protected types' accessibility.
+     */
+    boolean isTypeAccessible(TypeMirror type) {
+      if (isTypeAccessibleFrom(type, name.packageName())) {
+        return true;
+      }
+      // Check if the type is protected and accessible from current component.
+      if (type instanceof DeclaredType
+          && isProtectedMemberOf(
+              MoreTypes.asDeclared(type),
+              getComponentImplementation().componentDescriptor().typeElement())) {
+        return true;
+      }
+      return false;
+    }
+
+    // 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 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);
+    }
+
+    /**
+     * Adds the given cancellation statement to the cancellation listener method of the component.
+     */
+    void addCancellation(Key key, CodeBlock codeBlock) {
+      // Store cancellations by key to avoid adding the same cancellation twice.
+      cancellations.putIfAbsent(key, codeBlock);
+    }
+
+    /** Returns a new, unique field name for the component based on the given name. */
+    String getUniqueFieldName(String name) {
+      return componentFieldNames.getUniqueName(name);
+    }
+
+    String getUniqueAssistedParamName(String name) {
+      if (!initialized) {
+        // Assisted params will be used in switching provider, so they can't conflict with component
+        // field names in switching provider. {@link UniqueNameSet#getUniqueName} will add the
+        // component field names to the unique set if it does not exists. If the name already exists
+        // in the set, then a dedupe will be performed automatically on the passed in name, and the
+        // newly created unique name will then be added to the set.
+        componentFieldsByImplementation()
+            .values()
+            .forEach(fieldSpec -> assistedParamNames.getUniqueName(fieldSpec.name));
+        initialized = true;
+      }
+      return assistedParamNames.getUniqueName(name);
+    }
+
+    public String getUniqueFieldNameForAssistedParam(VariableElement element) {
+      if (uniqueAssistedName.containsKey(element)) {
+        return uniqueAssistedName.get(element);
+      }
+      String name = getUniqueAssistedParamName(element.getSimpleName().toString());
+      uniqueAssistedName.put(element, name);
+      return name;
+    }
+
+    /** Returns a new, unique nested class 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()));
+    }
+
+    /** Returns a new, unique method name for the component based on the given name. */
+    public String getUniqueClassName(String name) {
+      return componentClassNames.getUniqueName(name);
+    }
+
+    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) {
+      return constructorParameters.get(requirement).name;
+    }
+
+    /** Claims a new method name for the component. Does nothing if method name already exists. */
+    public void claimMethodName(CharSequence name) {
+      componentMethodNames.claim(name);
+    }
+
+    /** Generates the component and returns the resulting {@link TypeSpec.Builder}. */
+    private TypeSpec generate() {
+      TypeSpec.Builder builder = classBuilder(name);
+
+      if (isComponentShard()) {
+        TypeSpecs.addSupertype(builder, graph.componentTypeElement());
+        addCreator();
+        addFactoryMethods();
+        addInterfaceMethods();
+        addChildComponents();
+        addShards();
+      }
+
+      addConstructorAndInitializationMethods();
+
+      if (graph.componentDescriptor().isProduction()) {
+        if (isComponentShard() || !cancellations.isEmpty()) {
+          TypeSpecs.addSupertype(
+              builder, elements.getTypeElement(TypeNames.CANCELLATION_LISTENER.canonicalName()));
+          addCancellationListenerImplementation();
+        }
+      }
+
+      modifiers().forEach(builder::addModifiers);
+      fieldSpecsMap.asMap().values().forEach(builder::addFields);
+      methodSpecsMap.asMap().values().forEach(builder::addMethods);
+      typeSpecsMap.asMap().values().forEach(builder::addTypes);
+      typeSuppliers.stream().map(Supplier::get).forEach(builder::addType);
+      return builder.build();
+    }
+
+    private ImmutableSet<Modifier> modifiers() {
+      if (!isComponentShard()) {
+        // TODO(bcorso): Consider making shards static and unnested too?
+        return ImmutableSet.of(PRIVATE, FINAL);
+      } else if (isNested()) {
+        return ImmutableSet.of(PRIVATE, STATIC, FINAL);
+      }
+      return graph.componentTypeElement().isPublic()
+          // TODO(ronshapiro): perhaps all generated components should be non-public?
+          ? ImmutableSet.of(PUBLIC, FINAL)
+          : ImmutableSet.of(FINAL);
+    }
+
+    private void addCreator() {
+      componentCreatorImplementationFactoryProvider
+          .get()
+          .create()
+          .map(ComponentCreatorImplementation::spec)
+          .ifPresent(
+              creator ->
+                  rootComponentImplementation()
+                      .getComponentShard()
+                      .addType(TypeSpecKind.COMPONENT_CREATOR, creator));
+    }
+
+    private void addFactoryMethods() {
+      if (parent.isPresent()) {
+        graph
+            .factoryMethod()
+            .map(XConverters::toJavac)
+            .ifPresent(this::createSubcomponentFactoryMethod);
+      } else {
+        createRootComponentFactoryMethod();
+      }
+    }
+
+    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 = descriptor.typeElement().getClassName();
+        factoryMethodName = getSimpleName(descriptor.factoryMethod());
+        noArgFactoryMethod = descriptor.factoryParameters().isEmpty();
+      } else {
+        creatorKind = BUILDER;
+        creatorType = getCreatorName();
+        factoryMethodName = "build";
+        noArgFactoryMethod = true;
+      }
+      validateMethodNameDoesNotOverrideGeneratedCreator(creatorKind.methodName());
+      claimMethodName(creatorKind.methodName());
+      MethodSpec creatorFactoryMethod =
+          methodBuilder(creatorKind.methodName())
+              .addModifiers(PUBLIC, STATIC)
+              .returns(creatorType)
+              .addStatement("return new $T()", getCreatorName())
+              .build();
+      addMethod(MethodSpecKind.BUILDER_METHOD, creatorFactoryMethod);
+      if (noArgFactoryMethod && canInstantiateAllRequirements()) {
+        validateMethodNameDoesNotOverrideGeneratedCreator("create");
+        claimMethodName("create");
+        addMethod(
+            MethodSpecKind.BUILDER_METHOD,
+            methodBuilder("create")
+                .returns(graph.componentTypeElement().getClassName())
+                .addModifiers(PUBLIC, STATIC)
+                .addStatement("return new $L().$L()", creatorKind.typeName(), factoryMethodName)
+                .build());
+      }
+    }
+
+    private void validateMethodNameDoesNotOverrideGeneratedCreator(String creatorName) {
+      // Check if there is any client added method has the same signature as generated creatorName.
+      MoreElements.getAllMethods(toJavac(graph.componentTypeElement()), types, elements).stream()
+          .filter(method -> method.getSimpleName().contentEquals(creatorName))
+          .filter(method -> method.getParameters().isEmpty())
+          .filter(method -> !method.getModifiers().contains(Modifier.STATIC))
+          .forEach(
+              (ExecutableElement method) ->
+                  messager.printMessage(
+                      ERROR,
+                      String.format(
+                          "Cannot override generated method: %s.%s()",
+                          method.getEnclosingElement().getSimpleName(), method.getSimpleName())));
+    }
+
+    /** {@code true} if all of the graph's required dependencies can be automatically constructed */
+    private boolean canInstantiateAllRequirements() {
+      return !Iterables.any(
+          graph.componentRequirements(), ComponentRequirement::requiresAPassedInstance);
+    }
+
+    private void createSubcomponentFactoryMethod(ExecutableElement factoryMethod) {
+      checkState(parent.isPresent());
+      Collection<ParameterSpec> params =
+          Maps.transformValues(
+                  graph.factoryMethodParameters(),
+                  parameter -> ParameterSpec.get(toJavac(parameter)))
+              .values();
+      DeclaredType parentType =
+          asDeclared(toJavac(parent.get().graph().componentTypeElement()).asType());
+      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)",
+          name(),
+          parameterNames(
+              ImmutableList.<ParameterSpec>builder()
+                  .addAll(
+                      creatorComponentFields().stream()
+                          .map(field -> ParameterSpec.builder(field.type, field.name).build())
+                          .collect(toImmutableList()))
+                  .addAll(params)
+                  .build()));
+
+      parent.get().getComponentShard().addMethod(COMPONENT_METHOD, method.build());
+    }
+
+    private void addInterfaceMethods() {
+      // Each component method may have been declared by several supertypes. We want to implement
+      // only one method for each distinct signature.
+      XType componentType = graph.componentTypeElement().getType();
+      Set<MethodSignature> signatures = Sets.newHashSet();
+      for (ComponentMethodDescriptor method : graph.componentDescriptor().entryPointMethods()) {
+        if (signatures.add(MethodSignature.forComponentMethod(method, componentType))) {
+          addMethod(COMPONENT_METHOD, bindingExpressionsProvider.get().getComponentMethod(method));
+        }
+      }
+    }
+
+    private void addChildComponents() {
+      for (BindingGraph subgraph : graph.subgraphs()) {
+        rootComponentImplementation()
+            .getComponentShard()
+            .addType(
+                TypeSpecKind.SUBCOMPONENT,
+                childComponentImplementationFactory.create(subgraph).generate());
+      }
+    }
+
+    private void addShards() {
+      // Generate all shards and add them to this component implementation.
+      for (ShardImplementation shard : ImmutableSet.copyOf(shardsByBinding.values())) {
+        if (shardFieldsByImplementation.containsKey(shard)) {
+          addField(FieldSpecKind.COMPONENT_SHARD_FIELD, shardFieldsByImplementation.get(shard));
+          TypeSpec shardTypeSpec = shard.generate();
+          addType(TypeSpecKind.COMPONENT_SHARD_TYPE, shardTypeSpec);
+        }
+      }
+    }
+
+    /** Creates and adds the constructor and methods needed for initializing the component. */
+    private void addConstructorAndInitializationMethods() {
+      MethodSpec.Builder constructor = constructorBuilder().addModifiers(PRIVATE);
+      ImmutableList<ParameterSpec> parameters = constructorParameters.values().asList();
+
+      if (isComponentShard()) {
+        // Add a constructor parameter and initialization for each component field. We initialize
+        // these fields immediately so that we don't need to be pass them to each initialize method
+        // and shard constructor.
+        componentFieldsByImplementation()
+            .forEach(
+                (componentImplementation, field) -> {
+                  if (componentImplementation.equals(ComponentImplementation.this)) {
+                    // For the self-referenced component field,
+                    // just initialize it in the initializer.
+                    addField(
+                        FieldSpecKind.COMPONENT_REQUIREMENT_FIELD,
+                        field.toBuilder().initializer("this").build());
+                  } else {
+                    addField(FieldSpecKind.COMPONENT_REQUIREMENT_FIELD, field);
+                    constructor.addStatement("this.$1N = $1N", field);
+                    constructor.addParameter(field.type, field.name);
+                  }
+                });
+        constructor.addCode(CodeBlocks.concat(componentRequirementInitializations));
+      }
+      constructor.addParameters(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> initializationMethods =
+          createPartitionedMethods(
+              "initialize",
+              // TODO(bcorso): Rather than passing in all of the constructor parameters, keep track
+              // of which parameters are used during initialization and only pass those. This could
+              // be useful for FastInit, where most of the initializations are just calling
+              // SwitchingProvider with no parameters.
+              makeFinal(parameters),
+              initializations,
+              methodName ->
+                  methodBuilder(methodName)
+                      /* 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(suppressWarnings(UNCHECKED)));
+
+      for (MethodSpec initializationMethod : initializationMethods) {
+        constructor.addStatement("$N($L)", initializationMethod, args);
+        addMethod(MethodSpecKind.INITIALIZE_METHOD, initializationMethod);
+      }
+
+      if (isComponentShard()) {
+        constructor.addCode(CodeBlocks.concat(shardInitializations));
+      } else {
+        // This initialization is called from the componentShard, so we need to use those args.
+        CodeBlock componentArgs =
+            parameterNames(componentShard.constructorParameters.values().asList());
+        FieldSpec shardField = shardFieldsByImplementation.get(this);
+        shardInitializations.add(CodeBlock.of("$N = new $T($L);", shardField, name, componentArgs));
+      }
+
+      addMethod(MethodSpecKind.CONSTRUCTOR, constructor.build());
+    }
+
+    private void addCancellationListenerImplementation() {
+      MethodSpec.Builder methodBuilder =
+          methodBuilder(CANCELLATION_LISTENER_METHOD_NAME)
+              .addModifiers(PUBLIC)
+              .addAnnotation(Override.class)
+              .addParameter(MAY_INTERRUPT_IF_RUNNING_PARAM);
+
+      // 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.
+      if (isComponentShard()) {
+        methodBuilder.addCode(
+            CodeBlocks.concat(ImmutableList.copyOf(shardCancellations).reverse()));
+      } else if (!cancellations.isEmpty()) {
+        shardCancellations.add(
+            CodeBlock.of(
+                "$N.$N($N);",
+                shardFieldsByImplementation.get(this),
+                CANCELLATION_LISTENER_METHOD_NAME,
+                MAY_INTERRUPT_IF_RUNNING_PARAM));
+      }
+
+      ImmutableList<CodeBlock> cancellationStatements =
+          ImmutableList.copyOf(cancellations.values()).reverse();
+      if (cancellationStatements.size() < STATEMENTS_PER_METHOD) {
+        methodBuilder.addCode(CodeBlocks.concat(cancellationStatements)).build();
+      } else {
+        ImmutableList<MethodSpec> cancelProducersMethods =
+            createPartitionedMethods(
+                "cancelProducers",
+                ImmutableList.of(MAY_INTERRUPT_IF_RUNNING_PARAM),
+                cancellationStatements,
+                methodName -> methodBuilder(methodName).addModifiers(PRIVATE));
+        for (MethodSpec cancelProducersMethod : cancelProducersMethods) {
+          methodBuilder.addStatement(
+              "$N($N)", cancelProducersMethod, MAY_INTERRUPT_IF_RUNNING_PARAM);
+          addMethod(MethodSpecKind.CANCELLATION_LISTENER_METHOD, cancelProducersMethod);
+        }
+      }
+
+      if (isComponentShard()) {
+        cancelParentStatement().ifPresent(methodBuilder::addCode);
+      }
+
+      addMethod(MethodSpecKind.CANCELLATION_LISTENER_METHOD, methodBuilder.build());
+    }
+
+    private Optional<CodeBlock> cancelParentStatement() {
+      if (!shouldPropagateCancellationToParent()) {
+        return Optional.empty();
+      }
+      return Optional.of(
+          CodeBlock.builder()
+              .addStatement(
+                  "$L.$N($N)",
+                  parent.get().componentFieldReference(),
+                  CANCELLATION_LISTENER_METHOD_NAME,
+                  MAY_INTERRUPT_IF_RUNNING_PARAM)
+              .build());
+    }
+
+    private boolean shouldPropagateCancellationToParent() {
+      return parent.isPresent()
+          && parent
+              .get()
+              .componentDescriptor()
+              .cancellationPolicy()
+              .map(policy -> policy.fromSubcomponents().equals(PROPAGATE))
+              .orElse(false);
+    }
+
+    /**
+     * 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(getUniqueMethodName(methodName))
+                      .addModifiers(PRIVATE)
+                      .addParameters(parameters)
+                      .addCode(CodeBlocks.concat(partition))
+                      .build())
+          .collect(toImmutableList());
+    }
+  }
+
+  private static ImmutableList<ComponentRequirement> constructorRequirements(BindingGraph graph) {
+    if (graph.componentDescriptor().hasCreator()) {
+      return graph.componentRequirements().asList();
+    } else if (graph.factoryMethod().isPresent()) {
+      return graph.factoryMethodParameters().keySet().asList();
+    } else {
+      throw new AssertionError(
+          "Expected either a component creator or factory method but found neither.");
+    }
+  }
+
+  private static ImmutableList<ParameterSpec> makeFinal(List<ParameterSpec> parameters) {
+    return parameters.stream()
+        .map(param -> param.toBuilder().addModifiers(FINAL).build())
+        .collect(toImmutableList());
   }
 }
diff --git a/java/dagger/internal/codegen/writing/ComponentInstanceBindingExpression.java b/java/dagger/internal/codegen/writing/ComponentInstanceBindingExpression.java
deleted file mode 100644
index 9fa10c6..0000000
--- a/java/dagger/internal/codegen/writing/ComponentInstanceBindingExpression.java
+++ /dev/null
@@ -1,43 +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.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/ComponentInstanceRequestRepresentation.java b/java/dagger/internal/codegen/writing/ComponentInstanceRequestRepresentation.java
new file mode 100644
index 0000000..441ee83
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ComponentInstanceRequestRepresentation.java
@@ -0,0 +1,52 @@
+/*
+ * 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.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+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 ComponentInstanceRequestRepresentation extends RequestRepresentation {
+  private final ComponentImplementation componentImplementation;
+  private final ContributionBinding binding;
+
+  @AssistedInject
+  ComponentInstanceRequestRepresentation(
+      @Assisted ContributionBinding binding, ComponentImplementation componentImplementation) {
+    this.componentImplementation = componentImplementation;
+    this.binding = binding;
+  }
+
+  @Override
+  Expression getDependencyExpression(ClassName requestingClass) {
+    return Expression.create(
+        binding.key().type().java(),
+        componentImplementation.name().equals(requestingClass)
+            ? CodeBlock.of("this")
+            : componentImplementation.componentFieldReference());
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    ComponentInstanceRequestRepresentation create(ContributionBinding binding);
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/ComponentMethodBindingExpression.java b/java/dagger/internal/codegen/writing/ComponentMethodBindingExpression.java
deleted file mode 100644
index 7fa9aa3..0000000
--- a/java/dagger/internal/codegen/writing/ComponentMethodBindingExpression.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.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/ComponentMethodRequestRepresentation.java b/java/dagger/internal/codegen/writing/ComponentMethodRequestRepresentation.java
new file mode 100644
index 0000000..77e313f
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ComponentMethodRequestRepresentation.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.xprocessing.XElements.getSimpleName;
+
+import com.squareup.javapoet.CodeBlock;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
+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 ComponentMethodRequestRepresentation extends MethodRequestRepresentation {
+  private final RequestRepresentation wrappedRequestRepresentation;
+  private final ComponentImplementation componentImplementation;
+  private final ComponentMethodDescriptor componentMethod;
+  private final DaggerTypes types;
+
+  @AssistedInject
+  ComponentMethodRequestRepresentation(
+      @Assisted RequestRepresentation wrappedRequestRepresentation,
+      @Assisted ComponentMethodDescriptor componentMethod,
+      ComponentImplementation componentImplementation,
+      DaggerTypes types) {
+    super(componentImplementation.getComponentShard(), types);
+    this.wrappedRequestRepresentation = checkNotNull(wrappedRequestRepresentation);
+    this.componentMethod = checkNotNull(componentMethod);
+    this.componentImplementation = componentImplementation;
+    this.types = types;
+  }
+
+  @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.
+    // 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)
+        ? CodeBlock.of(
+            "return $L;",
+            wrappedRequestRepresentation
+                .getDependencyExpressionForComponentMethod(componentMethod, componentImplementation)
+                .codeBlock())
+        : super.getComponentMethodImplementation(componentMethod, component);
+  }
+
+  @Override
+  protected CodeBlock methodCall() {
+    return CodeBlock.of("$N()", getSimpleName(componentMethod.methodElement()));
+  }
+
+  @Override
+  protected TypeMirror returnType() {
+    return componentMethod.resolvedReturnType(types);
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    ComponentMethodRequestRepresentation create(
+        RequestRepresentation wrappedRequestRepresentation,
+        ComponentMethodDescriptor componentMethod);
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/ComponentNames.java b/java/dagger/internal/codegen/writing/ComponentNames.java
new file mode 100644
index 0000000..d53a0f4
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ComponentNames.java
@@ -0,0 +1,204 @@
+/*
+ * 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 com.google.common.base.Preconditions.checkState;
+import static dagger.internal.codegen.binding.SourceFiles.classFileName;
+import static dagger.internal.codegen.extension.DaggerCollectors.onlyElement;
+import static java.lang.String.format;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimaps;
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.base.ComponentCreatorKind;
+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.spi.model.ComponentPath;
+import dagger.spi.model.Key;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import javax.inject.Inject;
+
+/**
+ * Holds the unique simple names for all components, keyed by their {@link ComponentPath} and {@link
+ * Key} of the subcomponent builder.
+ */
+public final class ComponentNames {
+  /** Returns the class name for the root component. */
+  public static ClassName getRootComponentClassName(ComponentDescriptor componentDescriptor) {
+    checkState(!componentDescriptor.isSubcomponent());
+    ClassName componentName = componentDescriptor.typeElement().getClassName();
+    return ClassName.get(componentName.packageName(), "Dagger" + classFileName(componentName));
+  }
+
+  private static final Splitter QUALIFIED_NAME_SPLITTER = Splitter.on('.');
+
+  private final ClassName rootName;
+  private final ImmutableMap<ComponentPath, String> namesByPath;
+  private final ImmutableMap<ComponentPath, String> creatorNamesByPath;
+  private final ImmutableMultimap<Key, ComponentPath> pathsByCreatorKey;
+
+  @Inject
+  ComponentNames(@TopLevel BindingGraph graph, KeyFactory keyFactory) {
+    this.rootName = getRootComponentClassName(graph.componentDescriptor());
+    this.namesByPath = namesByPath(graph);
+    this.creatorNamesByPath = creatorNamesByPath(namesByPath, graph);
+    this.pathsByCreatorKey = pathsByCreatorKey(keyFactory, graph);
+  }
+
+  /** Returns the simple component name for the given {@link ComponentDescriptor}. */
+  ClassName get(ComponentPath componentPath) {
+    return componentPath.atRoot()
+        ? rootName
+        : rootName.nestedClass(namesByPath.get(componentPath) + "Impl");
+  }
+
+  /**
+   * Returns the component descriptor for the component with the given subcomponent creator {@link
+   * Key}.
+   */
+  ClassName getSubcomponentCreatorName(ComponentPath componentPath, Key creatorKey) {
+    checkArgument(pathsByCreatorKey.containsKey(creatorKey));
+    // First, find the subcomponent path corresponding to the subcomponent creator key.
+    // The key may correspond to multiple paths, so we need to find the one under this component.
+    ComponentPath subcomponentPath =
+        pathsByCreatorKey.get(creatorKey).stream()
+            .filter(path -> path.parent().equals(componentPath))
+            .collect(onlyElement());
+    return getCreatorName(subcomponentPath);
+  }
+
+  /**
+   * Returns the simple name for the subcomponent creator implementation for the given {@link
+   * ComponentDescriptor}.
+   */
+  ClassName getCreatorName(ComponentPath componentPath) {
+    checkArgument(creatorNamesByPath.containsKey(componentPath));
+    return rootName.nestedClass(creatorNamesByPath.get(componentPath));
+  }
+
+  private static ImmutableMap<ComponentPath, String> creatorNamesByPath(
+      ImmutableMap<ComponentPath, String> namesByPath, BindingGraph graph) {
+    ImmutableMap.Builder<ComponentPath, String> builder = ImmutableMap.builder();
+    graph
+        .componentDescriptorsByPath()
+        .forEach(
+            (componentPath, componentDescriptor) -> {
+              if (componentPath.atRoot()) {
+                ComponentCreatorKind creatorKind =
+                    componentDescriptor
+                        .creatorDescriptor()
+                        .map(ComponentCreatorDescriptor::kind)
+                        .orElse(ComponentCreatorKind.BUILDER);
+                builder.put(componentPath, creatorKind.typeName());
+              } else if (componentDescriptor.creatorDescriptor().isPresent()) {
+                ComponentCreatorDescriptor creatorDescriptor =
+                    componentDescriptor.creatorDescriptor().get();
+                String componentName = namesByPath.get(componentPath);
+                builder.put(componentPath, componentName + creatorDescriptor.kind().typeName());
+              }
+            });
+    return builder.build();
+  }
+
+  private static ImmutableMap<ComponentPath, String> namesByPath(BindingGraph graph) {
+    Map<ComponentPath, String> componentPathsBySimpleName = new LinkedHashMap<>();
+    Multimaps.index(graph.componentDescriptorsByPath().keySet(), ComponentNames::simpleName)
+        .asMap()
+        .values()
+        .stream()
+        .map(ComponentNames::disambiguateConflictingSimpleNames)
+        .forEach(componentPathsBySimpleName::putAll);
+    componentPathsBySimpleName.remove(graph.componentPath());
+    return ImmutableMap.copyOf(componentPathsBySimpleName);
+  }
+
+  private static ImmutableMultimap<Key, ComponentPath> pathsByCreatorKey(
+      KeyFactory keyFactory, BindingGraph graph) {
+    ImmutableMultimap.Builder<Key, ComponentPath> builder = ImmutableMultimap.builder();
+    graph
+        .componentDescriptorsByPath()
+        .forEach(
+            (componentPath, componentDescriptor) -> {
+              if (componentDescriptor.creatorDescriptor().isPresent()) {
+                Key creatorKey =
+                    keyFactory.forSubcomponentCreator(
+                        componentDescriptor.creatorDescriptor().get().typeElement().getType());
+                builder.put(creatorKey, componentPath);
+              }
+            });
+    return builder.build();
+  }
+
+  private static ImmutableMap<ComponentPath, String> disambiguateConflictingSimpleNames(
+      Collection<ComponentPath> componentsWithConflictingNames) {
+    // If there's only 1 component there's nothing to disambiguate so return the simple name.
+    if (componentsWithConflictingNames.size() == 1) {
+      ComponentPath componentPath = Iterables.getOnlyElement(componentsWithConflictingNames);
+      return ImmutableMap.of(componentPath, simpleName(componentPath));
+    }
+
+    // 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<ComponentPath, String> uniqueNames = ImmutableMap.builder();
+    for (ComponentPath componentPath : componentsWithConflictingNames) {
+      String simpleName = simpleName(componentPath);
+      String basePrefix = uniquingPrefix(componentPath);
+      uniqueNames.put(
+          componentPath, format("%s_%s", nameSet.getUniqueName(basePrefix), simpleName));
+    }
+    return uniqueNames.build();
+  }
+
+  private static String simpleName(ComponentPath componentPath) {
+    return componentPath.currentComponent().className().simpleName();
+  }
+
+  /** Returns a prefix that could make the component's simple name more unique. */
+  private static String uniquingPrefix(ComponentPath componentPath) {
+    ClassName component = componentPath.currentComponent().className();
+
+    if (component.enclosingClassName() != null) {
+      return CharMatcher.javaLowerCase().removeFrom(component.enclosingClassName().simpleName());
+    }
+
+    // Not in a normally named class. Prefix with the initials of the elements leading here.
+    Iterator<String> pieces = QUALIFIED_NAME_SPLITTER.split(component.canonicalName()).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/ComponentProvisionBindingExpression.java b/java/dagger/internal/codegen/writing/ComponentProvisionRequestRepresentation.java
similarity index 61%
rename from java/dagger/internal/codegen/writing/ComponentProvisionBindingExpression.java
rename to java/dagger/internal/codegen/writing/ComponentProvisionRequestRepresentation.java
index 53914b6..31eecb2 100644
--- a/java/dagger/internal/codegen/writing/ComponentProvisionBindingExpression.java
+++ b/java/dagger/internal/codegen/writing/ComponentProvisionRequestRepresentation.java
@@ -16,10 +16,13 @@
 
 package dagger.internal.codegen.writing;
 
-import static com.google.common.base.Preconditions.checkNotNull;
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
 import dagger.internal.Preconditions;
 import dagger.internal.codegen.binding.BindingGraph;
 import dagger.internal.codegen.binding.ComponentRequirement;
@@ -28,36 +31,48 @@
 import dagger.internal.codegen.javapoet.Expression;
 
 /** A binding expression for component provision methods. */
-final class ComponentProvisionBindingExpression extends SimpleInvocationBindingExpression {
+final class ComponentProvisionRequestRepresentation extends RequestRepresentation {
   private final ProvisionBinding binding;
   private final BindingGraph bindingGraph;
   private final ComponentRequirementExpressions componentRequirementExpressions;
   private final CompilerOptions compilerOptions;
+  private final boolean isExperimentalMergedMode;
 
-  ComponentProvisionBindingExpression(
-      ProvisionBinding binding,
+  @AssistedInject
+  ComponentProvisionRequestRepresentation(
+      @Assisted ProvisionBinding binding,
       BindingGraph bindingGraph,
+      ComponentImplementation componentImplementation,
       ComponentRequirementExpressions componentRequirementExpressions,
       CompilerOptions compilerOptions) {
-    super(binding);
     this.binding = binding;
-    this.bindingGraph = checkNotNull(bindingGraph);
-    this.componentRequirementExpressions = checkNotNull(componentRequirementExpressions);
-    this.compilerOptions = checkNotNull(compilerOptions);
+    this.bindingGraph = bindingGraph;
+    this.componentRequirementExpressions = componentRequirementExpressions;
+    this.compilerOptions = compilerOptions;
+    this.isExperimentalMergedMode =
+        componentImplementation.compilerMode().isExperimentalMergedMode();
   }
 
   @Override
   Expression getDependencyExpression(ClassName requestingClass) {
+    CodeBlock componentDependency =
+        isExperimentalMergedMode
+            ? CodeBlock.of("(($T) dependencies[0])", componentRequirement().type().getTypeName())
+            : getComponentRequirementExpression(requestingClass);
     CodeBlock invocation =
         CodeBlock.of(
             "$L.$L()",
-            componentRequirementExpressions.getExpression(componentRequirement(), requestingClass),
-            binding.bindingElement().get().getSimpleName());
+            componentDependency,
+            toJavac(binding.bindingElement().get()).getSimpleName());
     return Expression.create(
-        binding.contributedPrimitiveType().orElse(binding.key().type()),
+        binding.contributedPrimitiveType().orElse(binding.key().type().xprocessing()),
         maybeCheckForNull(binding, compilerOptions, invocation));
   }
 
+  CodeBlock getComponentRequirementExpression(ClassName requestingClass) {
+    return componentRequirementExpressions.getExpression(componentRequirement(), requestingClass);
+  }
+
   private ComponentRequirement componentRequirement() {
     return bindingGraph
         .componentDescriptor()
@@ -70,4 +85,9 @@
         ? CodeBlock.of("$T.checkNotNullFromComponent($L)", Preconditions.class, invocation)
         : invocation;
   }
+
+  @AssistedFactory
+  static interface Factory {
+    ComponentProvisionRequestRepresentation create(ProvisionBinding binding);
+  }
 }
diff --git a/java/dagger/internal/codegen/writing/ComponentRequestRepresentations.java b/java/dagger/internal/codegen/writing/ComponentRequestRepresentations.java
new file mode 100644
index 0000000..324cef6
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ComponentRequestRepresentations.java
@@ -0,0 +1,259 @@
+/*
+ * 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.Preconditions.checkState;
+import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
+import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
+import static dagger.internal.codegen.langmodel.Accessibility.isRawTypeAccessible;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+
+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.BindingRequest;
+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.ProductionBinding;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.xprocessing.MethodSpecs;
+import dagger.spi.model.DependencyRequest;
+import dagger.spi.model.RequestKind;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import javax.inject.Inject;
+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 ComponentRequestRepresentations {
+  // 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 RequestRepresentation.Factory create it.
+
+  private final Optional<ComponentRequestRepresentations> parent;
+  private final BindingGraph graph;
+  private final ComponentImplementation componentImplementation;
+  private final ComponentRequirementExpressions componentRequirementExpressions;
+  private final MembersInjectionBindingRepresentation.Factory
+      membersInjectionBindingRepresentationFactory;
+  private final ProvisionBindingRepresentation.Factory provisionBindingRepresentationFactory;
+  private final ProductionBindingRepresentation.Factory productionBindingRepresentationFactory;
+  private final ExperimentalSwitchingProviderDependencyRepresentation.Factory
+      experimentalSwitchingProviderDependencyRepresentationFactory;
+  private final DaggerTypes types;
+  private final Map<Binding, BindingRepresentation> representations = new HashMap<>();
+  private final Map<Binding, ExperimentalSwitchingProviderDependencyRepresentation>
+      experimentalSwitchingProviderDependencyRepresentations = new HashMap<>();
+
+  @Inject
+  ComponentRequestRepresentations(
+      @ParentComponent Optional<ComponentRequestRepresentations> parent,
+      BindingGraph graph,
+      ComponentImplementation componentImplementation,
+      ComponentRequirementExpressions componentRequirementExpressions,
+      MembersInjectionBindingRepresentation.Factory membersInjectionBindingRepresentationFactory,
+      ProvisionBindingRepresentation.Factory provisionBindingRepresentationFactory,
+      ProductionBindingRepresentation.Factory productionBindingRepresentationFactory,
+      ExperimentalSwitchingProviderDependencyRepresentation.Factory
+          experimentalSwitchingProviderDependencyRepresentationFactory,
+      DaggerTypes types) {
+    this.parent = parent;
+    this.graph = graph;
+    this.componentImplementation = componentImplementation;
+    this.membersInjectionBindingRepresentationFactory =
+        membersInjectionBindingRepresentationFactory;
+    this.provisionBindingRepresentationFactory = provisionBindingRepresentationFactory;
+    this.productionBindingRepresentationFactory = productionBindingRepresentationFactory;
+    this.experimentalSwitchingProviderDependencyRepresentationFactory =
+        experimentalSwitchingProviderDependencyRepresentationFactory;
+    this.componentRequirementExpressions = checkNotNull(componentRequirementExpressions);
+    this.types = types;
+  }
+
+  /**
+   * 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 getRequestRepresentation(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 getRequestRepresentation(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, ClassName requestingClass) {
+    return makeParametersCodeBlock(getCreateMethodArgumentsCodeBlocks(binding, requestingClass));
+  }
+
+  private ImmutableList<CodeBlock> getCreateMethodArgumentsCodeBlocks(
+      ContributionBinding binding, ClassName requestingClass) {
+    ImmutableList.Builder<CodeBlock> arguments = ImmutableList.builder();
+
+    if (binding.requiresModuleInstance()) {
+      arguments.add(
+          componentRequirementExpressions.getExpressionDuringInitialization(
+              ComponentRequirement.forModule(binding.contributingModule().get().getType()),
+              requestingClass));
+    }
+
+    binding.dependencies().stream()
+        .map(dependency -> frameworkRequest(binding, dependency))
+        .map(request -> getDependencyExpression(request, requestingClass))
+        .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().java();
+    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());
+    return MethodSpecs.overriding(
+            componentMethod.methodElement(), graph.componentTypeElement().getType())
+        .addCode(
+            getRequestRepresentation(request)
+                .getComponentMethodImplementation(componentMethod, componentImplementation))
+        .build();
+  }
+
+  /** Returns the {@link RequestRepresentation} for the given {@link BindingRequest}. */
+  RequestRepresentation getRequestRepresentation(BindingRequest request) {
+    Optional<Binding> localBinding =
+        request.isRequestKind(RequestKind.MEMBERS_INJECTION)
+            ? graph.localMembersInjectionBinding(request.key())
+            : graph.localContributionBinding(request.key());
+
+    if (localBinding.isPresent()) {
+      return getBindingRepresentation(localBinding.get()).getRequestRepresentation(request);
+    }
+
+    checkArgument(parent.isPresent(), "no expression found for %s", request);
+    return parent.get().getRequestRepresentation(request);
+  }
+
+  private BindingRepresentation getBindingRepresentation(Binding binding) {
+    return reentrantComputeIfAbsent(
+        representations, binding, this::getBindingRepresentationUncached);
+  }
+
+  private BindingRepresentation getBindingRepresentationUncached(Binding binding) {
+    switch (binding.bindingType()) {
+      case MEMBERS_INJECTION:
+        return membersInjectionBindingRepresentationFactory.create(
+            (MembersInjectionBinding) binding);
+      case PROVISION:
+        return provisionBindingRepresentationFactory.create((ProvisionBinding) binding);
+      case PRODUCTION:
+        return productionBindingRepresentationFactory.create((ProductionBinding) binding);
+    }
+    throw new AssertionError();
+  }
+
+  /**
+   * Returns an {@link ExperimentalSwitchingProviderDependencyRepresentation} for the requested
+   * binding to satisfy dependency requests on it from experimental switching providers. Cannot be
+   * used for Members Injection requests.
+   */
+  ExperimentalSwitchingProviderDependencyRepresentation
+      getExperimentalSwitchingProviderDependencyRepresentation(BindingRequest request) {
+    checkState(
+        componentImplementation.compilerMode().isExperimentalMergedMode(),
+        "Compiler mode should be experimentalMergedMode!");
+    Optional<Binding> localBinding = graph.localContributionBinding(request.key());
+
+    if (localBinding.isPresent()) {
+      return reentrantComputeIfAbsent(
+          experimentalSwitchingProviderDependencyRepresentations,
+          localBinding.get(),
+          binding ->
+              experimentalSwitchingProviderDependencyRepresentationFactory.create(
+                  (ProvisionBinding) binding));
+    }
+
+    checkArgument(parent.isPresent(), "no expression found for %s", request);
+    return parent.get().getExperimentalSwitchingProviderDependencyRepresentation(request);
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/ComponentRequirementExpression.java b/java/dagger/internal/codegen/writing/ComponentRequirementExpression.java
index 13008b8..cdfdf26 100644
--- a/java/dagger/internal/codegen/writing/ComponentRequirementExpression.java
+++ b/java/dagger/internal/codegen/writing/ComponentRequirementExpression.java
@@ -22,8 +22,8 @@
 
 /**
  * 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
+ * <em>not</em> a {@link RequestRepresentation}, since {@link ComponentRequirement}s do not have a
+ * {@link dagger.spi.model.Key}. See {@link ComponentRequirementRequestRepresentation} for binding
  * expressions that are themselves a component requirement.
  */
 interface ComponentRequirementExpression {
diff --git a/java/dagger/internal/codegen/writing/ComponentRequirementExpressions.java b/java/dagger/internal/codegen/writing/ComponentRequirementExpressions.java
index 653a7a2..2e9e5f1 100644
--- a/java/dagger/internal/codegen/writing/ComponentRequirementExpressions.java
+++ b/java/dagger/internal/codegen/writing/ComponentRequirementExpressions.java
@@ -23,6 +23,7 @@
 import static javax.lang.model.element.Modifier.FINAL;
 import static javax.lang.model.element.Modifier.PRIVATE;
 
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.common.base.Supplier;
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
@@ -31,11 +32,11 @@
 import dagger.internal.codegen.binding.BindingGraph;
 import dagger.internal.codegen.binding.ComponentRequirement;
 import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
 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
@@ -44,7 +45,7 @@
 @PerComponentImplementation
 public final class ComponentRequirementExpressions {
 
-  // TODO(dpb,ronshapiro): refactor this and ComponentBindingExpressions into a
+  // TODO(dpb,ronshapiro): refactor this and ComponentRequestRepresentations into a
   // HierarchicalComponentMap<K, V>, or perhaps this use a flattened ImmutableMap, built from its
   // parents? If so, maybe make ComponentRequirementExpression.Factory create it.
 
@@ -52,10 +53,9 @@
   private final Map<ComponentRequirement, ComponentRequirementExpression>
       componentRequirementExpressions = new HashMap<>();
   private final BindingGraph graph;
-  private final ComponentImplementation componentImplementation;
+  private final ShardImplementation componentShard;
   private final ModuleProxies moduleProxies;
 
-  // TODO(ronshapiro): give ComponentImplementation a graph() method
   @Inject
   ComponentRequirementExpressions(
       @ParentComponent Optional<ComponentRequirementExpressions> parent,
@@ -65,7 +65,8 @@
       ModuleProxies moduleProxies) {
     this.parent = parent;
     this.graph = graph;
-    this.componentImplementation = componentImplementation;
+    // All component requirements go in the componentShard.
+    this.componentShard = componentImplementation.getComponentShard();
     this.moduleProxies = moduleProxies;
   }
 
@@ -78,6 +79,19 @@
     return getExpression(componentRequirement).getExpression(requestingClass);
   }
 
+  private ComponentRequirementExpression getExpression(ComponentRequirement componentRequirement) {
+    if (graph.componentRequirements().contains(componentRequirement)) {
+      return componentRequirementExpressions.computeIfAbsent(
+          componentRequirement, this::createExpression);
+    }
+    if (parent.isPresent()) {
+      return parent.get().getExpression(componentRequirement);
+    }
+
+    throw new IllegalStateException(
+        "no component requirement expression found for " + componentRequirement);
+  }
+
   /**
    * Returns an expression for the {@code componentRequirement} to be used only within {@code
    * initialize()} methods, where the component constructor parameters are available.
@@ -90,58 +104,26 @@
     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));
+  private ComponentRequirementExpression createExpression(ComponentRequirement requirement) {
+    if (componentShard.componentDescriptor().hasCreator()
+        || (graph.factoryMethod().isPresent()
+            && graph.factoryMethodParameters().containsKey(requirement))) {
+      return new ComponentParameterField(requirement);
     } else if (requirement.kind().isModule()) {
-      return new InstantiableModuleField(requirement, componentImplementation);
+      return new InstantiableModuleField(requirement);
     } else {
       throw new AssertionError(
-          String.format("Can't create %s in %s", requirement, componentImplementation.name()));
+          String.format("Can't create %s in %s", requirement, componentShard.name()));
     }
   }
 
-  private abstract static class AbstractField implements ComponentRequirementExpression {
+  private abstract class AbstractField implements ComponentRequirementExpression {
     final ComponentRequirement componentRequirement;
-    final ComponentImplementation componentImplementation;
-    final String fieldName;
-    private final Supplier<MemberSelect> field = memoize(this::addField);
+    private final Supplier<MemberSelect> field = memoize(this::createField);
 
-    private AbstractField(
-        ComponentRequirement componentRequirement,
-        ComponentImplementation componentImplementation) {
+    private AbstractField(ComponentRequirement componentRequirement) {
       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
@@ -149,16 +131,13 @@
       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();
+    private MemberSelect createField() {
+      String fieldName = componentShard.getUniqueFieldName(componentRequirement.variableName());
+      TypeName fieldType = componentRequirement.type().getTypeName();
+      FieldSpec field = FieldSpec.builder(fieldType, fieldName, PRIVATE, FINAL).build();
+      componentShard.addField(COMPONENT_REQUIREMENT_FIELD, field);
+      componentShard.addComponentRequirementInitialization(fieldInitialization(field));
+      return MemberSelect.localField(componentShard, fieldName);
     }
 
     /** Returns the {@link CodeBlock} that initializes the component field during construction. */
@@ -170,11 +149,10 @@
    * 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 final XTypeElement moduleElement;
 
-    private InstantiableModuleField(
-        ComponentRequirement module, ComponentImplementation componentImplementation) {
-      super(module, componentImplementation);
+    InstantiableModuleField(ComponentRequirement module) {
+      super(module);
       checkArgument(module.kind().isModule());
       this.moduleElement = module.typeElement();
     }
@@ -184,7 +162,7 @@
       return CodeBlock.of(
           "this.$N = $L;",
           componentField,
-          moduleProxies.newModuleInstance(moduleElement, componentImplementation.name()));
+          moduleProxies.newModuleInstance(moduleElement, componentShard.name()));
     }
   }
 
@@ -192,29 +170,17 @@
    * 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 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);
+    ComponentParameterField(ComponentRequirement module) {
+      super(module);
+      this.parameterName = componentShard.getParameterName(componentRequirement);
     }
 
     @Override
     public CodeBlock getExpressionDuringInitialization(ClassName requestingClass) {
-      if (componentImplementation.name().equals(requestingClass)) {
+      if (componentShard.name().equals(requestingClass)) {
         return CodeBlock.of("$L", parameterName);
       } else {
         // requesting this component requirement during initialization of a child component requires
diff --git a/java/dagger/internal/codegen/writing/ComponentRequirementBindingExpression.java b/java/dagger/internal/codegen/writing/ComponentRequirementRequestRepresentation.java
similarity index 70%
rename from java/dagger/internal/codegen/writing/ComponentRequirementBindingExpression.java
rename to java/dagger/internal/codegen/writing/ComponentRequirementRequestRepresentation.java
index 299f279..f9e8f14 100644
--- a/java/dagger/internal/codegen/writing/ComponentRequirementBindingExpression.java
+++ b/java/dagger/internal/codegen/writing/ComponentRequirementRequestRepresentation.java
@@ -16,7 +16,12 @@
 
 package dagger.internal.codegen.writing;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import com.squareup.javapoet.ClassName;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
 import dagger.internal.codegen.binding.ComponentRequirement;
 import dagger.internal.codegen.binding.ContributionBinding;
 import dagger.internal.codegen.javapoet.Expression;
@@ -26,16 +31,16 @@
  * {@linkplain dagger.Component#dependencies() component} and {@linkplain
  * dagger.producers.ProductionComponent#dependencies() production component dependencies}.
  */
-final class ComponentRequirementBindingExpression extends SimpleInvocationBindingExpression {
+final class ComponentRequirementRequestRepresentation extends RequestRepresentation {
   private final ComponentRequirement componentRequirement;
   private final ComponentRequirementExpressions componentRequirementExpressions;
 
-  ComponentRequirementBindingExpression(
-      ContributionBinding binding,
-      ComponentRequirement componentRequirement,
+  @AssistedInject
+  ComponentRequirementRequestRepresentation(
+      @Assisted ContributionBinding binding,
+      @Assisted ComponentRequirement componentRequirement,
       ComponentRequirementExpressions componentRequirementExpressions) {
-    super(binding);
-    this.componentRequirement = componentRequirement;
+    this.componentRequirement = checkNotNull(componentRequirement);
     this.componentRequirementExpressions = componentRequirementExpressions;
   }
 
@@ -45,4 +50,10 @@
         componentRequirement.type(),
         componentRequirementExpressions.getExpression(componentRequirement, requestingClass));
   }
+
+  @AssistedFactory
+  static interface Factory {
+    ComponentRequirementRequestRepresentation create(
+        ContributionBinding binding, ComponentRequirement componentRequirement);
+  }
 }
diff --git a/java/dagger/internal/codegen/writing/DelegateBindingExpression.java b/java/dagger/internal/codegen/writing/DelegateRequestRepresentation.java
similarity index 67%
rename from java/dagger/internal/codegen/writing/DelegateBindingExpression.java
rename to java/dagger/internal/codegen/writing/DelegateRequestRepresentation.java
index 3361132..59aa394 100644
--- a/java/dagger/internal/codegen/writing/DelegateBindingExpression.java
+++ b/java/dagger/internal/codegen/writing/DelegateRequestRepresentation.java
@@ -16,15 +16,20 @@
 
 package dagger.internal.codegen.writing;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 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 static dagger.spi.model.BindingKind.DELEGATE;
 
 import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
 import dagger.internal.codegen.binding.Binding;
 import dagger.internal.codegen.binding.BindingGraph;
 import dagger.internal.codegen.binding.BindsTypeChecker;
@@ -32,27 +37,28 @@
 import dagger.internal.codegen.javapoet.Expression;
 import dagger.internal.codegen.langmodel.DaggerElements;
 import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.RequestKind;
+import dagger.spi.model.RequestKind;
 import javax.lang.model.type.TypeMirror;
 
-/** A {@link dagger.internal.codegen.writing.BindingExpression} for {@code @Binds} methods. */
-final class DelegateBindingExpression extends BindingExpression {
+/** A {@link dagger.internal.codegen.writing.RequestRepresentation} for {@code @Binds} methods. */
+final class DelegateRequestRepresentation extends RequestRepresentation {
   private final ContributionBinding binding;
   private final RequestKind requestKind;
-  private final ComponentBindingExpressions componentBindingExpressions;
+  private final ComponentRequestRepresentations componentRequestRepresentations;
   private final DaggerTypes types;
   private final BindsTypeChecker bindsTypeChecker;
 
-  DelegateBindingExpression(
-      ContributionBinding binding,
-      RequestKind requestKind,
-      ComponentBindingExpressions componentBindingExpressions,
+  @AssistedInject
+  DelegateRequestRepresentation(
+      @Assisted ContributionBinding binding,
+      @Assisted RequestKind requestKind,
+      ComponentRequestRepresentations componentRequestRepresentations,
       DaggerTypes types,
       DaggerElements elements) {
     this.binding = checkNotNull(binding);
     this.requestKind = checkNotNull(requestKind);
-    this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
-    this.types = checkNotNull(types);
+    this.componentRequestRepresentations = componentRequestRepresentations;
+    this.types = types;
     this.bindsTypeChecker = new BindsTypeChecker(types, elements);
   }
 
@@ -73,14 +79,14 @@
   @Override
   Expression getDependencyExpression(ClassName requestingClass) {
     Expression delegateExpression =
-        componentBindingExpressions.getDependencyExpression(
+        componentRequestRepresentations.getDependencyExpression(
             bindingRequest(getOnlyElement(binding.dependencies()).key(), requestKind),
             requestingClass);
 
-    TypeMirror contributedType = binding.contributedType();
+    TypeMirror contributedType = toJavac(binding.contributedType());
     switch (requestKind) {
       case INSTANCE:
-        return instanceRequiresCast(delegateExpression, requestingClass)
+        return instanceRequiresCast(binding, delegateExpression, requestingClass, bindsTypeChecker)
             ? delegateExpression.castTo(contributedType)
             : delegateExpression;
       default:
@@ -89,12 +95,17 @@
     }
   }
 
-  private boolean instanceRequiresCast(Expression delegateExpression, ClassName requestingClass) {
+  static boolean instanceRequiresCast(
+      ContributionBinding binding,
+      Expression delegateExpression,
+      ClassName requestingClass,
+      BindsTypeChecker bindsTypeChecker) {
     // delegateExpression.type() could be Object if expression is satisfied with a raw
     // Provider's get() method.
+    TypeMirror contributedType = toJavac(binding.contributedType());
     return !bindsTypeChecker.isAssignable(
-        delegateExpression.type(), binding.contributedType(), binding.contributionType())
-        && isTypeAccessibleFrom(binding.contributedType(), requestingClass.packageName());
+            delegateExpression.type(), contributedType, binding.contributionType())
+        && isTypeAccessibleFrom(contributedType, requestingClass.packageName());
   }
 
   /**
@@ -110,7 +121,13 @@
     if (types.isAssignable(delegateExpression.type(), desiredType)) {
       return delegateExpression;
     }
-    return delegateExpression.castTo(types.erasure(desiredType));
+    Expression castedExpression = delegateExpression.castTo(types.erasure(desiredType));
+    // Casted raw type provider expression has to be wrapped parentheses, otherwise there
+    // will be an error when DerivedFromFrameworkInstanceRequestRepresentation appends a `get()` to
+    // it.
+    // TODO(wanyingd): change the logic to only add parenthesis when necessary.
+    return Expression.create(
+        castedExpression.type(), CodeBlock.of("($L)", castedExpression.codeBlock()));
   }
 
   private enum ScopeKind {
@@ -130,4 +147,9 @@
       return this.ordinal() > other.ordinal();
     }
   }
+
+  @AssistedFactory
+  static interface Factory {
+    DelegateRequestRepresentation create(ContributionBinding binding, RequestKind requestKind);
+  }
 }
diff --git a/java/dagger/internal/codegen/writing/DelegatingFrameworkInstanceCreationExpression.java b/java/dagger/internal/codegen/writing/DelegatingFrameworkInstanceCreationExpression.java
index a7f9556..096d109 100644
--- a/java/dagger/internal/codegen/writing/DelegatingFrameworkInstanceCreationExpression.java
+++ b/java/dagger/internal/codegen/writing/DelegatingFrameworkInstanceCreationExpression.java
@@ -21,10 +21,14 @@
 import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
 
 import com.squareup.javapoet.CodeBlock;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
 import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.compileroption.CompilerOptions;
 import dagger.internal.codegen.javapoet.CodeBlocks;
 import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
-import dagger.model.DependencyRequest;
+import dagger.spi.model.DependencyRequest;
 
 /** A framework instance creation expression for a {@link dagger.Binds @Binds} binding. */
 final class DelegatingFrameworkInstanceCreationExpression
@@ -32,26 +36,33 @@
 
   private final ContributionBinding binding;
   private final ComponentImplementation componentImplementation;
-  private final ComponentBindingExpressions componentBindingExpressions;
+  private final ComponentRequestRepresentations componentRequestRepresentations;
 
+  @AssistedInject
   DelegatingFrameworkInstanceCreationExpression(
-      ContributionBinding binding,
+      @Assisted ContributionBinding binding,
       ComponentImplementation componentImplementation,
-      ComponentBindingExpressions componentBindingExpressions) {
+      ComponentRequestRepresentations componentRequestRepresentations,
+      CompilerOptions compilerOptions) {
     this.binding = checkNotNull(binding);
-    this.componentImplementation = checkNotNull(componentImplementation);
-    this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+    this.componentImplementation = componentImplementation;
+    this.componentRequestRepresentations = componentRequestRepresentations;
   }
 
   @Override
   public CodeBlock creationExpression() {
     DependencyRequest dependency = getOnlyElement(binding.dependencies());
     return CodeBlocks.cast(
-        componentBindingExpressions
+        componentRequestRepresentations
             .getDependencyExpression(
                 bindingRequest(dependency.key(), binding.frameworkType()),
-                componentImplementation.name())
+                componentImplementation.shardImplementation(binding).name())
             .codeBlock(),
-        binding.frameworkType().frameworkClass());
+        binding.frameworkType().frameworkClassName());
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    DelegatingFrameworkInstanceCreationExpression create(ContributionBinding binding);
   }
 }
diff --git a/java/dagger/internal/codegen/writing/DependencyMethodProducerCreationExpression.java b/java/dagger/internal/codegen/writing/DependencyMethodProducerCreationExpression.java
index 5ac1e8f..64a689e 100644
--- a/java/dagger/internal/codegen/writing/DependencyMethodProducerCreationExpression.java
+++ b/java/dagger/internal/codegen/writing/DependencyMethodProducerCreationExpression.java
@@ -16,6 +16,7 @@
 
 package dagger.internal.codegen.writing;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.squareup.javapoet.MethodSpec.methodBuilder;
 import static com.squareup.javapoet.TypeSpec.anonymousClassBuilder;
@@ -25,10 +26,12 @@
 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.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
 import dagger.internal.codegen.binding.BindingGraph;
 import dagger.internal.codegen.binding.ComponentRequirement;
 import dagger.internal.codegen.binding.ContributionBinding;
@@ -47,15 +50,16 @@
   private final ComponentRequirementExpressions componentRequirementExpressions;
   private final BindingGraph graph;
 
+  @AssistedInject
   DependencyMethodProducerCreationExpression(
-      ContributionBinding binding,
+      @Assisted ContributionBinding binding,
       ComponentImplementation componentImplementation,
       ComponentRequirementExpressions componentRequirementExpressions,
       BindingGraph graph) {
     this.binding = checkNotNull(binding);
-    this.componentImplementation = checkNotNull(componentImplementation);
-    this.componentRequirementExpressions = checkNotNull(componentRequirementExpressions);
-    this.graph = checkNotNull(graph);
+    this.componentImplementation = componentImplementation;
+    this.componentRequirementExpressions = componentRequirementExpressions;
+    this.graph = graph;
   }
 
   @Override
@@ -64,7 +68,7 @@
         graph.componentDescriptor().getDependencyThatDefinesMethod(binding.bindingElement().get());
     FieldSpec dependencyField =
         FieldSpec.builder(
-                ClassName.get(dependency.typeElement()), dependency.variableName(), PRIVATE, FINAL)
+                dependency.typeElement().getClassName(), dependency.variableName(), PRIVATE, FINAL)
             .initializer(
                 componentRequirementExpressions.getExpressionDuringInitialization(
                     dependency,
@@ -79,7 +83,7 @@
                     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());
+    TypeName keyType = TypeName.get(binding.key().type().java());
     return CodeBlock.of(
         "$L",
         anonymousClassBuilder("")
@@ -93,8 +97,13 @@
                     .addStatement(
                         "return $N.$L()",
                         dependencyField,
-                        binding.bindingElement().get().getSimpleName())
+                        toJavac(binding.bindingElement().get()).getSimpleName())
                     .build())
             .build());
   }
+
+  @AssistedFactory
+  static interface Factory {
+    DependencyMethodProducerCreationExpression create(ContributionBinding binding);
+  }
 }
diff --git a/java/dagger/internal/codegen/writing/DependencyMethodProviderCreationExpression.java b/java/dagger/internal/codegen/writing/DependencyMethodProviderCreationExpression.java
index af5d45e..8ea3742 100644
--- a/java/dagger/internal/codegen/writing/DependencyMethodProviderCreationExpression.java
+++ b/java/dagger/internal/codegen/writing/DependencyMethodProviderCreationExpression.java
@@ -16,29 +16,35 @@
 
 package dagger.internal.codegen.writing;
 
+import static androidx.room.compiler.processing.XElementKt.isMethod;
+import static com.google.common.base.Preconditions.checkArgument;
 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 dagger.internal.codegen.xprocessing.XElements.asMethod;
+import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
 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 androidx.room.compiler.processing.XMethodElement;
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
 import com.squareup.javapoet.MethodSpec;
 import com.squareup.javapoet.TypeName;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
 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.ComponentImplementation.ShardImplementation;
 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
@@ -48,23 +54,29 @@
 final class DependencyMethodProviderCreationExpression
     implements FrameworkInstanceCreationExpression {
 
-  private final ComponentImplementation componentImplementation;
+  private final ShardImplementation shardImplementation;
   private final ComponentRequirementExpressions componentRequirementExpressions;
   private final CompilerOptions compilerOptions;
   private final BindingGraph graph;
-  private final ContributionBinding binding;
+  private final ProvisionBinding binding;
+  private final XMethodElement provisionMethod;
 
+  @AssistedInject
   DependencyMethodProviderCreationExpression(
-      ContributionBinding binding,
+      @Assisted ProvisionBinding 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);
+    this.shardImplementation = componentImplementation.shardImplementation(binding);
+    this.componentRequirementExpressions = componentRequirementExpressions;
+    this.compilerOptions = compilerOptions;
+    this.graph = graph;
+
+    checkArgument(binding.bindingElement().isPresent());
+    checkArgument(isMethod(binding.bindingElement().get()));
+    provisionMethod = asMethod(binding.bindingElement().get());
   }
 
   @Override
@@ -76,13 +88,12 @@
     // using .class & String.format -- but that wouldn't be the whole story.
     // What should we do?
     CodeBlock invocation =
-        ComponentProvisionBindingExpression.maybeCheckForNull(
-            (ProvisionBinding) binding,
+        ComponentProvisionRequestRepresentation.maybeCheckForNull(
+            binding,
             compilerOptions,
-            CodeBlock.of(
-                "$N.$N()", dependency().variableName(), provisionMethod().getSimpleName()));
-    ClassName dependencyClassName = ClassName.get(dependency().typeElement());
-    TypeName keyType = TypeName.get(binding.key().type());
+            CodeBlock.of("$N.$N()", dependency().variableName(), getSimpleName(provisionMethod)));
+    ClassName dependencyClassName = dependency().typeElement().getClassName();
+    TypeName keyType = binding.key().type().xprocessing().getTypeName();
     MethodSpec.Builder getMethod =
         methodBuilder("get")
             .addAnnotation(Override.class)
@@ -90,11 +101,23 @@
             .returns(keyType)
             .addStatement("return $L", invocation);
     if (binding.nullableType().isPresent()) {
-      getMethod.addAnnotation(ClassName.get(MoreTypes.asTypeElement(binding.nullableType().get())));
+      getMethod.addAnnotation(binding.nullableType().get().getTypeElement().getClassName());
     }
-    componentImplementation.addType(
+
+    // We need to use the componentShard here since the generated type is static and shards are
+    // not static classes so it can't be nested inside the shard.
+    ShardImplementation componentShard =
+        shardImplementation.getComponentImplementation().getComponentShard();
+    ClassName factoryClassName =
+        componentShard
+            .name()
+            .nestedClass(
+                dependency().typeElement().getQualifiedName().replace('.', '_')
+                    + "_"
+                    + getSimpleName(provisionMethod));
+    componentShard.addType(
         COMPONENT_PROVISION_FACTORY,
-        classBuilder(factoryClassName())
+        classBuilder(factoryClassName)
             .addSuperinterface(providerOf(keyType))
             .addModifiers(PRIVATE, STATIC, FINAL)
             .addField(dependencyClassName, dependency().variableName(), PRIVATE, FINAL)
@@ -107,24 +130,17 @@
             .build());
     return CodeBlock.of(
         "new $T($L)",
-        factoryClassName(),
+        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);
+            dependency(), shardImplementation.name()));
   }
 
   private ComponentRequirement dependency() {
-    return graph.componentDescriptor().getDependencyThatDefinesMethod(provisionMethod());
+    return graph.componentDescriptor().getDependencyThatDefinesMethod(provisionMethod);
   }
 
-  private Element provisionMethod() {
-    return binding.bindingElement().get();
+  @AssistedFactory
+  static interface Factory {
+    DependencyMethodProviderCreationExpression create(ProvisionBinding binding);
   }
 }
diff --git a/java/dagger/internal/codegen/writing/DerivedFromFrameworkInstanceBindingExpression.java b/java/dagger/internal/codegen/writing/DerivedFromFrameworkInstanceBindingExpression.java
deleted file mode 100644
index 6e5dca8..0000000
--- a/java/dagger/internal/codegen/writing/DerivedFromFrameworkInstanceBindingExpression.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.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/DerivedFromFrameworkInstanceRequestRepresentation.java b/java/dagger/internal/codegen/writing/DerivedFromFrameworkInstanceRequestRepresentation.java
new file mode 100644
index 0000000..79895e9
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/DerivedFromFrameworkInstanceRequestRepresentation.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2018 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.writing.DelegateRequestRepresentation.instanceRequiresCast;
+
+import com.squareup.javapoet.ClassName;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import dagger.internal.codegen.binding.BindsTypeChecker;
+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.spi.model.BindingKind;
+import dagger.spi.model.RequestKind;
+
+/** A binding expression that depends on a framework instance. */
+final class DerivedFromFrameworkInstanceRequestRepresentation extends RequestRepresentation {
+  private final ContributionBinding binding;
+  private final RequestRepresentation frameworkRequestRepresentation;
+  private final RequestKind requestKind;
+  private final FrameworkType frameworkType;
+  private final DaggerTypes types;
+  private final BindsTypeChecker bindsTypeChecker;
+
+  @AssistedInject
+  DerivedFromFrameworkInstanceRequestRepresentation(
+      @Assisted ContributionBinding binding,
+      @Assisted RequestRepresentation frameworkRequestRepresentation,
+      @Assisted RequestKind requestKind,
+      @Assisted FrameworkType frameworkType,
+      DaggerTypes types,
+      DaggerElements elements) {
+    this.binding = binding;
+    this.frameworkRequestRepresentation = checkNotNull(frameworkRequestRepresentation);
+    this.requestKind = requestKind;
+    this.frameworkType = checkNotNull(frameworkType);
+    this.types = types;
+    this.bindsTypeChecker = new BindsTypeChecker(types, elements);
+  }
+
+  @Override
+  Expression getDependencyExpression(ClassName requestingClass) {
+    Expression expression =
+        frameworkType.to(
+            requestKind,
+            frameworkRequestRepresentation.getDependencyExpression(requestingClass),
+            types);
+    return requiresTypeCast(expression, requestingClass)
+        ? expression.castTo(binding.contributedType())
+        : expression;
+  }
+
+  @Override
+  Expression getDependencyExpressionForComponentMethod(
+      ComponentMethodDescriptor componentMethod, ComponentImplementation component) {
+    Expression expression =
+        frameworkType.to(
+            requestKind,
+            frameworkRequestRepresentation.getDependencyExpressionForComponentMethod(
+                componentMethod, component),
+            types);
+    return requiresTypeCast(expression, component.name())
+        ? expression.castTo(binding.contributedType())
+        : expression;
+  }
+
+  private boolean requiresTypeCast(Expression expression, ClassName requestingClass) {
+    return binding.kind().equals(BindingKind.DELEGATE)
+        && requestKind.equals(RequestKind.INSTANCE)
+        && instanceRequiresCast(binding, expression, requestingClass, bindsTypeChecker);
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    DerivedFromFrameworkInstanceRequestRepresentation create(
+        ContributionBinding binding,
+        RequestRepresentation frameworkRequestRepresentation,
+        RequestKind requestKind,
+        FrameworkType frameworkType);
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/DirectInstanceBindingRepresentation.java b/java/dagger/internal/codegen/writing/DirectInstanceBindingRepresentation.java
new file mode 100644
index 0000000..c90d813
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/DirectInstanceBindingRepresentation.java
@@ -0,0 +1,170 @@
+/*
+ * 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.writing;
+
+import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
+
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.BindingRequest;
+import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
+import dagger.spi.model.RequestKind;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+/** Returns request representation based on a direct instance expression. */
+final class DirectInstanceBindingRepresentation {
+  private final ProvisionBinding binding;
+  private final BindingGraph graph;
+  private final ComponentImplementation componentImplementation;
+  private final ComponentMethodRequestRepresentation.Factory
+      componentMethodRequestRepresentationFactory;
+  private final ImmediateFutureRequestRepresentation.Factory
+      immediateFutureRequestRepresentationFactory;
+  private final PrivateMethodRequestRepresentation.Factory
+      privateMethodRequestRepresentationFactory;
+  private final UnscopedDirectInstanceRequestRepresentationFactory
+      unscopedDirectInstanceRequestRepresentationFactory;
+  private final Map<BindingRequest, RequestRepresentation> requestRepresentations = new HashMap<>();
+
+  @AssistedInject
+  DirectInstanceBindingRepresentation(
+      @Assisted ProvisionBinding binding,
+      BindingGraph graph,
+      ComponentImplementation componentImplementation,
+      ComponentMethodRequestRepresentation.Factory componentMethodRequestRepresentationFactory,
+      ImmediateFutureRequestRepresentation.Factory immediateFutureRequestRepresentationFactory,
+      PrivateMethodRequestRepresentation.Factory privateMethodRequestRepresentationFactory,
+      UnscopedDirectInstanceRequestRepresentationFactory
+          unscopedDirectInstanceRequestRepresentationFactory) {
+    this.binding = binding;
+    this.graph = graph;
+    this.componentImplementation = componentImplementation;
+    this.componentMethodRequestRepresentationFactory = componentMethodRequestRepresentationFactory;
+    this.immediateFutureRequestRepresentationFactory = immediateFutureRequestRepresentationFactory;
+    this.privateMethodRequestRepresentationFactory = privateMethodRequestRepresentationFactory;
+    this.unscopedDirectInstanceRequestRepresentationFactory =
+        unscopedDirectInstanceRequestRepresentationFactory;
+  }
+
+  public RequestRepresentation getRequestRepresentation(BindingRequest request) {
+    return reentrantComputeIfAbsent(
+        requestRepresentations, request, this::getRequestRepresentationUncached);
+  }
+
+  private RequestRepresentation getRequestRepresentationUncached(BindingRequest request) {
+    switch (request.requestKind()) {
+      case INSTANCE:
+        return requiresMethodEncapsulation(binding)
+            ? wrapInMethod(unscopedDirectInstanceRequestRepresentationFactory.create(binding))
+            : unscopedDirectInstanceRequestRepresentationFactory.create(binding);
+
+      case FUTURE:
+        return immediateFutureRequestRepresentationFactory.create(
+            getRequestRepresentation(bindingRequest(binding.key(), RequestKind.INSTANCE)),
+            binding.key().type().java());
+
+      default:
+        throw new AssertionError(
+            String.format("Invalid binding request kind: %s", request.requestKind()));
+    }
+  }
+
+  /**
+   * 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.
+   */
+  RequestRepresentation wrapInMethod(RequestRepresentation bindingExpression) {
+    // If we've already wrapped the expression, then use the delegate.
+    if (bindingExpression instanceof MethodRequestRepresentation) {
+      return bindingExpression;
+    }
+
+    BindingRequest request = bindingRequest(binding.key(), RequestKind.INSTANCE);
+    Optional<ComponentMethodDescriptor> matchingComponentMethod =
+        graph.componentDescriptor().firstMatchingComponentMethod(request);
+
+    ShardImplementation shardImplementation = componentImplementation.shardImplementation(binding);
+
+    // 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 ComponentMethodRequestRepresentation rather than a
+    // PrivateMethodRequestRepresentation 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
+    // PrivateMethodRequestRepresentation and put the private method in the shard.
+    if (matchingComponentMethod.isPresent() && shardImplementation.isComponentShard()) {
+      ComponentMethodDescriptor componentMethod = matchingComponentMethod.get();
+      return componentMethodRequestRepresentationFactory.create(bindingExpression, componentMethod);
+    } else {
+      return privateMethodRequestRepresentationFactory.create(request, binding, bindingExpression);
+    }
+  }
+
+  private static boolean requiresMethodEncapsulation(ProvisionBinding binding) {
+    switch (binding.kind()) {
+      case COMPONENT:
+      case COMPONENT_PROVISION:
+      case SUBCOMPONENT_CREATOR:
+      case COMPONENT_DEPENDENCY:
+      case MULTIBOUND_SET:
+      case MULTIBOUND_MAP:
+      case BOUND_INSTANCE:
+      case ASSISTED_FACTORY:
+      case ASSISTED_INJECTION:
+      case INJECTION:
+      case PROVISION:
+        // These binding kinds satify a binding request with a component method or a private
+        // method when the requested binding has dependencies. The method will wrap the logic of
+        // creating the binding instance. Without the encapsulation, we might see many levels of
+        // nested instance creation code in a single statement to satisfy all dependencies of a
+        // binding request.
+        return !binding.dependencies().isEmpty();
+      case MEMBERS_INJECTOR:
+      case PRODUCTION:
+      case COMPONENT_PRODUCTION:
+      case OPTIONAL:
+      case DELEGATE:
+      case MEMBERS_INJECTION:
+        return false;
+    }
+    throw new AssertionError(String.format("No such binding kind: %s", binding.kind()));
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    DirectInstanceBindingRepresentation create(ProvisionBinding binding);
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/ExperimentalSwitchingProviderDependencyRepresentation.java b/java/dagger/internal/codegen/writing/ExperimentalSwitchingProviderDependencyRepresentation.java
new file mode 100644
index 0000000..817f76b
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ExperimentalSwitchingProviderDependencyRepresentation.java
@@ -0,0 +1,124 @@
+/*
+ * 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.writing;
+
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static dagger.internal.codegen.langmodel.Accessibility.isRawTypeAccessible;
+import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
+
+import com.squareup.javapoet.CodeBlock;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import dagger.internal.codegen.base.ContributionType;
+import dagger.internal.codegen.binding.BindsTypeChecker;
+import dagger.internal.codegen.binding.FrameworkType;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.internal.codegen.langmodel.DaggerElements;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
+import dagger.spi.model.BindingKind;
+import dagger.spi.model.DependencyRequest;
+import dagger.spi.model.RequestKind;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Returns type casted expressions to satisfy dependency requests from experimental switching
+ * providers.
+ */
+final class ExperimentalSwitchingProviderDependencyRepresentation {
+  private final ProvisionBinding binding;
+  private final ShardImplementation shardImplementation;
+  private final BindsTypeChecker bindsTypeChecker;
+  private final DaggerTypes types;
+  private final DaggerElements elements;
+  private final TypeMirror type;
+
+  @AssistedInject
+  ExperimentalSwitchingProviderDependencyRepresentation(
+      @Assisted ProvisionBinding binding,
+      ComponentImplementation componentImplementation,
+      DaggerTypes types,
+      DaggerElements elements) {
+    this.binding = binding;
+    this.shardImplementation = componentImplementation.shardImplementation(binding);
+    this.types = types;
+    this.elements = elements;
+    this.bindsTypeChecker = new BindsTypeChecker(types, elements);
+    this.type =
+        isDelegateSetValuesBinding()
+            ? types.erasure(elements.getTypeElement(TypeNames.COLLECTION).asType())
+            : toJavac(binding.contributedType());
+  }
+
+  Expression getDependencyExpression(RequestKind requestKind, ProvisionBinding requestingBinding) {
+    int index = findIndexOfDependency(requestingBinding);
+    TypeMirror frameworkType =
+        types.getDeclaredType(elements.getTypeElement(FrameworkType.PROVIDER.frameworkClassName()));
+    Expression expression =
+        FrameworkType.PROVIDER.to(
+            requestKind,
+            Expression.create(
+                frameworkType, CodeBlock.of("(($T) dependencies[$L])", frameworkType, index)),
+            types);
+    if (usesExplicitTypeCast(expression, requestKind)) {
+      return expression.castTo(type);
+    }
+    if (usesErasedTypeCast(requestKind)) {
+      return expression.castTo(types.erasure(type));
+    }
+    return expression;
+  }
+
+  private int findIndexOfDependency(ProvisionBinding requestingBinding) {
+    return requestingBinding.dependencies().stream()
+            .map(DependencyRequest::key)
+            .collect(toImmutableList())
+            .indexOf(binding.key())
+        + (requestingBinding.requiresModuleInstance()
+                && requestingBinding.contributingModule().isPresent()
+            ? 1
+            : 0);
+  }
+
+  private boolean isDelegateSetValuesBinding() {
+    return binding.kind().equals(BindingKind.DELEGATE)
+        && binding.contributionType().equals(ContributionType.SET_VALUES);
+  }
+
+  private boolean usesExplicitTypeCast(Expression expression, RequestKind requestKind) {
+    // If the type is accessible, we can directly cast the expression use the type.
+    return requestKind.equals(RequestKind.INSTANCE)
+        && !bindsTypeChecker.isAssignable(expression.type(), type, binding.contributionType())
+        && isTypeAccessibleFrom(type, shardImplementation.name().packageName());
+  }
+
+  private boolean usesErasedTypeCast(RequestKind requestKind) {
+    // If a type has inaccessible type arguments, then cast to raw type.
+    return requestKind.equals(RequestKind.INSTANCE)
+        && !isTypeAccessibleFrom(type, shardImplementation.name().packageName())
+        && isRawTypeAccessible(type, shardImplementation.name().packageName());
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    ExperimentalSwitchingProviderDependencyRepresentation create(ProvisionBinding binding);
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/ExperimentalSwitchingProviders.java b/java/dagger/internal/codegen/writing/ExperimentalSwitchingProviders.java
new file mode 100644
index 0000000..679be6b
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ExperimentalSwitchingProviders.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 androidx.room.compiler.processing.compat.XConverters.toJavac;
+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.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.Lists;
+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 com.squareup.javapoet.TypeVariableName;
+import dagger.internal.codegen.base.UniqueNameSet;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
+import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import dagger.spi.model.Key;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.TreeMap;
+import javax.inject.Inject;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Keeps track of all provider expression requests for a component.
+ *
+ * <p>The provider expression request will be satisfied by a single generated {@code Provider} class
+ * that can provide instances for all types by switching on an id.
+ */
+@PerComponentImplementation
+final class ExperimentalSwitchingProviders {
+  /**
+   * 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 ShardImplementation componentShard;
+  private final DaggerTypes types;
+  private final UniqueNameSet switchingProviderNames = new UniqueNameSet();
+  private final ComponentRequestRepresentations componentRequestRepresentations;
+  private final ComponentImplementation componentImplementation;
+
+  @Inject
+  ExperimentalSwitchingProviders(
+      ComponentImplementation componentImplementation,
+      ComponentRequestRepresentations componentRequestRepresentations,
+      DaggerTypes types) {
+    this.componentShard = checkNotNull(componentImplementation).getComponentShard();
+    this.componentRequestRepresentations = componentRequestRepresentations;
+    this.componentImplementation = componentImplementation;
+    this.types = checkNotNull(types);
+  }
+
+  /** Returns the framework instance creation expression for an inner switching provider class. */
+  FrameworkInstanceCreationExpression newFrameworkInstanceCreationExpression(
+      ProvisionBinding binding, RequestRepresentation unscopedInstanceRequestRepresentation) {
+    return new FrameworkInstanceCreationExpression() {
+      @Override
+      public CodeBlock creationExpression() {
+        return switchingProviderBuilders
+            .computeIfAbsent(binding.key(), key -> getSwitchingProviderBuilder())
+            .getNewInstanceCodeBlock(binding, unscopedInstanceRequestRepresentation);
+      }
+    };
+  }
+
+  private SwitchingProviderBuilder getSwitchingProviderBuilder() {
+    if (switchingProviderBuilders.size() % MAX_CASES_PER_CLASS == 0) {
+      String name = switchingProviderNames.getUniqueName("SwitchingProvider");
+      // TODO(wanyingd): move Switching Providers and injection methods to Shard classes to avoid
+      // exceeding component class constant pool limit.
+      SwitchingProviderBuilder switchingProviderBuilder =
+          new SwitchingProviderBuilder(componentShard.name().nestedClass(name));
+      componentShard.addTypeSupplier(switchingProviderBuilder::build);
+      return switchingProviderBuilder;
+    }
+    return getLast(switchingProviderBuilders.values());
+  }
+
+  // TODO(bcorso): Consider just merging this class with ExperimentalSwitchingProviders.
+  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);
+    }
+
+    private CodeBlock getNewInstanceCodeBlock(
+        ProvisionBinding binding, RequestRepresentation unscopedInstanceRequestRepresentation) {
+      Key key = binding.key();
+      if (!switchIds.containsKey(key)) {
+        int switchId = switchIds.size();
+        switchIds.put(key, switchId);
+        switchCases.put(
+            switchId, createSwitchCaseCodeBlock(key, unscopedInstanceRequestRepresentation));
+      }
+
+      ShardImplementation shardImplementation =
+          componentImplementation.shardImplementation(binding);
+      CodeBlock switchingProviderDependencies;
+      switch (binding.kind()) {
+          // TODO(wanyingd): there might be a better way to get component requirement information
+          // without using unscopedInstanceRequestRepresentation.
+        case COMPONENT_PROVISION:
+          switchingProviderDependencies =
+              ((ComponentProvisionRequestRepresentation) unscopedInstanceRequestRepresentation)
+                  .getComponentRequirementExpression(shardImplementation.name());
+          break;
+        case SUBCOMPONENT_CREATOR:
+          switchingProviderDependencies =
+              ((SubcomponentCreatorRequestRepresentation) unscopedInstanceRequestRepresentation)
+                  .getDependencyExpressionArguments();
+          break;
+        case MULTIBOUND_SET:
+        case MULTIBOUND_MAP:
+        case OPTIONAL:
+        case INJECTION:
+        case PROVISION:
+        case ASSISTED_FACTORY:
+          // Arguments built in the order of module reference, provision dependencies and members
+          // injection dependencies
+          switchingProviderDependencies =
+              componentRequestRepresentations.getCreateMethodArgumentsCodeBlock(
+                  binding, shardImplementation.name());
+          break;
+        default:
+          throw new IllegalArgumentException("Unexpected binding kind: " + binding.kind());
+      }
+
+      TypeMirror castedType =
+          shardImplementation.accessibleType(toJavac(binding.contributedType()));
+      return CodeBlock.of(
+          "new $T<$L>($L)",
+          switchingProviderType,
+          // Add the type parameter explicitly when the binding is scoped because Java can't resolve
+          // the type when wrapped. For example, the following will error:
+          //   fooProvider = DoubleCheck.provider(new SwitchingProvider<>(1));
+          CodeBlock.of("$T", castedType),
+          switchingProviderDependencies.isEmpty()
+              ? CodeBlock.of("$L", switchIds.get(key))
+              : CodeBlock.of("$L, $L", switchIds.get(key), switchingProviderDependencies));
+    }
+
+    private CodeBlock createSwitchCaseCodeBlock(
+        Key key, RequestRepresentation unscopedInstanceRequestRepresentation) {
+      // TODO(bcorso): Try to delay calling getDependencyExpression() until we are writing out the
+      // SwitchingProvider because calling it here makes FrameworkFieldInitializer think there's a
+      // cycle when initializing ExperimentalSwitchingProviders which adds an unnecessary
+      // DelegateFactory.
+      CodeBlock instanceCodeBlock =
+          unscopedInstanceRequestRepresentation
+              .getDependencyExpression(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(key), key)
+          .addStatement("return ($T) $L", T, instanceCodeBlock)
+          .build();
+    }
+
+    private TypeSpec build() {
+      TypeSpec.Builder builder =
+          classBuilder(switchingProviderType)
+              .addModifiers(PRIVATE, FINAL, STATIC)
+              .addTypeVariable(T)
+              .addSuperinterface(providerOf(T))
+              .addMethods(getMethods());
+
+      // The SwitchingProvider constructor lists switch id first and then the dependency array.
+      MethodSpec.Builder constructor = MethodSpec.constructorBuilder();
+      builder.addField(TypeName.INT, "id", PRIVATE, FINAL);
+      constructor.addParameter(TypeName.INT, "id").addStatement("this.id = id");
+      // Pass in provision dependencies and members injection dependencies.
+      builder.addField(Object[].class, "dependencies", FINAL, PRIVATE);
+      constructor
+          .addParameter(Object[].class, "dependencies")
+          .addStatement("this.dependencies = dependencies")
+          .varargs(true);
+
+      return builder.addMethod(constructor.build()).build();
+    }
+
+    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/FactoryGenerator.java b/java/dagger/internal/codegen/writing/FactoryGenerator.java
index 03ad27c..ea83778 100644
--- a/java/dagger/internal/codegen/writing/FactoryGenerator.java
+++ b/java/dagger/internal/codegen/writing/FactoryGenerator.java
@@ -17,35 +17,41 @@
 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.extension.DaggerStreams.presentValues;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
 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 dagger.internal.codegen.xprocessing.XElements.getSimpleName;
+import static dagger.spi.model.BindingKind.INJECTION;
+import static dagger.spi.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 androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XFiler;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
+import androidx.room.compiler.processing.compat.XConverters;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
+import com.squareup.javapoet.AnnotationSpec;
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
 import com.squareup.javapoet.FieldSpec;
@@ -56,22 +62,27 @@
 import dagger.internal.Factory;
 import dagger.internal.codegen.base.SourceFileGenerator;
 import dagger.internal.codegen.base.UniqueNameSet;
+import dagger.internal.codegen.binding.Binding;
 import dagger.internal.codegen.binding.ProvisionBinding;
 import dagger.internal.codegen.compileroption.CompilerOptions;
-import dagger.internal.codegen.javapoet.CodeBlocks;
+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.InjectionMethods.InjectionSiteMethod;
 import dagger.internal.codegen.writing.InjectionMethods.ProvisionMethod;
-import dagger.model.BindingKind;
-import dagger.model.DependencyRequest;
+import dagger.spi.model.BindingKind;
+import dagger.spi.model.DaggerAnnotation;
+import dagger.spi.model.DependencyRequest;
+import dagger.spi.model.Key;
+import dagger.spi.model.Scope;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
-import javax.annotation.processing.Filer;
+import java.util.stream.Stream;
 import javax.inject.Inject;
 import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
+import javax.lang.model.element.VariableElement;
 
 /**
  * Generates {@link Factory} implementations from {@link ProvisionBinding} instances for {@link
@@ -84,7 +95,7 @@
 
   @Inject
   FactoryGenerator(
-      Filer filer,
+      XFiler filer,
       SourceVersion sourceVersion,
       DaggerTypes types,
       DaggerElements elements,
@@ -97,7 +108,7 @@
   }
 
   @Override
-  public Element originatingElement(ProvisionBinding binding) {
+  public XElement originatingElement(ProvisionBinding binding) {
     // we only create factories for bindings that have a binding element
     return binding.bindingElement().get();
   }
@@ -108,7 +119,7 @@
     checkArgument(!binding.unresolved().isPresent());
     checkArgument(binding.bindingElement().isPresent());
 
-    if (binding.factoryCreationStrategy().equals(DELEGATE)) {
+    if (binding.kind() == BindingKind.DELEGATE) {
       return ImmutableList.of();
     }
 
@@ -121,6 +132,13 @@
             .addModifiers(PUBLIC, FINAL)
             .addTypeVariables(bindingTypeElementTypeVariableNames(binding));
 
+    if (binding.kind() == BindingKind.INJECTION
+        || binding.kind() == BindingKind.ASSISTED_INJECTION
+        || binding.kind() == BindingKind.PROVISION) {
+      factoryBuilder.addAnnotation(scopeMetadataAnnotation(binding));
+      factoryBuilder.addAnnotation(qualifierMetadataAnnotation(binding));
+    }
+
     factoryTypeName(binding).ifPresent(factoryBuilder::addSuperinterface);
     addConstructorAndFields(binding, factoryBuilder);
     factoryBuilder.addMethod(getMethod(binding));
@@ -133,7 +151,7 @@
   }
 
   private void addConstructorAndFields(ProvisionBinding binding, TypeSpec.Builder factoryBuilder) {
-    if (binding.factoryCreationStrategy().equals(SINGLETON_INSTANCE)) {
+    if (FactoryCreationStrategy.of(binding) == FactoryCreationStrategy.SINGLETON_INSTANCE) {
       return;
     }
     // TODO(bcorso): Make the constructor private?
@@ -157,7 +175,7 @@
   private Optional<ParameterSpec> moduleParameter(ProvisionBinding binding) {
     if (binding.requiresModuleInstance()) {
       // TODO(bcorso, dpb): Should this use contributingModule()?
-      TypeName type = TypeName.get(binding.bindingTypeElement().get().asType());
+      TypeName type = binding.bindingTypeElement().get().getType().getTypeName();
       return Optional.of(ParameterSpec.builder(type, "module").build());
     }
     return Optional.empty();
@@ -167,13 +185,16 @@
     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 ->
+    // We avoid Maps.transformValues here because it would implicitly depend on the order in which
+    // the transform function is evaluated on each entry in the map.
+    ImmutableMap.Builder<DependencyRequest, FieldSpec> builder = ImmutableMap.builder();
+    generateBindingFieldsForDependencies(binding).forEach(
+        (dependency, field) ->
+            builder.put(dependency,
                 FieldSpec.builder(
                         field.type(), uniqueFieldNames.getUniqueName(field.name()), PRIVATE, FINAL)
                     .build()));
+    return builder.build();
   }
 
   private void addCreateMethod(ProvisionBinding binding, TypeSpec.Builder factoryBuilder) {
@@ -186,7 +207,7 @@
             .returns(parameterizedGeneratedTypeNameForBinding(binding))
             .addTypeVariables(bindingTypeElementTypeVariableNames(binding));
 
-    switch (binding.factoryCreationStrategy()) {
+    switch (FactoryCreationStrategy.of(binding)) {
       case SINGLETON_INSTANCE:
         FieldSpec.Builder instanceFieldBuilder =
             FieldSpec.builder(
@@ -223,28 +244,36 @@
   }
 
   private MethodSpec getMethod(ProvisionBinding binding) {
+    UniqueNameSet uniqueFieldNames = new UniqueNameSet();
+    ImmutableMap<DependencyRequest, FieldSpec> frameworkFields = frameworkFields(binding);
+    frameworkFields.values().forEach(field -> uniqueFieldNames.claim(field.name));
+    Map<VariableElement, ParameterSpec> assistedParameters =
+        assistedParameters(binding).stream()
+            .collect(
+                toImmutableMap(
+                    XConverters::toJavac,
+                    element ->
+                        ParameterSpec.builder(
+                                element.getType().getTypeName(),
+                                uniqueFieldNames.getUniqueName(getSimpleName(element)))
+                            .build()));
     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()));
+            .addParameters(assistedParameters.values());
 
     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()),
+            param -> assistedParameters.get(param).name,
             generatedClassNameForBinding(binding),
             moduleParameter(binding).map(module -> CodeBlock.of("$N", module)),
             compilerOptions,
@@ -253,7 +282,9 @@
     if (binding.kind().equals(PROVISION)) {
       binding
           .nullableType()
-          .ifPresent(nullableType -> CodeBlocks.addAnnotation(getMethod, nullableType));
+          .map(XType::getTypeElement)
+          .map(XTypeElement::getClassName)
+          .ifPresent(getMethod::addAnnotation);
       getMethod.addStatement("return $L", invokeNewInstance);
     } else if (!binding.injectionSites().isEmpty()) {
       CodeBlock instance = CodeBlock.of("instance");
@@ -264,7 +295,7 @@
                   binding.injectionSites(),
                   generatedClassNameForBinding(binding),
                   instance,
-                  binding.key().type(),
+                  binding.key().type().java(),
                   frameworkFieldUsages(binding.dependencies(), frameworkFields)::get,
                   types,
                   metadataUtil))
@@ -275,8 +306,33 @@
     return getMethod.build();
   }
 
+  private AnnotationSpec scopeMetadataAnnotation(ProvisionBinding binding) {
+    AnnotationSpec.Builder builder = AnnotationSpec.builder(TypeNames.SCOPE_METADATA);
+    binding.scope()
+        .map(Scope::scopeAnnotation)
+        .map(DaggerAnnotation::className)
+        .map(ClassName::canonicalName)
+        .ifPresent(scopeCanonicalName -> builder.addMember("value", "$S", scopeCanonicalName));
+    return builder.build();
+  }
+
+  private AnnotationSpec qualifierMetadataAnnotation(ProvisionBinding binding) {
+    AnnotationSpec.Builder builder = AnnotationSpec.builder(TypeNames.QUALIFIER_METADATA);
+    // Collect all qualifiers on the binding itself or its dependencies
+    Stream.concat(
+            Stream.of(binding.key()),
+            binding.provisionDependencies().stream().map(DependencyRequest::key))
+        .map(Key::qualifier)
+        .flatMap(presentValues())
+        .map(DaggerAnnotation::className)
+        .map(ClassName::canonicalName)
+        .distinct()
+        .forEach(qualifier -> builder.addMember("value", "$S", qualifier));
+    return builder.build();
+  }
+
   private static TypeName providedTypeName(ProvisionBinding binding) {
-    return TypeName.get(binding.contributedType());
+    return binding.contributedType().getTypeName();
   }
 
   private static Optional<TypeName> factoryTypeName(ProvisionBinding binding) {
@@ -288,4 +344,31 @@
   private static ParameterSpec toParameter(FieldSpec field) {
     return ParameterSpec.builder(field.type, field.name).build();
   }
+
+  /** The strategy for getting an instance of a factory for a {@link Binding}. */
+  private enum FactoryCreationStrategy {
+    /** The factory class is a single instance. */
+    SINGLETON_INSTANCE,
+    /** The factory must be created by calling the constructor. */
+    CLASS_CONSTRUCTOR;
+
+    static FactoryCreationStrategy of(Binding binding) {
+      switch (binding.kind()) {
+        case DELEGATE:
+          throw new AssertionError("Delegate bindings don't have a factory.");
+        case PROVISION:
+          return binding.dependencies().isEmpty() && !binding.requiresModuleInstance()
+              ? SINGLETON_INSTANCE
+              : CLASS_CONSTRUCTOR;
+        case INJECTION:
+        case MULTIBOUND_SET:
+        case MULTIBOUND_MAP:
+          return binding.dependencies().isEmpty()
+              ? SINGLETON_INSTANCE
+              : CLASS_CONSTRUCTOR;
+        default:
+          return CLASS_CONSTRUCTOR;
+      }
+    }
+  }
 }
diff --git a/java/dagger/internal/codegen/writing/FrameworkFieldInitializer.java b/java/dagger/internal/codegen/writing/FrameworkFieldInitializer.java
index c17ff8e..811866b 100644
--- a/java/dagger/internal/codegen/writing/FrameworkFieldInitializer.java
+++ b/java/dagger/internal/codegen/writing/FrameworkFieldInitializer.java
@@ -34,8 +34,9 @@
 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 dagger.internal.codegen.writing.ComponentImplementation.CompilerMode;
+import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
+import dagger.spi.model.BindingKind;
 import java.util.Optional;
 
 /**
@@ -59,23 +60,12 @@
     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 ShardImplementation shardImplementation;
   private final ContributionBinding binding;
   private final FrameworkInstanceCreationExpression frameworkInstanceCreationExpression;
+  private final CompilerMode compilerMode;
   private FieldSpec fieldSpec;
   private InitializationState fieldInitializationState = InitializationState.UNINITIALIZED;
 
@@ -83,8 +73,9 @@
       ComponentImplementation componentImplementation,
       ContributionBinding binding,
       FrameworkInstanceCreationExpression frameworkInstanceCreationExpression) {
-    this.componentImplementation = checkNotNull(componentImplementation);
     this.binding = checkNotNull(binding);
+    this.compilerMode = componentImplementation.compilerMode();
+    this.shardImplementation = checkNotNull(componentImplementation).shardImplementation(binding);
     this.frameworkInstanceCreationExpression = checkNotNull(frameworkInstanceCreationExpression);
   }
 
@@ -95,14 +86,14 @@
   @Override
   public final MemberSelect memberSelect() {
     initializeField();
-    return MemberSelect.localField(componentImplementation.name(), checkNotNull(fieldSpec).name);
+    return MemberSelect.localField(shardImplementation, 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
+        // Change our state in case we are recursively invoked via initializeRequestRepresentation
         fieldInitializationState = InitializationState.INITIALIZING;
         CodeBlock.Builder codeBuilder = CodeBlock.builder();
         CodeBlock fieldInitialization = frameworkInstanceCreationExpression.creationExpression();
@@ -114,16 +105,24 @@
         } else {
           codeBuilder.add(initCode);
         }
-        componentImplementation.addInitialization(codeBuilder.build());
+        shardImplementation.addInitialization(codeBuilder.build());
 
         fieldInitializationState = InitializationState.INITIALIZED;
         break;
 
       case INITIALIZING:
-        // We were recursively invoked, so create a delegate factory instead
+        fieldSpec = getOrCreateField();
+        // We were recursively invoked, so create a delegate factory instead to break the loop.
+        // However, because SwitchingProvider takes no dependencies, even if they are recursively
+        // invoked, we don't need to delegate it since there is no dependency cycle.
+        if (FrameworkInstanceKind.from(binding, compilerMode)
+            .equals(FrameworkInstanceKind.SWITCHING_PROVIDER)) {
+          break;
+        }
+
         fieldInitializationState = InitializationState.DELEGATED;
-        componentImplementation.addInitialization(
-            CodeBlock.of("this.$N = new $T<>();", getOrCreateField(), delegateType()));
+        shardImplementation.addInitialization(
+            CodeBlock.of("this.$N = new $T<>();", fieldSpec, delegateType()));
         break;
 
       case DELEGATED:
@@ -140,7 +139,7 @@
     if (fieldSpec != null) {
       return fieldSpec;
     }
-    boolean useRawType = !componentImplementation.isTypeAccessible(binding.key().type());
+    boolean useRawType = !shardImplementation.isTypeAccessible(binding.key().type().java());
     FrameworkField contributionBindingField =
         FrameworkField.forBinding(
             binding, frameworkInstanceCreationExpression.alternativeFrameworkClass());
@@ -152,7 +151,7 @@
       // 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()
+          MoreTypes.asDeclared(binding.key().type().java()).getTypeArguments().stream()
               .map(TypeName::get)
               .toArray(TypeName[]::new);
       fieldType =
@@ -163,20 +162,20 @@
 
     FieldSpec.Builder contributionField =
         FieldSpec.builder(
-            fieldType, componentImplementation.getUniqueFieldName(contributionBindingField.name()));
+            fieldType, shardImplementation.getUniqueFieldName(contributionBindingField.name()));
     contributionField.addModifiers(PRIVATE);
     if (useRawType) {
       contributionField.addAnnotation(AnnotationSpecs.suppressWarnings(RAWTYPES));
     }
 
     fieldSpec = contributionField.build();
-    componentImplementation.addField(FRAMEWORK_FIELD, fieldSpec);
+    shardImplementation.addField(FRAMEWORK_FIELD, fieldSpec);
 
     return fieldSpec;
   }
 
-  private Class<?> delegateType() {
-    return isProvider() ? DelegateFactory.class : DelegateProducer.class;
+  private ClassName delegateType() {
+    return isProvider() ? TypeNames.DELEGATE_FACTORY : TypeNames.DELEGATE_PRODUCER;
   }
 
   private boolean isProvider() {
diff --git a/java/dagger/internal/codegen/writing/FrameworkInstanceBindingRepresentation.java b/java/dagger/internal/codegen/writing/FrameworkInstanceBindingRepresentation.java
new file mode 100644
index 0000000..d4fb497
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/FrameworkInstanceBindingRepresentation.java
@@ -0,0 +1,114 @@
+/*
+ * 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.writing;
+
+import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
+import static dagger.internal.codegen.writing.ProvisionBindingRepresentation.needsCaching;
+import static dagger.spi.model.BindingKind.DELEGATE;
+
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.BindingRequest;
+import dagger.internal.codegen.binding.FrameworkType;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.spi.model.RequestKind;
+import java.util.HashMap;
+import java.util.Map;
+
+/** Returns request representation that wraps a framework instance expression */
+final class FrameworkInstanceBindingRepresentation {
+  private final ProvisionBinding binding;
+  private final DerivedFromFrameworkInstanceRequestRepresentation.Factory
+      derivedFromFrameworkInstanceRequestRepresentationFactory;
+  private final ImmediateFutureRequestRepresentation.Factory
+      immediateFutureRequestRepresentationFactory;
+  private final Map<BindingRequest, RequestRepresentation> requestRepresentations = new HashMap<>();
+  private final RequestRepresentation providerRequestRepresentation;
+  private final RequestRepresentation producerFromProviderRepresentation;
+
+  @AssistedInject
+  FrameworkInstanceBindingRepresentation(
+      @Assisted ProvisionBinding binding,
+      BindingGraph graph,
+      @Assisted FrameworkInstanceSupplier providerField,
+      ComponentImplementation componentImplementation,
+      DelegateRequestRepresentation.Factory delegateRequestRepresentationFactory,
+      DerivedFromFrameworkInstanceRequestRepresentation.Factory
+          derivedFromFrameworkInstanceRequestRepresentationFactory,
+      ImmediateFutureRequestRepresentation.Factory immediateFutureRequestRepresentationFactory,
+      ProducerNodeInstanceRequestRepresentation.Factory
+          producerNodeInstanceRequestRepresentationFactory,
+      ProviderInstanceRequestRepresentation.Factory providerInstanceRequestRepresentationFactory,
+      ProducerFromProviderCreationExpression.Factory
+          producerFromProviderCreationExpressionFactory) {
+    this.binding = binding;
+    this.derivedFromFrameworkInstanceRequestRepresentationFactory =
+        derivedFromFrameworkInstanceRequestRepresentationFactory;
+    this.immediateFutureRequestRepresentationFactory = immediateFutureRequestRepresentationFactory;
+    this.providerRequestRepresentation =
+        binding.kind().equals(DELEGATE) && !needsCaching(binding, graph)
+            ? delegateRequestRepresentationFactory.create(binding, RequestKind.PROVIDER)
+            : providerInstanceRequestRepresentationFactory.create(binding, providerField);
+    this.producerFromProviderRepresentation =
+        producerNodeInstanceRequestRepresentationFactory.create(
+            binding,
+            new FrameworkFieldInitializer(
+                componentImplementation,
+                binding,
+                producerFromProviderCreationExpressionFactory.create(
+                    providerRequestRepresentation,
+                    componentImplementation.shardImplementation(binding).name())));
+  }
+
+  public RequestRepresentation getRequestRepresentation(BindingRequest request) {
+    return reentrantComputeIfAbsent(
+        requestRepresentations, request, this::getRequestRepresentationUncached);
+  }
+
+  private RequestRepresentation getRequestRepresentationUncached(BindingRequest request) {
+    switch (request.requestKind()) {
+      case INSTANCE:
+      case LAZY:
+      case PRODUCED:
+      case PROVIDER_OF_LAZY:
+        return derivedFromFrameworkInstanceRequestRepresentationFactory.create(
+            binding, providerRequestRepresentation, request.requestKind(), FrameworkType.PROVIDER);
+      case PROVIDER:
+        return providerRequestRepresentation;
+      case PRODUCER:
+        return producerFromProviderRepresentation;
+
+      case FUTURE:
+        return immediateFutureRequestRepresentationFactory.create(
+            getRequestRepresentation(bindingRequest(binding.key(), RequestKind.INSTANCE)),
+            binding.key().type().java());
+
+      default:
+        throw new AssertionError(
+            String.format("Invalid binding request kind: %s", request.requestKind()));
+    }
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    FrameworkInstanceBindingRepresentation create(
+        ProvisionBinding binding, FrameworkInstanceSupplier providerField);
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/FrameworkInstanceKind.java b/java/dagger/internal/codegen/writing/FrameworkInstanceKind.java
new file mode 100644
index 0000000..8bceddc
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/FrameworkInstanceKind.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.spi.model.BindingKind.DELEGATE;
+
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.writing.ComponentImplementation.CompilerMode;
+import dagger.spi.model.BindingKind;
+
+/** Generation mode for satisfying framework request to Provision Binding. */
+enum FrameworkInstanceKind {
+  SWITCHING_PROVIDER,
+  EXPERIMENTAL_SWITCHING_PROVIDER,
+  STATIC_FACTORY,
+  PROVIDER_FIELD;
+
+  public static FrameworkInstanceKind from(ContributionBinding binding, CompilerMode compilerMode) {
+    if (usesSwitchingProvider(binding, compilerMode)) {
+      if (compilerMode.isExperimentalMergedMode()) {
+        return EXPERIMENTAL_SWITCHING_PROVIDER;
+      } else if (compilerMode.isFastInit()) {
+        return SWITCHING_PROVIDER;
+      } else {
+        throw new IllegalStateException(
+            "Compiler mode " + compilerMode + " cannot use Switching Provider.");
+      }
+    } else if (usesStaticFactoryCreation(binding, compilerMode)) {
+      return STATIC_FACTORY;
+    } else {
+      return PROVIDER_FIELD;
+    }
+  }
+
+  private static boolean usesSwitchingProvider(
+      ContributionBinding binding, CompilerMode compilerMode) {
+    if (!compilerMode.isFastInit() && !compilerMode.isExperimentalMergedMode()) {
+      return false;
+    }
+    // TODO(wanyingd): remove this check once we allow inaccessible types in merged mode.
+    if (compilerMode.isExperimentalMergedMode()
+        && binding.kind().equals(BindingKind.ASSISTED_FACTORY)) {
+      return false;
+    }
+    switch (binding.kind()) {
+      case ASSISTED_INJECTION:
+      case BOUND_INSTANCE:
+      case COMPONENT:
+      case COMPONENT_DEPENDENCY:
+      case DELEGATE:
+      case MEMBERS_INJECTOR: // TODO(b/199889259): Consider optimizing this for fastInit mode.
+        // These binding kinds avoid SwitchingProvider when the backing instance already exists,
+        // e.g. a component provider can use FactoryInstance.create(this).
+        return false;
+      case MULTIBOUND_SET:
+      case MULTIBOUND_MAP:
+      case OPTIONAL:
+        // These binding kinds avoid SwitchingProvider when their are no dependencies,
+        // e.g. a multibound set with no dependency can use a singleton, SetFactory.empty().
+        return !binding.dependencies().isEmpty();
+      case INJECTION:
+      case PROVISION:
+      case ASSISTED_FACTORY:
+      case COMPONENT_PROVISION:
+      case SUBCOMPONENT_CREATOR:
+      case PRODUCTION:
+      case COMPONENT_PRODUCTION:
+      case MEMBERS_INJECTION:
+        return true;
+    }
+    throw new AssertionError(String.format("No such binding kind: %s", binding.kind()));
+  }
+
+  private static boolean usesStaticFactoryCreation(
+      ContributionBinding binding, CompilerMode compilerMode) {
+    // If {@code binding} is an unscoped provision binding with no factory arguments, then
+    // we don't need a field to hold its factory. In that case, this method returns the static
+    // select that returns the factory.
+    // member
+    if (!binding.dependencies().isEmpty() || binding.scope().isPresent()) {
+      return false;
+    }
+    switch (binding.kind()) {
+      case MULTIBOUND_MAP:
+      case MULTIBOUND_SET:
+        return true;
+      case PROVISION:
+        return !compilerMode.isFastInit()
+            && !compilerMode.isExperimentalMergedMode()
+            && !binding.requiresModuleInstance();
+      case INJECTION:
+        return !compilerMode.isFastInit() && !compilerMode.isExperimentalMergedMode();
+      default:
+        return false;
+    }
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/FrameworkInstanceBindingExpression.java b/java/dagger/internal/codegen/writing/FrameworkInstanceRequestRepresentation.java
similarity index 86%
rename from java/dagger/internal/codegen/writing/FrameworkInstanceBindingExpression.java
rename to java/dagger/internal/codegen/writing/FrameworkInstanceRequestRepresentation.java
index 56a6ef3..e8120e8 100644
--- a/java/dagger/internal/codegen/writing/FrameworkInstanceBindingExpression.java
+++ b/java/dagger/internal/codegen/writing/FrameworkInstanceRequestRepresentation.java
@@ -20,8 +20,6 @@
 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;
@@ -31,13 +29,13 @@
 import javax.lang.model.type.TypeMirror;
 
 /** A binding expression that uses a {@link FrameworkType} field. */
-abstract class FrameworkInstanceBindingExpression extends BindingExpression {
+abstract class FrameworkInstanceRequestRepresentation extends RequestRepresentation {
   private final ContributionBinding binding;
   private final FrameworkInstanceSupplier frameworkInstanceSupplier;
   private final DaggerTypes types;
   private final DaggerElements elements;
 
-  FrameworkInstanceBindingExpression(
+  FrameworkInstanceRequestRepresentation(
       ContributionBinding binding,
       FrameworkInstanceSupplier frameworkInstanceSupplier,
       DaggerTypes types,
@@ -49,10 +47,8 @@
   }
 
   /**
-   * 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.
+   * The expression for the framework instance for this binding. The field will be initialized and
+   * added to the component the first time this method is invoked.
    */
   @Override
   Expression getDependencyExpression(ClassName requestingClass) {
@@ -60,7 +56,7 @@
     TypeMirror expressionType =
         isTypeAccessibleFrom(binding.contributedType(), requestingClass.packageName())
                 || isInlinedFactoryCreation(memberSelect)
-            ? types.wrapType(binding.contributedType(), frameworkType().frameworkClass())
+            ? types.wrapType(binding.contributedType(), frameworkType().frameworkClassName())
             : rawFrameworkType();
     return Expression.create(expressionType, memberSelect.getExpressionFor(requestingClass));
   }
@@ -84,6 +80,6 @@
   }
 
   private DeclaredType rawFrameworkType() {
-    return types.getDeclaredType(elements.getTypeElement(frameworkType().frameworkClass()));
+    return types.getDeclaredType(elements.getTypeElement(frameworkType().frameworkClassName()));
   }
 }
diff --git a/java/dagger/internal/codegen/writing/GwtCompatibility.java b/java/dagger/internal/codegen/writing/GwtCompatibility.java
index 2df6a99..1daa92f 100644
--- a/java/dagger/internal/codegen/writing/GwtCompatibility.java
+++ b/java/dagger/internal/codegen/writing/GwtCompatibility.java
@@ -16,6 +16,7 @@
 
 package dagger.internal.codegen.writing;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 import static com.google.common.base.Preconditions.checkArgument;
 
 import com.squareup.javapoet.AnnotationSpec;
@@ -33,7 +34,7 @@
    */
   static Optional<AnnotationSpec> gwtIncompatibleAnnotation(Binding binding) {
     checkArgument(binding.bindingElement().isPresent());
-    Element element = binding.bindingElement().get();
+    Element element = toJavac(binding.bindingElement().get());
     while (element != null) {
       Optional<AnnotationSpec> gwtIncompatible =
           element
diff --git a/java/dagger/internal/codegen/writing/HjarSourceFileGenerator.java b/java/dagger/internal/codegen/writing/HjarSourceFileGenerator.java
index d4dbde7..4bafb5a 100644
--- a/java/dagger/internal/codegen/writing/HjarSourceFileGenerator.java
+++ b/java/dagger/internal/codegen/writing/HjarSourceFileGenerator.java
@@ -22,13 +22,13 @@
 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
 import static javax.lang.model.element.Modifier.PRIVATE;
 
+import androidx.room.compiler.processing.XElement;
 import com.google.common.collect.ImmutableList;
 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 javax.lang.model.element.Element;
 import javax.lang.model.element.Modifier;
 
 /**
@@ -48,7 +48,7 @@
   }
 
   @Override
-  public Element originatingElement(T input) {
+  public XElement originatingElement(T input) {
     return delegate.originatingElement(input);
   }
 
diff --git a/java/dagger/internal/codegen/writing/ImmediateFutureBindingExpression.java b/java/dagger/internal/codegen/writing/ImmediateFutureRequestRepresentation.java
similarity index 63%
rename from java/dagger/internal/codegen/writing/ImmediateFutureBindingExpression.java
rename to java/dagger/internal/codegen/writing/ImmediateFutureRequestRepresentation.java
index dcd02fa..158c095 100644
--- a/java/dagger/internal/codegen/writing/ImmediateFutureBindingExpression.java
+++ b/java/dagger/internal/codegen/writing/ImmediateFutureRequestRepresentation.java
@@ -17,31 +17,33 @@
 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.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
 import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.javapoet.TypeNames;
 import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.Key;
-import dagger.model.RequestKind;
 import javax.lang.model.SourceVersion;
+import javax.lang.model.type.TypeMirror;
 
-final class ImmediateFutureBindingExpression extends BindingExpression {
-  private final Key key;
-  private final ComponentBindingExpressions componentBindingExpressions;
+final class ImmediateFutureRequestRepresentation extends RequestRepresentation {
+  private final RequestRepresentation instanceRequestRepresentation;
+  private final TypeMirror type;
   private final DaggerTypes types;
   private final SourceVersion sourceVersion;
 
-  ImmediateFutureBindingExpression(
-      Key key,
-      ComponentBindingExpressions componentBindingExpressions,
+  @AssistedInject
+  ImmediateFutureRequestRepresentation(
+      @Assisted RequestRepresentation instanceRequestRepresentation,
+      @Assisted TypeMirror type,
       DaggerTypes types,
       SourceVersion sourceVersion) {
-    this.key = key;
-    this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+    this.instanceRequestRepresentation = instanceRequestRepresentation;
+    this.type = type;
     this.types = checkNotNull(types);
     this.sourceVersion = checkNotNull(sourceVersion);
   }
@@ -49,25 +51,29 @@
   @Override
   Expression getDependencyExpression(ClassName requestingClass) {
     return Expression.create(
-        types.wrapType(key.type(), ListenableFuture.class),
+        types.wrapType(type, TypeNames.LISTENABLE_FUTURE),
         CodeBlock.of("$T.immediateFuture($L)", Futures.class, instanceExpression(requestingClass)));
   }
 
   private CodeBlock instanceExpression(ClassName requestingClass) {
-    Expression expression =
-        componentBindingExpressions.getDependencyExpression(
-            bindingRequest(key, RequestKind.INSTANCE), requestingClass);
+    Expression expression = instanceRequestRepresentation.getDependencyExpression(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())) {
+      if (!types.isSameType(expression.type(), type)) {
         return CodeBlock.of(
-            "($T) $L", types.accessibleType(key.type(), requestingClass), expression.codeBlock());
+            "($T) $L", types.accessibleType(type, requestingClass), expression.codeBlock());
       }
     }
     return expression.codeBlock();
   }
+
+  @AssistedFactory
+  static interface Factory {
+    ImmediateFutureRequestRepresentation create(
+        RequestRepresentation instanceRequestRepresentation, TypeMirror type);
+  }
 }
diff --git a/java/dagger/internal/codegen/writing/InaccessibleMapKeyProxyGenerator.java b/java/dagger/internal/codegen/writing/InaccessibleMapKeyProxyGenerator.java
index 889f898..f48fd36 100644
--- a/java/dagger/internal/codegen/writing/InaccessibleMapKeyProxyGenerator.java
+++ b/java/dagger/internal/codegen/writing/InaccessibleMapKeyProxyGenerator.java
@@ -22,17 +22,17 @@
 import static javax.lang.model.element.Modifier.PRIVATE;
 import static javax.lang.model.element.Modifier.PUBLIC;
 
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XFiler;
+import androidx.room.compiler.processing.XProcessingEnv;
 import com.google.common.collect.ImmutableList;
 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 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
@@ -40,26 +40,27 @@
  */
 public final class InaccessibleMapKeyProxyGenerator
     extends SourceFileGenerator<ContributionBinding> {
-  private final DaggerTypes types;
-  private final DaggerElements elements;
+  private final XProcessingEnv processingEnv;
 
   @Inject
   InaccessibleMapKeyProxyGenerator(
-      Filer filer, DaggerTypes types, DaggerElements elements, SourceVersion sourceVersion) {
+      XProcessingEnv processingEnv,
+      XFiler filer,
+      DaggerElements elements,
+      SourceVersion sourceVersion) {
     super(filer, elements, sourceVersion);
-    this.types = types;
-    this.elements = elements;
+    this.processingEnv = processingEnv;
   }
 
   @Override
-  public Element originatingElement(ContributionBinding binding) {
+  public XElement originatingElement(ContributionBinding binding) {
     // a map key is only ever present on bindings that have a binding element
     return binding.bindingElement().get();
   }
 
   @Override
   public ImmutableList<TypeSpec.Builder> topLevelTypes(ContributionBinding binding) {
-    return MapKeys.mapKeyFactoryMethod(binding, types, elements)
+    return MapKeys.mapKeyFactoryMethod(binding, processingEnv)
         .map(
             method ->
                 classBuilder(MapKeys.mapKeyProxyClassName(binding))
diff --git a/java/dagger/internal/codegen/writing/InjectionMethods.java b/java/dagger/internal/codegen/writing/InjectionMethods.java
index 7cebc10..97d085f 100644
--- a/java/dagger/internal/codegen/writing/InjectionMethods.java
+++ b/java/dagger/internal/codegen/writing/InjectionMethods.java
@@ -16,6 +16,7 @@
 
 package dagger.internal.codegen.writing;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 import static com.google.auto.common.MoreElements.asExecutable;
 import static com.google.auto.common.MoreElements.asType;
 import static com.google.auto.common.MoreElements.asVariable;
@@ -23,7 +24,7 @@
 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.AssistedInjectionAnnotations.isAssistedParameter;
 import static dagger.internal.codegen.binding.ConfigurationAnnotations.getNullableType;
 import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding;
 import static dagger.internal.codegen.binding.SourceFiles.memberInjectedFieldSignatureForVariable;
@@ -38,11 +39,15 @@
 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.internal.codegen.xprocessing.XElements.asExecutable;
+import static dagger.internal.codegen.xprocessing.XElements.asMethodParameter;
 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 androidx.room.compiler.processing.XExecutableElement;
+import androidx.room.compiler.processing.XExecutableParameterElement;
 import com.google.auto.common.MoreElements;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -56,7 +61,6 @@
 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;
@@ -65,8 +69,8 @@
 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 dagger.spi.model.DaggerAnnotation;
+import dagger.spi.model.DependencyRequest;
 import java.util.List;
 import java.util.Optional;
 import java.util.function.Function;
@@ -76,7 +80,6 @@
 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. */
@@ -119,7 +122,7 @@
         ProvisionBinding binding,
         CompilerOptions compilerOptions,
         KotlinMetadataUtil metadataUtil) {
-      ExecutableElement element = asExecutable(binding.bindingElement().get());
+      ExecutableElement element = asExecutable(toJavac(binding.bindingElement().get()));
       switch (element.getKind()) {
         case CONSTRUCTOR:
           return constructorProxy(element);
@@ -142,13 +145,15 @@
     static CodeBlock invoke(
         ProvisionBinding binding,
         Function<DependencyRequest, CodeBlock> dependencyUsage,
+        Function<VariableElement, String> uniqueAssistedParameterName,
         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);
+      invokeArguments(binding, dependencyUsage, uniqueAssistedParameterName)
+          .forEach(arguments::add);
 
       ClassName enclosingClass = generatedClassNameForBinding(binding);
       MethodSpec methodSpec = create(binding, compilerOptions, metadataUtil);
@@ -158,23 +163,22 @@
     static ImmutableList<CodeBlock> invokeArguments(
         ProvisionBinding binding,
         Function<DependencyRequest, CodeBlock> dependencyUsage,
-        ClassName requestingClass) {
-      ImmutableMap<VariableElement, DependencyRequest> dependencyRequestMap =
+        Function<VariableElement, String> uniqueAssistedParameterName) {
+      ImmutableMap<XExecutableParameterElement, DependencyRequest> dependencyRequestMap =
           binding.provisionDependencies().stream()
               .collect(
                   toImmutableMap(
-                      request -> MoreElements.asVariable(request.requestElement().get()),
+                      request -> asMethodParameter(request.requestElement().get().xprocessing()),
                       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()));
+      XExecutableElement method = asExecutable(binding.bindingElement().get());
+      for (XExecutableParameterElement parameter : method.getParameters()) {
+        if (isAssistedParameter(parameter)) {
+          arguments.add(CodeBlock.of("$L", uniqueAssistedParameterName.apply(toJavac(parameter))));
         } else if (dependencyRequestMap.containsKey(parameter)) {
           DependencyRequest request = dependencyRequestMap.get(parameter);
-          arguments.add(
-              injectionMethodArgument(request, dependencyUsage.apply(request), requestingClass));
+          arguments.add(dependencyUsage.apply(request));
         } else {
           throw new AssertionError("Unexpected parameter: " + parameter);
         }
@@ -205,7 +209,7 @@
      */
     static boolean requiresInjectionMethod(
         ProvisionBinding binding, CompilerOptions compilerOptions, ClassName requestingClass) {
-      ExecutableElement method = MoreElements.asExecutable(binding.bindingElement().get());
+      ExecutableElement method = asExecutable(toJavac(binding.bindingElement().get()));
       return !binding.injectionSites().isEmpty()
           || binding.shouldCheckForNull(compilerOptions)
           || !isElementAccessibleFrom(method, requestingClass.packageName())
@@ -276,7 +280,8 @@
                   // methods for fields have a single dependency request
                   .collect(DaggerCollectors.onlyElement())
                   .key()
-                  .qualifier();
+                  .qualifier()
+                  .map(DaggerAnnotation::java);
           return fieldProxy(asVariable(injectionSite.element()), methodName, qualifier);
       }
       throw new AssertionError(injectionSite);
@@ -371,44 +376,6 @@
     }
   }
 
-  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;
 
diff --git a/java/dagger/internal/codegen/writing/InjectionOrProvisionProviderCreationExpression.java b/java/dagger/internal/codegen/writing/InjectionOrProvisionProviderCreationExpression.java
index b5135b0..b9bc70b 100644
--- a/java/dagger/internal/codegen/writing/InjectionOrProvisionProviderCreationExpression.java
+++ b/java/dagger/internal/codegen/writing/InjectionOrProvisionProviderCreationExpression.java
@@ -18,11 +18,16 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding;
-import static dagger.model.BindingKind.INJECTION;
+import static dagger.spi.model.BindingKind.INJECTION;
 
 import com.squareup.javapoet.CodeBlock;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
 import dagger.internal.codegen.binding.ContributionBinding;
 import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
 import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
 import javax.inject.Provider;
 
@@ -35,12 +40,17 @@
     implements FrameworkInstanceCreationExpression {
 
   private final ContributionBinding binding;
-  private final ComponentBindingExpressions componentBindingExpressions;
+  private final ShardImplementation shardImplementation;
+  private final ComponentRequestRepresentations componentRequestRepresentations;
 
+  @AssistedInject
   InjectionOrProvisionProviderCreationExpression(
-      ContributionBinding binding, ComponentBindingExpressions componentBindingExpressions) {
+      @Assisted ContributionBinding binding,
+      ComponentImplementation componentImplementation,
+      ComponentRequestRepresentations componentRequestRepresentations) {
     this.binding = checkNotNull(binding);
-    this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+    this.shardImplementation = componentImplementation.shardImplementation(binding);
+    this.componentRequestRepresentations = componentRequestRepresentations;
   }
 
   @Override
@@ -49,16 +59,22 @@
         CodeBlock.of(
             "$T.create($L)",
             generatedClassNameForBinding(binding),
-            componentBindingExpressions.getCreateMethodArgumentsCodeBlock(binding));
+            componentRequestRepresentations.getCreateMethodArgumentsCodeBlock(
+                binding, shardImplementation.name()));
 
     // 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);
+      return CodeBlocks.cast(createFactory, TypeNames.PROVIDER);
     } else {
       return createFactory;
     }
   }
+
+  @AssistedFactory
+  static interface Factory {
+    InjectionOrProvisionProviderCreationExpression create(ContributionBinding binding);
+  }
 }
diff --git a/java/dagger/internal/codegen/writing/InnerSwitchingProviders.java b/java/dagger/internal/codegen/writing/InnerSwitchingProviders.java
deleted file mode 100644
index c2f9893..0000000
--- a/java/dagger/internal/codegen/writing/InnerSwitchingProviders.java
+++ /dev/null
@@ -1,107 +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.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
index a7d6685..807c64c 100644
--- a/java/dagger/internal/codegen/writing/InstanceFactoryCreationExpression.java
+++ b/java/dagger/internal/codegen/writing/InstanceFactoryCreationExpression.java
@@ -49,9 +49,4 @@
         nullable ? "createNullable" : "create",
         instanceExpression.get());
   }
-
-  @Override
-  public boolean useInnerSwitchingProvider() {
-    return false;
-  }
 }
diff --git a/java/dagger/internal/codegen/writing/MapFactoryCreationExpression.java b/java/dagger/internal/codegen/writing/MapFactoryCreationExpression.java
index 104d48a..a326e0a 100644
--- a/java/dagger/internal/codegen/writing/MapFactoryCreationExpression.java
+++ b/java/dagger/internal/codegen/writing/MapFactoryCreationExpression.java
@@ -19,56 +19,57 @@
 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 static dagger.internal.codegen.extension.DaggerCollectors.toOptional;
 
-import com.google.common.collect.ImmutableSet;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XType;
 import com.squareup.javapoet.CodeBlock;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
 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;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.spi.model.DependencyRequest;
+import java.util.stream.Stream;
 
 /** A factory creation expression for a multibound map. */
 final class MapFactoryCreationExpression extends MultibindingFactoryCreationExpression {
 
+  private final XProcessingEnv processingEnv;
   private final ComponentImplementation componentImplementation;
   private final BindingGraph graph;
   private final ContributionBinding binding;
-  private final DaggerElements elements;
 
+  @AssistedInject
   MapFactoryCreationExpression(
-      ContributionBinding binding,
+      @Assisted ContributionBinding binding,
+      XProcessingEnv processingEnv,
       ComponentImplementation componentImplementation,
-      ComponentBindingExpressions componentBindingExpressions,
-      BindingGraph graph,
-      DaggerElements elements) {
-    super(binding, componentImplementation, componentBindingExpressions);
+      ComponentRequestRepresentations componentRequestRepresentations,
+      BindingGraph graph) {
+    super(binding, componentImplementation, componentRequestRepresentations);
+    this.processingEnv = processingEnv;
     this.binding = checkNotNull(binding);
-    this.componentImplementation = checkNotNull(componentImplementation);
-    this.graph = checkNotNull(graph);
-    this.elements = checkNotNull(elements);
+    this.componentImplementation = componentImplementation;
+    this.graph = graph;
   }
 
   @Override
   public CodeBlock creationExpression() {
     CodeBlock.Builder builder = CodeBlock.builder().add("$T.", mapFactoryClassName(binding));
     if (!useRawType()) {
-      MapType mapType = MapType.from(binding.key().type());
+      MapType mapType = MapType.from(binding.key());
       // 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);
+      XType valueType =
+          Stream.of(TypeNames.PROVIDER, TypeNames.PRODUCER, TypeNames.PRODUCED)
+              .filter(mapType::valuesAreTypeOf)
+              .map(mapType::unwrappedValueType)
+              .collect(toOptional())
+              .orElseGet(mapType::valueType);
+      builder.add("<$T, $T>", mapType.keyType().getTypeName(), valueType.getTypeName());
     }
 
     builder.add("builder($L)", binding.dependencies().size());
@@ -77,11 +78,16 @@
       ContributionBinding contributionBinding = graph.contributionBinding(dependency.key());
       builder.add(
           ".put($L, $L)",
-          getMapKeyExpression(contributionBinding, componentImplementation.name(), elements),
+          getMapKeyExpression(contributionBinding, componentImplementation.name(), processingEnv),
           multibindingDependencyExpression(dependency));
     }
     builder.add(".build()");
 
     return builder.build();
   }
+
+  @AssistedFactory
+  static interface Factory {
+    MapFactoryCreationExpression create(ContributionBinding binding);
+  }
 }
diff --git a/java/dagger/internal/codegen/writing/MapBindingExpression.java b/java/dagger/internal/codegen/writing/MapRequestRepresentation.java
similarity index 67%
rename from java/dagger/internal/codegen/writing/MapBindingExpression.java
rename to java/dagger/internal/codegen/writing/MapRequestRepresentation.java
index 255e85d..876c248 100644
--- a/java/dagger/internal/codegen/writing/MapBindingExpression.java
+++ b/java/dagger/internal/codegen/writing/MapRequestRepresentation.java
@@ -22,53 +22,57 @@
 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 static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
+import static dagger.spi.model.BindingKind.MULTIBOUND_MAP;
 
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XType;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
 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 dagger.internal.codegen.javapoet.TypeNames;
+import dagger.spi.model.BindingKind;
+import dagger.spi.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 {
+/** A {@link RequestRepresentation} for multibound maps. */
+final class MapRequestRepresentation extends RequestRepresentation {
   /** 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 XProcessingEnv processingEnv;
   private final ProvisionBinding binding;
   private final ImmutableMap<DependencyRequest, ContributionBinding> dependencies;
-  private final ComponentBindingExpressions componentBindingExpressions;
-  private final DaggerTypes types;
-  private final DaggerElements elements;
+  private final ComponentRequestRepresentations componentRequestRepresentations;
+  private final boolean isExperimentalMergedMode;
 
-  MapBindingExpression(
-      ProvisionBinding binding,
+  @AssistedInject
+  MapRequestRepresentation(
+      @Assisted ProvisionBinding binding,
+      XProcessingEnv processingEnv,
       BindingGraph graph,
-      ComponentBindingExpressions componentBindingExpressions,
-      DaggerTypes types,
-      DaggerElements elements) {
-    super(binding);
+      ComponentImplementation componentImplementation,
+      ComponentRequestRepresentations componentRequestRepresentations) {
     this.binding = binding;
+    this.processingEnv = processingEnv;
     BindingKind bindingKind = this.binding.kind();
     checkArgument(bindingKind.equals(MULTIBOUND_MAP), bindingKind);
-    this.componentBindingExpressions = componentBindingExpressions;
-    this.types = types;
-    this.elements = elements;
+    this.componentRequestRepresentations = componentRequestRepresentations;
     this.dependencies =
         Maps.toMap(binding.dependencies(), dep -> graph.contributionBinding(dep.key()));
+    this.isExperimentalMergedMode =
+        componentImplementation.compilerMode().isExperimentalMergedMode();
   }
 
   @Override
@@ -116,30 +120,38 @@
           instantiation.add(".put($L)", keyAndValueExpression(dependency, requestingClass));
         }
         return Expression.create(
-            isImmutableMapAvailable ? immutableMapType() : binding.key().type(),
+            isImmutableMapAvailable ? immutableMapType() : binding.key().type().xprocessing(),
             instantiation.add(".build()").build());
     }
   }
 
-  private DeclaredType immutableMapType() {
+  private XType immutableMapType() {
     MapType mapType = MapType.from(binding.key());
-    return types.getDeclaredType(
-        elements.getTypeElement(ImmutableMap.class), mapType.keyType(), mapType.valueType());
+    return processingEnv.getDeclaredType(
+        processingEnv.requireTypeElement(TypeNames.IMMUTABLE_MAP),
+        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());
+        getMapKeyExpression(dependencies.get(dependency), requestingClass, processingEnv),
+        isExperimentalMergedMode
+            ? componentRequestRepresentations
+                .getExperimentalSwitchingProviderDependencyRepresentation(
+                    bindingRequest(dependency))
+                .getDependencyExpression(dependency.kind(), binding)
+                .codeBlock()
+            : componentRequestRepresentations
+                .getDependencyExpression(bindingRequest(dependency), requestingClass)
+                .codeBlock());
   }
 
   private Expression collectionsStaticFactoryInvocation(
       ClassName requestingClass, CodeBlock methodInvocation) {
     return Expression.create(
-        binding.key().type(),
+        binding.key().type().java(),
         CodeBlock.builder()
             .add("$T.", Collections.class)
             .add(maybeTypeParameters(requestingClass))
@@ -148,23 +160,26 @@
   }
 
   private CodeBlock maybeTypeParameters(ClassName requestingClass) {
-    TypeMirror bindingKeyType = binding.key().type();
+    TypeMirror bindingKeyType = binding.key().type().java();
     MapType mapType = MapType.from(binding.key());
     return isTypeAccessibleFrom(bindingKeyType, requestingClass.packageName())
-        ? CodeBlock.of("<$T, $T>", mapType.keyType(), mapType.valueType())
+        ? CodeBlock.of(
+            "<$T, $T>", mapType.keyType().getTypeName(), mapType.valueType().getTypeName())
         : CodeBlock.of("");
   }
 
   private boolean isImmutableMapBuilderWithExpectedSizeAvailable() {
-    if (isImmutableMapAvailable()) {
-      return methodsIn(elements.getTypeElement(ImmutableMap.class).getEnclosedElements())
-          .stream()
-          .anyMatch(method -> method.getSimpleName().contentEquals("builderWithExpectedSize"));
-    }
-    return false;
+    return isImmutableMapAvailable()
+        && processingEnv.requireTypeElement(TypeNames.IMMUTABLE_MAP).getDeclaredMethods().stream()
+            .anyMatch(method -> getSimpleName(method).contentEquals("builderWithExpectedSize"));
   }
 
   private boolean isImmutableMapAvailable() {
-    return elements.getTypeElement(ImmutableMap.class) != null;
+    return processingEnv.findTypeElement(TypeNames.IMMUTABLE_MAP) != null;
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    MapRequestRepresentation create(ProvisionBinding binding);
   }
 }
diff --git a/java/dagger/internal/codegen/writing/MemberSelect.java b/java/dagger/internal/codegen/writing/MemberSelect.java
index b04e21b..d4e6750 100644
--- a/java/dagger/internal/codegen/writing/MemberSelect.java
+++ b/java/dagger/internal/codegen/writing/MemberSelect.java
@@ -17,32 +17,10 @@
 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;
+import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
 
 /**
  * Represents a {@link com.sun.source.tree.MemberSelectTree} as a {@link CodeBlock}.
@@ -51,20 +29,22 @@
 
   /**
    * 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).
+   * {@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);
+  static MemberSelect localField(ShardImplementation owningShard, String fieldName) {
+    return new LocalField(owningShard, fieldName);
   }
 
   private static final class LocalField extends MemberSelect {
+    final ShardImplementation owningShard;
     final String fieldName;
 
-    LocalField(ClassName owningClass, String fieldName) {
-      super(owningClass, false);
+    LocalField(ShardImplementation owningShard, String fieldName) {
+      super(owningShard.name(), false);
+      this.owningShard = owningShard;
       this.fieldName = checkNotNull(fieldName);
     }
 
@@ -72,165 +52,7 @@
     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);
-      }
+          : CodeBlock.of("$L.$N", owningShard.shardFieldReference(), fieldName);
     }
   }
 
diff --git a/java/dagger/internal/codegen/writing/MembersInjectionBindingExpression.java b/java/dagger/internal/codegen/writing/MembersInjectionBindingExpression.java
deleted file mode 100644
index abf9d03..0000000
--- a/java/dagger/internal/codegen/writing/MembersInjectionBindingExpression.java
+++ /dev/null
@@ -1,72 +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.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/MembersInjectionBindingRepresentation.java b/java/dagger/internal/codegen/writing/MembersInjectionBindingRepresentation.java
new file mode 100644
index 0000000..cfad745
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/MembersInjectionBindingRepresentation.java
@@ -0,0 +1,55 @@
+/*
+ * 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.writing;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import dagger.internal.codegen.binding.BindingRequest;
+import dagger.internal.codegen.binding.MembersInjectionBinding;
+import dagger.spi.model.RequestKind;
+
+/**
+ * A binding representation that wraps code generation methods that satisfy all kinds of request for
+ * that binding.
+ */
+final class MembersInjectionBindingRepresentation implements BindingRepresentation {
+  private final MembersInjectionBinding binding;
+  private final MembersInjectionRequestRepresentation membersInjectionRequestRepresentation;
+
+  @AssistedInject
+  MembersInjectionBindingRepresentation(
+      @Assisted MembersInjectionBinding binding,
+      MembersInjectionRequestRepresentation.Factory membersInjectionRequestRepresentationFactory) {
+    this.binding = binding;
+    this.membersInjectionRequestRepresentation =
+        membersInjectionRequestRepresentationFactory.create(binding);
+  }
+
+  @Override
+  public RequestRepresentation getRequestRepresentation(BindingRequest request) {
+    checkArgument(request.isRequestKind(RequestKind.MEMBERS_INJECTION), binding);
+    return membersInjectionRequestRepresentation;
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    MembersInjectionBindingRepresentation create(MembersInjectionBinding binding);
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/MembersInjectionMethods.java b/java/dagger/internal/codegen/writing/MembersInjectionMethods.java
index 3d602b3..ce332f5 100644
--- a/java/dagger/internal/codegen/writing/MembersInjectionMethods.java
+++ b/java/dagger/internal/codegen/writing/MembersInjectionMethods.java
@@ -17,11 +17,15 @@
 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.base.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
 import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
 import static dagger.internal.codegen.writing.ComponentImplementation.MethodSpecKind.MEMBERS_INJECTION_METHOD;
+import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
 import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.STATIC;
 
 import com.google.common.collect.ImmutableSet;
 import com.squareup.javapoet.ClassName;
@@ -34,30 +38,35 @@
 import dagger.internal.codegen.binding.MembersInjectionBinding;
 import dagger.internal.codegen.binding.MembersInjectionBinding.InjectionSite;
 import dagger.internal.codegen.binding.ProvisionBinding;
+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.ComponentImplementation.ShardImplementation;
 import dagger.internal.codegen.writing.InjectionMethods.InjectionSiteMethod;
-import dagger.model.Key;
+import dagger.spi.model.Key;
 import java.util.LinkedHashMap;
 import java.util.Map;
-import javax.lang.model.element.Name;
+import javax.inject.Inject;
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.type.TypeMirror;
 
 /** Manages the member injection methods for a component. */
+@PerComponentImplementation
 final class MembersInjectionMethods {
-  private final Map<Key, MethodSpec> membersInjectionMethods = new LinkedHashMap<>();
+  private final Map<Key, Expression> injectMethodExpressions = new LinkedHashMap<>();
+  private final Map<Key, Expression> experimentalInjectMethodExpressions = new LinkedHashMap<>();
   private final ComponentImplementation componentImplementation;
-  private final ComponentBindingExpressions bindingExpressions;
+  private final ComponentRequestRepresentations bindingExpressions;
   private final BindingGraph graph;
   private final DaggerElements elements;
   private final DaggerTypes types;
   private final KotlinMetadataUtil metadataUtil;
 
+  @Inject
   MembersInjectionMethods(
       ComponentImplementation componentImplementation,
-      ComponentBindingExpressions bindingExpressions,
+      ComponentRequestRepresentations bindingExpressions,
       BindingGraph graph,
       DaggerElements elements,
       DaggerTypes types,
@@ -71,34 +80,77 @@
   }
 
   /**
-   * Returns the members injection {@link MethodSpec} for the given {@link Key}, creating it if
+   * Returns the members injection {@link Expression} for the given {@link Key}, creating it if
    * necessary.
    */
-  MethodSpec getOrCreate(Key key) {
-    return reentrantComputeIfAbsent(membersInjectionMethods, key, this::membersInjectionMethod);
+  Expression getInjectExpression(Key key, CodeBlock instance, ClassName requestingClass) {
+    Binding binding =
+        graph.localMembersInjectionBinding(key).isPresent()
+            ? graph.localMembersInjectionBinding(key).get()
+            : graph.localContributionBinding(key).get();
+    Expression expression =
+        reentrantComputeIfAbsent(
+            injectMethodExpressions, key, k -> injectMethodExpression(binding, false));
+    ShardImplementation shardImplementation = componentImplementation.shardImplementation(binding);
+    return Expression.create(
+        expression.type(),
+        shardImplementation.name().equals(requestingClass)
+            ? CodeBlock.of("$L($L)", expression.codeBlock(), instance)
+            : CodeBlock.of(
+                "$L.$L($L)",
+                shardImplementation.shardFieldReference(),
+                expression.codeBlock(),
+                instance));
   }
 
-  private MethodSpec membersInjectionMethod(Key key) {
-    Binding binding =
-        graph.membersInjectionBinding(key).isPresent()
-            ? graph.membersInjectionBinding(key).get()
-            : graph.contributionBinding(key);
-    TypeMirror keyType = binding.key().type();
+  /**
+   * Returns the members injection {@link Expression} for the given {@link Key}, creating it if
+   * necessary.
+   */
+  Expression getInjectExpressionExperimental(
+      ProvisionBinding provisionBinding, CodeBlock instance, ClassName requestingClass) {
+    checkState(
+        componentImplementation.compilerMode().isExperimentalMergedMode(),
+        "Compiler mode should be experimentalMergedMode!");
+    Expression expression =
+        reentrantComputeIfAbsent(
+            experimentalInjectMethodExpressions,
+            provisionBinding.key(),
+            k -> injectMethodExpression(provisionBinding, true));
+    return Expression.create(
+        expression.type(), CodeBlock.of("$L($L, dependencies)", expression.codeBlock(), instance));
+  }
+
+  private Expression injectMethodExpression(Binding binding, boolean useStaticInjectionMethod) {
+    // TODO(wanyingd): move Switching Providers and injection methods to Shard classes to avoid
+    // exceeding component class constant pool limit.
+    // Add to Component Shard so that is can be accessible from Switching Providers.
+    ShardImplementation shardImplementation =
+        useStaticInjectionMethod
+            ? componentImplementation.getComponentShard()
+            : componentImplementation.shardImplementation(binding);
+    TypeMirror keyType = binding.key().type().java();
     TypeMirror membersInjectedType =
-        isTypeAccessibleFrom(keyType, componentImplementation.name().packageName())
+        isTypeAccessibleFrom(keyType, shardImplementation.name().packageName())
             ? keyType
-            : elements.getTypeElement(Object.class).asType();
+            : elements.getTypeElement(TypeName.OBJECT).asType();
     TypeName membersInjectedTypeName = TypeName.get(membersInjectedType);
-    Name bindingTypeName = binding.bindingTypeElement().get().getSimpleName();
+    String bindingTypeName = getSimpleName(binding.bindingTypeElement().get());
     // 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);
+    String methodName = shardImplementation.getUniqueMethodName("inject" + bindingTypeName);
     ParameterSpec parameter = ParameterSpec.builder(membersInjectedTypeName, "instance").build();
     MethodSpec.Builder methodBuilder =
-        methodBuilder(methodName)
-            .addModifiers(PRIVATE)
-            .returns(membersInjectedTypeName)
-            .addParameter(parameter);
+        useStaticInjectionMethod
+            ? methodBuilder(methodName)
+                .addModifiers(PRIVATE, STATIC)
+                .returns(membersInjectedTypeName)
+                .addParameter(parameter)
+                .addParameter(Object[].class, "dependencies")
+            : methodBuilder(methodName)
+                .addModifiers(PRIVATE)
+                .returns(membersInjectedTypeName)
+                .addParameter(parameter);
     TypeElement canIgnoreReturnValue =
         elements.getTypeElement("com.google.errorprone.annotations.CanIgnoreReturnValue");
     if (canIgnoreReturnValue != null) {
@@ -108,20 +160,25 @@
     methodBuilder.addCode(
         InjectionSiteMethod.invokeAll(
             injectionSites(binding),
-            componentImplementation.name(),
+            shardImplementation.name(),
             instance,
             membersInjectedType,
             request ->
-                bindingExpressions
-                    .getDependencyArgumentExpression(request, componentImplementation.name())
+                (useStaticInjectionMethod
+                        ? bindingExpressions
+                            .getExperimentalSwitchingProviderDependencyRepresentation(
+                                bindingRequest(request))
+                            .getDependencyExpression(request.kind(), (ProvisionBinding) binding)
+                        : bindingExpressions.getDependencyArgumentExpression(
+                            request, shardImplementation.name()))
                     .codeBlock(),
             types,
             metadataUtil));
     methodBuilder.addStatement("return $L", instance);
 
     MethodSpec method = methodBuilder.build();
-    componentImplementation.addMethod(MEMBERS_INJECTION_METHOD, method);
-    return method;
+    shardImplementation.addMethod(MEMBERS_INJECTION_METHOD, method);
+    return Expression.create(membersInjectedType, CodeBlock.of("$N", method));
   }
 
   private static ImmutableSet<InjectionSite> injectionSites(Binding binding) {
diff --git a/java/dagger/internal/codegen/writing/MembersInjectionRequestRepresentation.java b/java/dagger/internal/codegen/writing/MembersInjectionRequestRepresentation.java
new file mode 100644
index 0000000..748be80
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/MembersInjectionRequestRepresentation.java
@@ -0,0 +1,86 @@
+/*
+ * 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 androidx.room.compiler.processing.XTypeKt.isVoid;
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static com.google.common.collect.Iterables.getOnlyElement;
+
+import androidx.room.compiler.processing.XMethodElement;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.ParameterSpec;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.binding.MembersInjectionBinding;
+import dagger.internal.codegen.javapoet.Expression;
+
+/**
+ * A binding expression for members injection component methods. See {@link
+ * MembersInjectionMethods}.
+ */
+final class MembersInjectionRequestRepresentation extends RequestRepresentation {
+  private final MembersInjectionBinding binding;
+  private final MembersInjectionMethods membersInjectionMethods;
+
+  @AssistedInject
+  MembersInjectionRequestRepresentation(
+      @Assisted 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 RequestRepresentation, 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) {
+    XMethodElement methodElement = componentMethod.methodElement();
+    ParameterSpec parameter =
+        ParameterSpec.get(toJavac(getOnlyElement(methodElement.getParameters())));
+
+    if (binding.injectionSites().isEmpty()) {
+      return isVoid(methodElement.getReturnType())
+          ? CodeBlock.of("")
+          : CodeBlock.of("return $N;", parameter);
+    } else {
+      ClassName requestingClass = component.name();
+      return isVoid(methodElement.getReturnType())
+          ? CodeBlock.of("$L;", membersInjectionInvocation(parameter, requestingClass).codeBlock())
+          : CodeBlock.of(
+              "return $L;", membersInjectionInvocation(parameter, requestingClass).codeBlock());
+    }
+  }
+
+  private Expression membersInjectionInvocation(ParameterSpec target, ClassName requestingClass) {
+    return membersInjectionMethods.getInjectExpression(
+        binding.key(), CodeBlock.of("$N", target), requestingClass);
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    MembersInjectionRequestRepresentation create(MembersInjectionBinding binding);
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/MembersInjectorGenerator.java b/java/dagger/internal/codegen/writing/MembersInjectorGenerator.java
index 8630ce6..b906936 100644
--- a/java/dagger/internal/codegen/writing/MembersInjectorGenerator.java
+++ b/java/dagger/internal/codegen/writing/MembersInjectorGenerator.java
@@ -16,6 +16,7 @@
 
 package dagger.internal.codegen.writing;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 import static com.google.common.base.Preconditions.checkState;
 import static com.squareup.javapoet.MethodSpec.constructorBuilder;
 import static com.squareup.javapoet.MethodSpec.methodBuilder;
@@ -27,6 +28,7 @@
 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.extension.DaggerStreams.presentValues;
 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;
@@ -39,8 +41,11 @@
 import static javax.lang.model.element.Modifier.PUBLIC;
 import static javax.lang.model.element.Modifier.STATIC;
 
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XFiler;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.squareup.javapoet.AnnotationSpec;
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
 import com.squareup.javapoet.FieldSpec;
@@ -55,16 +60,17 @@
 import dagger.internal.codegen.binding.FrameworkField;
 import dagger.internal.codegen.binding.MembersInjectionBinding;
 import dagger.internal.codegen.binding.MembersInjectionBinding.InjectionSite;
+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.InjectionMethods.InjectionSiteMethod;
-import dagger.model.DependencyRequest;
+import dagger.spi.model.DaggerAnnotation;
+import dagger.spi.model.DependencyRequest;
+import dagger.spi.model.Key;
 import java.util.Map.Entry;
-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.
@@ -75,7 +81,7 @@
 
   @Inject
   MembersInjectorGenerator(
-      Filer filer,
+      XFiler filer,
       DaggerElements elements,
       DaggerTypes types,
       SourceVersion sourceVersion,
@@ -86,7 +92,7 @@
   }
 
   @Override
-  public Element originatingElement(MembersInjectionBinding binding) {
+  public XElement originatingElement(MembersInjectionBinding binding) {
     return binding.membersInjectedType();
   }
 
@@ -117,9 +123,10 @@
     TypeSpec.Builder injectorTypeBuilder =
         classBuilder(generatedTypeName)
             .addModifiers(PUBLIC, FINAL)
-            .addTypeVariables(typeParameters);
+            .addTypeVariables(typeParameters)
+            .addAnnotation(qualifierMetadataAnnotation(binding));
 
-    TypeName injectedTypeName = TypeName.get(binding.key().type());
+    TypeName injectedTypeName = TypeName.get(binding.key().type().java());
     TypeName implementedType = membersInjectorOf(injectedTypeName);
     injectorTypeBuilder.addSuperinterface(implementedType);
 
@@ -159,7 +166,7 @@
       // 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());
+          !isTypeAccessibleFrom(dependency.key().type().java(), generatedTypeName.packageName());
 
       String fieldName = fieldNames.getUniqueName(bindingField.name());
       TypeName fieldType = useRawFrameworkType ? bindingField.type().rawType : bindingField.type();
@@ -198,7 +205,7 @@
             binding.injectionSites(),
             generatedTypeName,
             CodeBlock.of("instance"),
-            binding.key().type(),
+            binding.key().type().java(),
             frameworkFieldUsages(binding.dependencies(), dependencyFields)::get,
             types,
             metadataUtil));
@@ -209,7 +216,10 @@
     injectorTypeBuilder.addMethod(injectMembersBuilder.build());
 
     for (InjectionSite injectionSite : binding.injectionSites()) {
-      if (injectionSite.element().getEnclosingElement().equals(binding.membersInjectedType())) {
+      if (injectionSite
+          .element()
+          .getEnclosingElement()
+          .equals(toJavac(binding.membersInjectedType()))) {
         injectorTypeBuilder.addMethod(InjectionSiteMethod.create(injectionSite, metadataUtil));
       }
     }
@@ -218,4 +228,26 @@
 
     return ImmutableList.of(injectorTypeBuilder);
   }
+
+  private AnnotationSpec qualifierMetadataAnnotation(MembersInjectionBinding binding) {
+    AnnotationSpec.Builder builder = AnnotationSpec.builder(TypeNames.QUALIFIER_METADATA);
+    binding.injectionSites().stream()
+        // filter out non-local injection sites. Injection sites for super types will be in their
+        // own generated _MembersInjector class.
+        .filter(
+            injectionSite ->
+                injectionSite
+                    .element()
+                    .getEnclosingElement()
+                    .equals(toJavac(binding.membersInjectedType())))
+        .flatMap(injectionSite -> injectionSite.dependencies().stream())
+        .map(DependencyRequest::key)
+        .map(Key::qualifier)
+        .flatMap(presentValues())
+        .map(DaggerAnnotation::className)
+        .map(ClassName::canonicalName)
+        .distinct()
+        .forEach(qualifier -> builder.addMember("value", "$S", qualifier));
+    return builder.build();
+  }
 }
diff --git a/java/dagger/internal/codegen/writing/MembersInjectorProviderCreationExpression.java b/java/dagger/internal/codegen/writing/MembersInjectorProviderCreationExpression.java
index 534c057..e00db01 100644
--- a/java/dagger/internal/codegen/writing/MembersInjectorProviderCreationExpression.java
+++ b/java/dagger/internal/codegen/writing/MembersInjectorProviderCreationExpression.java
@@ -24,7 +24,11 @@
 
 import com.google.auto.common.MoreTypes;
 import com.squareup.javapoet.CodeBlock;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
 import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
 import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.type.TypeMirror;
@@ -33,19 +37,24 @@
 final class MembersInjectorProviderCreationExpression
     implements FrameworkInstanceCreationExpression {
 
-  private final ComponentBindingExpressions componentBindingExpressions;
+  private final ShardImplementation shardImplementation;
+  private final ComponentRequestRepresentations componentRequestRepresentations;
   private final ProvisionBinding binding;
 
+  @AssistedInject
   MembersInjectorProviderCreationExpression(
-      ProvisionBinding binding, ComponentBindingExpressions componentBindingExpressions) {
+      @Assisted ProvisionBinding binding,
+      ComponentImplementation componentImplementation,
+      ComponentRequestRepresentations componentRequestRepresentations) {
     this.binding = checkNotNull(binding);
-    this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+    this.shardImplementation = componentImplementation.shardImplementation(binding);
+    this.componentRequestRepresentations = checkNotNull(componentRequestRepresentations);
   }
 
   @Override
   public CodeBlock creationExpression() {
     TypeMirror membersInjectedType =
-        getOnlyElement(MoreTypes.asDeclared(binding.key().type()).getTypeArguments());
+        getOnlyElement(MoreTypes.asDeclared(binding.key().type().java()).getTypeArguments());
 
     boolean castThroughRawType = false;
     CodeBlock membersInjector;
@@ -60,13 +69,16 @@
         injectedTypeElement = MoreTypes.asTypeElement(injectedTypeElement.getSuperclass());
       }
 
-      membersInjector = CodeBlock.of(
-          "$T.create($L)",
-          membersInjectorNameForType(injectedTypeElement),
-          componentBindingExpressions.getCreateMethodArgumentsCodeBlock(binding));
+      membersInjector =
+          CodeBlock.of(
+              "$T.create($L)",
+              membersInjectorNameForType(injectedTypeElement),
+              componentRequestRepresentations.getCreateMethodArgumentsCodeBlock(
+                  binding, shardImplementation.name()));
     }
 
-    // TODO(ronshapiro): consider adding a MembersInjectorBindingExpression to return this directly
+    // TODO(ronshapiro): consider adding a MembersInjectorRequestRepresentation 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
@@ -75,19 +87,19 @@
     // 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;
+        ? CodeBlock.of("($T) $L", INSTANCE_FACTORY, providerExpression)
+        : providerExpression;
   }
 
   private boolean hasLocalInjectionSites(TypeElement injectedTypeElement) {
-    return binding.injectionSites()
-        .stream()
+    return binding.injectionSites().stream()
         .anyMatch(
             injectionSite ->
                 injectionSite.element().getEnclosingElement().equals(injectedTypeElement));
   }
 
-  @Override
-  public boolean useInnerSwitchingProvider() {
-    return !binding.injectionSites().isEmpty();
+  @AssistedFactory
+  static interface Factory {
+    MembersInjectorProviderCreationExpression create(ProvisionBinding binding);
   }
 }
diff --git a/java/dagger/internal/codegen/writing/MethodBindingExpression.java b/java/dagger/internal/codegen/writing/MethodBindingExpression.java
deleted file mode 100644
index 9c0c74a..0000000
--- a/java/dagger/internal/codegen/writing/MethodBindingExpression.java
+++ /dev/null
@@ -1,294 +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.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/MethodRequestRepresentation.java b/java/dagger/internal/codegen/writing/MethodRequestRepresentation.java
new file mode 100644
index 0000000..714e5f0
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/MethodRequestRepresentation.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.writing;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
+import javax.lang.model.type.TypeMirror;
+
+/** A binding expression that wraps another in a nullary method on the component. */
+abstract class MethodRequestRepresentation extends RequestRepresentation {
+  private final ShardImplementation shardImplementation;
+  private final ProducerEntryPointView producerEntryPointView;
+
+  protected MethodRequestRepresentation(
+      ShardImplementation shardImplementation, DaggerTypes types) {
+    this.shardImplementation = shardImplementation;
+    this.producerEntryPointView = new ProducerEntryPointView(shardImplementation, types);
+  }
+
+  @Override
+  Expression getDependencyExpression(ClassName requestingClass) {
+    return Expression.create(
+        returnType(),
+        requestingClass.equals(shardImplementation.name())
+            ? methodCall()
+            : CodeBlock.of("$L.$L", shardImplementation.shardFieldReference(), methodCall()));
+  }
+
+  @Override
+  Expression getDependencyExpressionForComponentMethod(
+      ComponentMethodDescriptor componentMethod, ComponentImplementation component) {
+    return producerEntryPointView
+        .getProducerEntryPointField(this, componentMethod, component.name())
+        .orElseGet(
+            () -> super.getDependencyExpressionForComponentMethod(componentMethod, component));
+  }
+
+  /** Returns the return type for the dependency request. */
+  protected abstract TypeMirror returnType();
+
+  /** Returns the method call. */
+  protected abstract CodeBlock methodCall();
+}
diff --git a/java/dagger/internal/codegen/writing/ModuleProxies.java b/java/dagger/internal/codegen/writing/ModuleProxies.java
index 6d174f8..a88f6ed 100644
--- a/java/dagger/internal/codegen/writing/ModuleProxies.java
+++ b/java/dagger/internal/codegen/writing/ModuleProxies.java
@@ -16,98 +16,92 @@
 
 package dagger.internal.codegen.writing;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 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 dagger.internal.codegen.xprocessing.XTypeElements.isNested;
 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 androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XFiler;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.common.collect.ImmutableList;
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
 import com.squareup.javapoet.TypeSpec;
+import dagger.internal.codegen.base.ModuleKind;
 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) {
+  ModuleProxies(DaggerElements elements) {
     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> {
+      extends SourceFileGenerator<XTypeElement> {
 
     private final ModuleProxies moduleProxies;
-    private final KotlinMetadataUtil metadataUtil;
 
     @Inject
     ModuleConstructorProxyGenerator(
-        Filer filer,
+        XFiler filer,
         DaggerElements elements,
         SourceVersion sourceVersion,
-        ModuleProxies moduleProxies,
-        KotlinMetadataUtil metadataUtil) {
+        ModuleProxies moduleProxies) {
       super(filer, elements, sourceVersion);
       this.moduleProxies = moduleProxies;
-      this.metadataUtil = metadataUtil;
     }
 
     @Override
-    public Element originatingElement(TypeElement moduleElement) {
+    public XElement originatingElement(XTypeElement moduleElement) {
       return moduleElement;
     }
 
     @Override
-    public ImmutableList<TypeSpec.Builder> topLevelTypes(TypeElement moduleElement) {
-      ModuleKind.checkIsModule(moduleElement, metadataUtil);
+    public ImmutableList<TypeSpec.Builder> topLevelTypes(XTypeElement moduleElement) {
+      ModuleKind.checkIsModule(moduleElement);
       return moduleProxies.nonPublicNullaryConstructor(moduleElement).isPresent()
           ? ImmutableList.of(buildProxy(moduleElement))
           : ImmutableList.of();
     }
 
-    private TypeSpec.Builder buildProxy(TypeElement moduleElement) {
+    private TypeSpec.Builder buildProxy(XTypeElement moduleElement) {
       return classBuilder(moduleProxies.constructorProxyTypeName(moduleElement))
           .addModifiers(PUBLIC, FINAL)
           .addMethod(constructorBuilder().addModifiers(PRIVATE).build())
           .addMethod(
               methodBuilder("newInstance")
                   .addModifiers(PUBLIC, STATIC)
-                  .returns(ClassName.get(moduleElement))
-                  .addStatement("return new $T()", moduleElement)
+                  .returns(moduleElement.getClassName())
+                  .addStatement("return new $T()", moduleElement.getClassName())
                   .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);
+  private ClassName constructorProxyTypeName(XTypeElement moduleElement) {
+    ModuleKind.checkIsModule(moduleElement);
+    ClassName moduleClassName = moduleElement.getClassName();
     return moduleClassName
         .topLevelClassName()
         .peerClass(SourceFiles.classFileName(moduleClassName) + "_Proxy");
@@ -118,14 +112,12 @@
    * 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))) {
+  private Optional<ExecutableElement> nonPublicNullaryConstructor(XTypeElement moduleElement) {
+    ModuleKind.checkIsModule(moduleElement);
+    if (moduleElement.isAbstract() || (isNested(moduleElement) && !moduleElement.isStatic())) {
       return Optional.empty();
     }
-    return constructorsIn(elements.getAllMembers(moduleElement)).stream()
+    return constructorsIn(elements.getAllMembers(toJavac(moduleElement))).stream()
         .filter(constructor -> !Accessibility.isElementPubliclyAccessible(constructor))
         .filter(constructor -> !constructor.getModifiers().contains(PRIVATE))
         .filter(constructor -> constructor.getParameters().isEmpty())
@@ -137,14 +129,14 @@
    * 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);
+  public CodeBlock newModuleInstance(XTypeElement moduleElement, ClassName requestingClass) {
+    ModuleKind.checkIsModule(moduleElement);
     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));
+        .orElse(CodeBlock.of("new $T()", moduleElement.getClassName()));
   }
 }
diff --git a/java/dagger/internal/codegen/writing/MultibindingFactoryCreationExpression.java b/java/dagger/internal/codegen/writing/MultibindingFactoryCreationExpression.java
index 64e008e..034e835 100644
--- a/java/dagger/internal/codegen/writing/MultibindingFactoryCreationExpression.java
+++ b/java/dagger/internal/codegen/writing/MultibindingFactoryCreationExpression.java
@@ -22,36 +22,37 @@
 import dagger.internal.codegen.binding.BindingRequest;
 import dagger.internal.codegen.binding.ContributionBinding;
 import dagger.internal.codegen.javapoet.CodeBlocks;
+import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
 import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
-import dagger.model.DependencyRequest;
+import dagger.spi.model.DependencyRequest;
 
 /** An abstract factory creation expression for multibindings. */
 abstract class MultibindingFactoryCreationExpression
     implements FrameworkInstanceCreationExpression {
-  private final ComponentImplementation componentImplementation;
-  private final ComponentBindingExpressions componentBindingExpressions;
+  private final ShardImplementation shardImplementation;
+  private final ComponentRequestRepresentations componentRequestRepresentations;
   private final ContributionBinding binding;
 
   MultibindingFactoryCreationExpression(
       ContributionBinding binding,
       ComponentImplementation componentImplementation,
-      ComponentBindingExpressions componentBindingExpressions) {
+      ComponentRequestRepresentations componentRequestRepresentations) {
     this.binding = checkNotNull(binding);
-    this.componentImplementation = checkNotNull(componentImplementation);
-    this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+    this.shardImplementation = checkNotNull(componentImplementation).shardImplementation(binding);
+    this.componentRequestRepresentations = checkNotNull(componentRequestRepresentations);
   }
 
   /** Returns the expression for a dependency of this multibinding. */
   protected final CodeBlock multibindingDependencyExpression(DependencyRequest dependency) {
     CodeBlock expression =
-        componentBindingExpressions
+        componentRequestRepresentations
             .getDependencyExpression(
                 BindingRequest.bindingRequest(dependency.key(), binding.frameworkType()),
-                componentImplementation.name())
+                shardImplementation.name())
             .codeBlock();
 
     return useRawType()
-        ? CodeBlocks.cast(expression, binding.frameworkType().frameworkClass())
+        ? CodeBlocks.cast(expression, binding.frameworkType().frameworkClassName())
         : expression;
   }
 
@@ -65,11 +66,6 @@
    * 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();
+    return !shardImplementation.isTypeAccessible(binding.key().type().java());
   }
 }
diff --git a/java/dagger/internal/codegen/writing/OptionalBindingExpression.java b/java/dagger/internal/codegen/writing/OptionalBindingExpression.java
deleted file mode 100644
index 4faf3fa..0000000
--- a/java/dagger/internal/codegen/writing/OptionalBindingExpression.java
+++ /dev/null
@@ -1,94 +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.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
index 2d80963..07b497b 100644
--- a/java/dagger/internal/codegen/writing/OptionalFactories.java
+++ b/java/dagger/internal/codegen/writing/OptionalFactories.java
@@ -27,7 +27,6 @@
 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;
@@ -61,9 +60,11 @@
 import dagger.internal.codegen.binding.ContributionBinding;
 import dagger.internal.codegen.binding.FrameworkType;
 import dagger.internal.codegen.javapoet.AnnotationSpecs;
-import dagger.model.RequestKind;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
 import dagger.producers.Producer;
 import dagger.producers.internal.Producers;
+import dagger.spi.model.RequestKind;
 import java.util.Comparator;
 import java.util.Map;
 import java.util.Optional;
@@ -74,37 +75,50 @@
 
 /** 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;
+  /** Keeps track of the fields, methods, and classes already added to the generated file. */
+  @PerGeneratedFile
+  static final class PerGeneratedFileCache {
+    /**
+     * 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));
 
-  @Inject OptionalFactories(@TopLevel ComponentImplementation componentImplementation) {
-    this.componentImplementation = componentImplementation;
+    /**
+     * 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<>();
+
+    @Inject
+    PerGeneratedFileCache() {}
   }
 
-  /**
-   * 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));
+  private final PerGeneratedFileCache perGeneratedFileCache;
+  private final ShardImplementation rootComponentShard;
 
-  /**
-   * 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<>();
+  @Inject
+  OptionalFactories(
+      PerGeneratedFileCache perGeneratedFileCache,
+      ComponentImplementation componentImplementation) {
+    this.perGeneratedFileCache = perGeneratedFileCache;
+    this.rootComponentShard =
+        componentImplementation.rootComponentImplementation().getComponentShard();
+  }
 
   /**
    * Returns an expression that calls a static method that returns a {@code Provider<Optional<T>>}
@@ -118,11 +132,11 @@
     OptionalKind optionalKind = OptionalType.from(binding.key()).kind();
     return CodeBlock.of(
         "$N()",
-        absentOptionalProviderMethods.computeIfAbsent(
+        perGeneratedFileCache.absentOptionalProviderMethods.computeIfAbsent(
             optionalKind,
             kind -> {
               MethodSpec method = absentOptionalProviderMethod(kind);
-              componentImplementation.addMethod(ABSENT_OPTIONAL_METHOD, method);
+              rootComponentShard.addMethod(ABSENT_OPTIONAL_METHOD, method);
               return method;
             }));
   }
@@ -141,20 +155,20 @@
         .returns(providerOf(optionalKind.of(typeVariable)))
         .addJavadoc(
             "Returns a {@link $T} that returns {@code $L}.",
-            Provider.class,
+            TypeNames.PROVIDER,
             optionalKind.absentValueExpression())
         .addCode("$L // safe covariant cast\n", AnnotationSpecs.suppressWarnings(UNCHECKED))
-        .addCode(
-            "$1T provider = ($1T) $2N;",
+        .addStatement(
+            "$1T provider = ($1T) $2N",
             providerOf(optionalKind.of(typeVariable)),
-            absentOptionalProviderFields.computeIfAbsent(
+            perGeneratedFileCache.absentOptionalProviderFields.computeIfAbsent(
                 optionalKind,
                 kind -> {
                   FieldSpec field = absentOptionalProviderField(kind);
-                  componentImplementation.addField(ABSENT_OPTIONAL_FIELD, field);
+                  rootComponentShard.addField(ABSENT_OPTIONAL_FIELD, field);
                   return field;
                 }))
-        .addCode("return provider;")
+        .addStatement("return provider")
         .build();
   }
 
@@ -164,7 +178,7 @@
    */
   private FieldSpec absentOptionalProviderField(OptionalKind optionalKind) {
     return FieldSpec.builder(
-            PROVIDER,
+            TypeNames.PROVIDER,
             String.format("ABSENT_%s_PROVIDER", optionalKind.name()),
             PRIVATE,
             STATIC,
@@ -173,7 +187,7 @@
         .initializer("$T.create($L)", InstanceFactory.class, optionalKind.absentValueExpression())
         .addJavadoc(
             "A {@link $T} that returns {@code $L}.",
-            Provider.class,
+            TypeNames.PROVIDER,
             optionalKind.absentValueExpression())
         .build();
   }
@@ -258,7 +272,7 @@
       return new StringBuilder("Present")
           .append(UPPER_UNDERSCORE.to(UPPER_CAMEL, optionalKind().name()))
           .append(UPPER_UNDERSCORE.to(UPPER_CAMEL, valueKind().toString()))
-          .append(frameworkType().frameworkClass().getSimpleName())
+          .append(frameworkType().frameworkClassName().simpleName())
           .toString();
     }
 
@@ -296,11 +310,11 @@
   CodeBlock presentOptionalFactory(ContributionBinding binding, CodeBlock delegateFactory) {
     return CodeBlock.of(
         "$N.of($L)",
-        presentFactoryClasses.computeIfAbsent(
+        perGeneratedFileCache.presentFactoryClasses.computeIfAbsent(
             PresentFactorySpec.of(binding),
             spec -> {
               TypeSpec type = presentOptionalFactoryClass(spec);
-              componentImplementation.addType(PRESENT_FACTORY, type);
+              rootComponentShard.addType(PRESENT_FACTORY, type);
               return type;
             }),
         delegateFactory);
diff --git a/java/dagger/internal/codegen/writing/OptionalFactoryInstanceCreationExpression.java b/java/dagger/internal/codegen/writing/OptionalFactoryInstanceCreationExpression.java
index 593921e..df9d15e 100644
--- a/java/dagger/internal/codegen/writing/OptionalFactoryInstanceCreationExpression.java
+++ b/java/dagger/internal/codegen/writing/OptionalFactoryInstanceCreationExpression.java
@@ -20,11 +20,14 @@
 import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
 
 import com.squareup.javapoet.CodeBlock;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
 import dagger.internal.codegen.binding.ContributionBinding;
 import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
 
 /**
- * A {@link FrameworkInstanceCreationExpression} for {@link dagger.model.BindingKind#OPTIONAL
+ * A {@link FrameworkInstanceCreationExpression} for {@link dagger.spi.model.BindingKind#OPTIONAL
  * optional bindings}.
  */
 final class OptionalFactoryInstanceCreationExpression
@@ -32,17 +35,18 @@
   private final OptionalFactories optionalFactories;
   private final ContributionBinding binding;
   private final ComponentImplementation componentImplementation;
-  private final ComponentBindingExpressions componentBindingExpressions;
+  private final ComponentRequestRepresentations componentRequestRepresentations;
 
+  @AssistedInject
   OptionalFactoryInstanceCreationExpression(
+      @Assisted ContributionBinding binding,
       OptionalFactories optionalFactories,
-      ContributionBinding binding,
       ComponentImplementation componentImplementation,
-      ComponentBindingExpressions componentBindingExpressions) {
+      ComponentRequestRepresentations componentRequestRepresentations) {
     this.optionalFactories = optionalFactories;
     this.binding = binding;
     this.componentImplementation = componentImplementation;
-    this.componentBindingExpressions = componentBindingExpressions;
+    this.componentRequestRepresentations = componentRequestRepresentations;
   }
 
   @Override
@@ -51,18 +55,16 @@
         ? optionalFactories.absentOptionalProvider(binding)
         : optionalFactories.presentOptionalFactory(
             binding,
-            componentBindingExpressions
+            componentRequestRepresentations
                 .getDependencyExpression(
                     bindingRequest(
                         getOnlyElement(binding.dependencies()).key(), binding.frameworkType()),
-                    componentImplementation.name())
+                    componentImplementation.shardImplementation(binding).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();
+  @AssistedFactory
+  static interface Factory {
+    OptionalFactoryInstanceCreationExpression create(ContributionBinding binding);
   }
 }
diff --git a/java/dagger/internal/codegen/writing/OptionalRequestRepresentation.java b/java/dagger/internal/codegen/writing/OptionalRequestRepresentation.java
new file mode 100644
index 0000000..caef3a6
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/OptionalRequestRepresentation.java
@@ -0,0 +1,105 @@
+/*
+ * 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.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+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.spi.model.DependencyRequest;
+import javax.lang.model.SourceVersion;
+
+/** A binding expression for optional bindings. */
+final class OptionalRequestRepresentation extends RequestRepresentation {
+  private final ProvisionBinding binding;
+  private final ComponentRequestRepresentations componentRequestRepresentations;
+  private final DaggerTypes types;
+  private final SourceVersion sourceVersion;
+  private final boolean isExperimentalMergedMode;
+
+  @AssistedInject
+  OptionalRequestRepresentation(
+      @Assisted ProvisionBinding binding,
+      ComponentImplementation componentImplementation,
+      ComponentRequestRepresentations componentRequestRepresentations,
+      DaggerTypes types,
+      SourceVersion sourceVersion) {
+    this.binding = binding;
+    this.componentRequestRepresentations = componentRequestRepresentations;
+    this.types = types;
+    this.sourceVersion = sourceVersion;
+    this.isExperimentalMergedMode =
+        componentImplementation.compilerMode().isExperimentalMergedMode();
+  }
+
+  @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().java(), requestingClass.packageName())) {
+          return Expression.create(
+              binding.key().type().java(),
+              optionalKind.parameterizedAbsentValueExpression(optionalType));
+        }
+      }
+      return Expression.create(binding.key().type().java(), optionalKind.absentValueExpression());
+    }
+    DependencyRequest dependency = getOnlyElement(binding.dependencies());
+
+    CodeBlock dependencyExpression =
+        isExperimentalMergedMode
+            ? componentRequestRepresentations
+                .getExperimentalSwitchingProviderDependencyRepresentation(
+                    bindingRequest(dependency))
+                .getDependencyExpression(dependency.kind(), binding)
+                .codeBlock()
+            : componentRequestRepresentations
+                .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().java(), requestingClass.packageName())
+        ? Expression.create(
+            binding.key().type().java(), optionalKind.presentExpression(dependencyExpression))
+        : Expression.create(
+            types.erasure(binding.key().type().java()),
+            optionalKind.presentObjectExpression(dependencyExpression));
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    OptionalRequestRepresentation create(ProvisionBinding binding);
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/PrivateMethodBindingExpression.java b/java/dagger/internal/codegen/writing/PrivateMethodBindingExpression.java
deleted file mode 100644
index b6502ea..0000000
--- a/java/dagger/internal/codegen/writing/PrivateMethodBindingExpression.java
+++ /dev/null
@@ -1,96 +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.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/PrivateMethodRequestRepresentation.java b/java/dagger/internal/codegen/writing/PrivateMethodRequestRepresentation.java
new file mode 100644
index 0000000..1a828ba
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/PrivateMethodRequestRepresentation.java
@@ -0,0 +1,113 @@
+/*
+ * 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 androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+import static dagger.internal.codegen.writing.ComponentImplementation.MethodSpecKind.PRIVATE_METHOD;
+import static javax.lang.model.element.Modifier.PRIVATE;
+
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.TypeName;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+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.internal.codegen.writing.ComponentImplementation.ShardImplementation;
+import dagger.spi.model.RequestKind;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * 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 PrivateMethodRequestRepresentation extends MethodRequestRepresentation {
+  private final ShardImplementation shardImplementation;
+  private final ContributionBinding binding;
+  private final BindingRequest request;
+  private final RequestRepresentation wrappedRequestRepresentation;
+  private final CompilerOptions compilerOptions;
+  private final DaggerTypes types;
+  private String methodName;
+
+  @AssistedInject
+  PrivateMethodRequestRepresentation(
+      @Assisted BindingRequest request,
+      @Assisted ContributionBinding binding,
+      @Assisted RequestRepresentation wrappedRequestRepresentation,
+      ComponentImplementation componentImplementation,
+      DaggerTypes types,
+      CompilerOptions compilerOptions) {
+    super(componentImplementation.shardImplementation(binding), types);
+    this.binding = checkNotNull(binding);
+    this.request = checkNotNull(request);
+    this.wrappedRequestRepresentation = checkNotNull(wrappedRequestRepresentation);
+    this.shardImplementation = componentImplementation.shardImplementation(binding);
+    this.compilerOptions = compilerOptions;
+    this.types = types;
+  }
+
+  @Override
+  protected CodeBlock methodCall() {
+    return CodeBlock.of("$N()", methodName());
+  }
+
+  @Override
+  protected TypeMirror returnType() {
+    if (request.isRequestKind(RequestKind.INSTANCE)
+        && binding.contributedPrimitiveType().isPresent()) {
+      return toJavac(binding.contributedPrimitiveType().get());
+    }
+
+    TypeMirror requestedType = request.requestedType(binding.contributedType(), types);
+    return types.accessibleType(requestedType, shardImplementation.name());
+  }
+
+  private String methodName() {
+    if (methodName == null) {
+      // Have to set methodName field before implementing the method in order to handle recursion.
+      methodName = shardImplementation.getUniqueMethodName(request);
+
+      // TODO(bcorso): Fix the order that these generated methods are written to the component.
+      shardImplementation.addMethod(
+          PRIVATE_METHOD,
+          methodBuilder(methodName)
+              .addModifiers(PRIVATE)
+              .returns(TypeName.get(returnType()))
+              .addStatement(
+                  "return $L",
+                  wrappedRequestRepresentation
+                      .getDependencyExpression(shardImplementation.name())
+                      .codeBlock())
+              .build());
+    }
+    return methodName;
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    PrivateMethodRequestRepresentation create(
+        BindingRequest request,
+        ContributionBinding binding,
+        RequestRepresentation wrappedRequestRepresentation);
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/ProducerCreationExpression.java b/java/dagger/internal/codegen/writing/ProducerCreationExpression.java
index 0d1ccf3..8e8c271 100644
--- a/java/dagger/internal/codegen/writing/ProducerCreationExpression.java
+++ b/java/dagger/internal/codegen/writing/ProducerCreationExpression.java
@@ -20,7 +20,11 @@
 import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding;
 
 import com.squareup.javapoet.CodeBlock;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
 import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
 import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
 
 /**
@@ -30,13 +34,18 @@
 // TODO(dpb): Resolve with InjectionOrProvisionProviderCreationExpression.
 final class ProducerCreationExpression implements FrameworkInstanceCreationExpression {
 
-  private final ComponentBindingExpressions componentBindingExpressions;
+  private final ShardImplementation shardImplementation;
+  private final ComponentRequestRepresentations componentRequestRepresentations;
   private final ContributionBinding binding;
 
+  @AssistedInject
   ProducerCreationExpression(
-      ContributionBinding binding, ComponentBindingExpressions componentBindingExpressions) {
+      @Assisted ContributionBinding binding,
+      ComponentImplementation componentImplementation,
+      ComponentRequestRepresentations componentRequestRepresentations) {
     this.binding = checkNotNull(binding);
-    this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+    this.shardImplementation = componentImplementation.shardImplementation(binding);
+    this.componentRequestRepresentations = checkNotNull(componentRequestRepresentations);
   }
 
   @Override
@@ -44,6 +53,12 @@
     return CodeBlock.of(
         "$T.create($L)",
         generatedClassNameForBinding(binding),
-        componentBindingExpressions.getCreateMethodArgumentsCodeBlock(binding));
+        componentRequestRepresentations.getCreateMethodArgumentsCodeBlock(
+            binding, shardImplementation.name()));
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    ProducerCreationExpression create(ContributionBinding binding);
   }
 }
diff --git a/java/dagger/internal/codegen/writing/ProducerEntryPointView.java b/java/dagger/internal/codegen/writing/ProducerEntryPointView.java
index c58f4e0..271da51 100644
--- a/java/dagger/internal/codegen/writing/ProducerEntryPointView.java
+++ b/java/dagger/internal/codegen/writing/ProducerEntryPointView.java
@@ -17,18 +17,22 @@
 package dagger.internal.codegen.writing;
 
 import static dagger.internal.codegen.writing.ComponentImplementation.FieldSpecKind.FRAMEWORK_FIELD;
+import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
 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.codegen.binding.ComponentDescriptor.ComponentMethodDescriptor;
 import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.javapoet.TypeNames;
 import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.RequestKind;
+import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
 import dagger.producers.Producer;
 import dagger.producers.internal.CancellationListener;
 import dagger.producers.internal.Producers;
+import dagger.spi.model.RequestKind;
 import java.util.Optional;
 import javax.lang.model.type.TypeMirror;
 
@@ -37,9 +41,11 @@
  * views} of {@link Producer}s.
  */
 final class ProducerEntryPointView {
+  private final ShardImplementation shardImplementation;
   private final DaggerTypes types;
 
-  ProducerEntryPointView(DaggerTypes types) {
+  ProducerEntryPointView(ShardImplementation shardImplementation, DaggerTypes types) {
+    this.shardImplementation = shardImplementation;
     this.types = types;
   }
 
@@ -49,22 +55,20 @@
    * 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,
+   * dagger.internal.codegen.writing.RequestRepresentation#getDependencyExpressionForComponentMethod(ComponentMethodDescriptor,
    * ComponentImplementation)}, and in cases where {@link Optional#empty()} is returned, callers
    * should call {@code super.getDependencyExpressionForComponentMethod()}.
    */
   Optional<Expression> getProducerEntryPointField(
-      BindingExpression producerExpression,
+      RequestRepresentation producerExpression,
       ComponentMethodDescriptor componentMethod,
-      ComponentImplementation component) {
-    if (component.componentDescriptor().isProduction()
+      ClassName requestingClass) {
+    if (shardImplementation.componentDescriptor().isProduction()
         && (componentMethod.dependencyRequest().get().kind().equals(RequestKind.FUTURE)
             || componentMethod.dependencyRequest().get().kind().equals(RequestKind.PRODUCER))) {
+      MemberSelect field = createField(producerExpression, componentMethod);
       return Optional.of(
-          Expression.create(
-              fieldType(componentMethod),
-              "$N",
-              createField(producerExpression, componentMethod, component)));
+          Expression.create(fieldType(componentMethod), field.getExpressionFor(requestingClass)));
     } 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
@@ -75,36 +79,43 @@
     }
   }
 
-  private FieldSpec createField(
-      BindingExpression producerExpression,
-      ComponentMethodDescriptor componentMethod,
-      ComponentImplementation component) {
+  private MemberSelect createField(
+      RequestRepresentation producerExpression, ComponentMethodDescriptor componentMethod) {
     // 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();
+    String methodName = getSimpleName(componentMethod.methodElement());
     FieldSpec field =
         FieldSpec.builder(
                 TypeName.get(fieldType(componentMethod)),
-                component.getUniqueFieldName(methodName + "EntryPoint"),
+                shardImplementation.getUniqueFieldName(methodName + "EntryPoint"),
                 PRIVATE)
             .build();
-    component.addField(FRAMEWORK_FIELD, field);
+    shardImplementation.addField(FRAMEWORK_FIELD, field);
 
     CodeBlock fieldInitialization =
         CodeBlock.of(
-            "this.$N = $T.entryPointViewOf($L, this);",
+            "this.$N = $T.entryPointViewOf($L, $L);",
             field,
             Producers.class,
-            producerExpression.getDependencyExpression(component.name()).codeBlock());
-    component.addInitialization(fieldInitialization);
+            producerExpression.getDependencyExpression(shardImplementation.name()).codeBlock(),
+            // Always pass in the componentShard reference here rather than the owning shard for
+            // this key because this needs to be the root CancellationListener.
+            shardImplementation.isComponentShard()
+                ? "this"
+                : shardImplementation
+                    .getComponentImplementation()
+                    .getComponentShard()
+                    .shardFieldReference());
+    shardImplementation.addInitialization(fieldInitialization);
 
-    return field;
+    return MemberSelect.localField(shardImplementation, field.name);
   }
 
   // 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);
+    return types.wrapType(
+        componentMethod.dependencyRequest().get().key().type().java(), TypeNames.PRODUCER);
   }
 }
diff --git a/java/dagger/internal/codegen/writing/ProducerFactoryGenerator.java b/java/dagger/internal/codegen/writing/ProducerFactoryGenerator.java
index a013be9..1a92ba6 100644
--- a/java/dagger/internal/codegen/writing/ProducerFactoryGenerator.java
+++ b/java/dagger/internal/codegen/writing/ProducerFactoryGenerator.java
@@ -16,6 +16,7 @@
 
 package dagger.internal.codegen.writing;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Verify.verifyNotNull;
 import static com.squareup.javapoet.ClassName.OBJECT;
@@ -40,13 +41,16 @@
 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 java.util.stream.Collectors.toList;
 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 androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XFiler;
+import androidx.room.compiler.processing.XType;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -70,21 +74,18 @@
 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 dagger.spi.model.DependencyRequest;
+import dagger.spi.model.Key;
+import dagger.spi.model.RequestKind;
 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> {
@@ -93,7 +94,7 @@
 
   @Inject
   ProducerFactoryGenerator(
-      Filer filer,
+      XFiler filer,
       DaggerElements elements,
       SourceVersion sourceVersion,
       CompilerOptions compilerOptions,
@@ -103,9 +104,8 @@
     this.keyFactory = keyFactory;
   }
 
-
   @Override
-  public Element originatingElement(ProductionBinding binding) {
+  public XElement originatingElement(ProductionBinding binding) {
     // we only create factories for bindings that have a binding element
     return binding.bindingElement().get();
   }
@@ -116,7 +116,7 @@
     checkArgument(!binding.unresolved().isPresent());
     checkArgument(binding.bindingElement().isPresent());
 
-    TypeName providedTypeName = TypeName.get(binding.contributedType());
+    TypeName providedTypeName = binding.contributedType().getTypeName();
     TypeName futureTypeName = listenableFutureOf(providedTypeName);
 
     ClassName generatedTypeName = generatedClassNameForBinding(binding);
@@ -137,7 +137,7 @@
                     factoryBuilder,
                     constructorBuilder,
                     uniqueFieldNames.getUniqueName("module"),
-                    TypeName.get(binding.bindingTypeElement().get().asType())))
+                    binding.bindingTypeElement().get().getType().getTypeName()))
             : Optional.empty();
 
     List<CodeBlock> frameworkFieldAssignments = new ArrayList<>();
@@ -206,7 +206,7 @@
             .addAnnotation(Override.class)
             .addModifiers(PUBLIC)
             .addParameter(futureTransform.applyArgType(), futureTransform.applyArgName())
-            .addExceptions(getThrownTypeNames(binding.thrownTypes()))
+            .addExceptions(binding.thrownTypes().stream().map(XType::getTypeName).collect(toList()))
             .addCode(
                 getInvocationCodeBlock(
                     binding, providedTypeName, futureTransform.parameterCodeBlocks()));
@@ -295,15 +295,15 @@
                 "$S",
                 String.format(
                     "%s#%s",
-                    ClassName.get(binding.bindingTypeElement().get()),
-                    binding.bindingElement().get().getSimpleName()))
+                    binding.bindingTypeElement().get().getClassName(),
+                    toJavac(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";
+    return dependency.requestElement().get().java().getSimpleName() + "Future";
   }
 
   /** Represents the transformation of an input future by a producer method. */
@@ -405,7 +405,7 @@
 
     @Override
     String applyArgName() {
-      String argName = asyncDependency.requestElement().get().getSimpleName().toString();
+      String argName = asyncDependency.requestElement().get().java().getSimpleName().toString();
       if (argName.equals("module")) {
         return "moduleArg";
       }
@@ -495,7 +495,7 @@
   }
 
   private static TypeName asyncDependencyType(DependencyRequest dependency) {
-    TypeName keyName = TypeName.get(dependency.key().type());
+    TypeName keyName = TypeName.get(dependency.key().type().java());
     switch (dependency.kind()) {
       case INSTANCE:
         return keyName;
@@ -523,8 +523,8 @@
             "$L.$L($L)",
             binding.requiresModuleInstance()
                 ? "module"
-                : CodeBlock.of("$T", ClassName.get(binding.bindingTypeElement().get())),
-            binding.bindingElement().get().getSimpleName(),
+                : CodeBlock.of("$T", binding.bindingTypeElement().get().getClassName()),
+            toJavac(binding.bindingElement().get()).getSimpleName(),
             makeParametersCodeBlock(parameterCodeBlocks));
 
     final CodeBlock returnCodeBlock;
@@ -545,16 +545,6 @@
     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
diff --git a/java/dagger/internal/codegen/writing/ProducerFromProviderCreationExpression.java b/java/dagger/internal/codegen/writing/ProducerFromProviderCreationExpression.java
index 9b0d4e8..d385613 100644
--- a/java/dagger/internal/codegen/writing/ProducerFromProviderCreationExpression.java
+++ b/java/dagger/internal/codegen/writing/ProducerFromProviderCreationExpression.java
@@ -16,43 +16,37 @@
 
 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.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
 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 dagger.spi.model.RequestKind;
 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;
+  private final RequestRepresentation providerRequestRepresentation;
+  private final ClassName requestingClass;
 
+  @AssistedInject
   ProducerFromProviderCreationExpression(
-      ContributionBinding binding,
-      ComponentImplementation componentImplementation,
-      ComponentBindingExpressions componentBindingExpressions) {
-    this.binding = checkNotNull(binding);
-    this.componentImplementation = checkNotNull(componentImplementation);
-    this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
+      @Assisted RequestRepresentation providerRequestRepresentation,
+      @Assisted ClassName requestingClass) {
+    this.providerRequestRepresentation = providerRequestRepresentation;
+    this.requestingClass = requestingClass;
   }
 
   @Override
   public CodeBlock creationExpression() {
     return FrameworkType.PROVIDER.to(
         RequestKind.PRODUCER,
-        componentBindingExpressions
-            .getDependencyExpression(
-                bindingRequest(binding.key(), FrameworkType.PROVIDER),
-                componentImplementation.name())
-            .codeBlock());
+        providerRequestRepresentation.getDependencyExpression(requestingClass).codeBlock());
   }
 
   @Override
@@ -60,5 +54,9 @@
     return Optional.of(TypeNames.PRODUCER);
   }
 
-  // TODO(ronshapiro): should this have a simple factory if the delegate expression is simple?
+  @AssistedFactory
+  static interface Factory {
+    ProducerFromProviderCreationExpression create(
+        RequestRepresentation providerRequestRepresentation, ClassName requestingClass);
+  }
 }
diff --git a/java/dagger/internal/codegen/writing/ProducerNodeInstanceBindingExpression.java b/java/dagger/internal/codegen/writing/ProducerNodeInstanceRequestRepresentation.java
similarity index 61%
rename from java/dagger/internal/codegen/writing/ProducerNodeInstanceBindingExpression.java
rename to java/dagger/internal/codegen/writing/ProducerNodeInstanceRequestRepresentation.java
index 0d3f7e8..dafb052 100644
--- a/java/dagger/internal/codegen/writing/ProducerNodeInstanceBindingExpression.java
+++ b/java/dagger/internal/codegen/writing/ProducerNodeInstanceRequestRepresentation.java
@@ -16,34 +16,39 @@
 
 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.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
 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;
+import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
+import dagger.producers.internal.Producers;
+import dagger.spi.model.Key;
 
 /** Binding expression for producer node instances. */
-final class ProducerNodeInstanceBindingExpression extends FrameworkInstanceBindingExpression {
-  /** The component defining this binding. */
-  private final ComponentImplementation componentImplementation;
+final class ProducerNodeInstanceRequestRepresentation
+    extends FrameworkInstanceRequestRepresentation {
+  private final ShardImplementation shardImplementation;
   private final Key key;
   private final ProducerEntryPointView producerEntryPointView;
 
-  ProducerNodeInstanceBindingExpression(
-      ContributionBinding binding,
-      FrameworkInstanceSupplier frameworkInstanceSupplier,
+  @AssistedInject
+  ProducerNodeInstanceRequestRepresentation(
+      @Assisted ContributionBinding binding,
+      @Assisted FrameworkInstanceSupplier frameworkInstanceSupplier,
       DaggerTypes types,
       DaggerElements elements,
       ComponentImplementation componentImplementation) {
     super(binding, frameworkInstanceSupplier, types, elements);
-    this.componentImplementation = checkNotNull(componentImplementation);
+    this.shardImplementation = componentImplementation.shardImplementation(binding);
     this.key = binding.key();
-    this.producerEntryPointView = new ProducerEntryPointView(types);
+    this.producerEntryPointView = new ProducerEntryPointView(shardImplementation, types);
   }
 
   @Override
@@ -54,7 +59,13 @@
   @Override
   Expression getDependencyExpression(ClassName requestingClass) {
     Expression result = super.getDependencyExpression(requestingClass);
-    componentImplementation.addCancellableProducerKey(key);
+    shardImplementation.addCancellation(
+        key,
+        CodeBlock.of(
+            "$T.cancel($L, $N);",
+            Producers.class,
+            result.codeBlock(),
+            ComponentImplementation.MAY_INTERRUPT_IF_RUNNING_PARAM));
     return result;
   }
 
@@ -62,8 +73,14 @@
   Expression getDependencyExpressionForComponentMethod(
       ComponentMethodDescriptor componentMethod, ComponentImplementation component) {
     return producerEntryPointView
-        .getProducerEntryPointField(this, componentMethod, component)
+        .getProducerEntryPointField(this, componentMethod, component.name())
         .orElseGet(
             () -> super.getDependencyExpressionForComponentMethod(componentMethod, component));
   }
+
+  @AssistedFactory
+  static interface Factory {
+    ProducerNodeInstanceRequestRepresentation create(
+        ContributionBinding binding, FrameworkInstanceSupplier frameworkInstanceSupplier);
+  }
 }
diff --git a/java/dagger/internal/codegen/writing/ProductionBindingRepresentation.java b/java/dagger/internal/codegen/writing/ProductionBindingRepresentation.java
new file mode 100644
index 0000000..f2da690
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ProductionBindingRepresentation.java
@@ -0,0 +1,111 @@
+/*
+ * 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.writing;
+
+import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
+import static dagger.internal.codegen.writing.BindingRepresentations.scope;
+import static dagger.spi.model.BindingKind.MULTIBOUND_MAP;
+import static dagger.spi.model.BindingKind.MULTIBOUND_SET;
+
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import dagger.internal.codegen.binding.BindingRequest;
+import dagger.internal.codegen.binding.FrameworkType;
+import dagger.internal.codegen.binding.ProductionBinding;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * A binding representation that wraps code generation methods that satisfy all kinds of request for
+ * that binding.
+ */
+final class ProductionBindingRepresentation implements BindingRepresentation {
+  private final ProductionBinding binding;
+  private final DerivedFromFrameworkInstanceRequestRepresentation.Factory
+      derivedFromFrameworkInstanceRequestRepresentationFactory;
+  private final RequestRepresentation frameworkInstanceRequestRepresentation;
+  private final Map<BindingRequest, RequestRepresentation> requestRepresentations = new HashMap<>();
+
+  @AssistedInject
+  ProductionBindingRepresentation(
+      @Assisted ProductionBinding binding,
+      ComponentImplementation componentImplementation,
+      DerivedFromFrameworkInstanceRequestRepresentation.Factory
+          derivedFromFrameworkInstanceRequestRepresentationFactory,
+      ProducerNodeInstanceRequestRepresentation.Factory
+          producerNodeInstanceRequestRepresentationFactory,
+      UnscopedFrameworkInstanceCreationExpressionFactory
+          unscopedFrameworkInstanceCreationExpressionFactory,
+      DaggerTypes types) {
+    this.binding = binding;
+    this.derivedFromFrameworkInstanceRequestRepresentationFactory =
+        derivedFromFrameworkInstanceRequestRepresentationFactory;
+    Optional<MemberSelect> staticMethod = staticFactoryCreation();
+    FrameworkInstanceSupplier frameworkInstanceSupplier =
+        staticMethod.isPresent()
+            ? staticMethod::get
+            : new FrameworkFieldInitializer(
+                componentImplementation,
+                binding,
+                binding.scope().isPresent()
+                    ? scope(
+                        binding, unscopedFrameworkInstanceCreationExpressionFactory.create(binding))
+                    : unscopedFrameworkInstanceCreationExpressionFactory.create(binding));
+    this.frameworkInstanceRequestRepresentation =
+        producerNodeInstanceRequestRepresentationFactory.create(binding, frameworkInstanceSupplier);
+  }
+
+  @Override
+  public RequestRepresentation getRequestRepresentation(BindingRequest request) {
+    return reentrantComputeIfAbsent(
+        requestRepresentations, request, this::getRequestRepresentationUncached);
+  }
+
+  private RequestRepresentation getRequestRepresentationUncached(BindingRequest request) {
+    return request.frameworkType().isPresent()
+        ? frameworkInstanceRequestRepresentation
+        : derivedFromFrameworkInstanceRequestRepresentationFactory.create(
+            binding,
+            frameworkInstanceRequestRepresentation,
+            request.requestKind(),
+            FrameworkType.PRODUCER_NODE);
+  }
+  /**
+   * If {@code resolvedBindings} is an unscoped provision binding with no factory arguments, 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.
+   */
+  private Optional<MemberSelect> staticFactoryCreation() {
+    if (binding.dependencies().isEmpty()) {
+      if (binding.kind().equals(MULTIBOUND_MAP)) {
+        return Optional.of(StaticMemberSelects.emptyMapFactory(binding));
+      }
+      if (binding.kind().equals(MULTIBOUND_SET)) {
+        return Optional.of(StaticMemberSelects.emptySetFactory(binding));
+      }
+    }
+    return Optional.empty();
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    ProductionBindingRepresentation create(ProductionBinding binding);
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/ProviderInstanceBindingExpression.java b/java/dagger/internal/codegen/writing/ProviderInstanceRequestRepresentation.java
similarity index 62%
rename from java/dagger/internal/codegen/writing/ProviderInstanceBindingExpression.java
rename to java/dagger/internal/codegen/writing/ProviderInstanceRequestRepresentation.java
index 400c6a2..53c49d1 100644
--- a/java/dagger/internal/codegen/writing/ProviderInstanceBindingExpression.java
+++ b/java/dagger/internal/codegen/writing/ProviderInstanceRequestRepresentation.java
@@ -16,28 +16,34 @@
 
 package dagger.internal.codegen.writing;
 
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
 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 {
+final class ProviderInstanceRequestRepresentation extends FrameworkInstanceRequestRepresentation {
 
-  ProviderInstanceBindingExpression(
-      ContributionBinding binding,
-      FrameworkInstanceSupplier frameworkInstanceSupplier,
+  @AssistedInject
+  ProviderInstanceRequestRepresentation(
+      @Assisted ContributionBinding binding,
+      @Assisted FrameworkInstanceSupplier frameworkInstanceSupplier,
       DaggerTypes types,
       DaggerElements elements) {
-    super(
-        binding,
-        frameworkInstanceSupplier,
-        types,
-        elements);
+    super(binding, frameworkInstanceSupplier, types, elements);
   }
 
   @Override
   protected FrameworkType frameworkType() {
     return FrameworkType.PROVIDER;
   }
+
+  @AssistedFactory
+  static interface Factory {
+    ProviderInstanceRequestRepresentation create(
+        ContributionBinding binding, FrameworkInstanceSupplier frameworkInstanceSupplier);
+  }
 }
diff --git a/java/dagger/internal/codegen/writing/ProviderInstanceSupplier.java b/java/dagger/internal/codegen/writing/ProviderInstanceSupplier.java
new file mode 100644
index 0000000..d95d4e2
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ProviderInstanceSupplier.java
@@ -0,0 +1,58 @@
+/*
+ * 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.writing;
+
+import static dagger.internal.codegen.writing.BindingRepresentations.scope;
+
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+
+/** An object that initializes a framework-type component field for a binding. */
+final class ProviderInstanceSupplier implements FrameworkInstanceSupplier {
+  private final FrameworkInstanceSupplier frameworkInstanceSupplier;
+
+  @AssistedInject
+  ProviderInstanceSupplier(
+      @Assisted ProvisionBinding binding,
+      ComponentImplementation componentImplementation,
+      FrameworkInstanceBindingRepresentation.Factory frameworkInstanceBindingRepresentationFactory,
+      UnscopedFrameworkInstanceCreationExpressionFactory
+          unscopedFrameworkInstanceCreationExpressionFactory) {
+    FrameworkInstanceCreationExpression frameworkInstanceCreationExpression =
+        unscopedFrameworkInstanceCreationExpressionFactory.create(binding);
+    this.frameworkInstanceSupplier =
+        new FrameworkFieldInitializer(
+            componentImplementation,
+            binding,
+            binding.scope().isPresent()
+                ? scope(binding, frameworkInstanceCreationExpression)
+                : frameworkInstanceCreationExpression);
+  }
+
+  @Override
+  public MemberSelect memberSelect() {
+    return frameworkInstanceSupplier.memberSelect();
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    ProviderInstanceSupplier create(ProvisionBinding binding);
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/ProvisionBindingRepresentation.java b/java/dagger/internal/codegen/writing/ProvisionBindingRepresentation.java
new file mode 100644
index 0000000..0085d32
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/ProvisionBindingRepresentation.java
@@ -0,0 +1,145 @@
+/*
+ * 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.writing;
+
+import static dagger.internal.codegen.writing.DelegateRequestRepresentation.isBindsScopeStrongerThanDependencyScope;
+import static dagger.spi.model.BindingKind.DELEGATE;
+
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.BindingRequest;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.compileroption.CompilerOptions;
+import dagger.internal.codegen.langmodel.DaggerTypes;
+import dagger.internal.codegen.writing.ComponentImplementation.CompilerMode;
+import dagger.spi.model.RequestKind;
+
+/**
+ * A binding representation that wraps code generation methods that satisfy all kinds of request for
+ * that binding.
+ */
+final class ProvisionBindingRepresentation implements BindingRepresentation {
+  private final BindingGraph graph;
+  private final CompilerMode compilerMode;
+  private final ProvisionBinding binding;
+  private final DirectInstanceBindingRepresentation directInstanceBindingRepresentation;
+  private final FrameworkInstanceBindingRepresentation frameworkInstanceBindingRepresentation;
+
+  @AssistedInject
+  ProvisionBindingRepresentation(
+      @Assisted ProvisionBinding binding,
+      BindingGraph graph,
+      ComponentImplementation componentImplementation,
+      DirectInstanceBindingRepresentation.Factory directInstanceBindingRepresentationFactory,
+      FrameworkInstanceBindingRepresentation.Factory frameworkInstanceBindingRepresentationFactory,
+      SwitchingProviderInstanceSupplier.Factory switchingProviderInstanceSupplierFactory,
+      ProviderInstanceSupplier.Factory providerInstanceSupplierFactory,
+      StaticFactoryInstanceSupplier.Factory staticFactoryInstanceSupplierFactory,
+      CompilerOptions compilerOptions,
+      DaggerTypes types) {
+    this.binding = binding;
+    this.graph = graph;
+    this.compilerMode = componentImplementation.compilerMode();
+    this.directInstanceBindingRepresentation =
+        directInstanceBindingRepresentationFactory.create(binding);
+    FrameworkInstanceSupplier frameworkInstanceSupplier = null;
+    switch (FrameworkInstanceKind.from(binding, compilerMode)) {
+      case SWITCHING_PROVIDER:
+      case EXPERIMENTAL_SWITCHING_PROVIDER:
+        frameworkInstanceSupplier = switchingProviderInstanceSupplierFactory.create(binding);
+        break;
+      case STATIC_FACTORY:
+        frameworkInstanceSupplier = staticFactoryInstanceSupplierFactory.create(binding);
+        break;
+      case PROVIDER_FIELD:
+        frameworkInstanceSupplier = providerInstanceSupplierFactory.create(binding);
+        break;
+    }
+    this.frameworkInstanceBindingRepresentation =
+        frameworkInstanceBindingRepresentationFactory.create(binding, frameworkInstanceSupplier);
+  }
+
+  @Override
+  public RequestRepresentation getRequestRepresentation(BindingRequest request) {
+    return usesDirectInstanceExpression(request.requestKind())
+        ? directInstanceBindingRepresentation.getRequestRepresentation(request)
+        : frameworkInstanceBindingRepresentation.getRequestRepresentation(request);
+  }
+
+  private boolean usesDirectInstanceExpression(RequestKind requestKind) {
+    if (compilerMode.isExperimentalMergedMode()) {
+      return false;
+    }
+    if (requestKind != RequestKind.INSTANCE && requestKind != RequestKind.FUTURE) {
+      return false;
+    }
+
+    // In fast init mode, we can avoid generating direct instance expressions if a framework
+    // instance expression already exists in the graph. Default mode has more edge cases, so can not
+    // be handled with simple pre-check in the graph. For example, a provider for a subcomponent
+    // builder is backed with its direct instance, returning framework instance for both cases will
+    // form a loop. There are also difficulties introduced by manually created framework requests.
+    // TODO(wanyingd): refactor framework instance so that we don't need to generate both direct
+    // instance and framework instance representation for the same binding.
+    if (compilerMode.isFastInit() && graph.topLevelBindingGraph().hasFrameworkRequest(binding)) {
+      return false;
+    }
+
+    switch (binding.kind()) {
+      case MEMBERS_INJECTOR:
+        // Currently, we always use a framework instance for MembersInjectors, e.g.
+        // InstanceFactory.create(Foo_MembersInjector.create(...)).
+        // TODO(b/199889259): Consider optimizing this for fastInit mode.
+      case ASSISTED_FACTORY:
+        // Assisted factory binding can be requested with framework request, and it is essentially a
+        // provider for assisted injection binding. So we will always return framework instance for
+        // assisted factory bindings.
+        return false;
+      case ASSISTED_INJECTION:
+        throw new IllegalStateException(
+            "Assisted injection binding shouldn't be requested with an instance request.");
+      default:
+        // We don't need to use Provider#get() if there's no caching, so use a direct instance.
+        // TODO(bcorso): This can be optimized in cases where we know a Provider field already
+        // exists, in which case even if it's not scoped we might as well call Provider#get().
+        return !needsCaching(binding, graph);
+    }
+  }
+
+  /**
+   * 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.
+   */
+  static boolean needsCaching(ProvisionBinding binding, BindingGraph graph) {
+    if (!binding.scope().isPresent()) {
+      return false;
+    }
+    if (binding.kind().equals(DELEGATE)) {
+      return isBindsScopeStrongerThanDependencyScope(binding, graph);
+    }
+    return true;
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    ProvisionBindingRepresentation create(ProvisionBinding binding);
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/BindingExpression.java b/java/dagger/internal/codegen/writing/RequestRepresentation.java
similarity index 91%
rename from java/dagger/internal/codegen/writing/BindingExpression.java
rename to java/dagger/internal/codegen/writing/RequestRepresentation.java
index bd45f60..66c31dc 100644
--- a/java/dagger/internal/codegen/writing/BindingExpression.java
+++ b/java/dagger/internal/codegen/writing/RequestRepresentation.java
@@ -23,7 +23,7 @@
 
 /** 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 {
+abstract class RequestRepresentation {
 
   /**
    * Returns an expression that evaluates to the value of a request based on the given requesting
@@ -43,11 +43,6 @@
     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.
    *
diff --git a/java/dagger/internal/codegen/writing/SetFactoryCreationExpression.java b/java/dagger/internal/codegen/writing/SetFactoryCreationExpression.java
index 754f909..624a412 100644
--- a/java/dagger/internal/codegen/writing/SetFactoryCreationExpression.java
+++ b/java/dagger/internal/codegen/writing/SetFactoryCreationExpression.java
@@ -20,27 +20,31 @@
 import static dagger.internal.codegen.binding.SourceFiles.setFactoryClassName;
 
 import com.squareup.javapoet.CodeBlock;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
 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;
+import dagger.internal.codegen.javapoet.TypeNames;
+import dagger.spi.model.DependencyRequest;
 
 /** A factory creation expression for a multibound set. */
 final class SetFactoryCreationExpression extends MultibindingFactoryCreationExpression {
   private final BindingGraph graph;
   private final ContributionBinding binding;
 
+  @AssistedInject
   SetFactoryCreationExpression(
-      ContributionBinding binding,
+      @Assisted ContributionBinding binding,
       ComponentImplementation componentImplementation,
-      ComponentBindingExpressions componentBindingExpressions,
+      ComponentRequestRepresentations componentRequestRepresentations,
       BindingGraph graph) {
-    super(binding, componentImplementation, componentBindingExpressions);
+    super(binding, componentImplementation, componentRequestRepresentations);
     this.binding = checkNotNull(binding);
-    this.graph = checkNotNull(graph);
+    this.graph = graph;
   }
 
   @Override
@@ -50,9 +54,9 @@
       SetType setType = SetType.from(binding.key());
       builder.add(
           "<$T>",
-          setType.elementsAreTypeOf(Produced.class)
-              ? setType.unwrappedElementType(Produced.class)
-              : setType.elementType());
+          setType.elementsAreTypeOf(TypeNames.PRODUCED)
+              ? setType.unwrappedElementType(TypeNames.PRODUCED).getTypeName()
+              : setType.elementType().getTypeName());
     }
 
     int individualProviders = 0;
@@ -89,4 +93,9 @@
 
     return builder.add(".build()").build();
   }
+
+  @AssistedFactory
+  static interface Factory {
+    SetFactoryCreationExpression create(ContributionBinding binding);
+  }
 }
diff --git a/java/dagger/internal/codegen/writing/SetBindingExpression.java b/java/dagger/internal/codegen/writing/SetRequestRepresentation.java
similarity index 66%
rename from java/dagger/internal/codegen/writing/SetBindingExpression.java
rename to java/dagger/internal/codegen/writing/SetRequestRepresentation.java
index 0b2e11a..7fec86a 100644
--- a/java/dagger/internal/codegen/writing/SetBindingExpression.java
+++ b/java/dagger/internal/codegen/writing/SetRequestRepresentation.java
@@ -16,48 +16,58 @@
 
 package dagger.internal.codegen.writing;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 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 androidx.room.compiler.processing.XType;
 import com.google.common.collect.ImmutableSet;
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
 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.CodeBlocks;
 import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.javapoet.TypeNames;
 import dagger.internal.codegen.langmodel.DaggerElements;
 import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.DependencyRequest;
+import dagger.spi.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 {
+final class SetRequestRepresentation extends RequestRepresentation {
   private final ProvisionBinding binding;
   private final BindingGraph graph;
-  private final ComponentBindingExpressions componentBindingExpressions;
+  private final ComponentRequestRepresentations componentRequestRepresentations;
   private final DaggerTypes types;
   private final DaggerElements elements;
+  private final boolean isExperimentalMergedMode;
 
-  SetBindingExpression(
-      ProvisionBinding binding,
+  @AssistedInject
+  SetRequestRepresentation(
+      @Assisted ProvisionBinding binding,
       BindingGraph graph,
-      ComponentBindingExpressions componentBindingExpressions,
+      ComponentImplementation componentImplementation,
+      ComponentRequestRepresentations componentRequestRepresentations,
       DaggerTypes types,
       DaggerElements elements) {
-    super(binding);
     this.binding = binding;
     this.graph = graph;
-    this.componentBindingExpressions = componentBindingExpressions;
+    this.componentRequestRepresentations = componentRequestRepresentations;
     this.types = types;
     this.elements = elements;
+    this.isExperimentalMergedMode =
+        componentImplementation.compilerMode().isExperimentalMergedMode();
   }
 
   @Override
@@ -120,27 +130,48 @@
         }
         instantiation.add(".build()");
         return Expression.create(
-            isImmutableSetAvailable ? immutableSetType() : binding.key().type(),
+            isImmutableSetAvailable ? immutableSetType() : binding.key().type().java(),
             instantiation.build());
     }
   }
 
   private DeclaredType immutableSetType() {
     return types.getDeclaredType(
-        elements.getTypeElement(ImmutableSet.class), SetType.from(binding.key()).elementType());
+        elements.getTypeElement(TypeNames.IMMUTABLE_SET),
+        toJavac(SetType.from(binding.key()).elementType()));
   }
 
   private CodeBlock getContributionExpression(
       DependencyRequest dependency, ClassName requestingClass) {
-    return componentBindingExpressions
-        .getDependencyExpression(bindingRequest(dependency), requestingClass)
-        .codeBlock();
+    RequestRepresentation bindingExpression =
+        componentRequestRepresentations.getRequestRepresentation(bindingRequest(dependency));
+    CodeBlock expression =
+        isExperimentalMergedMode
+            ? componentRequestRepresentations
+                .getExperimentalSwitchingProviderDependencyRepresentation(
+                    bindingRequest(dependency))
+                .getDependencyExpression(dependency.kind(), binding)
+                .codeBlock()
+            : bindingExpression.getDependencyExpression(requestingClass).codeBlock();
+
+    // TODO(b/211774331): Type casting should be Set after contributions to Set multibinding are
+    // limited to be Set.
+    // Add a cast to "(Collection)" when the contribution is a raw "Provider" type because the
+    // "addAll()" method expects a collection. For example, ".addAll((Collection)
+    // provideInaccessibleSetOfFoo.get())"
+    return (!isSingleValue(dependency)
+            && !isTypeAccessibleFrom(binding.key().type().java(), requestingClass.packageName())
+            // TODO(wanyingd): Replace instanceof checks with validation on the binding.
+            && (bindingExpression instanceof DerivedFromFrameworkInstanceRequestRepresentation
+                || bindingExpression instanceof DelegateRequestRepresentation))
+        ? CodeBlocks.cast(expression, TypeNames.COLLECTION)
+        : expression;
   }
 
   private Expression collectionsStaticFactoryInvocation(
       ClassName requestingClass, CodeBlock methodInvocation) {
     return Expression.create(
-        binding.key().type(),
+        binding.key().type().java(),
         CodeBlock.builder()
             .add("$T.", Collections.class)
             .add(maybeTypeParameter(requestingClass))
@@ -149,9 +180,9 @@
   }
 
   private CodeBlock maybeTypeParameter(ClassName requestingClass) {
-    TypeMirror elementType = SetType.from(binding.key()).elementType();
-    return isTypeAccessibleFrom(elementType, requestingClass.packageName())
-        ? CodeBlock.of("<$T>", elementType)
+    XType elementType = SetType.from(binding.key()).elementType();
+    return isTypeAccessibleFrom(toJavac(elementType), requestingClass.packageName())
+        ? CodeBlock.of("<$T>", elementType.getTypeName())
         : CodeBlock.of("");
   }
 
@@ -163,7 +194,7 @@
 
   private boolean isImmutableSetBuilderWithExpectedSizeAvailable() {
     if (isImmutableSetAvailable()) {
-      return methodsIn(elements.getTypeElement(ImmutableSet.class).getEnclosedElements())
+      return methodsIn(elements.getTypeElement(TypeNames.IMMUTABLE_SET).getEnclosedElements())
           .stream()
           .anyMatch(method -> method.getSimpleName().contentEquals("builderWithExpectedSize"));
     }
@@ -171,6 +202,11 @@
   }
 
   private boolean isImmutableSetAvailable() {
-    return elements.getTypeElement(ImmutableSet.class) != null;
+    return elements.getTypeElement(TypeNames.IMMUTABLE_SET) != null;
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    SetRequestRepresentation create(ProvisionBinding binding);
   }
 }
diff --git a/java/dagger/internal/codegen/writing/SimpleInvocationBindingExpression.java b/java/dagger/internal/codegen/writing/SimpleInvocationBindingExpression.java
deleted file mode 100644
index f2062f4..0000000
--- a/java/dagger/internal/codegen/writing/SimpleInvocationBindingExpression.java
+++ /dev/null
@@ -1,35 +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.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/SimpleMethodRequestRepresentation.java
similarity index 64%
rename from java/dagger/internal/codegen/writing/SimpleMethodBindingExpression.java
rename to java/dagger/internal/codegen/writing/SimpleMethodRequestRepresentation.java
index 82e6628..20c6ac6 100644
--- a/java/dagger/internal/codegen/writing/SimpleMethodBindingExpression.java
+++ b/java/dagger/internal/codegen/writing/SimpleMethodRequestRepresentation.java
@@ -16,58 +16,64 @@
 
 package dagger.internal.codegen.writing;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 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.binding.BindingRequest.bindingRequest;
 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 androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
 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.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
 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.ComponentImplementation.ShardImplementation;
 import dagger.internal.codegen.writing.InjectionMethods.ProvisionMethod;
-import dagger.model.DependencyRequest;
+import dagger.spi.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.
+ * {@link dagger.spi.model.RequestKind#INSTANCE} requests.
  */
-final class SimpleMethodBindingExpression extends SimpleInvocationBindingExpression {
+final class SimpleMethodRequestRepresentation extends RequestRepresentation {
   private final CompilerOptions compilerOptions;
   private final ProvisionBinding provisionBinding;
-  private final ComponentBindingExpressions componentBindingExpressions;
+  private final ComponentRequestRepresentations componentRequestRepresentations;
   private final MembersInjectionMethods membersInjectionMethods;
   private final ComponentRequirementExpressions componentRequirementExpressions;
-  private final DaggerElements elements;
   private final SourceVersion sourceVersion;
   private final KotlinMetadataUtil metadataUtil;
+  private final ShardImplementation shardImplementation;
+  private final boolean isExperimentalMergedMode;
 
-  SimpleMethodBindingExpression(
-      ProvisionBinding binding,
-      CompilerOptions compilerOptions,
-      ComponentBindingExpressions componentBindingExpressions,
+  @AssistedInject
+  SimpleMethodRequestRepresentation(
+      @Assisted ProvisionBinding binding,
       MembersInjectionMethods membersInjectionMethods,
+      CompilerOptions compilerOptions,
+      ComponentRequestRepresentations componentRequestRepresentations,
       ComponentRequirementExpressions componentRequirementExpressions,
-      DaggerElements elements,
       SourceVersion sourceVersion,
-      KotlinMetadataUtil metadataUtil) {
-    super(binding);
+      KotlinMetadataUtil metadataUtil,
+      ComponentImplementation componentImplementation,
+      ExperimentalSwitchingProviders switchingProviders) {
     this.compilerOptions = compilerOptions;
     this.provisionBinding = binding;
     this.metadataUtil = metadataUtil;
@@ -75,11 +81,13 @@
         provisionBinding.implicitDependencies().isEmpty(),
         "framework deps are not currently supported");
     checkArgument(provisionBinding.bindingElement().isPresent());
-    this.componentBindingExpressions = componentBindingExpressions;
+    this.componentRequestRepresentations = componentRequestRepresentations;
     this.membersInjectionMethods = membersInjectionMethods;
     this.componentRequirementExpressions = componentRequirementExpressions;
-    this.elements = elements;
     this.sourceVersion = sourceVersion;
+    this.shardImplementation = componentImplementation.shardImplementation(binding);
+    this.isExperimentalMergedMode =
+        componentImplementation.compilerMode().isExperimentalMergedMode();
   }
 
   @Override
@@ -96,8 +104,8 @@
             ProvisionMethod.invokeArguments(
                 provisionBinding,
                 request -> dependencyArgument(request, requestingClass).codeBlock(),
-                requestingClass));
-    ExecutableElement method = asExecutable(provisionBinding.bindingElement().get());
+                shardImplementation::getUniqueFieldNameForAssistedParam));
+    ExecutableElement method = asExecutable(toJavac(provisionBinding.bindingElement().get()));
     CodeBlock invocation;
     switch (method.getKind()) {
       case CONSTRUCTOR:
@@ -111,9 +119,11 @@
         } 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());
+          module =
+              CodeBlock.of(
+                  "$T.INSTANCE", provisionBinding.bindingTypeElement().get().getClassName());
         } else {
-          module = CodeBlock.of("$T", provisionBinding.bindingTypeElement().get());
+          module = CodeBlock.of("$T", provisionBinding.bindingTypeElement().get().getClassName());
         }
         invocation = CodeBlock.of("$L.$L($L)", module, method.getSimpleName(), arguments);
         break;
@@ -125,7 +135,7 @@
   }
 
   private TypeName constructorTypeName(ClassName requestingClass) {
-    DeclaredType type = MoreTypes.asDeclared(provisionBinding.key().type());
+    DeclaredType type = MoreTypes.asDeclared(provisionBinding.key().type().java());
     TypeName typeName = TypeName.get(type);
     if (type.getTypeArguments().stream()
         .allMatch(t -> isTypeAccessibleFrom(t, requestingClass.packageName()))) {
@@ -139,17 +149,24 @@
         ProvisionMethod.invoke(
             provisionBinding,
             request -> dependencyArgument(request, requestingClass).codeBlock(),
+            shardImplementation::getUniqueFieldNameForAssistedParam,
             requestingClass,
             moduleReference(requestingClass),
             compilerOptions,
-            metadataUtil));
+            metadataUtil),
+        requestingClass);
   }
 
   private Expression dependencyArgument(DependencyRequest dependency, ClassName requestingClass) {
-    return componentBindingExpressions.getDependencyArgumentExpression(dependency, requestingClass);
+    return isExperimentalMergedMode
+        ? componentRequestRepresentations
+            .getExperimentalSwitchingProviderDependencyRepresentation(bindingRequest(dependency))
+            .getDependencyExpression(dependency.kind(), provisionBinding)
+        : componentRequestRepresentations.getDependencyArgumentExpression(
+            dependency, requestingClass);
   }
 
-  private Expression injectMembers(CodeBlock instance) {
+  private Expression injectMembers(CodeBlock instance, ClassName requestingClass) {
     if (provisionBinding.injectionSites().isEmpty()) {
       return Expression.create(simpleMethodReturnType(), instance);
     }
@@ -157,30 +174,42 @@
       // 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());
+      if (!MoreTypes.asDeclared(provisionBinding.key().type().java())
+          .getTypeArguments()
+          .isEmpty()) {
+        TypeName keyType = TypeName.get(provisionBinding.key().type().java());
         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));
+    return isExperimentalMergedMode
+        ? membersInjectionMethods.getInjectExpressionExperimental(
+            provisionBinding, instance, requestingClass)
+        : membersInjectionMethods.getInjectExpression(
+            provisionBinding.key(), instance, requestingClass);
   }
 
   private Optional<CodeBlock> moduleReference(ClassName requestingClass) {
     return provisionBinding.requiresModuleInstance()
         ? provisionBinding
             .contributingModule()
-            .map(Element::asType)
+            .map(XTypeElement::getType)
             .map(ComponentRequirement::forModule)
-            .map(module -> componentRequirementExpressions.getExpression(module, requestingClass))
+            .map(
+                module ->
+                    isExperimentalMergedMode
+                        ? CodeBlock.of("(($T) dependencies[0])", module.type().getTypeName())
+                        : componentRequirementExpressions.getExpression(module, requestingClass))
         : Optional.empty();
   }
 
-  private TypeMirror simpleMethodReturnType() {
-    return provisionBinding.contributedPrimitiveType().orElse(provisionBinding.key().type());
+  private XType simpleMethodReturnType() {
+    return provisionBinding
+        .contributedPrimitiveType()
+        .orElse(provisionBinding.key().type().xprocessing());
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    SimpleMethodRequestRepresentation create(ProvisionBinding binding);
   }
 }
diff --git a/java/dagger/internal/codegen/writing/StaticFactoryInstanceSupplier.java b/java/dagger/internal/codegen/writing/StaticFactoryInstanceSupplier.java
new file mode 100644
index 0000000..5537c8f
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/StaticFactoryInstanceSupplier.java
@@ -0,0 +1,65 @@
+/*
+ * 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.writing;
+
+import static dagger.spi.model.BindingKind.MULTIBOUND_MAP;
+import static dagger.spi.model.BindingKind.MULTIBOUND_SET;
+
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import dagger.internal.codegen.binding.ContributionBinding;
+
+/** An object that returns static factory to satisfy framework instance request. */
+final class StaticFactoryInstanceSupplier implements FrameworkInstanceSupplier {
+  private final FrameworkInstanceSupplier frameworkInstanceSupplier;
+
+  @AssistedInject
+  StaticFactoryInstanceSupplier(
+      @Assisted ContributionBinding binding,
+      FrameworkInstanceBindingRepresentation.Factory
+          frameworkInstanceBindingRepresentationFactory) {
+    this.frameworkInstanceSupplier = () -> staticFactoryCreation(binding);
+  }
+
+  @Override
+  public MemberSelect memberSelect() {
+    return frameworkInstanceSupplier.memberSelect();
+  }
+
+  // TODO(wanyingd): no-op members injector is currently handled in
+  // `MembersInjectorProviderCreationExpression`, we should inline the logic here so we won't create
+  // an extra field for it.
+  private MemberSelect staticFactoryCreation(ContributionBinding binding) {
+    switch (binding.kind()) {
+      case MULTIBOUND_MAP:
+        return StaticMemberSelects.emptyMapFactory(binding);
+      case MULTIBOUND_SET:
+        return StaticMemberSelects.emptySetFactory(binding);
+      case PROVISION:
+      case INJECTION:
+        return StaticMemberSelects.factoryCreateNoArgumentMethod(binding);
+      default:
+        throw new AssertionError(String.format("Invalid binding kind: %s", binding.kind()));
+    }
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    StaticFactoryInstanceSupplier create(ContributionBinding binding);
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/StaticMemberSelects.java b/java/dagger/internal/codegen/writing/StaticMemberSelects.java
new file mode 100644
index 0000000..e547ab5
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/StaticMemberSelects.java
@@ -0,0 +1,155 @@
+/*
+ * 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.writing;
+
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+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.Binding;
+import dagger.internal.codegen.binding.BindingType;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.javapoet.CodeBlocks;
+import java.util.List;
+import javax.lang.model.type.TypeMirror;
+
+/** Helper class for static member select creation. */
+final class StaticMemberSelects {
+  /** A {@link MemberSelect} for a factory of an empty map. */
+  static MemberSelect emptyMapFactory(Binding binding) {
+    BindingType bindingType = binding.bindingType();
+    ImmutableList<TypeMirror> typeParameters =
+        ImmutableList.copyOf(MoreTypes.asDeclared(binding.key().type().java()).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.
+   */
+  static MemberSelect emptySetFactory(ContributionBinding binding) {
+    return new ParameterizedStaticMethod(
+        setFactoryClassName(binding),
+        ImmutableList.of(toJavac(SetType.from(binding.key()).elementType())),
+        CodeBlock.of("empty()"),
+        FACTORY);
+  }
+
+  /**
+   * Returns a {@link MemberSelect} for the instance of a {@code create()} method on a factory with
+   * no arguments.
+   */
+  static MemberSelect factoryCreateNoArgumentMethod(Binding binding) {
+    checkArgument(
+        binding.bindingType().equals(BindingType.PROVISION),
+        "Invalid binding type: %s",
+        binding.bindingType());
+    checkArgument(
+        binding.dependencies().isEmpty() && !binding.scope().isPresent(),
+        "%s should have no dependencies and be unscoped to create a no argument factory.",
+        binding);
+
+    ClassName factoryName = generatedClassNameForBinding(binding);
+    TypeMirror keyType = binding.key().type().java();
+    if (keyType.getKind().equals(DECLARED)) {
+      ImmutableList<TypeVariableName> typeVariables = bindingTypeElementTypeVariableNames(binding);
+      if (!typeVariables.isEmpty()) {
+        List<? extends TypeMirror> typeArguments = MoreTypes.asDeclared(keyType).getTypeArguments();
+        return new ParameterizedStaticMethod(
+            factoryName, ImmutableList.copyOf(typeArguments), CodeBlock.of("create()"), FACTORY);
+      }
+    }
+    return new StaticMethod(factoryName, CodeBlock.of("create()"));
+  }
+
+  private static final class StaticMethod extends MemberSelect {
+    private 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);
+    }
+  }
+
+  private static final class ParameterizedStaticMethod extends MemberSelect {
+    private final ImmutableList<TypeMirror> typeParameters;
+    private final CodeBlock methodCodeBlock;
+    private 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 StaticMemberSelects() {}
+}
diff --git a/java/dagger/internal/codegen/writing/SubcomponentCreatorBindingExpression.java b/java/dagger/internal/codegen/writing/SubcomponentCreatorBindingExpression.java
deleted file mode 100644
index 3099048..0000000
--- a/java/dagger/internal/codegen/writing/SubcomponentCreatorBindingExpression.java
+++ /dev/null
@@ -1,40 +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.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/SubcomponentCreatorRequestRepresentation.java b/java/dagger/internal/codegen/writing/SubcomponentCreatorRequestRepresentation.java
new file mode 100644
index 0000000..74ebbad
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/SubcomponentCreatorRequestRepresentation.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.writing;
+
+import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock;
+
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.javapoet.Expression;
+import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
+import java.util.ArrayList;
+import java.util.List;
+
+/** A binding expression for a subcomponent creator that just invokes the constructor. */
+final class SubcomponentCreatorRequestRepresentation extends RequestRepresentation {
+  private final ShardImplementation shardImplementation;
+  private final ContributionBinding binding;
+  private final boolean isExperimentalMergedMode;
+
+  @AssistedInject
+  SubcomponentCreatorRequestRepresentation(
+      @Assisted ContributionBinding binding, ComponentImplementation componentImplementation) {
+    this.binding = binding;
+    this.shardImplementation = componentImplementation.shardImplementation(binding);
+    this.isExperimentalMergedMode =
+        componentImplementation.compilerMode().isExperimentalMergedMode();
+  }
+
+  @Override
+  Expression getDependencyExpression(ClassName requestingClass) {
+    return Expression.create(
+        binding.key().type().java(),
+        "new $T($L)",
+        shardImplementation.getSubcomponentCreatorSimpleName(binding.key()),
+        isExperimentalMergedMode
+            ? getDependenciesExperimental()
+            : shardImplementation.componentFieldsByImplementation().values().stream()
+                .map(field -> CodeBlock.of("$N", field))
+                .collect(toParametersCodeBlock()));
+  }
+
+  private CodeBlock getDependenciesExperimental() {
+    List<CodeBlock> expressions = new ArrayList<>();
+    int index = 0;
+    for (FieldSpec field : shardImplementation.componentFieldsByImplementation().values()) {
+      expressions.add(CodeBlock.of("($T) dependencies[$L]", field.type, index++));
+    }
+    return expressions.stream().collect(toParametersCodeBlock());
+  }
+
+  CodeBlock getDependencyExpressionArguments() {
+    return shardImplementation.componentFieldsByImplementation().values().stream()
+        .map(field -> CodeBlock.of("$N", field))
+        .collect(toParametersCodeBlock());
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    SubcomponentCreatorRequestRepresentation create(ContributionBinding binding);
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/SubcomponentNames.java b/java/dagger/internal/codegen/writing/SubcomponentNames.java
deleted file mode 100644
index fa0037b..0000000
--- a/java/dagger/internal/codegen/writing/SubcomponentNames.java
+++ /dev/null
@@ -1,156 +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.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/SwitchingProviderInstanceSupplier.java b/java/dagger/internal/codegen/writing/SwitchingProviderInstanceSupplier.java
new file mode 100644
index 0000000..ef6bd49
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/SwitchingProviderInstanceSupplier.java
@@ -0,0 +1,84 @@
+/*
+ * 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.writing;
+
+import static dagger.internal.codegen.javapoet.TypeNames.DOUBLE_CHECK;
+import static dagger.internal.codegen.javapoet.TypeNames.SINGLE_CHECK;
+
+import com.squareup.javapoet.CodeBlock;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import dagger.internal.codegen.binding.Binding;
+import dagger.internal.codegen.binding.BindingGraph;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import dagger.spi.model.BindingKind;
+
+/**
+ * An object that initializes a framework-type component field for a binding using instances created
+ * by switching providers.
+ */
+final class SwitchingProviderInstanceSupplier implements FrameworkInstanceSupplier {
+  private final FrameworkInstanceSupplier frameworkInstanceSupplier;
+
+  @AssistedInject
+  SwitchingProviderInstanceSupplier(
+      @Assisted ProvisionBinding binding,
+      SwitchingProviders switchingProviders,
+      ExperimentalSwitchingProviders experimentalSwitchingProviders,
+      BindingGraph graph,
+      ComponentImplementation componentImplementation,
+      UnscopedDirectInstanceRequestRepresentationFactory
+          unscopedDirectInstanceRequestRepresentationFactory) {
+    FrameworkInstanceCreationExpression frameworkInstanceCreationExpression =
+        componentImplementation.compilerMode().isExperimentalMergedMode()
+            ? experimentalSwitchingProviders.newFrameworkInstanceCreationExpression(
+                binding, unscopedDirectInstanceRequestRepresentationFactory.create(binding))
+            : switchingProviders.newFrameworkInstanceCreationExpression(
+                binding, unscopedDirectInstanceRequestRepresentationFactory.create(binding));
+    this.frameworkInstanceSupplier =
+        new FrameworkFieldInitializer(
+            componentImplementation, binding, scope(binding, frameworkInstanceCreationExpression));
+  }
+
+  @Override
+  public MemberSelect memberSelect() {
+    return frameworkInstanceSupplier.memberSelect();
+  }
+
+  private FrameworkInstanceCreationExpression scope(
+      Binding binding, FrameworkInstanceCreationExpression unscoped) {
+    // Caching assisted factory provider, so that there won't be new factory created for each
+    // provider.get() call.
+    if (!binding.scope().isPresent() && !binding.kind().equals(BindingKind.ASSISTED_FACTORY)) {
+      return unscoped;
+    }
+    return () ->
+        CodeBlock.of(
+            "$T.provider($L)",
+            binding.scope().isPresent()
+                ? (binding.scope().get().isReusable() ? SINGLE_CHECK : DOUBLE_CHECK)
+                : SINGLE_CHECK,
+            unscoped.creationExpression());
+  }
+
+  @AssistedFactory
+  static interface Factory {
+    SwitchingProviderInstanceSupplier create(ProvisionBinding binding);
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/SwitchingProviders.java b/java/dagger/internal/codegen/writing/SwitchingProviders.java
index d38b9d9..b922b17 100644
--- a/java/dagger/internal/codegen/writing/SwitchingProviders.java
+++ b/java/dagger/internal/codegen/writing/SwitchingProviders.java
@@ -16,6 +16,7 @@
 
 package dagger.internal.codegen.writing;
 
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.collect.Iterables.getLast;
 import static com.google.common.collect.Iterables.getOnlyElement;
@@ -25,57 +26,41 @@
 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.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.Lists;
 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 com.squareup.javapoet.TypeVariableName;
 import dagger.internal.codegen.base.UniqueNameSet;
+import dagger.internal.codegen.binding.ContributionBinding;
 import dagger.internal.codegen.javapoet.CodeBlocks;
-import dagger.internal.codegen.javapoet.Expression;
 import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.Key;
+import dagger.internal.codegen.writing.ComponentImplementation.ShardImplementation;
+import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import dagger.spi.model.BindingKind;
+import dagger.spi.model.Key;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.TreeMap;
+import javax.inject.Inject;
 
 /**
  * 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.
+ * <p>The provider expression request will be satisfied by a single generated {@code Provider} 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);
-  }
-
+@PerComponentImplementation
+final class SwitchingProviders {
   /**
    * 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
@@ -96,35 +81,36 @@
   private final Map<Key, SwitchingProviderBuilder> switchingProviderBuilders =
       new LinkedHashMap<>();
 
-  private final ComponentImplementation componentImplementation;
-  private final ClassName owningComponent;
+  private final ShardImplementation shardImplementation;
   private final DaggerTypes types;
   private final UniqueNameSet switchingProviderNames = new UniqueNameSet();
 
+  @Inject
   SwitchingProviders(ComponentImplementation componentImplementation, DaggerTypes types) {
-    this.componentImplementation = checkNotNull(componentImplementation);
+    // Currently, the SwitchingProviders types are only added to the componentShard.
+    this.shardImplementation = checkNotNull(componentImplementation).getComponentShard();
     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);
+  /** Returns the framework instance creation expression for an inner switching provider class. */
+  FrameworkInstanceCreationExpression newFrameworkInstanceCreationExpression(
+      ContributionBinding binding, RequestRepresentation unscopedInstanceRequestRepresentation) {
+    return new FrameworkInstanceCreationExpression() {
+      @Override
+      public CodeBlock creationExpression() {
+        return switchingProviderBuilders
+            .computeIfAbsent(binding.key(), key -> getSwitchingProviderBuilder())
+            .getNewInstanceCodeBlock(binding, unscopedInstanceRequestRepresentation);
+      }
+    };
   }
 
   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);
+          new SwitchingProviderBuilder(shardImplementation.name().nestedClass(name));
+      shardImplementation.addTypeSupplier(switchingProviderBuilder::build);
       return switchingProviderBuilder;
     }
     return getLast(switchingProviderBuilders.values());
@@ -142,33 +128,72 @@
       this.switchingProviderType = checkNotNull(switchingProviderType);
     }
 
-    Expression getProviderExpression(SwitchCase switchCase) {
-      Key key = switchCase.key();
+    private CodeBlock getNewInstanceCodeBlock(
+        ContributionBinding binding, RequestRepresentation unscopedInstanceRequestRepresentation) {
+      Key key = binding.key();
       if (!switchIds.containsKey(key)) {
         int switchId = switchIds.size();
         switchIds.put(key, switchId);
-        switchCases.put(switchId, createSwitchCaseCodeBlock(switchCase));
+        switchCases.put(
+            switchId, createSwitchCaseCodeBlock(key, unscopedInstanceRequestRepresentation));
       }
-      return switchCase.getProviderExpression(switchingProviderType, switchIds.get(key));
+      return CodeBlock.of(
+          "new $T<$L>($L, $L)",
+          switchingProviderType,
+          // Add the type parameter explicitly when the binding is scoped because Java can't resolve
+          // the type when wrapped. For example, the following will error:
+          //   fooProvider = DoubleCheck.provider(new SwitchingProvider<>(1));
+          (binding.scope().isPresent() || binding.kind().equals(BindingKind.ASSISTED_FACTORY))
+              ? CodeBlock.of(
+                  "$T", shardImplementation.accessibleType(toJavac(binding.contributedType())))
+              : "",
+          shardImplementation.componentFieldsByImplementation().values().stream()
+              .map(field -> CodeBlock.of("$N", field))
+              .collect(CodeBlocks.toParametersCodeBlock()),
+          switchIds.get(key));
     }
 
-    private CodeBlock createSwitchCaseCodeBlock(SwitchCase switchCase) {
+    private CodeBlock createSwitchCaseCodeBlock(
+        Key key, RequestRepresentation unscopedInstanceRequestRepresentation) {
+      // TODO(bcorso): Try to delay calling getDependencyExpression() until we are writing out the
+      // SwitchingProvider because calling it here makes FrameworkFieldInitializer think there's a
+      // cycle when initializing SwitchingProviders which adds an uncessary DelegateFactory.
       CodeBlock instanceCodeBlock =
-          switchCase.getReturnExpression(switchingProviderType).box(types).codeBlock();
+          unscopedInstanceRequestRepresentation
+              .getDependencyExpression(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())
+          .add("case $L: // $L \n", switchIds.get(key), key)
           .addStatement("return ($T) $L", T, instanceCodeBlock)
           .build();
     }
 
     private TypeSpec build() {
-      return createSwitchingProviderType(
+      TypeSpec.Builder builder =
           classBuilder(switchingProviderType)
+              .addModifiers(PRIVATE, FINAL, STATIC)
               .addTypeVariable(T)
               .addSuperinterface(providerOf(T))
-              .addMethods(getMethods()));
+              .addMethods(getMethods());
+
+      // The SwitchingProvider constructor lists all component parameters first and switch id last.
+      MethodSpec.Builder constructor = MethodSpec.constructorBuilder();
+      shardImplementation
+          .componentFieldsByImplementation()
+          .values()
+          .forEach(
+              field -> {
+                builder.addField(field);
+                constructor.addParameter(field.type, field.name);
+                constructor.addStatement("this.$1N = $1N", field);
+              });
+      builder.addField(TypeName.INT, "id", PRIVATE, FINAL);
+      constructor.addParameter(TypeName.INT, "id").addStatement("this.id = id");
+
+      return builder.addMethod(constructor.build()).build();
     }
 
     private ImmutableList<MethodSpec> getMethods() {
diff --git a/java/dagger/internal/codegen/writing/UnscopedDirectInstanceRequestRepresentationFactory.java b/java/dagger/internal/codegen/writing/UnscopedDirectInstanceRequestRepresentationFactory.java
new file mode 100644
index 0000000..28c2370
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/UnscopedDirectInstanceRequestRepresentationFactory.java
@@ -0,0 +1,131 @@
+/*
+ * 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.writing;
+
+import dagger.internal.codegen.binding.ComponentRequirement;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.spi.model.RequestKind;
+import javax.inject.Inject;
+
+/**
+ * A factory for creating a binding expression for an unscoped instance.
+ *
+ * <p>Note that these binding expressions are for getting "direct" instances -- i.e. instances that
+ * are created via constructors or modules (e.g. {@code new Foo()} or {@code
+ * FooModule.provideFoo()}) as opposed to an instance created from calling a getter on a framework
+ * type (e.g. {@code fooProvider.get()}). See {@link FrameworkInstanceRequestRepresentation} for
+ * binding expressions that are created from framework types.
+ */
+final class UnscopedDirectInstanceRequestRepresentationFactory {
+  private final AssistedFactoryRequestRepresentation.Factory
+      assistedFactoryRequestRepresentationFactory;
+  private final ComponentInstanceRequestRepresentation.Factory
+      componentInstanceRequestRepresentationFactory;
+  private final ComponentProvisionRequestRepresentation.Factory
+      componentProvisionRequestRepresentationFactory;
+  private final ComponentRequirementRequestRepresentation.Factory
+      componentRequirementRequestRepresentationFactory;
+  private final DelegateRequestRepresentation.Factory delegateRequestRepresentationFactory;
+  private final MapRequestRepresentation.Factory mapRequestRepresentationFactory;
+  private final OptionalRequestRepresentation.Factory optionalRequestRepresentationFactory;
+  private final SetRequestRepresentation.Factory setRequestRepresentationFactory;
+  private final SimpleMethodRequestRepresentation.Factory simpleMethodRequestRepresentationFactory;
+  private final SubcomponentCreatorRequestRepresentation.Factory
+      subcomponentCreatorRequestRepresentationFactory;
+
+  @Inject
+  UnscopedDirectInstanceRequestRepresentationFactory(
+      ComponentImplementation componentImplementation,
+      AssistedFactoryRequestRepresentation.Factory assistedFactoryRequestRepresentationFactory,
+      ComponentInstanceRequestRepresentation.Factory componentInstanceRequestRepresentationFactory,
+      ComponentProvisionRequestRepresentation.Factory
+          componentProvisionRequestRepresentationFactory,
+      ComponentRequirementRequestRepresentation.Factory
+          componentRequirementRequestRepresentationFactory,
+      DelegateRequestRepresentation.Factory delegateRequestRepresentationFactory,
+      MapRequestRepresentation.Factory mapRequestRepresentationFactory,
+      OptionalRequestRepresentation.Factory optionalRequestRepresentationFactory,
+      SetRequestRepresentation.Factory setRequestRepresentationFactory,
+      SimpleMethodRequestRepresentation.Factory simpleMethodRequestRepresentationFactory,
+      SubcomponentCreatorRequestRepresentation.Factory
+          subcomponentCreatorRequestRepresentationFactory) {
+    this.assistedFactoryRequestRepresentationFactory = assistedFactoryRequestRepresentationFactory;
+    this.componentInstanceRequestRepresentationFactory =
+        componentInstanceRequestRepresentationFactory;
+    this.componentProvisionRequestRepresentationFactory =
+        componentProvisionRequestRepresentationFactory;
+    this.componentRequirementRequestRepresentationFactory =
+        componentRequirementRequestRepresentationFactory;
+    this.delegateRequestRepresentationFactory = delegateRequestRepresentationFactory;
+    this.mapRequestRepresentationFactory = mapRequestRepresentationFactory;
+    this.optionalRequestRepresentationFactory = optionalRequestRepresentationFactory;
+    this.setRequestRepresentationFactory = setRequestRepresentationFactory;
+    this.simpleMethodRequestRepresentationFactory = simpleMethodRequestRepresentationFactory;
+    this.subcomponentCreatorRequestRepresentationFactory =
+        subcomponentCreatorRequestRepresentationFactory;
+  }
+
+  /** Returns a direct, unscoped binding expression for a {@link RequestKind#INSTANCE} request. */
+  RequestRepresentation create(ContributionBinding binding) {
+    switch (binding.kind()) {
+      case DELEGATE:
+        return delegateRequestRepresentationFactory.create(binding, RequestKind.INSTANCE);
+
+      case COMPONENT:
+        return componentInstanceRequestRepresentationFactory.create(binding);
+
+      case COMPONENT_DEPENDENCY:
+        return componentRequirementRequestRepresentationFactory.create(
+            binding, ComponentRequirement.forDependency(binding.key().type().xprocessing()));
+
+      case COMPONENT_PROVISION:
+        return componentProvisionRequestRepresentationFactory.create((ProvisionBinding) binding);
+
+      case SUBCOMPONENT_CREATOR:
+        return subcomponentCreatorRequestRepresentationFactory.create(binding);
+
+      case MULTIBOUND_SET:
+        return setRequestRepresentationFactory.create((ProvisionBinding) binding);
+
+      case MULTIBOUND_MAP:
+        return mapRequestRepresentationFactory.create((ProvisionBinding) binding);
+
+      case OPTIONAL:
+        return optionalRequestRepresentationFactory.create((ProvisionBinding) binding);
+
+      case BOUND_INSTANCE:
+        return componentRequirementRequestRepresentationFactory.create(
+            binding, ComponentRequirement.forBoundInstance(binding));
+
+      case ASSISTED_FACTORY:
+        return assistedFactoryRequestRepresentationFactory.create((ProvisionBinding) binding);
+
+      case INJECTION:
+      case PROVISION:
+        return simpleMethodRequestRepresentationFactory.create((ProvisionBinding) binding);
+
+      case ASSISTED_INJECTION:
+      case MEMBERS_INJECTOR:
+      case MEMBERS_INJECTION:
+      case COMPONENT_PRODUCTION:
+      case PRODUCTION:
+        // Fall through
+    }
+    throw new AssertionError("Unexpected binding kind: " + binding.kind());
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/UnscopedFrameworkInstanceCreationExpressionFactory.java b/java/dagger/internal/codegen/writing/UnscopedFrameworkInstanceCreationExpressionFactory.java
new file mode 100644
index 0000000..d77b146
--- /dev/null
+++ b/java/dagger/internal/codegen/writing/UnscopedFrameworkInstanceCreationExpressionFactory.java
@@ -0,0 +1,164 @@
+/*
+ * 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.writing;
+
+import com.squareup.javapoet.CodeBlock;
+import dagger.internal.codegen.binding.ComponentRequirement;
+import dagger.internal.codegen.binding.ContributionBinding;
+import dagger.internal.codegen.binding.ProvisionBinding;
+import dagger.internal.codegen.writing.FrameworkFieldInitializer.FrameworkInstanceCreationExpression;
+import javax.inject.Inject;
+
+/**
+ * A factory for creating unscoped creation expressions for a provision or production binding.
+ *
+ * <p>A creation expression is responsible for creating the factory for a given binding (e.g. by
+ * calling the generated factory create method, {@code Foo_Factory.create(...)}). Note that this
+ * class does not handle scoping of these factories (e.g. wrapping in {@code
+ * DoubleCheck.provider()}).
+ */
+final class UnscopedFrameworkInstanceCreationExpressionFactory {
+  private final ComponentImplementation componentImplementation;
+  private final ComponentRequirementExpressions componentRequirementExpressions;
+  private final AnonymousProviderCreationExpression.Factory
+      anonymousProviderCreationExpressionFactory;
+  private final DelegatingFrameworkInstanceCreationExpression.Factory
+      delegatingFrameworkInstanceCreationExpressionFactory;
+  private final DependencyMethodProducerCreationExpression.Factory
+      dependencyMethodProducerCreationExpressionFactory;
+  private final DependencyMethodProviderCreationExpression.Factory
+      dependencyMethodProviderCreationExpressionFactory;
+  private final InjectionOrProvisionProviderCreationExpression.Factory
+      injectionOrProvisionProviderCreationExpressionFactory;
+  private final MapFactoryCreationExpression.Factory mapFactoryCreationExpressionFactory;
+  private final MembersInjectorProviderCreationExpression.Factory
+      membersInjectorProviderCreationExpressionFactory;
+  private final OptionalFactoryInstanceCreationExpression.Factory
+      optionalFactoryInstanceCreationExpressionFactory;
+  private final ProducerCreationExpression.Factory producerCreationExpressionFactory;
+  private final SetFactoryCreationExpression.Factory setFactoryCreationExpressionFactory;
+
+  @Inject
+  UnscopedFrameworkInstanceCreationExpressionFactory(
+      ComponentImplementation componentImplementation,
+      ComponentRequirementExpressions componentRequirementExpressions,
+      AnonymousProviderCreationExpression.Factory anonymousProviderCreationExpressionFactory,
+      DelegatingFrameworkInstanceCreationExpression.Factory
+          delegatingFrameworkInstanceCreationExpressionFactory,
+      DependencyMethodProducerCreationExpression.Factory
+          dependencyMethodProducerCreationExpressionFactory,
+      DependencyMethodProviderCreationExpression.Factory
+          dependencyMethodProviderCreationExpressionFactory,
+      InjectionOrProvisionProviderCreationExpression.Factory
+          injectionOrProvisionProviderCreationExpressionFactory,
+      MapFactoryCreationExpression.Factory mapFactoryCreationExpressionFactory,
+      MembersInjectorProviderCreationExpression.Factory
+          membersInjectorProviderCreationExpressionFactory,
+      OptionalFactoryInstanceCreationExpression.Factory
+          optionalFactoryInstanceCreationExpressionFactory,
+      ProducerCreationExpression.Factory producerCreationExpressionFactory,
+      SetFactoryCreationExpression.Factory setFactoryCreationExpressionFactory) {
+    this.componentImplementation = componentImplementation;
+    this.componentRequirementExpressions = componentRequirementExpressions;
+    this.anonymousProviderCreationExpressionFactory = anonymousProviderCreationExpressionFactory;
+    this.delegatingFrameworkInstanceCreationExpressionFactory =
+        delegatingFrameworkInstanceCreationExpressionFactory;
+    this.dependencyMethodProducerCreationExpressionFactory =
+        dependencyMethodProducerCreationExpressionFactory;
+    this.dependencyMethodProviderCreationExpressionFactory =
+        dependencyMethodProviderCreationExpressionFactory;
+    this.injectionOrProvisionProviderCreationExpressionFactory =
+        injectionOrProvisionProviderCreationExpressionFactory;
+    this.mapFactoryCreationExpressionFactory = mapFactoryCreationExpressionFactory;
+    this.membersInjectorProviderCreationExpressionFactory =
+        membersInjectorProviderCreationExpressionFactory;
+    this.optionalFactoryInstanceCreationExpressionFactory =
+        optionalFactoryInstanceCreationExpressionFactory;
+    this.producerCreationExpressionFactory = producerCreationExpressionFactory;
+    this.setFactoryCreationExpressionFactory = setFactoryCreationExpressionFactory;
+  }
+
+  /**
+   * Returns an unscoped creation expression for a {@link javax.inject.Provider} for provision
+   * bindings or a {@link dagger.producers.Producer} for production bindings.
+   */
+  FrameworkInstanceCreationExpression create(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) $L",
+                    binding.key().type().java(),
+                    componentImplementation.componentFieldReference()));
+
+      case BOUND_INSTANCE:
+        return instanceFactoryCreationExpression(
+            binding, ComponentRequirement.forBoundInstance(binding));
+
+      case COMPONENT_DEPENDENCY:
+        return instanceFactoryCreationExpression(
+            binding, ComponentRequirement.forDependency(binding.key().type().xprocessing()));
+
+      case COMPONENT_PROVISION:
+        return dependencyMethodProviderCreationExpressionFactory.create((ProvisionBinding) binding);
+
+      case SUBCOMPONENT_CREATOR:
+        return anonymousProviderCreationExpressionFactory.create(binding);
+
+      case ASSISTED_FACTORY:
+      case ASSISTED_INJECTION:
+      case INJECTION:
+      case PROVISION:
+        return injectionOrProvisionProviderCreationExpressionFactory.create(binding);
+
+      case COMPONENT_PRODUCTION:
+        return dependencyMethodProducerCreationExpressionFactory.create(binding);
+
+      case PRODUCTION:
+        return producerCreationExpressionFactory.create(binding);
+
+      case MULTIBOUND_SET:
+        return setFactoryCreationExpressionFactory.create(binding);
+
+      case MULTIBOUND_MAP:
+        return mapFactoryCreationExpressionFactory.create(binding);
+
+      case DELEGATE:
+        return delegatingFrameworkInstanceCreationExpressionFactory.create(binding);
+
+      case OPTIONAL:
+        return optionalFactoryInstanceCreationExpressionFactory.create(binding);
+
+      case MEMBERS_INJECTOR:
+        return membersInjectorProviderCreationExpressionFactory.create((ProvisionBinding) binding);
+
+      default:
+        throw new AssertionError(binding);
+    }
+  }
+
+  private InstanceFactoryCreationExpression instanceFactoryCreationExpression(
+      ContributionBinding binding, ComponentRequirement componentRequirement) {
+    return new InstanceFactoryCreationExpression(
+        binding.nullableType().isPresent(),
+        () ->
+            componentRequirementExpressions.getExpressionDuringInitialization(
+                componentRequirement, componentImplementation.name()));
+  }
+}
diff --git a/java/dagger/internal/codegen/writing/UnwrappedMapKeyGenerator.java b/java/dagger/internal/codegen/writing/UnwrappedMapKeyGenerator.java
index f07b882..094f07a 100644
--- a/java/dagger/internal/codegen/writing/UnwrappedMapKeyGenerator.java
+++ b/java/dagger/internal/codegen/writing/UnwrappedMapKeyGenerator.java
@@ -16,13 +16,13 @@
 
 package dagger.internal.codegen.writing;
 
+import androidx.room.compiler.processing.XFiler;
+import androidx.room.compiler.processing.XTypeElement;
 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
@@ -56,13 +56,13 @@
 public final class UnwrappedMapKeyGenerator extends AnnotationCreatorGenerator {
 
   @Inject
-  UnwrappedMapKeyGenerator(Filer filer, DaggerElements elements, SourceVersion sourceVersion) {
+  UnwrappedMapKeyGenerator(XFiler filer, DaggerElements elements, SourceVersion sourceVersion) {
     super(filer, elements, sourceVersion);
   }
 
   @Override
-  protected Set<TypeElement> annotationsToCreate(TypeElement annotationElement) {
-    Set<TypeElement> nestedAnnotationElements = super.annotationsToCreate(annotationElement);
+  protected Set<XTypeElement> annotationsToCreate(XTypeElement annotationElement) {
+    Set<XTypeElement> nestedAnnotationElements = super.annotationsToCreate(annotationElement);
     nestedAnnotationElements.remove(annotationElement);
     return nestedAnnotationElements;
   }
diff --git a/java/dagger/internal/codegen/xprocessing/BUILD b/java/dagger/internal/codegen/xprocessing/BUILD
new file mode 100644
index 0000000..80a0c02
--- /dev/null
+++ b/java/dagger/internal/codegen/xprocessing/BUILD
@@ -0,0 +1,51 @@
+# 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:
+#   Import for including XProcessing in Dagger.
+
+load("@rules_java//java:defs.bzl", "java_import")
+
+package(default_visibility = ["//:src"])
+
+java_library(
+    name = "xprocessing",
+    # TODO(b/181056551): Ideally, all of the methods in these utility classes
+    # will move directly into XProcessing, and we can then remove these classes.
+    srcs = glob(["*.java"]),
+    exports = [
+        ":xprocessing-lib",
+    ],
+    deps = [
+        ":xprocessing-lib",
+        "//java/dagger/internal/codegen/extension",
+        "//third_party/java/auto:common",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/javapoet",
+        "@maven//:org_jetbrains_kotlin_kotlin_stdlib",
+        "@maven//:org_jetbrains_kotlin_kotlin_stdlib_jdk8",
+    ],
+)
+
+alias(
+    name = "xprocessing-lib",
+    actual = ":xprocessing-jar",
+    visibility = ["//visibility:private"],
+)
+
+java_import(
+    name = "xprocessing-jar",
+    jars = ["xprocessing.jar"],
+)
diff --git a/java/dagger/internal/codegen/xprocessing/MethodSpecs.java b/java/dagger/internal/codegen/xprocessing/MethodSpecs.java
new file mode 100644
index 0000000..6c0322c
--- /dev/null
+++ b/java/dagger/internal/codegen/xprocessing/MethodSpecs.java
@@ -0,0 +1,58 @@
+/*
+ * 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.xprocessing;
+
+import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
+import static javax.lang.model.element.Modifier.PROTECTED;
+import static javax.lang.model.element.Modifier.PUBLIC;
+
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XMethodType;
+import androidx.room.compiler.processing.XType;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+
+// TODO(bcorso): Consider moving these methods into XProcessing library.
+/** A utility class for {@link MethodSpec} helper methods. */
+public final class MethodSpecs {
+
+  /** Returns a {@link MethodSpec} that overrides the given method. */
+  public static MethodSpec.Builder overriding(XMethodElement method, XType owner) {
+    XMethodType methodType = method.asMemberOf(owner);
+    MethodSpec.Builder builder =
+        MethodSpec.methodBuilder(getSimpleName(method))
+            .addAnnotation(Override.class)
+            .addTypeVariables(methodType.getTypeVariableNames())
+            .varargs(method.isVarArgs())
+            .returns(methodType.getReturnType().getTypeName());
+    if (method.isPublic()) {
+      builder.addModifiers(PUBLIC);
+    } else if (method.isProtected()) {
+      builder.addModifiers(PROTECTED);
+    }
+    for (int i = 0; i < methodType.getParameterTypes().size(); i++) {
+      String parameterName = getSimpleName(method.getParameters().get(i));
+      TypeName parameterType = methodType.getParameterTypes().get(i).getTypeName();
+      builder.addParameter(ParameterSpec.builder(parameterType, parameterName).build());
+    }
+    method.getThrownTypes().stream().map(XType::getTypeName).forEach(builder::addException);
+    return builder;
+  }
+
+  private MethodSpecs() {}
+}
diff --git a/java/dagger/internal/codegen/xprocessing/XAnnotations.java b/java/dagger/internal/codegen/xprocessing/XAnnotations.java
new file mode 100644
index 0000000..9e77a86
--- /dev/null
+++ b/java/dagger/internal/codegen/xprocessing/XAnnotations.java
@@ -0,0 +1,40 @@
+/*
+ * 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.xprocessing;
+
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+
+import androidx.room.compiler.processing.XAnnotation;
+import com.google.auto.common.AnnotationMirrors;
+import com.squareup.javapoet.ClassName;
+
+// TODO(bcorso): Consider moving these methods into XProcessing library.
+/** A utility class for {@link XAnnotation} helper methods. */
+public final class XAnnotations {
+
+  /** Returns the string representation of the given annotation. */
+  public static String toString(XAnnotation annotation) {
+    return AnnotationMirrors.toString(toJavac(annotation));
+  }
+
+  /** Returns the class name of the given annotation */
+  public static ClassName getClassName(XAnnotation annotation) {
+    return annotation.getType().getTypeElement().getClassName();
+  }
+
+  private XAnnotations() {}
+}
diff --git a/java/dagger/internal/codegen/xprocessing/XElements.java b/java/dagger/internal/codegen/xprocessing/XElements.java
new file mode 100644
index 0000000..4c338ef
--- /dev/null
+++ b/java/dagger/internal/codegen/xprocessing/XElements.java
@@ -0,0 +1,177 @@
+/*
+ * 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.xprocessing;
+
+import static androidx.room.compiler.processing.XElementKt.isConstructor;
+import static androidx.room.compiler.processing.XElementKt.isField;
+import static androidx.room.compiler.processing.XElementKt.isMethod;
+import static androidx.room.compiler.processing.XElementKt.isMethodParameter;
+import static androidx.room.compiler.processing.XElementKt.isTypeElement;
+import static androidx.room.compiler.processing.XElementKt.isVariableElement;
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static com.google.common.base.Preconditions.checkState;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+
+import androidx.room.compiler.processing.XAnnotated;
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XConstructorElement;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XEnumEntry;
+import androidx.room.compiler.processing.XExecutableElement;
+import androidx.room.compiler.processing.XExecutableParameterElement;
+import androidx.room.compiler.processing.XFieldElement;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XTypeElement;
+import androidx.room.compiler.processing.XVariableElement;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import java.util.Collection;
+import java.util.Optional;
+import javax.lang.model.element.ElementKind;
+
+// TODO(bcorso): Consider moving these methods into XProcessing library.
+/** A utility class for {@link XElement} helper methods. */
+public final class XElements {
+
+  // TODO(bcorso): Replace usages with getJvmName() once it exists.
+  /** Returns the simple name of the element. */
+  public static String getSimpleName(XElement element) {
+    return toJavac(element).getSimpleName().toString();
+  }
+
+  /**
+   * Returns the closest enclosing element that is a {@link XTypeElement} or throws an {@link
+   * IllegalStateException} if one doesn't exists.
+   */
+  public static XTypeElement closestEnclosingTypeElement(XElement element) {
+    return optionalClosestEnclosingTypeElement(element)
+        .orElseThrow(() -> new IllegalStateException("No enclosing TypeElement for: " + element));
+  }
+
+  private static Optional<XTypeElement> optionalClosestEnclosingTypeElement(XElement element) {
+    if (isTypeElement(element)) {
+      return Optional.of(asTypeElement(element));
+    } else if (isConstructor(element)) {
+      return Optional.of(asConstructor(element).getEnclosingElement());
+    } else if (isMethod(element)) {
+      return optionalClosestEnclosingTypeElement(asMethod(element).getEnclosingElement());
+    } else if (isField(element)) {
+      return optionalClosestEnclosingTypeElement(asField(element).getEnclosingElement());
+    } else if (isMethodParameter(element)) {
+      return optionalClosestEnclosingTypeElement(
+          asMethodParameter(element).getEnclosingMethodElement());
+    }
+    return Optional.empty();
+  }
+
+  public static boolean isEnumEntry(XElement element) {
+    return element instanceof XEnumEntry;
+  }
+
+  public static boolean isEnum(XElement element) {
+    return toJavac(element).getKind() == ElementKind.ENUM;
+  }
+
+  public static boolean isExecutable(XElement element) {
+    return isConstructor(element) || isMethod(element);
+  }
+
+  public static XExecutableElement asExecutable(XElement element) {
+    checkState(isExecutable(element));
+    return (XExecutableElement) element;
+  }
+
+  public static XTypeElement asTypeElement(XElement element) {
+    checkState(isTypeElement(element));
+    return (XTypeElement) element;
+  }
+
+  // TODO(bcorso): Rename this and the XElementKt.isMethodParameter to isExecutableParameter.
+  public static XExecutableParameterElement asMethodParameter(XElement element) {
+    checkState(isMethodParameter(element));
+    return (XExecutableParameterElement) element;
+  }
+
+  public static XFieldElement asField(XElement element) {
+    checkState(isField(element));
+    return (XFieldElement) element;
+  }
+
+  public static XVariableElement asVariable(XElement element) {
+    checkState(isVariableElement(element));
+    return (XVariableElement) element;
+  }
+
+  public static XConstructorElement asConstructor(XElement element) {
+    checkState(isConstructor(element));
+    return (XConstructorElement) element;
+  }
+
+  public static XMethodElement asMethod(XElement element) {
+    checkState(isMethod(element));
+    return (XMethodElement) element;
+  }
+
+  public static ImmutableSet<XAnnotation> getAnnotatedAnnotations(
+      XAnnotated annotated, ClassName annotationName) {
+    return annotated.getAllAnnotations().stream()
+        .filter(annotation -> annotation.getType().getTypeElement().hasAnnotation(annotationName))
+        .collect(toImmutableSet());
+  }
+
+  /** Returns {@code true} if {@code annotated} is annotated with any of the given annotations. */
+  public static boolean hasAnyAnnotation(XAnnotated annotated, ClassName... annotations) {
+    return hasAnyAnnotation(annotated, ImmutableSet.copyOf(annotations));
+  }
+
+  /** Returns {@code true} if {@code annotated} is annotated with any of the given annotations. */
+  public static boolean hasAnyAnnotation(XAnnotated annotated, Collection<ClassName> annotations) {
+    return annotations.stream().anyMatch(annotated::hasAnnotation);
+  }
+
+  /**
+   * Returns any annotation from {@code annotations} that annotates {@code annotated} or else {@code
+   * Optional.empty()}.
+   */
+  public static Optional<XAnnotation> getAnyAnnotation(
+      XAnnotated annotated, ClassName... annotations) {
+    return getAnyAnnotation(annotated, ImmutableSet.copyOf(annotations));
+  }
+
+  /**
+   * Returns any annotation from {@code annotations} that annotates {@code annotated} or else
+   * {@code Optional.empty()}.
+   */
+  public static Optional<XAnnotation> getAnyAnnotation(
+      XAnnotated annotated, Collection<ClassName> annotations) {
+    return annotations.stream()
+        .filter(annotated::hasAnnotation)
+        .map(annotated::getAnnotation)
+        .findFirst();
+  }
+
+  /** Returns all annotations from {@code annotations} that annotate {@code annotated}. */
+  public static ImmutableSet<XAnnotation> getAllAnnotations(
+      XAnnotated annotated, Collection<ClassName> annotations) {
+    return annotations.stream()
+        .filter(annotated::hasAnnotation)
+        .map(annotated::getAnnotation)
+        .collect(toImmutableSet());
+  }
+
+  private XElements() {}
+}
diff --git a/java/dagger/internal/codegen/xprocessing/XMethodElements.java b/java/dagger/internal/codegen/xprocessing/XMethodElements.java
new file mode 100644
index 0000000..3cd8711
--- /dev/null
+++ b/java/dagger/internal/codegen/xprocessing/XMethodElements.java
@@ -0,0 +1,40 @@
+/*
+ * 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.xprocessing;
+
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XTypeElement;
+
+// TODO(bcorso): Consider moving these methods into XProcessing library.
+/** A utility class for {@link XMethodElement} helper methods. */
+public final class XMethodElements {
+
+  /** Returns the type this method is enclosed in. */
+  public static XTypeElement getEnclosingTypeElement(XMethodElement method) {
+    // TODO(bcorso): In Javac, a method is always enclosed in a type; however, once we start
+    //  processing Kotlin we'll want to check this explicitly and add an error to the validation
+    //  report if the method is not enclosed in a type.
+    return method.getEnclosingElement().getType().getTypeElement();
+  }
+
+  /** Returns {@code true} if the given method has type parameters. */
+  public static boolean hasTypeParameters(XMethodElement method) {
+    return !method.getExecutableType().getTypeVariableNames().isEmpty();
+  }
+
+  private XMethodElements() {}
+}
diff --git a/java/dagger/internal/codegen/xprocessing/XTypeElements.java b/java/dagger/internal/codegen/xprocessing/XTypeElements.java
new file mode 100644
index 0000000..c0759d1
--- /dev/null
+++ b/java/dagger/internal/codegen/xprocessing/XTypeElements.java
@@ -0,0 +1,96 @@
+/*
+ * 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.xprocessing;
+
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
+import static kotlin.streams.jdk8.StreamsKt.asStream;
+
+import androidx.room.compiler.processing.XHasModifiers;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XTypeElement;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+// TODO(bcorso): Consider moving these methods into XProcessing library.
+/** A utility class for {@link XTypeElement} helper methods. */
+public final class XTypeElements {
+  private enum Visibility {
+    PUBLIC,
+    PRIVATE,
+    OTHER;
+
+    /** Returns the visibility of the given {@link XTypeElement}. */
+    private static Visibility of(XTypeElement element) {
+      checkNotNull(element);
+      if (element.isPrivate()) {
+        return Visibility.PRIVATE;
+      } else if (element.isPublic()) {
+        return Visibility.PUBLIC;
+      } else {
+        return Visibility.OTHER;
+      }
+    }
+  }
+
+  /** Returns {@code true} if the given element is nested. */
+  public static boolean isNested(XTypeElement typeElement) {
+    return typeElement.getEnclosingTypeElement() != null;
+  }
+
+  /** Returns {@code true} if the given {@code type} has type parameters. */
+  public static boolean hasTypeParameters(XTypeElement type) {
+    // TODO(bcorso): Add support for XTypeElement#getTypeParameters() or at least
+    // XTypeElement#hasTypeParameters() in XProcessing. XTypes#getTypeArguments() isn't quite the
+    // same -- it tells you if the declared type has parameters rather than the element itself.
+    return !toJavac(type).getTypeParameters().isEmpty();
+  }
+
+  /** Returns all non-private, non-static, abstract methods in {@code type}. */
+  public static ImmutableList<XMethodElement> getAllUnimplementedMethods(XTypeElement type) {
+    return asStream(type.getAllNonPrivateInstanceMethods())
+        .filter(XHasModifiers::isAbstract)
+        .collect(toImmutableList());
+  }
+
+  public static boolean isEffectivelyPublic(XTypeElement element) {
+    return allVisibilities(element).stream()
+        .allMatch(visibility -> visibility.equals(Visibility.PUBLIC));
+  }
+
+  public static boolean isEffectivelyPrivate(XTypeElement element) {
+    return allVisibilities(element).contains(Visibility.PRIVATE);
+  }
+
+  /**
+   * Returns a list of visibilities containing visibility of the given element and the visibility of
+   * its enclosing elements.
+   */
+  private static ImmutableSet<Visibility> allVisibilities(XTypeElement element) {
+    checkNotNull(element);
+    ImmutableSet.Builder<Visibility> visibilities = ImmutableSet.builder();
+    XTypeElement currentElement = element;
+    while (currentElement != null) {
+      visibilities.add(Visibility.of(currentElement));
+      currentElement = currentElement.getEnclosingTypeElement();
+    }
+    return visibilities.build();
+  }
+
+  private XTypeElements() {}
+}
diff --git a/java/dagger/internal/codegen/xprocessing/XTypes.java b/java/dagger/internal/codegen/xprocessing/XTypes.java
new file mode 100644
index 0000000..8a3c537
--- /dev/null
+++ b/java/dagger/internal/codegen/xprocessing/XTypes.java
@@ -0,0 +1,81 @@
+/*
+ * 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.xprocessing;
+
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+
+import androidx.room.compiler.processing.XArrayType;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.compat.XConverters;
+import com.squareup.javapoet.ClassName;
+import javax.lang.model.type.TypeKind;
+
+// TODO(bcorso): Consider moving these methods into XProcessing library.
+/** A utility class for {@link XType} helper methods. */
+public final class XTypes {
+
+  /** Returns {@code true} if the given type is a raw type of a parameterized type. */
+  public static boolean isRawParameterizedType(XType type) {
+    return isDeclared(type)
+        && type.getTypeArguments().isEmpty()
+        && !type.getTypeElement().getType().getTypeArguments().isEmpty();
+  }
+
+  /** Returns the given {@code type} as an {@link XArrayType}. */
+  public static XArrayType asArray(XType type) {
+    return (XArrayType) type;
+  }
+
+  /** Returns {@code true} if the raw type of {@code type} is equal to {@code className}. */
+  public static boolean isTypeOf(XType type, ClassName className) {
+    return isDeclared(type) && type.getTypeElement().getClassName().equals(className);
+  }
+
+  /** Returns {@code true} if the given type is a declared type. */
+  public static boolean isWildcard(XType type) {
+    return toJavac(type).getKind().equals(TypeKind.WILDCARD);
+  }
+
+  /** Returns {@code true} if the given type is a declared type. */
+  public static boolean isDeclared(XType type) {
+    return type.getTypeElement() != null;
+  }
+
+  /** Returns {@code true} if the given type is a type variable. */
+  public static boolean isTypeVariable(XType type) {
+    return XConverters.toJavac(type).getKind() == TypeKind.TYPEVAR;
+  }
+
+  /**
+   * Returns {@code true} if {@code type1} is equivalent to {@code type2}.
+   */
+  public static boolean areEquivalentTypes(XType type1, XType type2) {
+    return type1.getTypeName().equals(type2.getTypeName());
+  }
+
+  /** Returns {@code true} if the given type is a primitive type. */
+  public static boolean isPrimitive(XType type) {
+    return XConverters.toJavac(type).getKind().isPrimitive();
+  }
+
+  /** Returns {@code true} if the given type has type parameters. */
+  public static boolean hasTypeParameters(XType type) {
+    return !type.getTypeArguments().isEmpty();
+  }
+
+  private XTypes() {}
+}
diff --git a/java/dagger/internal/codegen/xprocessing/xprocessing.jar b/java/dagger/internal/codegen/xprocessing/xprocessing.jar
new file mode 100644
index 0000000..2b02668
--- /dev/null
+++ b/java/dagger/internal/codegen/xprocessing/xprocessing.jar
Binary files differ
diff --git a/java/dagger/internal/guava/BUILD b/java/dagger/internal/guava/BUILD
deleted file mode 100644
index 0bd4dcc..0000000
--- a/java/dagger/internal/guava/BUILD
+++ /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.
-
-# 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/BUILD b/java/dagger/lint/BUILD
index 3e0ffec..173d035 100644
--- a/java/dagger/lint/BUILD
+++ b/java/dagger/lint/BUILD
@@ -26,7 +26,7 @@
     srcs = glob(["*.kt"]),
     tags = ["maven_coordinates=com.google.dagger:dagger-lint:" + POM_VERSION],
     deps = [
-        "@google_bazel_common//third_party/java/auto:service",
+        "//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",
diff --git a/java/dagger/lint/DaggerIssueRegistry.kt b/java/dagger/lint/DaggerIssueRegistry.kt
index 113e85c..f821688 100644
--- a/java/dagger/lint/DaggerIssueRegistry.kt
+++ b/java/dagger/lint/DaggerIssueRegistry.kt
@@ -16,6 +16,7 @@
 package dagger.lint
 
 import com.android.tools.lint.client.api.IssueRegistry
+import com.android.tools.lint.client.api.Vendor
 import com.android.tools.lint.detector.api.CURRENT_API
 import com.android.tools.lint.detector.api.Issue
 import com.google.auto.service.AutoService
@@ -35,6 +36,12 @@
   // 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 api: Int = 11
   override val issues: List<Issue> = DaggerKotlinIssueDetector.issues
+  override val vendor = Vendor(
+    vendorName = "Google",
+    identifier = "com.google.dagger:dagger-lint",
+    feedbackUrl = "https://github.com/google/dagger/issues",
+    contact = "https://github.com/google/dagger"
+  )
 }
diff --git a/java/dagger/lint/DaggerKotlinIssueDetector.kt b/java/dagger/lint/DaggerKotlinIssueDetector.kt
index f3fdbd3..8ca8623 100644
--- a/java/dagger/lint/DaggerKotlinIssueDetector.kt
+++ b/java/dagger/lint/DaggerKotlinIssueDetector.kt
@@ -42,7 +42,6 @@
 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
 
 /**
@@ -61,7 +60,9 @@
  * `@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.
+@Suppress(
+  "UnstableApiUsage" // Lots of Lint APIs are marked with @Beta.
+)
 class DaggerKotlinIssueDetector : Detector(), SourceCodeScanner {
 
   companion object {
@@ -164,9 +165,9 @@
         }
         // Can't use hasAnnotation because it doesn't capture all annotations!
         val injectAnnotation =
-          node.annotations.find { it.qualifiedName == INJECT_ANNOTATION } ?: return
+          node.uAnnotations.find { it.qualifiedName == INJECT_ANNOTATION } ?: return
         // Look for qualifier annotations
-        node.annotations.forEach { annotation ->
+        node.uAnnotations.forEach { annotation ->
           if (annotation === injectAnnotation) {
             // Skip the inject annotation
             return@forEach
@@ -257,6 +258,6 @@
 
   /** @return whether or not the [this] is a Kotlin `object` type. */
   private fun UClass.isObject(): Boolean {
-    return this is KotlinUClass && ktClass is KtObjectDeclaration
+    return sourcePsi is KtObjectDeclaration
   }
 }
diff --git a/java/dagger/model/BUILD b/java/dagger/model/BUILD
index 0be8fc5..71a6c6b 100644
--- a/java/dagger/model/BUILD
+++ b/java/dagger/model/BUILD
@@ -32,13 +32,10 @@
     ],
 )
 
-INTERNAL_PROXIES = ["BindingGraphProxies.java"]
-
 filegroup(
     name = "model-srcs",
     srcs = glob(
         ["*.java"],
-        exclude = INTERNAL_PROXIES,
     ),
 )
 
@@ -49,27 +46,14 @@
     deps = [
         "//java/dagger:core",
         "//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: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",
-    ],
-)
-
-java_library(
-    name = "internal-proxies",
-    srcs = INTERNAL_PROXIES,
-    tags = ["maven:merged"],
-    visibility = ["//:src"],
-    deps = [
-        ":model",
-        "//java/dagger/internal/guava:collect",
-        "//java/dagger/internal/guava:graph",
-        "@google_bazel_common//third_party/java/auto:value",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:value",
+        "//third_party/java/error_prone:annotations",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/graph",
+        "//third_party/java/javapoet",
+        "//third_party/java/jsr330_inject",
     ],
 )
diff --git a/java/dagger/model/BindingGraphProxies.java b/java/dagger/model/BindingGraphProxies.java
deleted file mode 100644
index 85d4df8..0000000
--- a/java/dagger/model/BindingGraphProxies.java
+++ /dev/null
@@ -1,66 +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.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;
-import dagger.model.BindingGraph.Node;
-
-/**
- * Exposes package-private constructors to the {@code dagger.internal.codegen} package. <em>This
- * class should only be used in the Dagger implementation and is not part of any documented
- * 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 new AutoValue_BindingGraphProxies_BindingGraphImpl(
-        ImmutableNetwork.copyOf(network), isFullBindingGraph);
-  }
-
-  /** Creates a new {@link MissingBinding}. */
-  public static MissingBinding missingBindingNode(ComponentPath component, Key key) {
-    return new AutoValue_BindingGraphProxies_MissingBindingImpl(component, key);
-  }
-
-  private BindingGraphProxies() {}
-}
diff --git a/java/dagger/model/Key.java b/java/dagger/model/Key.java
index 03dd41c..aeeabdf 100644
--- a/java/dagger/model/Key.java
+++ b/java/dagger/model/Key.java
@@ -111,7 +111,7 @@
    * and {@code @A(c = "c", b = "b", attributeWithDefaultValue = "default value")}.
    */
   // TODO(ronshapiro): move this to auto-common
-  private static String stableAnnotationMirrorToString(AnnotationMirror qualifier) {
+  static String stableAnnotationMirrorToString(AnnotationMirror qualifier) {
     StringBuilder builder = new StringBuilder("@").append(qualifier.getAnnotationType());
     ImmutableMap<ExecutableElement, AnnotationValue> elementValues =
         AnnotationMirrors.getAnnotationValuesWithDefaults(qualifier);
diff --git a/java/dagger/model/Scope.java b/java/dagger/model/Scope.java
index f48fa16..c7ebfa8 100644
--- a/java/dagger/model/Scope.java
+++ b/java/dagger/model/Scope.java
@@ -24,9 +24,9 @@
 import com.google.auto.common.MoreTypes;
 import com.google.auto.value.AutoValue;
 import com.google.common.base.Equivalence;
+import com.squareup.javapoet.ClassName;
 import dagger.Reusable;
 import dagger.producers.ProductionScope;
-import java.lang.annotation.Annotation;
 import javax.inject.Singleton;
 import javax.lang.model.element.AnnotationMirror;
 import javax.lang.model.element.TypeElement;
@@ -66,26 +66,35 @@
    * Returns {@code true} if {@code scopeAnnotationType} is a {@link javax.inject.Scope} annotation.
    */
   public static boolean isScope(TypeElement scopeAnnotationType) {
-    return isAnnotationPresent(scopeAnnotationType, javax.inject.Scope.class);
+    return isAnnotationPresent(scopeAnnotationType, SCOPE.canonicalName())
+        || isAnnotationPresent(scopeAnnotationType, SCOPE_JAVAX.canonicalName());
   }
 
+  private static final ClassName PRODUCTION_SCOPE =
+      ClassName.get("dagger.producers", "ProductionScope");
+  private static final ClassName SINGLETON = ClassName.get("jakarta.inject", "Singleton");
+  private static final ClassName SINGLETON_JAVAX = ClassName.get("javax.inject", "Singleton");
+  private static final ClassName REUSABLE = ClassName.get("dagger", "Reusable");
+  private static final ClassName SCOPE = ClassName.get("jakarta.inject", "Scope");
+  private static final ClassName SCOPE_JAVAX = ClassName.get("javax.inject", "Scope");
+
   /** Returns {@code true} if this scope is the {@link Singleton @Singleton} scope. */
   public final boolean isSingleton() {
-    return isScope(Singleton.class);
+    return isScope(SINGLETON) || isScope(SINGLETON_JAVAX);
   }
 
   /** Returns {@code true} if this scope is the {@link Reusable @Reusable} scope. */
   public final boolean isReusable() {
-    return isScope(Reusable.class);
+    return isScope(REUSABLE);
   }
 
   /** Returns {@code true} if this scope is the {@link ProductionScope @ProductionScope} scope. */
   public final boolean isProductionScope() {
-    return isScope(ProductionScope.class);
+    return isScope(PRODUCTION_SCOPE);
   }
 
-  private boolean isScope(Class<? extends Annotation> annotation) {
-    return scopeAnnotationElement().getQualifiedName().contentEquals(annotation.getCanonicalName());
+  private boolean isScope(ClassName className) {
+    return scopeAnnotationElement().getQualifiedName().contentEquals(className.canonicalName());
   }
 
   /** Returns a debug representation of the scope. */
diff --git a/java/dagger/model/testing/BUILD b/java/dagger/model/testing/BUILD
index 9c9f44a..722990a 100644
--- a/java/dagger/model/testing/BUILD
+++ b/java/dagger/model/testing/BUILD
@@ -31,11 +31,11 @@
     javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
     deps = [
         "//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/truth",
+        "//third_party/java/auto:value",
+        "//third_party/java/checker_framework_annotations",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/truth",
     ],
 )
diff --git a/java/dagger/multibindings/ClassKey.java b/java/dagger/multibindings/ClassKey.java
index ac25545..edc9ea3 100644
--- a/java/dagger/multibindings/ClassKey.java
+++ b/java/dagger/multibindings/ClassKey.java
@@ -16,11 +16,11 @@
 
 package dagger.multibindings;
 
-import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import dagger.MapKey;
 import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
@@ -31,7 +31,11 @@
  * member whose type is {@code Class<? extends Something>}.
  */
 @Documented
-@Target(METHOD)
+// While METHOD is the only valid target for Dagger, FIELD was added to support Hilt's
+// @BindValueIntoMap and TYPE was added to support external extension types since it likely won't
+// cause confusion/maintenance issues as this isn't part of Dagger's core API.
+// See discussion on https://github.com/google/dagger/pull/2831#issuecomment-919417457 for details.
+@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
 @Retention(RUNTIME)
 @MapKey
 public @interface ClassKey {
diff --git a/java/dagger/multibindings/IntKey.java b/java/dagger/multibindings/IntKey.java
index 55e79a1..1e7960f 100644
--- a/java/dagger/multibindings/IntKey.java
+++ b/java/dagger/multibindings/IntKey.java
@@ -16,17 +16,21 @@
 
 package dagger.multibindings;
 
-import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import dagger.MapKey;
 import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 /** A {@link MapKey} annotation for maps with {@code int} keys. */
 @Documented
-@Target(METHOD)
+// While METHOD is the only valid target for Dagger, FIELD was added to support Hilt's
+// @BindValueIntoMap and TYPE was added to support external extension types since it likely won't
+// cause confusion/maintenance issues as this isn't part of Dagger's core API.
+// See discussion on https://github.com/google/dagger/pull/2831#issuecomment-919417457 for details.
+@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
 @Retention(RUNTIME)
 @MapKey
 public @interface IntKey {
diff --git a/java/dagger/multibindings/LongKey.java b/java/dagger/multibindings/LongKey.java
index 71d0fe1..802478f 100644
--- a/java/dagger/multibindings/LongKey.java
+++ b/java/dagger/multibindings/LongKey.java
@@ -16,17 +16,21 @@
 
 package dagger.multibindings;
 
-import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import dagger.MapKey;
 import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 /** A {@link MapKey} annotation for maps with {@code long} keys. */
 @Documented
-@Target(METHOD)
+// While METHOD is the only valid target for Dagger, FIELD was added to support Hilt's
+// @BindValueIntoMap and TYPE was added to support external extension types since it likely won't
+// cause confusion/maintenance issues as this isn't part of Dagger's core API.
+// See discussion on https://github.com/google/dagger/pull/2831#issuecomment-919417457 for details.
+@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
 @Retention(RUNTIME)
 @MapKey
 public @interface LongKey {
diff --git a/java/dagger/multibindings/StringKey.java b/java/dagger/multibindings/StringKey.java
index 5dad8e3..c773cea 100644
--- a/java/dagger/multibindings/StringKey.java
+++ b/java/dagger/multibindings/StringKey.java
@@ -16,17 +16,21 @@
 
 package dagger.multibindings;
 
-import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import dagger.MapKey;
 import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 /** A {@link MapKey} annotation for maps with {@link String} keys. */
 @Documented
-@Target(METHOD)
+// While METHOD is the only valid target for Dagger, FIELD was added to support Hilt's
+// @BindValueIntoMap and TYPE was added to support external extension types since it likely won't
+// cause confusion/maintenance issues as this isn't part of Dagger's core API.
+// See discussion on https://github.com/google/dagger/pull/2831#issuecomment-919417457 for details.
+@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
 @Retention(RUNTIME)
 @MapKey
 public @interface StringKey {
diff --git a/java/dagger/producers/BUILD b/java/dagger/producers/BUILD
index 41762e6..b966ad5 100644
--- a/java/dagger/producers/BUILD
+++ b/java/dagger/producers/BUILD
@@ -42,18 +42,18 @@
     javacopts = SOURCE_7_TARGET_7 + DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
     tags = ["maven_coordinates=com.google.dagger:dagger-producers:" + POM_VERSION],
     exports = [
-        "//java/dagger/internal/guava:base",
-        "//java/dagger/internal/guava:concurrent",
-        "@google_bazel_common//third_party/java/jsr330_inject",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/util/concurrent",
+        "//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/jsr330_inject",
+        "//third_party/java/checker_framework_annotations",
+        "//third_party/java/error_prone:annotations",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/util/concurrent",
+        "//third_party/java/jsr330_inject",
     ],
 )
 
diff --git a/java/dagger/producers/internal/AnnotationUsages.java b/java/dagger/producers/internal/AnnotationUsages.java
new file mode 100644
index 0000000..9e77608
--- /dev/null
+++ b/java/dagger/producers/internal/AnnotationUsages.java
@@ -0,0 +1,39 @@
+/*
+ * 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.producers.internal;
+
+import dagger.producers.Production;
+import dagger.producers.ProductionScope;
+
+/**
+ * This class should never be referenced directly!
+ *
+ * This class should only be used by Dagger's annotation processor to get access to the annotations
+ * types.
+ */
+final class AnnotationUsages {
+  @Production
+  static final class ProductionUsage {}
+
+  @ProductionImplementation
+  static final class ProductionImplementationUsage {}
+
+  @ProductionScope
+  static final class ProductionScopeUsage {}
+
+  private AnnotationUsages() {}
+}
diff --git a/java/dagger/spi/BUILD b/java/dagger/spi/BUILD
index cbec465..529842a 100644
--- a/java/dagger/spi/BUILD
+++ b/java/dagger/spi/BUILD
@@ -30,6 +30,7 @@
     name = "spi-srcs",
     srcs = glob(["*.java"]) + [
         "//java/dagger/model:model-srcs",
+        "//java/dagger/spi/model:model-srcs",
     ],
 )
 
@@ -40,15 +41,13 @@
     tags = ["maven_coordinates=com.google.dagger:dagger-spi:" + POM_VERSION],
     exports = [
         "//java/dagger/model",
+        "//java/dagger/spi/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/jsr330_inject",
+        "//third_party/java/auto:value",
+        "//third_party/java/error_prone:annotations",
     ],
 )
 
@@ -59,23 +58,33 @@
     artifact_target = ":spi",
     artifact_target_libs = [
         "//java/dagger/internal/codegen/extension",
+        "//java/dagger/internal/codegen/xprocessing",
         "//java/dagger/model",
+        "//java/dagger/spi/model",
     ],
     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.devtools.ksp:symbol-processing-api",
         "com.google.guava:failureaccess",
         "com.google.guava:guava",
         "com.squareup:javapoet",
         "javax.inject:javax.inject",
+        "org.jetbrains.kotlin:kotlin-stdlib",
+        "org.jetbrains.kotlin:kotlin-stdlib-jdk8",
     ],
     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"],
+    # The shaded deps are added using jarjar, but they won't be shaded until later
+    # due to: https://github.com/google/dagger/issues/2765. For the shaded rules see
+    # util/deploy-dagger.sh
+    shaded_deps = [
+        "//third_party/java/auto:common",
+        "//java/dagger/internal/codegen/xprocessing:xprocessing-jar",
+    ],
 )
diff --git a/java/dagger/spi/model/BUILD b/java/dagger/spi/model/BUILD
new file mode 100644
index 0000000..9c825b4
--- /dev/null
+++ b/java/dagger/spi/model/BUILD
@@ -0,0 +1,60 @@
+# 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:
+#   Dagger's core APIs exposed for plugins
+
+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__",
+    ],
+)
+
+filegroup(
+    name = "model-srcs",
+    srcs = glob(["*.java"]),
+)
+
+java_library(
+    name = "model",
+    srcs = [":model-srcs"],
+    javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
+    deps = [
+        "//java/dagger:core",
+        "//java/dagger/internal/codegen/extension",
+        "//java/dagger/internal/codegen/xprocessing",
+        "//java/dagger/producers",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:value",
+        "//third_party/java/error_prone:annotations",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/graph",
+        "//third_party/java/javapoet",
+        "//third_party/java/jsr305_annotations",
+        "//third_party/java/jsr330_inject",
+        "@maven//:com_google_devtools_ksp_symbol_processing_api",
+    ],
+)
diff --git a/java/dagger/spi/model/Binding.java b/java/dagger/spi/model/Binding.java
new file mode 100644
index 0000000..3c6c6a1
--- /dev/null
+++ b/java/dagger/spi/model/Binding.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.spi.model;
+
+import com.google.common.collect.ImmutableSet;
+import dagger.spi.model.BindingGraph.MaybeBinding;
+import java.util.Optional;
+
+/**
+ * The association between a {@link Key} and the way in which instances of the key are provided.
+ * Includes any {@linkplain DependencyRequest dependencies} that are needed in order to provide the
+ * instances.
+ *
+ * <p>If a binding is owned by more than one component, there is one {@code Binding} for every
+ * owning component.
+ */
+public interface Binding extends MaybeBinding {
+  @Override
+  ComponentPath componentPath();
+
+  /** @deprecated This always returns {@code Optional.of(this)}. */
+  @Override
+  @Deprecated
+  default Optional<Binding> binding() {
+    return Optional.of(this);
+  }
+  /**
+   * The dependencies of this binding. The order of the dependencies corresponds to the order in
+   * which they will be injected when the binding is requested.
+   */
+  ImmutableSet<DependencyRequest> dependencies();
+
+  /**
+   * The {@link DaggerElement} 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.
+   */
+  Optional<DaggerElement> bindingElement();
+
+  /**
+   * The {@link DaggerTypeElement} of the module which contributes this binding. Absent for bindings
+   * that have no {@link #bindingElement() binding element}.
+   */
+  Optional<DaggerTypeElement> contributingModule();
+
+  /**
+   * Returns {@code true} if using this binding requires an instance of the {@link
+   * #contributingModule()}.
+   */
+  boolean requiresModuleInstance();
+
+  /** The scope of this binding if it has one. */
+  Optional<Scope> scope();
+
+  /**
+   * 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}.
+   */
+  boolean isNullable();
+
+  /** Returns {@code true} if this is a production binding, e.g. an {@code @Produces} method. */
+  boolean isProduction();
+
+  /** The kind of binding this instance represents. */
+  BindingKind kind();
+
+}
diff --git a/java/dagger/spi/model/BindingGraph.java b/java/dagger/spi/model/BindingGraph.java
new file mode 100644
index 0000000..f10ffe2
--- /dev/null
+++ b/java/dagger/spi/model/BindingGraph.java
@@ -0,0 +1,442 @@
+/*
+ * 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.spi.model;
+
+import static com.google.common.collect.Sets.intersection;
+import static com.google.common.graph.Graphs.inducedSubgraph;
+import static com.google.common.graph.Graphs.reachableNodes;
+import static com.google.common.graph.Graphs.transpose;
+import static dagger.internal.codegen.extension.DaggerStreams.instancesOf;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSetMultimap;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.graph.EndpointPair;
+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.Module;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+/**
+ * A graph of bindings, dependency requests, and components.
+ *
+ * <p>A {@link BindingGraph} represents one of the following:
+ *
+ * <ul>
+ *   <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 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()} ()}
+ * </ul>
+ *
+ * In the case of a {@link BindingGraph} representing a module, the root {@link ComponentNode} will
+ * actually represent the module type. The graph will also be a {@linkplain #isFullBindingGraph()
+ * full binding graph}, which means it will contain all bindings in all modules, as well as nodes
+ * for their dependencies. Otherwise it will contain only bindings that are reachable from at least
+ * one {@linkplain #entryPointEdges() entry point}.
+ *
+ * <h3>Nodes</h3>
+ *
+ * <p>There is a <b>{@link Binding}</b> for each owned binding in the graph. If a binding is owned
+ * by more than one component, there is one binding object for that binding for every owning
+ * component.
+ *
+ * <p>There is a <b>{@linkplain ComponentNode component node}</b> (without a binding) for each
+ * component in the graph.
+ *
+ * <h3>Edges</h3>
+ *
+ * <p>There is a <b>{@linkplain DependencyEdge dependency edge}</b> for each dependency request in
+ * the graph. Its target node is the binding for the binding that satisfies the request. For entry
+ * point dependency requests, the source node is the component node for the component for which it
+ * is an entry point. For other dependency requests, the source node is the binding for the binding
+ * that contains the request.
+ *
+ * <p>There is a <b>subcomponent edge</b> for each parent-child component relationship in the graph.
+ * The target node is the component node for the child component. For subcomponents defined by a
+ * {@linkplain SubcomponentCreatorBindingEdge subcomponent creator binding} (either a method on the
+ * component or a set of {@code @Module.subcomponents} annotation values), the source node is the
+ * binding for the {@code @Subcomponent.Builder} type. For subcomponents defined by {@linkplain
+ * ChildFactoryMethodEdge subcomponent factory methods}, the source node is the component node for
+ * the parent.
+ *
+ * <p><b>Note that this API is experimental and will change.</b>
+ */
+public abstract class BindingGraph {
+  /** Returns the graph in its {@link Network} representation. */
+  public abstract ImmutableNetwork<Node, Edge> network();
+
+  @Override
+  public String toString() {
+    return network().toString();
+  }
+
+  /**
+   * Returns {@code true} if this graph was constructed from a module for full binding graph
+   * validation.
+   *
+   * @deprecated use {@link #isFullBindingGraph()} to tell if this is a full binding graph, or
+   *     {@link ComponentNode#isRealComponent() rootComponentNode().isRealComponent()} to tell if
+   *     the root component node is really a component or derived from a module. Dagger can generate
+   *     full binding graphs for components and subcomponents as well as modules.
+   */
+  @Deprecated
+  public boolean isModuleBindingGraph() {
+    return !rootComponentNode().isRealComponent();
+  }
+
+  /**
+   * Returns {@code true} if this is a full binding graph, which contains all bindings installed in
+   * the component, or {@code false} if it is a reachable binding graph, which contains only
+   * bindings that are reachable from at least one {@linkplain #entryPointEdges() entry point}.
+   *
+   * @see <a href="https://dagger.dev/compiler-options#full-binding-graph-validation">Full binding
+   *     graph validation</a>
+   */
+  public abstract boolean isFullBindingGraph();
+
+  /**
+   * Returns {@code true} if the {@link #rootComponentNode()} is a subcomponent. This occurs in
+   * when {@code -Adagger.fullBindingGraphValidation} is used in a compilation with a subcomponent.
+   *
+   * @deprecated use {@link ComponentNode#isSubcomponent() rootComponentNode().isSubcomponent()}
+   *     instead
+   */
+  @Deprecated
+  public boolean isPartialBindingGraph() {
+    return rootComponentNode().isSubcomponent();
+  }
+
+  /** Returns the bindings. */
+  public ImmutableSet<Binding> bindings() {
+    return nodes(Binding.class);
+  }
+
+  /** Returns the bindings for a 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 ImmutableSet<MissingBinding> missingBindings() {
+    return nodes(MissingBinding.class);
+  }
+
+  /** Returns the component nodes. */
+  public ImmutableSet<ComponentNode> componentNodes() {
+    return nodes(ComponentNode.class);
+  }
+
+  /** Returns the component node for a 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 ImmutableSet<ComponentNode> componentNodes(DaggerTypeElement component) {
+    return componentNodes().stream()
+        .filter(node -> node.componentPath().currentComponent().equals(component))
+        .collect(toImmutableSet());
+  }
+
+  /** Returns the component node for the root component. */
+  public ComponentNode rootComponentNode() {
+    return componentNodes().stream()
+        .filter(node -> node.componentPath().atRoot())
+        .findFirst()
+        .get();
+  }
+
+  /** Returns the dependency edges. */
+  public ImmutableSet<DependencyEdge> dependencyEdges() {
+    return dependencyEdgeStream().collect(toImmutableSet());
+  }
+
+  /**
+   * Returns the dependency edges for the dependencies of a binding. For valid graphs, each {@link
+   * DependencyRequest} will map to a single {@link DependencyEdge}. When conflicting bindings exist
+   * for a key, the multimap will have several edges for that {@link DependencyRequest}. Graphs that
+   * have no binding for a key will have an edge whose {@linkplain EndpointPair#target() target
+   * node} is a {@link MissingBinding}.
+   */
+  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 ImmutableSet<DependencyEdge> dependencyEdges(DependencyRequest dependencyRequest) {
+    return dependencyEdgeStream()
+        .filter(edge -> edge.dependencyRequest().equals(dependencyRequest))
+        .collect(toImmutableSet());
+  }
+
+  /**
+   * 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 ImmutableSet<DependencyEdge> entryPointEdges(ComponentPath component) {
+    return dependencyEdgeStream(componentNode(component).get()).collect(toImmutableSet());
+  }
+
+  private Stream<DependencyEdge> dependencyEdgeStream(Node node) {
+    return network().outEdges(node).stream().flatMap(instancesOf(DependencyEdge.class));
+  }
+
+  /**
+   * Returns the dependency edges for all entry points for all components and subcomponents. Each
+   * edge's source node is a component node.
+   */
+  public ImmutableSet<DependencyEdge> entryPointEdges() {
+    return entryPointEdgeStream().collect(toImmutableSet());
+  }
+
+  /** Returns the binding or missing binding nodes that directly satisfy entry points. */
+  public ImmutableSet<MaybeBinding> entryPointBindings() {
+    return entryPointEdgeStream()
+        .map(edge -> (MaybeBinding) network().incidentNodes(edge).target())
+        .collect(toImmutableSet());
+  }
+
+  /**
+   * Returns the edges for entry points that transitively depend on a binding or missing binding for
+   * a key.
+   */
+  public ImmutableSet<DependencyEdge> entryPointEdgesDependingOnBinding(
+      MaybeBinding binding) {
+    ImmutableNetwork<Node, DependencyEdge> dependencyGraph = dependencyGraph();
+    Network<Node, DependencyEdge> subgraphDependingOnBinding =
+        inducedSubgraph(
+            dependencyGraph, reachableNodes(transpose(dependencyGraph).asGraph(), binding));
+    return intersection(entryPointEdges(), subgraphDependingOnBinding.edges()).immutableCopy();
+  }
+
+  /** Returns the bindings that directly request a given binding as a dependency. */
+  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 requests as a dependency. Does not include
+   * any {@link MissingBinding}s.
+   *
+   * @see #requestedMaybeMissingBindings(Binding)
+   */
+  public ImmutableSet<Binding> requestedBindings(Binding binding) {
+    return network().successors(binding).stream()
+        .flatMap(instancesOf(Binding.class))
+        .collect(toImmutableSet());
+  }
+
+  /**
+   * Returns the bindings or missing bindings that a given binding directly requests as a
+   * dependency.
+   *
+   * @see #requestedBindings(Binding)
+   */
+  public ImmutableSet<MaybeBinding> requestedMaybeMissingBindings(Binding binding) {
+    return network().successors(binding).stream()
+        .flatMap(instancesOf(MaybeBinding.class))
+        .collect(toImmutableSet());
+  }
+
+  /** Returns a subnetwork that contains all nodes but only {@link DependencyEdge}s. */
+  // TODO(dpb): Make public. Cache.
+  private ImmutableNetwork<Node, DependencyEdge> dependencyGraph() {
+    MutableNetwork<Node, DependencyEdge> dependencyGraph =
+        NetworkBuilder.from(network())
+            .expectedNodeCount(network().nodes().size())
+            .expectedEdgeCount((int) dependencyEdgeStream().count())
+            .build();
+    network().nodes().forEach(dependencyGraph::addNode); // include disconnected nodes
+    dependencyEdgeStream()
+        .forEach(
+            edge -> {
+              EndpointPair<Node> endpoints = network().incidentNodes(edge);
+              dependencyGraph.addEdge(endpoints.source(), endpoints.target(), edge);
+            });
+    return ImmutableNetwork.copyOf(dependencyGraph);
+  }
+
+  @SuppressWarnings({"rawtypes", "unchecked"})
+  private <N extends Node> ImmutableSet<N> nodes(Class<N> clazz) {
+    return (ImmutableSet) nodesByClass().get(clazz);
+  }
+
+  private static final ImmutableSet<Class<? extends Node>> NODE_TYPES =
+      ImmutableSet.of(Binding.class, MissingBinding.class, ComponentNode.class);
+
+  protected ImmutableSetMultimap<Class<? extends Node>, ? extends Node> nodesByClass() {
+    return network().nodes().stream()
+        .collect(
+            toImmutableSetMultimap(
+                node ->
+                    NODE_TYPES.stream().filter(clazz -> clazz.isInstance(node)).findFirst().get(),
+                node -> node));
+  }
+
+  private Stream<DependencyEdge> dependencyEdgeStream() {
+    return network().edges().stream().flatMap(instancesOf(DependencyEdge.class));
+  }
+
+  private Stream<DependencyEdge> entryPointEdgeStream() {
+    return dependencyEdgeStream().filter(DependencyEdge::isEntryPoint);
+  }
+
+  /**
+   * An edge in the binding graph. Either a {@link DependencyEdge}, a {@link
+   * ChildFactoryMethodEdge}, or a {@link SubcomponentCreatorBindingEdge}.
+   */
+  public interface Edge {}
+
+  /**
+   * An edge that represents a dependency on a binding.
+   *
+   * <p>Because one {@link DependencyRequest} may represent a dependency from two bindings (e.g., a
+   * dependency of {@code Foo<String>} and {@code Foo<Number>} may have the same key and request
+   * element), this class does not override {@link #equals(Object)} to use value semantics.
+   *
+   * <p>For entry points, the source node is the {@link ComponentNode} that contains the entry
+   * point. Otherwise the source node is a {@link Binding}.
+   *
+   * <p>For dependencies on missing bindings, the target node is a {@link MissingBinding}. Otherwise
+   * the target node is a {@link Binding}.
+   */
+  public interface DependencyEdge extends Edge {
+    /** The dependency request. */
+    DependencyRequest dependencyRequest();
+
+    /** Returns {@code true} if this edge represents an entry point. */
+    boolean isEntryPoint();
+  }
+
+  /**
+   * An edge that represents a subcomponent factory method linking a parent component to a child
+   * subcomponent.
+   */
+  public interface ChildFactoryMethodEdge extends Edge {
+    /** The subcomponent factory method element. */
+    DaggerExecutableElement factoryMethod();
+  }
+
+  /**
+   * An edge that represents the link between a parent component and a child subcomponent implied by
+   * a subcomponent creator ({@linkplain dagger.Subcomponent.Builder builder} or {@linkplain
+   * dagger.Subcomponent.Factory factory}) binding.
+   *
+   * <p>The {@linkplain com.google.common.graph.EndpointPair#source() source node} of this edge is a
+   * {@link Binding} for the subcomponent creator {@link Key} and the {@linkplain
+   * com.google.common.graph.EndpointPair#target() target node} is a {@link ComponentNode} for the
+   * child subcomponent.
+   */
+  public interface SubcomponentCreatorBindingEdge extends Edge {
+    /**
+     * The modules that {@linkplain Module#subcomponents() declare the subcomponent} that generated
+     * this edge. Empty if the parent component has a subcomponent creator method and there are no
+     * declaring modules.
+     */
+    ImmutableSet<DaggerTypeElement> declaringModules();
+  }
+
+  /** A node in the binding graph. Either a {@link Binding} or a {@link ComponentNode}. */
+  // TODO(dpb): Make all the node/edge types top-level.
+  public interface Node {
+    /** The component this node belongs to. */
+    ComponentPath componentPath();
+  }
+
+  /** A node in the binding graph that is either a {@link Binding} or a {@link MissingBinding}. */
+  public interface MaybeBinding extends Node {
+
+    /** The component that owns the binding, or in which the binding is missing. */
+    @Override
+    ComponentPath componentPath();
+
+    /** The key of the binding, or for which there is no binding. */
+    Key key();
+
+    /** The binding, or empty if missing. */
+    Optional<Binding> binding();
+  }
+
+  /** A node in the binding graph that represents a missing binding for a key in a component. */
+  public abstract static class MissingBinding implements MaybeBinding {
+    /** 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 Optional<Binding> binding() {
+      return Optional.empty();
+    }
+
+    @Override
+    public String toString() {
+      return String.format("missing binding for %s in %s", key(), componentPath());
+    }
+  }
+
+  /**
+   * A <b>component node</b> in the graph. Every entry point {@linkplain DependencyEdge dependency
+   * edge}'s source node is a component node for the component containing the entry point.
+   */
+  public interface ComponentNode extends Node {
+
+    /** The component represented by this node. */
+    @Override
+    ComponentPath componentPath();
+
+    /**
+     * Returns {@code true} if the component is a {@code @Subcomponent} or
+     * {@code @ProductionSubcomponent}.
+     */
+    boolean isSubcomponent();
+
+    /**
+     * Returns {@code true} if the component is a real component, or {@code false} if it is a
+     * fictional component based on a module.
+     */
+    boolean isRealComponent();
+
+    /** The entry points on this component. */
+    ImmutableSet<DependencyRequest> entryPoints();
+
+    /** The scopes declared on this component. */
+    ImmutableSet<Scope> scopes();
+  }
+}
diff --git a/java/dagger/spi/model/BindingGraphPlugin.java b/java/dagger/spi/model/BindingGraphPlugin.java
new file mode 100644
index 0000000..41e4219
--- /dev/null
+++ b/java/dagger/spi/model/BindingGraphPlugin.java
@@ -0,0 +1,94 @@
+/*
+ * 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.spi.model;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.Messager;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+// TODO(bcorso): Move this into dagger/spi?
+/**
+ * A pluggable visitor for {@link BindingGraph}.
+ *
+ * <p>Note: This is still experimental and will change.
+ */
+public interface BindingGraphPlugin {
+  /**
+   * Called once for each valid root binding graph encountered by the Dagger processor. May report
+   * diagnostics using {@code diagnosticReporter}.
+   */
+  void visitGraph(BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter);
+
+  /**
+   * Initializes this plugin with a {@link Filer} that it can use to write Java or other files based
+   * on the binding graph. This will be called once per instance of this plugin, before any graph is
+   * {@linkplain #visitGraph(BindingGraph, DiagnosticReporter) visited}.
+   *
+   * @see javax.annotation.processing.ProcessingEnvironment#getFiler()
+   */
+  default void initFiler(Filer filer) {}
+
+  /**
+   * Initializes this plugin with a {@link Types} instance. This will be called once per instance of
+   * this plugin, before any graph is {@linkplain #visitGraph(BindingGraph, DiagnosticReporter)
+   * visited}.
+   *
+   * @see javax.annotation.processing.ProcessingEnvironment#getTypeUtils()
+   */
+  default void initTypes(Types types) {}
+
+  /**
+   * Initializes this plugin with a {@link Elements} instance. This will be called once per instance
+   * of this plugin, before any graph is {@linkplain #visitGraph(BindingGraph, DiagnosticReporter)
+   * visited}.
+   *
+   * @see javax.annotation.processing.ProcessingEnvironment#getElementUtils()
+   */
+  default void initElements(Elements elements) {}
+
+  /**
+   * Initializes this plugin with a filtered view of the options passed on the {@code javac}
+   * command-line for all keys from {@link #supportedOptions()}. This will be called once per
+   * instance of this plugin, before any graph is {@linkplain #visitGraph(BindingGraph,
+   * DiagnosticReporter) visited}.
+   *
+   * @see javax.annotation.processing.ProcessingEnvironment#getOptions()
+   */
+  default void initOptions(Map<String, String> options) {}
+
+  /**
+   * Returns the annotation-processing options that this plugin uses to configure behavior.
+   *
+   * @see javax.annotation.processing.Processor#getSupportedOptions()
+   */
+  default Set<String> supportedOptions() {
+    return Collections.emptySet();
+  }
+
+  /**
+   * A distinguishing name of the plugin that will be used in diagnostics printed to the {@link
+   * Messager}. By default, the {@linkplain Class#getCanonicalName() fully qualified name} of the
+   * plugin is used.
+   */
+  default String pluginName() {
+    return getClass().getCanonicalName();
+  }
+}
diff --git a/java/dagger/spi/model/BindingKind.java b/java/dagger/spi/model/BindingKind.java
new file mode 100644
index 0000000..b854b53
--- /dev/null
+++ b/java/dagger/spi/model/BindingKind.java
@@ -0,0 +1,126 @@
+/*
+ * 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.spi.model;
+
+/** Represents the different kinds of {@link Binding}s that can exist in a binding graph. */
+public enum BindingKind {
+  /** A binding for an {@link javax.inject.Inject}-annotated constructor. */
+  INJECTION,
+
+  /** A binding for a {@link dagger.Provides}-annotated method. */
+  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.
+   */
+  COMPONENT,
+
+  /**
+   * A binding for a provision method on a component's {@linkplain dagger.Component#dependencies()
+   * dependency}.
+   */
+  COMPONENT_PROVISION,
+
+  /**
+   * A binding for an instance of a component's {@linkplain dagger.Component#dependencies()
+   * dependency}.
+   */
+  COMPONENT_DEPENDENCY,
+
+  /** A binding for a {@link dagger.MembersInjector} of a type. */
+  MEMBERS_INJECTOR,
+
+  /**
+   * A binding for a subcomponent creator (a {@linkplain dagger.Subcomponent.Builder builder} or
+   * {@linkplain dagger.Subcomponent.Factory factory}).
+   *
+   * @since 2.22 (previously named {@code SUBCOMPONENT_BUILDER})
+   */
+  SUBCOMPONENT_CREATOR,
+
+  /** A binding for a {@link dagger.BindsInstance}-annotated builder method. */
+  BOUND_INSTANCE,
+
+  /** A binding for a {@link dagger.producers.Produces}-annotated method. */
+  PRODUCTION,
+
+  /**
+   * A binding 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} or {@link
+   * com.google.common.util.concurrent.FluentFuture}. Methods on production component dependencies
+   * that don't return a future are considered {@linkplain #COMPONENT_PROVISION component provision
+   * bindings}.
+   */
+  COMPONENT_PRODUCTION,
+
+  /**
+   * A synthetic binding for a multibound set that depends on individual multibinding {@link
+   * #PROVISION} or {@link #PRODUCTION} contributions.
+   */
+  MULTIBOUND_SET,
+
+  /**
+   * A synthetic binding for a multibound map that depends on the individual multibinding {@link
+   * #PROVISION} or {@link #PRODUCTION} contributions.
+   */
+  MULTIBOUND_MAP,
+
+  /**
+   * A synthetic binding for {@code Optional} of a type or a {@link javax.inject.Provider}, {@link
+   * dagger.Lazy}, or {@code Provider} of {@code Lazy} of a type. Generated by a {@link
+   * dagger.BindsOptionalOf} declaration.
+   */
+  OPTIONAL,
+
+  /**
+   * A binding for {@link dagger.Binds}-annotated method that that delegates from requests for one
+   * key to another.
+   */
+  // TODO(dpb,ronshapiro): This name is confusing and could use work. Not all usages of @Binds
+  // bindings are simple delegations and we should have a name that better reflects that
+  DELEGATE,
+
+  /** A binding for a members injection method on a component. */
+  MEMBERS_INJECTION,
+  ;
+
+  /**
+   * Returns {@code true} if this is a kind of multibinding (not a contribution to a multibinding,
+   * but the multibinding itself).
+   */
+  public boolean isMultibinding() {
+    switch (this) {
+      case MULTIBOUND_MAP:
+      case MULTIBOUND_SET:
+        return true;
+
+      default:
+        return false;
+    }
+  }
+}
diff --git a/java/dagger/internal/MemoizedSentinel.java b/java/dagger/spi/model/CompilerEnvironment.java
similarity index 75%
rename from java/dagger/internal/MemoizedSentinel.java
rename to java/dagger/spi/model/CompilerEnvironment.java
index dd24dcd..28553e4 100644
--- a/java/dagger/internal/MemoizedSentinel.java
+++ b/java/dagger/spi/model/CompilerEnvironment.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Dagger Authors.
+ * 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.
@@ -14,7 +14,10 @@
  * limitations under the License.
  */
 
-package dagger.internal;
+package dagger.spi.model;
 
-/** A sentinel used to memoize a scoped binding in a component. */
-public final class MemoizedSentinel {}
+/** Types for the compiler in use for annotation processing. */
+public enum CompilerEnvironment {
+  JAVA,
+  KSP
+}
diff --git a/java/dagger/spi/model/ComponentPath.java b/java/dagger/spi/model/ComponentPath.java
new file mode 100644
index 0000000..74195e4
--- /dev/null
+++ b/java/dagger/spi/model/ComponentPath.java
@@ -0,0 +1,105 @@
+/*
+ * 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.spi.model;
+
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.getLast;
+import static java.util.stream.Collectors.joining;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.collect.ImmutableList;
+import com.squareup.javapoet.ClassName;
+
+/** A path containing a component and all of its ancestor components. */
+@AutoValue
+public abstract class ComponentPath {
+  /** Returns a new {@link ComponentPath} from {@code components}. */
+  public static ComponentPath create(Iterable<DaggerTypeElement> components) {
+    return new AutoValue_ComponentPath(ImmutableList.copyOf(components));
+  }
+
+  /**
+   * Returns the component types, starting from the {@linkplain #rootComponent() root
+   * component} and ending with the {@linkplain #currentComponent() current component}.
+   */
+  public abstract ImmutableList<DaggerTypeElement> components();
+
+  /**
+   * Returns the root {@link dagger.Component}- or {@link
+   * dagger.producers.ProductionComponent}-annotated type
+   */
+  public final DaggerTypeElement rootComponent() {
+    return components().get(0);
+  }
+
+  /** Returns the component at the end of the path. */
+  @Memoized
+  public DaggerTypeElement currentComponent() {
+    return getLast(components());
+  }
+
+  /**
+   * Returns the parent of the {@linkplain #currentComponent()} current component}.
+   *
+   * @throws IllegalStateException if the current graph is the {@linkplain #atRoot() root component}
+   */
+  public final DaggerTypeElement parentComponent() {
+    checkState(!atRoot());
+    return components().reverse().get(1);
+  }
+
+  /**
+   * Returns this path's parent path.
+   *
+   * @throws IllegalStateException if the current graph is the {@linkplain #atRoot() root component}
+   */
+  // TODO(ronshapiro): consider memoizing this
+  public final ComponentPath parent() {
+    checkState(!atRoot());
+    return create(components().subList(0, components().size() - 1));
+  }
+
+  /** Returns the path from the root component to the {@code child} of the current component. */
+  public final ComponentPath childPath(DaggerTypeElement child) {
+    return create(
+        ImmutableList.<DaggerTypeElement>builder().addAll(components()).add(child).build());
+  }
+
+  /**
+   * Returns {@code true} if the {@linkplain #currentComponent()} current component} is the
+   * {@linkplain #rootComponent()} root component}.
+   */
+  public final boolean atRoot() {
+    return components().size() == 1;
+  }
+
+  @Override
+  public final String toString() {
+    return components().stream()
+        .map(DaggerTypeElement::className)
+        .map(ClassName::canonicalName)
+        .collect(joining(" → "));
+  }
+
+  @Memoized
+  @Override
+  public abstract int hashCode();
+
+  @Override
+  public abstract boolean equals(Object obj);
+}
diff --git a/java/dagger/spi/model/DaggerAnnotation.java b/java/dagger/spi/model/DaggerAnnotation.java
new file mode 100644
index 0000000..42b12d5
--- /dev/null
+++ b/java/dagger/spi/model/DaggerAnnotation.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.spi.model;
+
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+
+import androidx.room.compiler.processing.XAnnotation;
+import com.google.auto.common.AnnotationMirrors;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Equivalence;
+import com.google.common.base.Preconditions;
+import com.squareup.javapoet.ClassName;
+import javax.lang.model.element.AnnotationMirror;
+
+/** Wrapper type for an annotation. */
+@AutoValue
+public abstract class DaggerAnnotation {
+  private XAnnotation annotation;
+
+  public static DaggerAnnotation from(XAnnotation annotation) {
+    Preconditions.checkNotNull(annotation);
+    DaggerAnnotation daggerAnnotation =
+        new AutoValue_DaggerAnnotation(AnnotationMirrors.equivalence().wrap(toJavac(annotation)));
+    daggerAnnotation.annotation = annotation;
+    return daggerAnnotation;
+  }
+
+  abstract Equivalence.Wrapper<AnnotationMirror> annotationMirror();
+
+  public DaggerTypeElement annotationTypeElement() {
+    return DaggerTypeElement.from(annotation.getType().getTypeElement());
+  }
+
+  public ClassName className() {
+    return annotationTypeElement().className();
+  }
+
+  public XAnnotation xprocessing() {
+    return annotation;
+  }
+
+  public AnnotationMirror java() {
+    return toJavac(annotation);
+  }
+
+  // TODO(b/204390647): We'll need to update to auto-common to 1.2 before using AnnotationMirrors.
+  @SuppressWarnings("AnnotationMirrorToString")
+  @Override
+  public final String toString() {
+    return java().toString();
+  }
+}
diff --git a/java/dagger/spi/model/DaggerElement.java b/java/dagger/spi/model/DaggerElement.java
new file mode 100644
index 0000000..aa1b4a5
--- /dev/null
+++ b/java/dagger/spi/model/DaggerElement.java
@@ -0,0 +1,42 @@
+/*
+ * 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.spi.model;
+
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+
+import androidx.room.compiler.processing.XElement;
+import com.google.auto.value.AutoValue;
+import javax.lang.model.element.Element;
+
+/** Wrapper type for an element. */
+@AutoValue
+public abstract class DaggerElement {
+  public static DaggerElement from(XElement element) {
+    return new AutoValue_DaggerElement(element);
+  }
+
+  public abstract XElement xprocessing();
+
+  public Element java() {
+    return toJavac(xprocessing());
+  }
+
+  @Override
+  public final String toString() {
+    return xprocessing().toString();
+  }
+}
diff --git a/java/dagger/spi/model/DaggerExecutableElement.java b/java/dagger/spi/model/DaggerExecutableElement.java
new file mode 100644
index 0000000..e15ca37
--- /dev/null
+++ b/java/dagger/spi/model/DaggerExecutableElement.java
@@ -0,0 +1,43 @@
+/*
+ * 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.spi.model;
+
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import androidx.room.compiler.processing.XExecutableElement;
+import com.google.auto.value.AutoValue;
+import javax.lang.model.element.ExecutableElement;
+
+/** Wrapper type for an executable element. */
+@AutoValue
+public abstract class DaggerExecutableElement {
+  public static DaggerExecutableElement from(XExecutableElement executableElement) {
+    return new AutoValue_DaggerExecutableElement(checkNotNull(executableElement));
+  }
+
+  public abstract XExecutableElement xprocessing();
+
+  public ExecutableElement java() {
+    return toJavac(xprocessing());
+  }
+
+  @Override
+  public final String toString() {
+    return xprocessing().toString();
+  }
+}
diff --git a/java/dagger/spi/model/DaggerType.java b/java/dagger/spi/model/DaggerType.java
new file mode 100644
index 0000000..34cc6dc
--- /dev/null
+++ b/java/dagger/spi/model/DaggerType.java
@@ -0,0 +1,54 @@
+/*
+ * 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.spi.model;
+
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+
+import androidx.room.compiler.processing.XType;
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Equivalence;
+import com.google.common.base.Preconditions;
+import javax.lang.model.type.TypeMirror;
+
+/** Wrapper type for a type. */
+@AutoValue
+public abstract class DaggerType {
+  private XType type;
+
+  public static DaggerType from(XType type) {
+    Preconditions.checkNotNull(type);
+    DaggerType daggerType = new AutoValue_DaggerType(MoreTypes.equivalence().wrap(toJavac(type)));
+    daggerType.type = type;
+    return daggerType;
+  }
+
+  abstract Equivalence.Wrapper<TypeMirror> typeMirror();
+
+  public XType xprocessing() {
+    return type;
+  }
+
+  public TypeMirror java() {
+    return toJavac(type);
+  }
+
+  @Override
+  public final String toString() {
+    return type.toString();
+  }
+}
diff --git a/java/dagger/spi/model/DaggerTypeElement.java b/java/dagger/spi/model/DaggerTypeElement.java
new file mode 100644
index 0000000..aa47507
--- /dev/null
+++ b/java/dagger/spi/model/DaggerTypeElement.java
@@ -0,0 +1,47 @@
+/*
+ * 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.spi.model;
+
+import static androidx.room.compiler.processing.compat.XConverters.toJavac;
+
+import androidx.room.compiler.processing.XTypeElement;
+import com.google.auto.value.AutoValue;
+import com.squareup.javapoet.ClassName;
+import javax.lang.model.element.TypeElement;
+
+/** Wrapper type for a type element. */
+@AutoValue
+public abstract class DaggerTypeElement {
+  public static DaggerTypeElement from(XTypeElement typeElement) {
+    return new AutoValue_DaggerTypeElement(typeElement);
+  }
+
+  public abstract XTypeElement xprocessing();
+
+  public TypeElement java() {
+    return toJavac(xprocessing());
+  }
+
+  public ClassName className() {
+    return xprocessing().getClassName();
+  }
+
+  @Override
+  public final String toString() {
+    return xprocessing().toString();
+  }
+}
diff --git a/java/dagger/spi/model/DependencyRequest.java b/java/dagger/spi/model/DependencyRequest.java
new file mode 100644
index 0000000..64c0f28
--- /dev/null
+++ b/java/dagger/spi/model/DependencyRequest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.spi.model;
+
+import com.google.auto.value.AutoValue;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.google.errorprone.annotations.CheckReturnValue;
+import dagger.Provides;
+import java.util.Optional;
+import javax.inject.Inject;
+
+/**
+ * Represents a request for a {@link Key} at an injection point. For example, parameters to {@link
+ * Inject} constructors, {@link Provides} methods, and component methods are all dependency
+ * requests.
+ *
+ * <p id="synthetic">A dependency request is considered to be <em>synthetic</em> if it does not have
+ * an {@link DaggerElement} in code that requests the key directly. For example, an {@link
+ * java.util.concurrent.Executor} is required for all {@code @Produces} methods to run
+ * asynchronously even though it is not directly specified as a parameter to the binding method.
+ */
+@AutoValue
+public abstract class DependencyRequest {
+  /** The kind of this request. */
+  public abstract RequestKind kind();
+
+  /** The key of this request. */
+  public abstract Key key();
+
+  /**
+   * The element that declares this dependency request. Absent for <a href="#synthetic">synthetic
+   * </a> requests.
+   */
+  public abstract Optional<DaggerElement> requestElement();
+
+  /**
+   * Returns {@code true} if this request allows null objects. A request is nullable if it is
+   * has an annotation with "Nullable" as its simple name.
+   */
+  public abstract boolean isNullable();
+
+  /** Returns a new builder of dependency requests. */
+  public static DependencyRequest.Builder builder() {
+    return new AutoValue_DependencyRequest.Builder().isNullable(false);
+  }
+
+  /** A builder of {@link DependencyRequest}s. */
+  @CanIgnoreReturnValue
+  @AutoValue.Builder
+  public abstract static class Builder {
+    public abstract Builder kind(RequestKind kind);
+
+    public abstract Builder key(Key key);
+
+    public abstract Builder requestElement(DaggerElement element);
+
+    public abstract Builder isNullable(boolean isNullable);
+
+    @CheckReturnValue
+    public abstract DependencyRequest build();
+  }
+}
diff --git a/java/dagger/spi/model/DiagnosticReporter.java b/java/dagger/spi/model/DiagnosticReporter.java
new file mode 100644
index 0000000..a3d2229
--- /dev/null
+++ b/java/dagger/spi/model/DiagnosticReporter.java
@@ -0,0 +1,103 @@
+/*
+ * 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.spi.model;
+
+import com.google.errorprone.annotations.FormatMethod;
+import dagger.spi.model.BindingGraph.ChildFactoryMethodEdge;
+import dagger.spi.model.BindingGraph.ComponentNode;
+import dagger.spi.model.BindingGraph.DependencyEdge;
+import dagger.spi.model.BindingGraph.MaybeBinding;
+import javax.tools.Diagnostic;
+
+// TODO(bcorso): Move this into dagger/spi?
+/**
+ * An object that {@link BindingGraphPlugin}s can use to report diagnostics while visiting a {@link
+ * BindingGraph}.
+ *
+ * <p>Note: This API is still experimental and will change.
+ */
+public interface DiagnosticReporter {
+  /**
+   * Reports a diagnostic for a component. For non-root components, includes information about the
+   * path from the root component.
+   */
+  void reportComponent(Diagnostic.Kind diagnosticKind, ComponentNode componentNode, String message);
+
+  /**
+   * Reports a diagnostic for a component. For non-root components, includes information about the
+   * path from the root component.
+   */
+  @FormatMethod
+  void reportComponent(
+      Diagnostic.Kind diagnosticKind,
+      ComponentNode componentNode,
+      String messageFormat,
+      Object firstArg,
+      Object... moreArgs);
+
+  /**
+   * Reports a diagnostic for a binding or missing binding. Includes information about how the
+   * binding is reachable from entry points.
+   */
+  void reportBinding(Diagnostic.Kind diagnosticKind, MaybeBinding binding, String message);
+
+  /**
+   * Reports a diagnostic for a binding or missing binding. Includes information about how the
+   * binding is reachable from entry points.
+   */
+  @FormatMethod
+  void reportBinding(
+      Diagnostic.Kind diagnosticKind,
+      MaybeBinding binding,
+      String messageFormat,
+      Object firstArg,
+      Object... moreArgs);
+
+  /**
+   * Reports a diagnostic for a dependency. Includes information about how the dependency is
+   * reachable from entry points.
+   */
+  void reportDependency(
+      Diagnostic.Kind diagnosticKind, DependencyEdge dependencyEdge, String message);
+
+  /**
+   * Reports a diagnostic for a dependency. Includes information about how the dependency is
+   * reachable from entry points.
+   */
+  @FormatMethod
+  void reportDependency(
+      Diagnostic.Kind diagnosticKind,
+      DependencyEdge dependencyEdge,
+      String messageFormat,
+      Object firstArg,
+      Object... moreArgs);
+
+  /** Reports a diagnostic for a subcomponent factory method. */
+  void reportSubcomponentFactoryMethod(
+      Diagnostic.Kind diagnosticKind,
+      ChildFactoryMethodEdge childFactoryMethodEdge,
+      String message);
+
+  /** Reports a diagnostic for a subcomponent factory method. */
+  @FormatMethod
+  void reportSubcomponentFactoryMethod(
+      Diagnostic.Kind diagnosticKind,
+      ChildFactoryMethodEdge childFactoryMethodEdge,
+      String messageFormat,
+      Object firstArg,
+      Object... moreArgs);
+}
diff --git a/java/dagger/spi/model/Key.java b/java/dagger/spi/model/Key.java
new file mode 100644
index 0000000..ad9b6c7
--- /dev/null
+++ b/java/dagger/spi/model/Key.java
@@ -0,0 +1,176 @@
+/*
+ * 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.spi.model;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.base.Joiner;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.google.errorprone.annotations.CheckReturnValue;
+import java.util.Objects;
+import java.util.Optional;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * A {@linkplain DaggerType type} and an optional {@linkplain javax.inject.Qualifier qualifier} that
+ * is the lookup key for a binding.
+ */
+@AutoValue
+public abstract class Key {
+  /**
+   * A {@link javax.inject.Qualifier} annotation that provides a unique namespace prefix for the
+   * type of this key.
+   */
+  public abstract Optional<DaggerAnnotation> qualifier();
+
+  /** The type represented by this key. */
+  public abstract DaggerType type();
+
+  /**
+   * Distinguishes keys for multibinding contributions that share a {@link #type()} and {@link
+   * #qualifier()}.
+   *
+   * <p>Each multibound map and set has a synthetic multibinding that depends on the specific
+   * contributions to that map or set using keys that identify those multibinding contributions.
+   *
+   * <p>Absent except for multibinding contributions.
+   */
+  public abstract Optional<MultibindingContributionIdentifier> multibindingContributionIdentifier();
+
+  /** Returns a {@link Builder} that inherits the properties of this key. */
+  public abstract Builder toBuilder();
+
+  // The main hashCode/equality bottleneck is in MoreTypes.equivalence(). It's possible that we can
+  // avoid this by tuning that method. Perhaps we can also avoid the issue entirely by interning all
+  // Keys
+  @Memoized
+  @Override
+  public abstract int hashCode();
+
+  @Override
+  public abstract boolean equals(Object o);
+
+  @Override
+  public final String toString() {
+    return Joiner.on(' ')
+        .skipNulls()
+        .join(
+            qualifier().map(MoreAnnotationMirrors::toStableString).orElse(null),
+            type(),
+            multibindingContributionIdentifier().orElse(null));
+  }
+
+  /** Returns a builder for {@link Key}s. */
+  public static Builder builder(DaggerType type) {
+    return new AutoValue_Key.Builder().type(type);
+  }
+
+  /** A builder for {@link Key}s. */
+  @CanIgnoreReturnValue
+  @AutoValue.Builder
+  public abstract static class Builder {
+    public abstract Builder type(DaggerType type);
+
+    public abstract Builder qualifier(Optional<DaggerAnnotation> qualifier);
+
+    public abstract Builder qualifier(DaggerAnnotation qualifier);
+
+    public abstract Builder multibindingContributionIdentifier(
+        Optional<MultibindingContributionIdentifier> identifier);
+
+    public abstract Builder multibindingContributionIdentifier(
+        MultibindingContributionIdentifier identifier);
+
+    @CheckReturnValue
+    public abstract Key build();
+  }
+
+  /**
+   * An object that identifies a multibinding contribution method and the module class that
+   * contributes it to the graph.
+   *
+   * @see #multibindingContributionIdentifier()
+   */
+  public static final class MultibindingContributionIdentifier {
+    private final String module;
+    private final String bindingElement;
+
+    /**
+     * @deprecated This is only meant to be called from code in {@code dagger.internal.codegen}.
+     * It is not part of a specified API and may change at any point.
+     */
+    @Deprecated
+    public MultibindingContributionIdentifier(
+        // TODO(ronshapiro): reverse the order of these parameters
+        ExecutableElement bindingMethod, TypeElement contributingModule) {
+      this(
+          bindingMethod.getSimpleName().toString(),
+          contributingModule.getQualifiedName().toString());
+    }
+
+    // TODO(ronshapiro,dpb): create KeyProxies so that these constructors don't need to be public.
+    @Deprecated
+    public MultibindingContributionIdentifier(String bindingElement, String module) {
+      this.module = module;
+      this.bindingElement = bindingElement;
+    }
+
+    /**
+     * @deprecated This is only meant to be called from code in {@code dagger.internal.codegen}.
+     * It is not part of a specified API and may change at any point.
+     */
+    @Deprecated
+    public String module() {
+      return module;
+    }
+
+    /**
+     * @deprecated This is only meant to be called from code in {@code dagger.internal.codegen}.
+     * It is not part of a specified API and may change at any point.
+     */
+    @Deprecated
+    public String bindingElement() {
+      return bindingElement;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>The returned string is human-readable and distinguishes the keys in the same way as the
+     * whole object.
+     */
+    @Override
+    public String toString() {
+      return String.format("%s#%s", module, bindingElement);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (obj instanceof MultibindingContributionIdentifier) {
+        MultibindingContributionIdentifier other = (MultibindingContributionIdentifier) obj;
+        return module.equals(other.module) && bindingElement.equals(other.bindingElement);
+      }
+      return false;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(module, bindingElement);
+    }
+  }
+}
diff --git a/java/dagger/spi/model/MoreAnnotationMirrors.java b/java/dagger/spi/model/MoreAnnotationMirrors.java
new file mode 100644
index 0000000..44c0363
--- /dev/null
+++ b/java/dagger/spi/model/MoreAnnotationMirrors.java
@@ -0,0 +1,102 @@
+/*
+ * 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.spi.model;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValuesWithDefaults;
+import static java.util.stream.Collectors.joining;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableMap;
+import com.squareup.javapoet.CodeBlock;
+import java.util.List;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.util.SimpleAnnotationValueVisitor8;
+
+/** Utility class for qualifier transformations */
+final class MoreAnnotationMirrors {
+  /**
+   * Returns a String rendering of an {@link AnnotationMirror} that includes attributes in the order
+   * defined in the annotation type.
+   */
+  public static String toStableString(DaggerAnnotation qualifier) {
+    return stableAnnotationMirrorToString(qualifier.java());
+  }
+
+  /**
+   * Returns a String rendering of an {@link AnnotationMirror} that includes attributes in the order
+   * defined in the annotation type. This will produce the same output for {@linkplain
+   * com.google.auto.common.AnnotationMirrors#equivalence() equal} {@link AnnotationMirror}s even if
+   * default values are omitted or their attributes were written in different orders, e.g.
+   * {@code @A(b = "b", c = "c")} and {@code @A(c = "c", b = "b", attributeWithDefaultValue =
+   * "default value")}.
+   */
+  // TODO(ronshapiro): move this to auto-common
+  private static String stableAnnotationMirrorToString(AnnotationMirror qualifier) {
+    StringBuilder builder = new StringBuilder("@").append(qualifier.getAnnotationType());
+    ImmutableMap<ExecutableElement, AnnotationValue> elementValues =
+        getAnnotationValuesWithDefaults(qualifier);
+    if (!elementValues.isEmpty()) {
+      ImmutableMap.Builder<String, String> namedValuesBuilder = ImmutableMap.builder();
+      elementValues.forEach(
+          (key, value) ->
+              namedValuesBuilder.put(
+                  key.getSimpleName().toString(), stableAnnotationValueToString(value)));
+      ImmutableMap<String, String> namedValues = namedValuesBuilder.build();
+      builder.append('(');
+      if (namedValues.size() == 1 && namedValues.containsKey("value")) {
+        // Omit "value ="
+        builder.append(namedValues.get("value"));
+      } else {
+        builder.append(Joiner.on(", ").withKeyValueSeparator("=").join(namedValues));
+      }
+      builder.append(')');
+    }
+    return builder.toString();
+  }
+
+  private static String stableAnnotationValueToString(AnnotationValue annotationValue) {
+    return annotationValue.accept(
+        new SimpleAnnotationValueVisitor8<String, Void>() {
+          @Override
+          protected String defaultAction(Object value, Void ignore) {
+            return value.toString();
+          }
+
+          @Override
+          public String visitString(String value, Void ignore) {
+            return CodeBlock.of("$S", value).toString();
+          }
+
+          @Override
+          public String visitAnnotation(AnnotationMirror value, Void ignore) {
+            return stableAnnotationMirrorToString(value);
+          }
+
+          @Override
+          public String visitArray(List<? extends AnnotationValue> value, Void ignore) {
+            return value.stream()
+                .map(MoreAnnotationMirrors::stableAnnotationValueToString)
+                .collect(joining(", ", "{", "}"));
+          }
+        },
+        null);
+  }
+
+  private MoreAnnotationMirrors() {}
+}
diff --git a/java/dagger/spi/model/RequestKind.java b/java/dagger/spi/model/RequestKind.java
new file mode 100644
index 0000000..62f46cd
--- /dev/null
+++ b/java/dagger/spi/model/RequestKind.java
@@ -0,0 +1,85 @@
+/*
+ * 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.spi.model;
+
+import static com.google.common.base.CaseFormat.UPPER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
+
+import dagger.Lazy;
+import dagger.producers.Produced;
+import dagger.producers.Producer;
+import javax.inject.Provider;
+
+/**
+ * Represents the different kinds of {@link javax.lang.model.type.TypeMirror types} that may be
+ * requested as dependencies for the same key. For example, {@code String}, {@code
+ * Provider<String>}, and {@code Lazy<String>} can all be requested if a key exists for {@code
+ * String}; they have the {@link #INSTANCE}, {@link #PROVIDER}, and {@link #LAZY} request kinds,
+ * respectively.
+ */
+public enum RequestKind {
+  /** A default request for an instance. E.g.: {@code FooType} */
+  INSTANCE,
+
+  /** A request for a {@link Provider}. E.g.: {@code Provider<FooType>} */
+  PROVIDER,
+
+  /** A request for a {@link Lazy}. E.g.: {@code Lazy<FooType>} */
+  LAZY,
+
+  /** A request for a {@link Provider} of a {@link Lazy}. E.g.: {@code Provider<Lazy<FooType>>} */
+  PROVIDER_OF_LAZY,
+
+  /**
+   * A request for a members injection. E.g. {@code void injectMembers(FooType);}. Can only be
+   * requested by component interfaces.
+   */
+  MEMBERS_INJECTION,
+
+  /** A request for a {@link Producer}. E.g.: {@code Producer<FooType>} */
+  PRODUCER,
+
+  /** A request for a {@link Produced}. E.g.: {@code Produced<FooType>} */
+  PRODUCED,
+
+  /**
+   * A request for a {@link com.google.common.util.concurrent.ListenableFuture}. E.g.: {@code
+   * ListenableFuture<FooType>}. These can only be requested by component interfaces.
+   */
+  FUTURE,
+  ;
+
+  /** Returns a string that represents requests of this kind for a key. */
+  public String format(Key key) {
+    switch (this) {
+      case INSTANCE:
+        return key.toString();
+
+      case PROVIDER_OF_LAZY:
+        return String.format("Provider<Lazy<%s>>", key);
+
+      case MEMBERS_INJECTION:
+        return String.format("injectMembers(%s)", key);
+
+      case FUTURE:
+        return String.format("ListenableFuture<%s>", key);
+
+      default:
+        return String.format("%s<%s>", UPPER_UNDERSCORE.to(UPPER_CAMEL, name()), key);
+    }
+  }
+}
diff --git a/java/dagger/spi/model/Scope.java b/java/dagger/spi/model/Scope.java
new file mode 100644
index 0000000..c3a9803
--- /dev/null
+++ b/java/dagger/spi/model/Scope.java
@@ -0,0 +1,94 @@
+/*
+ * 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.spi.model;
+
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.auto.value.AutoValue;
+import com.squareup.javapoet.ClassName;
+
+/** A representation of a {@link javax.inject.Scope}. */
+@AutoValue
+public abstract class Scope {
+  /**
+   * Creates a {@link Scope} object from the {@link javax.inject.Scope}-annotated annotation type.
+   */
+  public static Scope scope(DaggerAnnotation scopeAnnotation) {
+    checkArgument(isScope(scopeAnnotation));
+    return new AutoValue_Scope(scopeAnnotation);
+  }
+
+  /**
+   * Returns {@code true} if {@link #scopeAnnotation()} is a {@link javax.inject.Scope} annotation.
+   */
+  public static boolean isScope(DaggerAnnotation scopeAnnotation) {
+    return isScope(scopeAnnotation.annotationTypeElement());
+  }
+
+  /**
+   * Returns {@code true} if {@code scopeAnnotationType} is a {@link javax.inject.Scope} annotation.
+   */
+  public static boolean isScope(DaggerTypeElement scopeAnnotationType) {
+    return isAnnotationPresent(scopeAnnotationType.java(), SCOPE.canonicalName())
+        || isAnnotationPresent(scopeAnnotationType.java(), SCOPE_JAVAX.canonicalName());
+  }
+
+  private static final ClassName PRODUCTION_SCOPE =
+      ClassName.get("dagger.producers", "ProductionScope");
+  private static final ClassName SINGLETON = ClassName.get("jakarta.inject", "Singleton");
+  private static final ClassName SINGLETON_JAVAX = ClassName.get("javax.inject", "Singleton");
+  private static final ClassName REUSABLE = ClassName.get("dagger", "Reusable");
+  private static final ClassName SCOPE = ClassName.get("jakarta.inject", "Scope");
+  private static final ClassName SCOPE_JAVAX = ClassName.get("javax.inject", "Scope");
+
+
+  /** The {@link DaggerAnnotation} that represents the scope annotation. */
+  public abstract DaggerAnnotation scopeAnnotation();
+
+  public final ClassName className() {
+    return scopeAnnotation().className();
+  }
+
+  /** Returns {@code true} if this scope is the {@link javax.inject.Singleton @Singleton} scope. */
+  public final boolean isSingleton() {
+    return isScope(SINGLETON) || isScope(SINGLETON_JAVAX);
+  }
+
+  /** Returns {@code true} if this scope is the {@link dagger.Reusable @Reusable} scope. */
+  public final boolean isReusable() {
+    return isScope(REUSABLE);
+  }
+
+  /**
+   * Returns {@code true} if this scope is the {@link
+   * dagger.producers.ProductionScope @ProductionScope} scope.
+   */
+  public final boolean isProductionScope() {
+    return isScope(PRODUCTION_SCOPE);
+  }
+
+  private boolean isScope(ClassName annotation) {
+    return scopeAnnotation().className().equals(annotation);
+  }
+
+  /** Returns a debug representation of the scope. */
+  @Override
+  public final String toString() {
+    return scopeAnnotation().toString();
+  }
+}
diff --git a/java/dagger/spi/model/package-info.java b/java/dagger/spi/model/package-info.java
new file mode 100644
index 0000000..133a542
--- /dev/null
+++ b/java/dagger/spi/model/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+/**
+ * This package contains the APIs that are core to Dagger's internal model of bindings and the
+ * binding graph. The types are shared with the Dagger processor and are exposed to clients of the
+ * Dagger SPI.
+ *
+ * <p>Unless otherwise specified, the types/interfaces are only intended to be implemented in this
+ * package (i.e. via {@code @AutoValue}) or by Dagger's processor. This applies to test code as
+ * well, so if you need a fake, please file a feature request instead of implementing it yourself.
+ */
+@CheckReturnValue
+@Beta
+package dagger.spi.model;
+
+import com.google.errorprone.annotations.CheckReturnValue;
+import dagger.internal.Beta;
diff --git a/java/dagger/spi/model/testing/BUILD b/java/dagger/spi/model/testing/BUILD
new file mode 100644
index 0000000..939e411
--- /dev/null
+++ b/java/dagger/spi/model/testing/BUILD
@@ -0,0 +1,39 @@
+# 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:
+#   Test utilities for the Dagger model
+
+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/extension",
+        "//java/dagger/spi",
+        "//third_party/java/checker_framework_annotations",
+        "//third_party/java/guava/collect",
+        "//third_party/java/truth",
+    ],
+)
diff --git a/java/dagger/spi/model/testing/BindingGraphSubject.java b/java/dagger/spi/model/testing/BindingGraphSubject.java
new file mode 100644
index 0000000..32bf152
--- /dev/null
+++ b/java/dagger/spi/model/testing/BindingGraphSubject.java
@@ -0,0 +1,150 @@
+/*
+ * 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.spi.model.testing;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.common.truth.Truth.assertAbout;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.truth.FailureMetadata;
+import com.google.common.truth.Subject;
+import dagger.spi.model.Binding;
+import dagger.spi.model.BindingGraph;
+import javax.lang.model.type.TypeMirror;
+import org.checkerframework.checker.nullness.compatqual.NullableDecl;
+
+/** A Truth subject for making assertions on a {@link BindingGraph}. */
+public final class BindingGraphSubject extends Subject {
+
+  /** Starts a fluent assertion about a {@link BindingGraph}. */
+  public static BindingGraphSubject assertThat(BindingGraph bindingGraph) {
+    return assertAbout(BindingGraphSubject::new).that(bindingGraph);
+  }
+
+  private final BindingGraph actual;
+
+  private BindingGraphSubject(FailureMetadata metadata, @NullableDecl BindingGraph actual) {
+    super(metadata, actual);
+    this.actual = actual;
+  }
+
+  /**
+   * Asserts that the graph has at least one binding with an unqualified key.
+   *
+   * @param type the canonical name of the type, as returned by {@link TypeMirror#toString()}
+   */
+  public void hasBindingWithKey(String type) {
+    bindingWithKey(type);
+  }
+
+  /**
+   * Asserts that the graph has at least one binding with a qualified key.
+   *
+   * @param qualifier the canonical string form of the qualifier, as returned by {@link
+   *     javax.lang.model.element.AnnotationMirror AnnotationMirror.toString()}
+   * @param type the canonical name of the type, as returned by {@link TypeMirror#toString()}
+   */
+  public void hasBindingWithKey(String qualifier, String type) {
+    bindingWithKey(qualifier, type);
+  }
+
+  /**
+   * Returns a subject for testing the binding for an unqualified key.
+   *
+   * @param type the canonical name of the type, as returned by {@link TypeMirror#toString()}
+   */
+  public BindingSubject bindingWithKey(String type) {
+    return bindingWithKeyString(keyString(type));
+  }
+
+  /**
+   * Returns a subject for testing the binding for a qualified key.
+   *
+   * @param qualifier the canonical string form of the qualifier, as returned by {@link
+   *     javax.lang.model.element.AnnotationMirror AnnotationMirror.toString()}
+   * @param type the canonical name of the type, as returned by {@link TypeMirror#toString()}
+   */
+  public BindingSubject bindingWithKey(String qualifier, String type) {
+    return bindingWithKeyString(keyString(qualifier, type));
+  }
+
+  private BindingSubject bindingWithKeyString(String keyString) {
+    ImmutableSet<Binding> bindings = getBindingNodes(keyString);
+    // TODO(dpb): Handle multiple bindings for the same key.
+    check("bindingsWithKey(%s)", keyString).that(bindings).hasSize(1);
+    return check("bindingWithKey(%s)", keyString)
+        .about(BindingSubject::new)
+        .that(getOnlyElement(bindings));
+  }
+
+  private ImmutableSet<Binding> getBindingNodes(String keyString) {
+    return actual.bindings().stream()
+        .filter(binding -> binding.key().toString().equals(keyString))
+        .collect(toImmutableSet());
+  }
+
+  private static String keyString(String type) {
+    return type;
+  }
+
+  private static String keyString(String qualifier, String type) {
+    return String.format("%s %s", qualifier, type);
+  }
+
+  /** A Truth subject for a {@link Binding}. */
+  public final class BindingSubject extends Subject {
+
+    private final Binding actual;
+
+    BindingSubject(FailureMetadata metadata, @NullableDecl Binding actual) {
+      super(metadata, actual);
+      this.actual = actual;
+    }
+
+    /**
+     * Asserts that the binding depends on a binding with an unqualified key.
+     *
+     * @param type the canonical name of the type, as returned by {@link TypeMirror#toString()}
+     */
+    public void dependsOnBindingWithKey(String type) {
+      dependsOnBindingWithKeyString(keyString(type));
+    }
+
+    /**
+     * Asserts that the binding depends on a binding with a qualified key.
+     *
+     * @param qualifier the canonical string form of the qualifier, as returned by {@link
+     *     javax.lang.model.element.AnnotationMirror AnnotationMirror.toString()}
+     * @param type the canonical name of the type, as returned by {@link TypeMirror#toString()}
+     */
+    public void dependsOnBindingWithKey(String qualifier, String type) {
+      dependsOnBindingWithKeyString(keyString(qualifier, type));
+    }
+
+    private void dependsOnBindingWithKeyString(String keyString) {
+      if (actualBindingGraph().requestedBindings(actual).stream()
+          .noneMatch(binding -> binding.key().toString().equals(keyString))) {
+        failWithActual("expected to depend on binding with key", keyString);
+      }
+    }
+
+    private BindingGraph actualBindingGraph() {
+      return BindingGraphSubject.this.actual;
+    }
+  }
+}
diff --git a/java/dagger/testing/compile/BUILD b/java/dagger/testing/compile/BUILD
index 20e6a3d..dbad09f 100644
--- a/java/dagger/testing/compile/BUILD
+++ b/java/dagger/testing/compile/BUILD
@@ -24,9 +24,11 @@
     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",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/io",
+        "//third_party/java/compile_testing",
+        "//java/dagger/internal/codegen:processor",
+        "@maven//:com_github_tschuchortdev_kotlin_compile_testing",
     ],
 )
diff --git a/java/dagger/testing/compile/CompilerTests.java b/java/dagger/testing/compile/CompilerTests.java
index 6750d0c..a7fbef7 100644
--- a/java/dagger/testing/compile/CompilerTests.java
+++ b/java/dagger/testing/compile/CompilerTests.java
@@ -29,10 +29,10 @@
 import java.nio.file.Paths;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import com.tschuchort.compiletesting.KotlinCompilation;
+import dagger.internal.codegen.ComponentProcessor;
 
-/**
- * A helper class for working with java compiler tests.
- */
+/** A helper class for working with java compiler tests. */
 public final class CompilerTests {
   private CompilerTests() {}
 
@@ -53,6 +53,18 @@
     return javac().withClasspath(ImmutableList.of(compilerDepsJar()));
   }
 
+  public static KotlinCompilation kotlinCompiler() {
+    KotlinCompilation compilation = new KotlinCompilation();
+    compilation.setAnnotationProcessors(ImmutableList.of(new ComponentProcessor()));
+    compilation.setClasspaths(
+        ImmutableList.<java.io.File>builder()
+            .addAll(compilation.getClasspaths())
+            .add(compilerDepsJar())
+            .build()
+    );
+    return compilation;
+  }
+
   private static File getRunfilesDir() {
     return getRunfilesPath().toFile();
   }
diff --git a/javatests/artifacts/dagger-android/simple/build.gradle b/javatests/artifacts/dagger-android/simple/build.gradle
index d29aa0c..3fedf3b 100644
--- a/javatests/artifacts/dagger-android/simple/build.gradle
+++ b/javatests/artifacts/dagger-android/simple/build.gradle
@@ -16,11 +16,11 @@
 
 buildscript {
     ext {
-        agp_version = System.getenv('AGP_VERSION') ?: "4.2.0-beta04"
+        agp_version = System.getenv('AGP_VERSION') ?: "4.2.0"
     }
     repositories {
         google()
-        jcenter()
+        mavenCentral()
     }
     dependencies {
         classpath "com.android.tools.build:gradle:$agp_version"
@@ -30,7 +30,6 @@
 allprojects {
     repositories {
       google()
-      jcenter()
       mavenCentral()
       mavenLocal()
     }
diff --git a/javatests/artifacts/dagger-android/simple/gradle/wrapper/gradle-wrapper.properties b/javatests/artifacts/dagger-android/simple/gradle/wrapper/gradle-wrapper.properties
index 4d9ca16..0f80bbf 100644
--- a/javatests/artifacts/dagger-android/simple/gradle/wrapper/gradle-wrapper.properties
+++ b/javatests/artifacts/dagger-android/simple/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/java/dagger/example/gradle/simple/build.gradle b/javatests/artifacts/dagger/build-tests/build.gradle
similarity index 63%
rename from java/dagger/example/gradle/simple/build.gradle
rename to javatests/artifacts/dagger/build-tests/build.gradle
index c9e59ef..407c7ef 100644
--- a/java/dagger/example/gradle/simple/build.gradle
+++ b/javatests/artifacts/dagger/build-tests/build.gradle
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Dagger Authors.
+ * 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.
@@ -19,22 +19,13 @@
   id 'application'
 }
 
-repositories {
-  mavenCentral()
-  mavenLocal()
-}
-
-sourceSets {
-  main {
-    java {
-      srcDir '.'
-    }
-  }
+// Set the versions in a system property so that tests can access it.
+test {
+    systemProperty 'dagger_version', "$dagger_version"
 }
 
 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
+  testImplementation "com.google.truth:truth:$truth_version"
+  testImplementation "junit:junit:$junit_version"
+  testImplementation gradleTestKit()
+}
\ No newline at end of file
diff --git a/javatests/artifacts/dagger/build-tests/src/main/java/buildtests/GradleFile.java b/javatests/artifacts/dagger/build-tests/src/main/java/buildtests/GradleFile.java
new file mode 100644
index 0000000..6cd279c
--- /dev/null
+++ b/javatests/artifacts/dagger/build-tests/src/main/java/buildtests/GradleFile.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package buildtests;
+
+/** Stores the name and content of a file. */
+public final class GradleFile {
+  /** Creates a {@link GradleFile} with the given name and content */
+  public static GradleFile create(String fileName, String... fileContent) {
+    return new GradleFile(fileName, fileContent);
+  }
+
+  private final String fileName;
+  private final String[] fileContent;
+
+  GradleFile(String fileName, String... fileContent) {
+    this.fileName = fileName;
+    this.fileContent = fileContent;
+  }
+
+  String fileName() {
+    return fileName;
+  }
+
+  String[] fileContent() {
+    return fileContent;
+  }
+}
diff --git a/javatests/artifacts/dagger/build-tests/src/main/java/buildtests/GradleModule.java b/javatests/artifacts/dagger/build-tests/src/main/java/buildtests/GradleModule.java
new file mode 100644
index 0000000..d02dd91
--- /dev/null
+++ b/javatests/artifacts/dagger/build-tests/src/main/java/buildtests/GradleModule.java
@@ -0,0 +1,96 @@
+/*
+ * 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 buildtests;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+/** Used to create files for a Gradle module in a particular directory. */
+public final class GradleModule {
+  public static GradleModule create(File moduleDir) {
+    return new GradleModule(moduleDir);
+  }
+
+  public static GradleModule create(File projectDir, String moduleName) {
+    return new GradleModule(new File(projectDir, moduleName));
+  }
+
+  private final File moduleDir;
+  private final File moduleSrcDir;
+
+  private GradleModule(File moduleDir) {
+    this.moduleDir = moduleDir;
+    this.moduleSrcDir = new File(moduleDir, "src/main/java/");
+  }
+
+  public GradleModule addBuildFile(String... content) throws IOException {
+    writeFile(createFile(moduleDir, "build.gradle"), content);
+    return this;
+  }
+
+  public GradleModule addSettingsFile(String... content) throws IOException {
+    writeFile(createFile(moduleDir, "settings.gradle"), content);
+    return this;
+  }
+
+  public GradleModule addFile(GradleFile gradleFile) throws IOException {
+    return addFile(gradleFile.fileName(), gradleFile.fileContent());
+  }
+
+  public GradleModule addFile(String fileName, String... content) throws IOException {
+    writeFile(createFile(moduleDir, fileName), content);
+    return this;
+  }
+
+  public GradleModule addSrcFiles(GradleFile... gradleFiles) throws IOException {
+    for (GradleFile gradleFile : gradleFiles) {
+      addSrcFile(gradleFile.fileName(), gradleFile.fileContent());
+    }
+    return this;
+  }
+
+  public GradleModule addSrcFile(GradleFile gradleFile) throws IOException {
+    return addSrcFile(gradleFile.fileName(), gradleFile.fileContent());
+  }
+
+  public GradleModule addSrcFile(String fileName, String... content) throws IOException {
+    writeFile(createFile(moduleSrcDir, fileName), content);
+    return this;
+  }
+
+  private static File createFile(File dir, String fileName) {
+    File file = new File(dir, fileName);
+    file.getParentFile().mkdirs();
+    return file;
+  }
+
+  private static void writeFile(File destination, String... content) throws IOException {
+    BufferedWriter output = null;
+    try {
+      output = new BufferedWriter(new FileWriter(destination));
+      for (String line : content) {
+        output.write(line + "\n");
+      }
+    } finally {
+      if (output != null) {
+        output.close();
+      }
+    }
+  }
+}
diff --git a/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveBindsQualifierTest.java b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveBindsQualifierTest.java
new file mode 100644
index 0000000..6be8e3d
--- /dev/null
+++ b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveBindsQualifierTest.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package buildtests;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.gradle.testkit.runner.TaskOutcome.SUCCESS;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import org.gradle.testkit.runner.BuildResult;
+import org.gradle.testkit.runner.GradleRunner;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// This is a regression test for https://github.com/google/dagger/issues/3136
+@RunWith(Parameterized.class)
+public class TransitiveBindsQualifierTest {
+  @Parameters(name = "{0}")
+  public static Collection<Object[]> parameters() {
+    return Arrays.asList(new Object[][] {{ "implementation" }, { "api" }});
+  }
+
+  @Rule public TemporaryFolder folder = new TemporaryFolder();
+
+  private final String transitiveDependencyType;
+
+  public TransitiveBindsQualifierTest(String transitiveDependencyType) {
+    this.transitiveDependencyType = transitiveDependencyType;
+  }
+
+  @Test
+  public void testQualifierOnBindsMethod() throws IOException {
+    BuildResult result;
+    switch (transitiveDependencyType) {
+      case "implementation":
+        result = setupRunner().buildAndFail();
+        assertThat(result.getOutput()).contains("Task :app:compileJava FAILED");
+        assertThat(result.getOutput())
+            .contains(
+                "ComponentProcessingStep was unable to process 'app.MyComponent' because "
+                    + "'library2.MyQualifier' could not be resolved."
+                    + "\n  "
+                    + "\n  Dependency trace:"
+                    + "\n      => element (INTERFACE): library1.MyModule"
+                    + "\n      => element (METHOD): bindObject(java.lang.Number)"
+                    + "\n      => element (PARAMETER): arg0"
+                    + "\n      => annotation: @library2.MyQualifier");
+        break;
+      case "api":
+        result = setupRunner().build();
+        assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
+        assertThat(result.getOutput())
+            .contains("BINDINGS: ["
+                + "@library2.MyQualifier java.lang.Object, "
+                + "@library2.MyQualifier java.lang.Number, "
+                + "java.lang.Integer"
+                + "]");
+        break;
+    }
+  }
+
+  private GradleRunner setupRunner() throws IOException {
+    File projectDir = folder.getRoot();
+    GradleModule.create(projectDir)
+        .addSettingsFile(
+            "include 'app'",
+            "include 'library1'",
+            "include 'library2'",
+            "include 'spi-plugin'")
+        .addBuildFile(
+            "buildscript {",
+            "  ext {",
+            String.format("dagger_version = \"%s\"", System.getProperty("dagger_version")),
+            "  }",
+            "}",
+            "",
+            "allprojects {",
+            "  repositories {",
+            "    mavenCentral()",
+            "    mavenLocal()",
+            "  }",
+            "}");
+
+    GradleModule.create(projectDir, "app")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'application'",
+            "}",
+            "compileJava {",
+            "    options.compilerArgs << '-Adagger.pluginsVisitFullBindingGraphs=ENABLED'",
+            "}",
+            "dependencies {",
+            "  implementation project(':library1')",
+            "  annotationProcessor project(':spi-plugin')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "MyComponent.java",
+            "package app;",
+            "",
+            "import dagger.BindsInstance;",
+            "import dagger.Component;",
+            "import library1.MyModule;",
+            "",
+            "@Component(modules = MyModule.class)",
+            "public interface MyComponent {",
+            "  @Component.Factory",
+            "  interface Factory {",
+            "    MyComponent create(@BindsInstance int i);",
+            "  }",
+            "}");
+
+    GradleModule.create(projectDir, "library1")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            transitiveDependencyType + " project(':library2')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "MyModule.java",
+            "package library1;",
+            "",
+            "import dagger.Binds;",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "import library2.MyQualifier;",
+            "",
+            "@Module",
+            "public interface MyModule {",
+            "  @Binds",
+            "  @MyQualifier",
+            "  Object bindObject(@MyQualifier Number number);",
+            "",
+            "  @Binds",
+            "  @MyQualifier",
+            "  Number bindNumber(int i);",
+            "}");
+
+    GradleModule.create(projectDir, "library2")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            "  implementation 'javax.inject:javax.inject:1'",
+            "}")
+        .addSrcFile(
+            "MyQualifier.java",
+            "package library2;",
+            "",
+            "import javax.inject.Qualifier;",
+            "",
+            "@Qualifier",
+            "public @interface MyQualifier {}");
+
+    // This plugin is used to print output about bindings that we can assert on in tests.
+    GradleModule.create(projectDir, "spi-plugin")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "}",
+            "dependencies {",
+            "  implementation \"com.google.dagger:dagger-spi:$dagger_version\"",
+            "  implementation 'com.google.auto.service:auto-service-annotations:1.0.1'",
+            "  annotationProcessor 'com.google.auto.service:auto-service:1.0.1'",
+            "}")
+        .addSrcFile(
+            "TestBindingGraphPlugin.java",
+            "package spiplugin;",
+            "",
+            "import com.google.auto.service.AutoService;",
+            "import dagger.model.Binding;",
+            "import dagger.model.BindingGraph;",
+            "import dagger.model.BindingGraph.DependencyEdge;",
+            "import dagger.model.DependencyRequest;",
+            "import dagger.spi.BindingGraphPlugin;",
+            "import dagger.spi.DiagnosticReporter;",
+            "import java.util.stream.Collectors;",
+            "",
+            "@AutoService(BindingGraphPlugin.class)",
+            "public class TestBindingGraphPlugin implements BindingGraphPlugin {",
+            "  @Override",
+            "  public void visitGraph(",
+            "      BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {",
+            "    if (!bindingGraph.isFullBindingGraph() || bindingGraph.isModuleBindingGraph()) {",
+            "      return;",
+            "    }",
+            "    System.out.print(",
+            "        \"BINDINGS: \"",
+            "            + bindingGraph.bindings().stream()",
+            "                  .map(Binding::key)",
+            "                  .collect(Collectors.toList()));",
+            "  }",
+            "}");
+
+    return GradleRunner.create()
+        .withArguments("--stacktrace", "build")
+        .withProjectDir(projectDir);
+  }
+}
diff --git a/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveBindsScopeTest.java b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveBindsScopeTest.java
new file mode 100644
index 0000000..34716fe
--- /dev/null
+++ b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveBindsScopeTest.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package buildtests;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.gradle.testkit.runner.TaskOutcome.SUCCESS;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import org.gradle.testkit.runner.BuildResult;
+import org.gradle.testkit.runner.GradleRunner;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// This is a regression test for https://github.com/google/dagger/issues/3136
+@RunWith(Parameterized.class)
+public class TransitiveBindsScopeTest {
+  @Parameters(name = "{0}")
+  public static Collection<Object[]> parameters() {
+    return Arrays.asList(new Object[][] {{ "implementation" }, { "api" }});
+  }
+
+  @Rule public TemporaryFolder folder = new TemporaryFolder();
+
+  private final String transitiveDependencyType;
+
+  public TransitiveBindsScopeTest(String transitiveDependencyType) {
+    this.transitiveDependencyType = transitiveDependencyType;
+  }
+
+  @Test
+  public void testScopeOnBindsMethod() throws IOException {
+    BuildResult result;
+    switch (transitiveDependencyType) {
+      case "implementation":
+        result = setupRunner().buildAndFail();
+        assertThat(result.getOutput()).contains("Task :app:compileJava FAILED");
+        assertThat(result.getOutput())
+            .contains(
+                "ComponentProcessingStep was unable to process 'app.MyComponent' because "
+                    + "'library2.MyScope' could not be resolved."
+                    + "\n  "
+                    + "\n  Dependency trace:"
+                    + "\n      => element (INTERFACE): library1.MyModule"
+                    + "\n      => element (METHOD): bindObject(java.lang.String)"
+                    + "\n      => annotation: @library2.MyScope");
+        break;
+      case "api":
+        result = setupRunner().build();
+        assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
+        assertThat(result.getOutput())
+            .contains(
+                "@Binds @library2.MyScope Object library1.MyModule.bindObject(String): SCOPED");
+        break;
+    }
+  }
+
+  private GradleRunner setupRunner() throws IOException {
+    File projectDir = folder.getRoot();
+    GradleModule.create(projectDir)
+        .addSettingsFile(
+            "include 'app'",
+            "include 'library1'",
+            "include 'library2'",
+            "include 'spi-plugin'")
+        .addBuildFile(
+            "buildscript {",
+            "  ext {",
+            String.format("dagger_version = \"%s\"", System.getProperty("dagger_version")),
+            "  }",
+            "}",
+            "",
+            "allprojects {",
+            "  repositories {",
+            "    mavenCentral()",
+            "    mavenLocal()",
+            "  }",
+            "}");
+
+    GradleModule.create(projectDir, "app")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'application'",
+            "}",
+            "tasks.withType(JavaCompile) {",
+            "    options.compilerArgs += '-Adagger.experimentalDaggerErrorMessages=ENABLED'",
+            "}",
+            "dependencies {",
+            "  implementation project(':library1')",
+            "  annotationProcessor project(':spi-plugin')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "MyComponent.java",
+            "package app;",
+            "",
+            "import dagger.Component;",
+            "import library1.MySubcomponent;",
+            "",
+            "@Component",
+            "public interface MyComponent {",
+            "  MySubcomponent subcomponent();",
+            "}");
+
+    GradleModule.create(projectDir, "library1")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            transitiveDependencyType + " project(':library2')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        // Note: In order to repro the issue we place MyScope on a subcomponent so that it can be a
+        // transitive dependency of the component. If MyScope was placed on directly on the
+        // component, it would need to be a direct dependency of the component.
+        .addSrcFile(
+            "MySubcomponent.java",
+            "package library1;",
+            "",
+            "import dagger.Subcomponent;",
+            "import library2.MyScope;",
+            "",
+            "@MyScope",
+            "@Subcomponent(modules = MyModule.class)",
+            "public interface MySubcomponent {",
+            "  Object object();",
+            "}")
+        .addSrcFile(
+            "MyModule.java",
+            "package library1;",
+            "",
+            "import dagger.Binds;",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "import library2.MyScope;",
+            "",
+            "@Module",
+            "public interface MyModule {",
+            "  @MyScope",
+            "  @Binds",
+            "  Object bindObject(String string);",
+            "",
+            "  @Provides",
+            "  static String provideString() {",
+            "    return \"\";",
+            "  }",
+            "}");
+
+    GradleModule.create(projectDir, "library2")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            "  implementation 'javax.inject:javax.inject:1'",
+            "}")
+        .addSrcFile(
+            "MyScope.java",
+            "package library2;",
+            "",
+            "import javax.inject.Scope;",
+            "",
+            "@Scope",
+            "public @interface MyScope {}");
+
+    // This plugin is used to print output about bindings that we can assert on in tests.
+    GradleModule.create(projectDir, "spi-plugin")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "}",
+            "dependencies {",
+            "  implementation \"com.google.dagger:dagger-spi:$dagger_version\"",
+            "  implementation 'com.google.auto.service:auto-service-annotations:1.0.1'",
+            "  annotationProcessor 'com.google.auto.service:auto-service:1.0.1'",
+            "}")
+        .addSrcFile(
+            "TestBindingGraphPlugin.java",
+            "package spiplugin;",
+            "",
+            "import com.google.auto.service.AutoService;",
+            "import dagger.model.BindingGraph;",
+            "import dagger.model.BindingGraph.DependencyEdge;",
+            "import dagger.model.DependencyRequest;",
+            "import dagger.spi.BindingGraphPlugin;",
+            "import dagger.spi.DiagnosticReporter;",
+            "",
+            "@AutoService(BindingGraphPlugin.class)",
+            "public class TestBindingGraphPlugin implements BindingGraphPlugin {",
+            "  @Override",
+            "  public void visitGraph(",
+            "      BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {",
+            "    bindingGraph.bindings().stream()",
+            "        .filter(binding -> binding.scope().isPresent())",
+            "        .forEach(binding -> System.out.println(binding + \": SCOPED\"));",
+            "    bindingGraph.bindings().stream()",
+            "        .filter(binding -> !binding.scope().isPresent())",
+            "        .forEach(binding -> System.out.println(binding + \": UNSCOPED\"));",
+            "  }",
+            "}");
+
+    return GradleRunner.create()
+        .withArguments("--stacktrace", "build")
+        .withProjectDir(projectDir);
+  }
+}
diff --git a/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveComponentDependenciesTest.java b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveComponentDependenciesTest.java
new file mode 100644
index 0000000..9a1adcf
--- /dev/null
+++ b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveComponentDependenciesTest.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package buildtests;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.gradle.testkit.runner.TaskOutcome.SUCCESS;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import org.gradle.testkit.runner.BuildResult;
+import org.gradle.testkit.runner.GradleRunner;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// This is a regression test for https://github.com/google/dagger/issues/3136
+@RunWith(Parameterized.class)
+public class TransitiveComponentDependenciesTest {
+  @Parameters(name = "{0}")
+  public static Collection<Object[]> parameters() {
+    return Arrays.asList(new Object[][] {{"implementation"}, {"api"}});
+  }
+
+  @Rule public TemporaryFolder folder = new TemporaryFolder();
+
+  private final String transitiveDependencyType;
+
+  public TransitiveComponentDependenciesTest(String transitiveDependencyType) {
+    this.transitiveDependencyType = transitiveDependencyType;
+  }
+
+  @Test
+  public void testsComponentDependencies() throws IOException {
+    BuildResult result;
+    switch (transitiveDependencyType) {
+      case "implementation":
+        result = setupRunner().buildAndFail();
+        assertThat(result.getOutput()).contains("Task :app:compileJava FAILED");
+        String expectedErrorMsg =
+            "error: ComponentProcessingStep was unable to process 'app.ComponentC' because"
+                + " 'libraryA.ComponentA' could not be resolved."
+                + "\n  "
+                + "\n  Dependency trace:"
+                + "\n      => element (CLASS): libraryB.ComponentB"
+                + "\n      => annotation:"
+                + " @dagger.Component(dependencies = libraryA.ComponentA.class)"
+                + "\n      => annotation method: java.lang.Class<?>[] dependencies()"
+                + "\n      => annotation value (ARRAY):"
+                + " value 'libraryA.ComponentA.class' with expected type java.lang.Class<?>[]"
+                + "\n      => annotation value (TYPE):"
+                + " value 'libraryA.ComponentA' with expected type java.lang.Class<?>"
+                + "\n      => type (ERROR annotation value type): libraryA.ComponentA";
+        assertThat(result.getOutput()).contains(expectedErrorMsg);
+        break;
+      case "api":
+        result = setupRunner().build();
+        assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
+        break;
+    }
+  }
+
+  private GradleRunner setupRunner() throws IOException {
+    File projectDir = folder.getRoot();
+    GradleModule.create(projectDir)
+        .addSettingsFile("include 'app'", "include 'libraryB'", "include 'libraryA'")
+        .addBuildFile(
+            "buildscript {",
+            "  ext {",
+            String.format("dagger_version = \"%s\"", System.getProperty("dagger_version")),
+            "  }",
+            "}",
+            "",
+            "allprojects {",
+            "  repositories {",
+            "    mavenCentral()",
+            "    mavenLocal()",
+            "  }",
+            "}");
+
+    GradleModule.create(projectDir, "app")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'application'",
+            "}",
+            "tasks.withType(JavaCompile) {",
+            "    options.compilerArgs += '-Adagger.experimentalDaggerErrorMessages=ENABLED'",
+            "}",
+            "dependencies {",
+            "  implementation project(':libraryB')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "ComponentC.java",
+            "package app;",
+            "",
+            "import dagger.Component;",
+            "import libraryB.ComponentB;",
+            "",
+            "@Component(dependencies = ComponentB.class)",
+            "public interface ComponentC {",
+            "  public abstract C getC();",
+            "}")
+        .addSrcFile(
+            "C.java",
+            "package app;",
+            "",
+            "import javax.inject.Inject;",
+            "import libraryB.B;",
+            "",
+            "public class C {",
+            "  @Inject C(B b) {}",
+            "}");
+
+    GradleModule.create(projectDir, "libraryB")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            transitiveDependencyType + " project(':libraryA')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "ComponentB.java",
+            "package libraryB;",
+            "",
+            "import dagger.Component;",
+            "import libraryA.ComponentA;",
+            "",
+            "@Component(dependencies = ComponentA.class)",
+            "public abstract class ComponentB {",
+            "  public abstract B getB();",
+            "}")
+        .addSrcFile(
+            "B.java",
+            "package libraryB;",
+            "",
+            "import javax.inject.Inject;",
+            "import libraryA.A;",
+            "",
+            "public class B {",
+            "  @Inject B(A a) {}",
+            "}");
+
+    GradleModule.create(projectDir, "libraryA")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "ComponentA.java",
+            "package libraryA;",
+            "",
+            "import dagger.Component;",
+            "",
+            "@Component",
+            "public abstract class ComponentA {",
+            "  public abstract A getA();",
+            "}")
+        .addSrcFile(
+            "A.java",
+            "package libraryA;",
+            "",
+            "import javax.inject.Inject;",
+            "",
+            "public class A {",
+            "  @Inject A() {}",
+            "}")
+        .addSrcFile(
+            "AScope.java",
+            "package libraryA;",
+            "",
+            "import javax.inject.Scope;",
+            "",
+            "@Scope",
+            "public @interface AScope {}");
+
+    return GradleRunner.create().withArguments("--stacktrace", "build").withProjectDir(projectDir);
+  }
+}
diff --git a/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveMapKeyTest.java b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveMapKeyTest.java
new file mode 100644
index 0000000..0458b08
--- /dev/null
+++ b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveMapKeyTest.java
@@ -0,0 +1,157 @@
+/*
+ * 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 buildtests;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.gradle.testkit.runner.TaskOutcome.SUCCESS;
+
+import java.io.File;
+import java.io.IOException;
+import org.gradle.testkit.runner.BuildResult;
+import org.gradle.testkit.runner.GradleRunner;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+// This is a regression test for https://github.com/google/dagger/issues/3133
+@RunWith(JUnit4.class)
+public class TransitiveMapKeyTest {
+  @Rule public TemporaryFolder folder = new TemporaryFolder();
+
+  @Test
+  public void testTransitiveMapKey_WithImplementation() throws IOException {
+    BuildResult result = setupRunnerWith("implementation").buildAndFail();
+    assertThat(result.getOutput()).contains("Task :app:compileJava FAILED");
+    assertThat(result.getOutput())
+        .contains(
+            "Missing map key annotation for method: library1.MyModule#provideString(). "
+                + "That method was annotated with: "
+                + "@dagger.Provides,"
+                + "@dagger.multibindings.IntoMap,"
+                + "@library2.MyMapKey(\"some-key\")");
+  }
+
+  @Test
+  public void testTransitiveMapKey_WithApi() throws IOException {
+    // Test that if we use an "api" dependency for the custom map key things work properly.
+    BuildResult result = setupRunnerWith("api").build();
+    assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
+  }
+
+  private GradleRunner setupRunnerWith(String dependencyType) throws IOException {
+    File projectDir = folder.getRoot();
+    GradleModule.create(projectDir)
+        .addSettingsFile(
+            "include 'app'",
+            "include 'library1'",
+            "include 'library2'")
+        .addBuildFile(
+            "buildscript {",
+            "  ext {",
+            String.format("dagger_version = \"%s\"", System.getProperty("dagger_version")),
+            "  }",
+            "}",
+            "",
+            "allprojects {",
+            "  repositories {",
+            "    mavenCentral()",
+            "    mavenLocal()",
+            "  }",
+            "}");
+
+    GradleModule.create(projectDir, "app")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'application'",
+            "}",
+            "dependencies {",
+            "  implementation project(':library1')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "MyComponent.java",
+            "package app;",
+            "",
+            "import dagger.Component;",
+            "import library1.MyModule;",
+            "import java.util.Map;",
+            "",
+            "@Component(modules = MyModule.class)",
+            "public interface MyComponent {",
+            "  Map<String, String> multiMap();",
+            "}");
+
+    GradleModule.create(projectDir, "library1")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            dependencyType + " project(':library2')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "MyModule.java",
+            "package library1;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "import dagger.multibindings.IntoMap;",
+            "import library2.MyMapKey;",
+            "",
+            "@Module",
+            "public interface MyModule {",
+            "  @Provides",
+            "  @IntoMap",
+            "  @MyMapKey(\"some-key\")",
+            "  static String provideString() {",
+            "    return \"\";",
+            "  }",
+            "}");
+
+    GradleModule.create(projectDir, "library2")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "MyMapKey.java",
+            "package library2;",
+            "",
+            "import dagger.MapKey;",
+            "",
+            "@MapKey",
+            "public @interface MyMapKey {",
+            "  String value();",
+            "}");
+
+    return GradleRunner.create()
+        .withArguments("--stacktrace", "build")
+        .withProjectDir(projectDir);
+  }
+}
diff --git a/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveProvidesParameterizedTypeTest.java b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveProvidesParameterizedTypeTest.java
new file mode 100644
index 0000000..181c9d2
--- /dev/null
+++ b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveProvidesParameterizedTypeTest.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package buildtests;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.gradle.testkit.runner.TaskOutcome.SUCCESS;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import org.gradle.testkit.runner.BuildResult;
+import org.gradle.testkit.runner.GradleRunner;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// This is a regression test for https://github.com/google/dagger/issues/3163
+@RunWith(Parameterized.class)
+public class TransitiveProvidesParameterizedTypeTest {
+  @Parameters(name = "{0}")
+  public static Collection<Object[]> parameters() {
+    return Arrays.asList(new Object[][] {{ "implementation" }, { "api" }});
+  }
+
+  @Rule public TemporaryFolder folder = new TemporaryFolder();
+
+  private final String transitiveDependencyType;
+
+  public TransitiveProvidesParameterizedTypeTest(String transitiveDependencyType) {
+    this.transitiveDependencyType = transitiveDependencyType;
+  }
+
+  @Test
+  public void testQualifierOnProvidesMethodParameter() throws IOException {
+    BuildResult result;
+    switch (transitiveDependencyType) {
+      case "implementation":
+        result = setupRunner().buildAndFail();
+        assertThat(result.getOutput()).contains("Task :app:compileJava FAILED");
+        assertThat(result.getOutput())
+            .contains(
+                "error: ComponentProcessingStep was unable to process 'app.MyComponent' because"
+                    + " 'library2.TransitiveType' could not be resolved."
+                    + "\n  "
+                    + "\n  Dependency trace:"
+                    + "\n      => element (INTERFACE): library1.MyModule"
+                    + "\n      => element (METHOD):"
+                    + " provideInt(library2.TransitiveType<java.lang.String>)"
+                    + "\n      => type (EXECUTABLE method):"
+                    + " (library2.TransitiveType<java.lang.String>)java.lang.String"
+                    + "\n      => type (ERROR parameter type):"
+                    + " library2.TransitiveType<java.lang.String>");
+        break;
+      case "api":
+        result = setupRunner().build();
+        assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
+        assertThat(result.getOutput())
+            .contains("Binding: library2.TransitiveType<java.lang.String>");
+        break;
+    }
+  }
+
+  private GradleRunner setupRunner() throws IOException {
+    File projectDir = folder.getRoot();
+    GradleModule.create(projectDir)
+        .addSettingsFile(
+            "include 'app'",
+            "include 'library1'",
+            "include 'library2'",
+            "include 'spi-plugin'")
+        .addBuildFile(
+            "buildscript {",
+            "  ext {",
+            String.format("dagger_version = \"%s\"", System.getProperty("dagger_version")),
+            "  }",
+            "}",
+            "",
+            "allprojects {",
+            "  repositories {",
+            "    mavenCentral()",
+            "    mavenLocal()",
+            "  }",
+            "}");
+
+    GradleModule.create(projectDir, "app")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'application'",
+            "}",
+            "tasks.withType(JavaCompile) {",
+            "    options.compilerArgs += '-Adagger.experimentalDaggerErrorMessages=ENABLED'",
+            "}",
+            "dependencies {",
+            "  implementation project(':library1')",
+            "  annotationProcessor project(':spi-plugin')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "MyComponent.java",
+            "package app;",
+            "",
+            "import dagger.Component;",
+            "import library1.MyModule;",
+            "",
+            "@Component(modules = MyModule.class)",
+            "public interface MyComponent {",
+            "  String string();",
+            "}");
+
+    GradleModule.create(projectDir, "library1")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            transitiveDependencyType + " project(':library2')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "MyModule.java",
+            "package library1;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "import library2.TransitiveType;",
+            "",
+            "@Module",
+            "public interface MyModule {",
+            "  @Provides",
+            "  static String provideInt(TransitiveType<String> transitiveType) {",
+            "    return \"\";",
+            "  }",
+            "",
+            "  @Provides",
+            "  static TransitiveType<String> provideTransitiveType() {",
+            "    return new TransitiveType<>();",
+            "  }",
+            "}");
+
+    GradleModule.create(projectDir, "library2")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            "  implementation 'javax.inject:javax.inject:1'",
+            "}")
+        .addSrcFile(
+            "TransitiveType.java",
+            "package library2;",
+            "",
+            "public class TransitiveType<T> {}");
+
+    // This plugin is used to print output about bindings that we can assert on in tests.
+    GradleModule.create(projectDir, "spi-plugin")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "}",
+            "dependencies {",
+            "  implementation \"com.google.dagger:dagger-spi:$dagger_version\"",
+            "  implementation 'com.google.auto.service:auto-service-annotations:1.0.1'",
+            "  annotationProcessor 'com.google.auto.service:auto-service:1.0.1'",
+            "}")
+        .addSrcFile(
+            "TestBindingGraphPlugin.java",
+            "package spiplugin;",
+            "",
+            "import com.google.auto.service.AutoService;",
+            "import dagger.model.Binding;",
+            "import dagger.model.BindingGraph;",
+            "import dagger.model.BindingGraph.DependencyEdge;",
+            "import dagger.model.DependencyRequest;",
+            "import dagger.spi.BindingGraphPlugin;",
+            "import dagger.spi.DiagnosticReporter;",
+            "",
+            "@AutoService(BindingGraphPlugin.class)",
+            "public class TestBindingGraphPlugin implements BindingGraphPlugin {",
+            "  @Override",
+            "  public void visitGraph(",
+            "      BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {",
+            "    bindingGraph.bindings().stream()",
+            "        .map(Binding::key)",
+            "        .forEach(key -> System.out.println(\"Binding: \" + key));",
+            "  }",
+            "}");
+
+    return GradleRunner.create()
+        .withArguments("--stacktrace", "build")
+        .withProjectDir(projectDir);
+  }
+}
diff --git a/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveProvidesQualifierTest.java b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveProvidesQualifierTest.java
new file mode 100644
index 0000000..aaf7745
--- /dev/null
+++ b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveProvidesQualifierTest.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package buildtests;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.gradle.testkit.runner.TaskOutcome.SUCCESS;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import org.gradle.testkit.runner.BuildResult;
+import org.gradle.testkit.runner.GradleRunner;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// This is a regression test for https://github.com/google/dagger/issues/3136
+@RunWith(Parameterized.class)
+public class TransitiveProvidesQualifierTest {
+  @Parameters(name = "{0}")
+  public static Collection<Object[]> parameters() {
+    return Arrays.asList(new Object[][] {{ "implementation" }, { "api" }});
+  }
+
+  @Rule public TemporaryFolder folder = new TemporaryFolder();
+
+  private final String transitiveDependencyType;
+
+  public TransitiveProvidesQualifierTest(String transitiveDependencyType) {
+    this.transitiveDependencyType = transitiveDependencyType;
+  }
+
+  @Test
+  public void testQualifierOnProvidesMethodParameter() throws IOException {
+    BuildResult result;
+    switch (transitiveDependencyType) {
+      case "implementation":
+        result = setupRunner().buildAndFail();
+        assertThat(result.getOutput()).contains("Task :app:compileJava FAILED");
+        assertThat(result.getOutput())
+            .contains(
+                "ComponentProcessingStep was unable to process 'app.MyComponent' because "
+                    + "'library2.MyQualifier' could not be resolved."
+                    + "\n  "
+                    + "\n  Dependency trace:"
+                    + "\n      => element (INTERFACE): library1.MyModule"
+                    + "\n      => element (METHOD): provideString(int)"
+                    + "\n      => element (PARAMETER): i"
+                    + "\n      => annotation: @library2.MyQualifier");
+        break;
+      case "api":
+        result = setupRunner().build();
+        assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
+        assertThat(result.getOutput()).contains("REQUEST: @library2.MyQualifier java.lang.Integer");
+        break;
+    }
+  }
+
+  private GradleRunner setupRunner() throws IOException {
+    File projectDir = folder.getRoot();
+    GradleModule.create(projectDir)
+        .addSettingsFile(
+            "include 'app'",
+            "include 'library1'",
+            "include 'library2'",
+            "include 'spi-plugin'")
+        .addBuildFile(
+            "buildscript {",
+            "  ext {",
+            String.format("dagger_version = \"%s\"", System.getProperty("dagger_version")),
+            "  }",
+            "}",
+            "",
+            "allprojects {",
+            "  repositories {",
+            "    mavenCentral()",
+            "    mavenLocal()",
+            "  }",
+            "}");
+
+    GradleModule.create(projectDir, "app")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'application'",
+            "}",
+            "tasks.withType(JavaCompile) {",
+            "    options.compilerArgs += '-Adagger.experimentalDaggerErrorMessages=ENABLED'",
+            "}",
+            "dependencies {",
+            "  implementation project(':library1')",
+            "  annotationProcessor project(':spi-plugin')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "MyComponent.java",
+            "package app;",
+            "",
+            "import dagger.Component;",
+            "import library1.MyModule;",
+            "",
+            "@Component(modules = MyModule.class)",
+            "public interface MyComponent {",
+            "  String string();",
+            "}");
+
+    GradleModule.create(projectDir, "library1")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            transitiveDependencyType + " project(':library2')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "MyModule.java",
+            "package library1;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "import library2.MyQualifier;",
+            "",
+            "@Module",
+            "public interface MyModule {",
+            "  @Provides",
+            "  static String provideString(@MyQualifier int i) {",
+            "    return \"\";",
+            "  }",
+            "",
+            "  @Provides",
+            "  @MyQualifier",
+            "  static int provideInt() {",
+            "    return 0;",
+            "  }",
+            "}");
+
+    GradleModule.create(projectDir, "library2")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            "  implementation 'javax.inject:javax.inject:1'",
+            "}")
+        .addSrcFile(
+            "MyQualifier.java",
+            "package library2;",
+            "",
+            "import javax.inject.Qualifier;",
+            "",
+            "@Qualifier",
+            "public @interface MyQualifier {}");
+
+    // This plugin is used to print output about bindings that we can assert on in tests.
+    GradleModule.create(projectDir, "spi-plugin")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "}",
+            "dependencies {",
+            "  implementation \"com.google.dagger:dagger-spi:$dagger_version\"",
+            "  implementation 'com.google.auto.service:auto-service-annotations:1.0.1'",
+            "  annotationProcessor 'com.google.auto.service:auto-service:1.0.1'",
+            "}")
+        .addSrcFile(
+            "TestBindingGraphPlugin.java",
+            "package spiplugin;",
+            "",
+            "import com.google.auto.service.AutoService;",
+            "import dagger.model.BindingGraph;",
+            "import dagger.model.BindingGraph.DependencyEdge;",
+            "import dagger.model.DependencyRequest;",
+            "import dagger.spi.BindingGraphPlugin;",
+            "import dagger.spi.DiagnosticReporter;",
+            "",
+            "@AutoService(BindingGraphPlugin.class)",
+            "public class TestBindingGraphPlugin implements BindingGraphPlugin {",
+            "  @Override",
+            "  public void visitGraph(",
+            "      BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {",
+            "    bindingGraph.dependencyEdges().stream()",
+            "        .map(DependencyEdge::dependencyRequest)",
+            "        .map(DependencyRequest::key)",
+            "        .forEach(key -> System.out.println(\"REQUEST: \" + key));",
+            "  }",
+            "}");
+
+    return GradleRunner.create()
+        .withArguments("--stacktrace", "build")
+        .withProjectDir(projectDir);
+  }
+}
diff --git a/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveProvidesScopeTest.java b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveProvidesScopeTest.java
new file mode 100644
index 0000000..36f3cdc
--- /dev/null
+++ b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveProvidesScopeTest.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package buildtests;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.gradle.testkit.runner.TaskOutcome.SUCCESS;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import org.gradle.testkit.runner.BuildResult;
+import org.gradle.testkit.runner.GradleRunner;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// This is a regression test for https://github.com/google/dagger/issues/3136
+@RunWith(Parameterized.class)
+public class TransitiveProvidesScopeTest {
+  @Parameters(name = "{0}")
+  public static Collection<Object[]> parameters() {
+    return Arrays.asList(new Object[][] {{ "implementation" }, { "api" }});
+  }
+
+  @Rule public TemporaryFolder folder = new TemporaryFolder();
+
+  private final String transitiveDependencyType;
+
+  public TransitiveProvidesScopeTest(String transitiveDependencyType) {
+    this.transitiveDependencyType = transitiveDependencyType;
+  }
+
+  @Test
+  public void testScopeOnProvidesMethod() throws IOException {
+    BuildResult result;
+    switch (transitiveDependencyType) {
+      case "implementation":
+        result = setupRunner().buildAndFail();
+        assertThat(result.getOutput()).contains("Task :app:compileJava FAILED");
+        assertThat(result.getOutput())
+            .contains(
+                "ComponentProcessingStep was unable to process 'app.MyComponent' because "
+                    + "'library2.MyScope' could not be resolved."
+                    + "\n  "
+                    + "\n  Dependency trace:"
+                    + "\n      => element (INTERFACE): library1.MyModule"
+                    + "\n      => element (METHOD): provideString()"
+                    + "\n      => annotation: @library2.MyScope");
+        break;
+      case "api":
+        result = setupRunner().build();
+        assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
+        assertThat(result.getOutput())
+            .contains(
+                "@Provides @library2.MyScope String library1.MyModule.provideString(): SCOPED");
+        break;
+    }
+  }
+
+  private GradleRunner setupRunner() throws IOException {
+    File projectDir = folder.getRoot();
+    GradleModule.create(projectDir)
+        .addSettingsFile(
+            "include 'app'",
+            "include 'library1'",
+            "include 'library2'",
+            "include 'spi-plugin'")
+        .addBuildFile(
+            "buildscript {",
+            "  ext {",
+            String.format("dagger_version = \"%s\"", System.getProperty("dagger_version")),
+            "  }",
+            "}",
+            "",
+            "allprojects {",
+            "  repositories {",
+            "    mavenCentral()",
+            "    mavenLocal()",
+            "  }",
+            "}");
+
+    GradleModule.create(projectDir, "app")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'application'",
+            "}",
+            "tasks.withType(JavaCompile) {",
+            "    options.compilerArgs += '-Adagger.experimentalDaggerErrorMessages=ENABLED'",
+            "}",
+            "dependencies {",
+            "  implementation project(':library1')",
+            "  annotationProcessor project(':spi-plugin')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "MyComponent.java",
+            "package app;",
+            "",
+            "import dagger.Component;",
+            "import library1.MySubcomponent;",
+            "",
+            "@Component",
+            "public interface MyComponent {",
+            "  MySubcomponent subcomponent();",
+            "}");
+
+    GradleModule.create(projectDir, "library1")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            transitiveDependencyType + " project(':library2')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        // Note: In order to repro the issue we place MyScope on a subcomponent so that it can be a
+        // transitive dependency of the component. If MyScope was placed on directly on the
+        // component, it would need to be a direct dependency of the component.
+        .addSrcFile(
+            "MySubcomponent.java",
+            "package library1;",
+            "",
+            "import dagger.Subcomponent;",
+            "import library2.MyScope;",
+            "",
+            "@MyScope",
+            "@Subcomponent(modules = MyModule.class)",
+            "public interface MySubcomponent {",
+            "  String string();",
+            "}")
+        .addSrcFile(
+            "MyModule.java",
+            "package library1;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "import library2.MyScope;",
+            "",
+            "@Module",
+            "public interface MyModule {",
+            "  @MyScope",
+            "  @Provides",
+            "  static String provideString() {",
+            "    return \"\";",
+            "  }",
+            "}");
+
+    GradleModule.create(projectDir, "library2")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            "  implementation 'javax.inject:javax.inject:1'",
+            "}")
+        .addSrcFile(
+            "MyScope.java",
+            "package library2;",
+            "",
+            "import javax.inject.Scope;",
+            "",
+            "@Scope",
+            "public @interface MyScope {}");
+
+    // This plugin is used to print output about bindings that we can assert on in tests.
+    GradleModule.create(projectDir, "spi-plugin")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "}",
+            "dependencies {",
+            "  implementation \"com.google.dagger:dagger-spi:$dagger_version\"",
+            "  implementation 'com.google.auto.service:auto-service-annotations:1.0.1'",
+            "  annotationProcessor 'com.google.auto.service:auto-service:1.0.1'",
+            "}")
+        .addSrcFile(
+            "TestBindingGraphPlugin.java",
+            "package spiplugin;",
+            "",
+            "import com.google.auto.service.AutoService;",
+            "import dagger.model.BindingGraph;",
+            "import dagger.model.BindingGraph.DependencyEdge;",
+            "import dagger.model.DependencyRequest;",
+            "import dagger.spi.BindingGraphPlugin;",
+            "import dagger.spi.DiagnosticReporter;",
+            "",
+            "@AutoService(BindingGraphPlugin.class)",
+            "public class TestBindingGraphPlugin implements BindingGraphPlugin {",
+            "  @Override",
+            "  public void visitGraph(",
+            "      BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {",
+            "    bindingGraph.bindings().stream()",
+            "        .filter(binding -> binding.scope().isPresent())",
+            "        .forEach(binding -> System.out.println(binding + \": SCOPED\"));",
+            "    bindingGraph.bindings().stream()",
+            "        .filter(binding -> !binding.scope().isPresent())",
+            "        .forEach(binding -> System.out.println(binding + \": UNSCOPED\"));",
+            "  }",
+            "}");
+
+    return GradleRunner.create()
+        .withArguments("--stacktrace", "build")
+        .withProjectDir(projectDir);
+  }
+}
diff --git a/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveQualifierTest.java b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveQualifierTest.java
new file mode 100644
index 0000000..1cf1ba5
--- /dev/null
+++ b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveQualifierTest.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package buildtests;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.gradle.testkit.runner.TaskOutcome.SUCCESS;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import org.gradle.testkit.runner.BuildResult;
+import org.gradle.testkit.runner.GradleRunner;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// This is a regression test for https://github.com/google/dagger/issues/3136
+@RunWith(Parameterized.class)
+public class TransitiveQualifierTest {
+  @Parameters(name = "transitiveDependencyType = {0}, strictSuperficialValidationMode = {1}")
+  public static Collection<Object[]> parameters() {
+    return Arrays.asList(
+        new Object[][] {
+          { "implementation", "ENABLED" },
+          { "implementation", "DISABLED" },
+          { "api", "ENABLED" },
+          { "api", "DISABLED" }
+        });
+  }
+
+  @Rule public TemporaryFolder folder = new TemporaryFolder();
+
+  private final String transitiveDependencyType;
+  private final String strictSuperficialValidationMode;
+
+  public TransitiveQualifierTest(
+      String transitiveDependencyType, String strictSuperficialValidationMode) {
+    this.transitiveDependencyType = transitiveDependencyType;
+    this.strictSuperficialValidationMode = strictSuperficialValidationMode;
+  }
+
+  @Test
+  public void testQualifierOnInjectConstructorParameter() throws IOException {
+    GradleRunner runner =
+        setupRunnerWith(
+            GradleFile.create(
+                "QualifierUsage.java",
+                "package library1;",
+                "",
+                "import javax.inject.Inject;",
+                "import library2.MyQualifier;",
+                "",
+                "public class QualifierUsage {",
+                "  @Inject QualifierUsage(@MyQualifier int i) {}",
+                "}"));
+    BuildResult result;
+    switch (transitiveDependencyType) {
+      case "implementation":
+        switch (strictSuperficialValidationMode) {
+          case "ENABLED":
+            result = runner.buildAndFail();
+            assertThat(result.getOutput()).contains("Task :app:compileJava FAILED");
+            // TODO(bcorso): Give more context about what couldn't be resolved once we've fixed the
+            // issue described in https://github.com/google/dagger/issues/2208.
+            assertThat(result.getOutput())
+                .contains(
+                    "ComponentProcessingStep was unable to process 'app.MyComponent' because "
+                        + "'library2.MyQualifier' could not be resolved."
+                        + "\n  "
+                        + "\n  Dependency trace:"
+                        + "\n      => element (INTERFACE): library1.MyModule"
+                        + "\n      => element (METHOD): provideInt()"
+                        + "\n      => annotation: @library2.MyQualifier");
+            break;
+          case "DISABLED":
+            // When strict mode is disabled we fall back to the old behavior where the qualifier is
+            // missing and we do not throw an exception.
+            result = runner.build();
+            assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
+            assertThat(result.getOutput()).contains("REQUEST: java.lang.Integer");
+            break;
+          default: throw new AssertionError("Unexpected mode: " + strictSuperficialValidationMode);
+        }
+        break;
+      case "api":
+        result = runner.build();
+        assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
+        assertThat(result.getOutput()).contains("REQUEST: @library2.MyQualifier java.lang.Integer");
+        break;
+    }
+  }
+
+  @Test
+  public void testQualifierOnInjectField() throws IOException {
+    GradleRunner runner =
+        setupRunnerWith(
+            GradleFile.create(
+                "QualifierUsage.java",
+                "package library1;",
+                "",
+                "import javax.inject.Inject;",
+                "import library2.MyQualifier;",
+                "",
+                "public class QualifierUsage {",
+                "  @Inject @MyQualifier int i;",
+                "",
+                "  @Inject QualifierUsage() {}",
+                "}"));
+    BuildResult result;
+    switch (transitiveDependencyType) {
+      case "implementation":
+        switch (strictSuperficialValidationMode) {
+          case "ENABLED":
+            result = runner.buildAndFail();
+            assertThat(result.getOutput()).contains("Task :app:compileJava FAILED");
+            // TODO(bcorso): Give more context about what couldn't be resolved once we've fixed the
+            // issue described in https://github.com/google/dagger/issues/2208.
+            assertThat(result.getOutput())
+                .contains(
+                    "ComponentProcessingStep was unable to process 'app.MyComponent' because "
+                        + "'library2.MyQualifier' could not be resolved."
+                        + "\n  "
+                        + "\n  Dependency trace:"
+                        + "\n      => element (INTERFACE): library1.MyModule"
+                        + "\n      => element (METHOD): provideInt()"
+                        + "\n      => annotation: @library2.MyQualifier");
+            break;
+          case "DISABLED":
+            // When strict mode is disabled we fall back to the old behavior where the qualifier is
+            // missing and we do not throw an exception.
+            result = runner.build();
+            assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
+            assertThat(result.getOutput()).contains("REQUEST: java.lang.Integer");
+            break;
+          default: throw new AssertionError("Unexpected mode: " + strictSuperficialValidationMode);
+        }
+        break;
+      case "api":
+        result = runner.build();
+        assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
+        assertThat(result.getOutput()).contains("REQUEST: @library2.MyQualifier java.lang.Integer");
+        break;
+    }
+  }
+
+  @Test
+  public void testQualifierOnInjectMethodParameter() throws IOException {
+    GradleRunner runner =
+        setupRunnerWith(
+            GradleFile.create(
+                "QualifierUsage.java",
+                "package library1;",
+                "",
+                "import javax.inject.Inject;",
+                "import library2.MyQualifier;",
+                "",
+                "public class QualifierUsage {",
+                "  @Inject QualifierUsage() {}",
+                "",
+                "  @Inject void injectMethod(@MyQualifier int i) {}",
+                "}"));
+    BuildResult result;
+    switch (transitiveDependencyType) {
+      case "implementation":
+        switch (strictSuperficialValidationMode) {
+          case "ENABLED":
+            result = runner.buildAndFail();
+            assertThat(result.getOutput()).contains("Task :app:compileJava FAILED");
+            // TODO(bcorso): Give more context about what couldn't be resolved once we've fixed the
+            // issue described in https://github.com/google/dagger/issues/2208.
+            assertThat(result.getOutput())
+                .contains(
+                    "ComponentProcessingStep was unable to process 'app.MyComponent' because "
+                        + "'library2.MyQualifier' could not be resolved."
+                        + "\n  "
+                        + "\n  Dependency trace:"
+                        + "\n      => element (INTERFACE): library1.MyModule"
+                        + "\n      => element (METHOD): provideInt()"
+                        + "\n      => annotation: @library2.MyQualifier");
+            break;
+          case "DISABLED":
+            // When strict mode is disabled we fall back to the old behavior where the qualifier is
+            // missing and we do not throw an exception.
+            result = runner.build();
+            assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
+            assertThat(result.getOutput()).contains("REQUEST: java.lang.Integer");
+            break;
+          default: throw new AssertionError("Unexpected mode: " + strictSuperficialValidationMode);
+        }
+        break;
+      case "api":
+        result = runner.build();
+        assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
+        assertThat(result.getOutput()).contains("REQUEST: @library2.MyQualifier java.lang.Integer");
+        break;
+    }
+  }
+
+  private GradleRunner setupRunnerWith(GradleFile qualifierUsage) throws IOException {
+    File projectDir = folder.getRoot();
+    GradleModule.create(projectDir)
+        .addSettingsFile(
+            "include 'app'",
+            "include 'library1'",
+            "include 'library2'",
+            "include 'spi-plugin'")
+        .addBuildFile(
+            "buildscript {",
+            "  ext {",
+            String.format("dagger_version = \"%s\"", System.getProperty("dagger_version")),
+            "  }",
+            "}",
+            "",
+            "allprojects {",
+            "  repositories {",
+            "    mavenCentral()",
+            "    mavenLocal()",
+            "  }",
+            "}");
+
+    GradleModule.create(projectDir, "app")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'application'",
+            "}",
+            "tasks.withType(JavaCompile) {",
+            String.format(
+                "    options.compilerArgs += '-Adagger.strictSuperficialValidation=%s'",
+                strictSuperficialValidationMode),
+            "}",
+            "dependencies {",
+            "  implementation project(':library1')",
+            "  annotationProcessor project(':spi-plugin')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "MyComponent.java",
+            "package app;",
+            "",
+            "import dagger.Component;",
+            "import library1.MyModule;",
+            "import library1.QualifierUsage;",
+            "",
+            "@Component(modules = MyModule.class)",
+            "public interface MyComponent {",
+            "  QualifierUsage qualifierUsage();",
+            "}");
+
+    GradleModule.create(projectDir, "library1")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            transitiveDependencyType + " project(':library2')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "MyModule.java",
+            "package library1;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "import library2.MyQualifier;",
+            "",
+            "@Module",
+            "public interface MyModule {",
+            "  @Provides",
+            "  @MyQualifier",
+            "  static int provideInt() {",
+            "    return 0;",
+            "  }",
+            "}")
+        .addSrcFile(qualifierUsage);
+
+    GradleModule.create(projectDir, "library2")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            "  implementation 'javax.inject:javax.inject:1'",
+            "}")
+        .addSrcFile(
+            "MyQualifier.java",
+            "package library2;",
+            "",
+            "import javax.inject.Qualifier;",
+            "",
+            "@Qualifier",
+            "public @interface MyQualifier {}");
+
+    // This plugin is used to print output about bindings that we can assert on in tests.
+    GradleModule.create(projectDir, "spi-plugin")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "}",
+            "dependencies {",
+            "  implementation \"com.google.dagger:dagger-spi:$dagger_version\"",
+            "  implementation 'com.google.auto.service:auto-service-annotations:1.0.1'",
+            "  annotationProcessor 'com.google.auto.service:auto-service:1.0.1'",
+            "}")
+        .addSrcFile(
+            "TestBindingGraphPlugin.java",
+            "package spiplugin;",
+            "",
+            "import com.google.auto.service.AutoService;",
+            "import dagger.model.BindingGraph;",
+            "import dagger.model.BindingGraph.DependencyEdge;",
+            "import dagger.model.DependencyRequest;",
+            "import dagger.spi.BindingGraphPlugin;",
+            "import dagger.spi.DiagnosticReporter;",
+            "",
+            "@AutoService(BindingGraphPlugin.class)",
+            "public class TestBindingGraphPlugin implements BindingGraphPlugin {",
+            "  @Override",
+            "  public void visitGraph(",
+            "      BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {",
+            "    bindingGraph.dependencyEdges().stream()",
+            "        .map(DependencyEdge::dependencyRequest)",
+            "        .map(DependencyRequest::key)",
+            "        .forEach(key -> System.out.println(\"REQUEST: \" + key));",
+            "  }",
+            "}");
+
+    return GradleRunner.create()
+        .withArguments("--stacktrace", "build")
+        .withProjectDir(projectDir);
+  }
+}
diff --git a/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveScopeTest.java b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveScopeTest.java
new file mode 100644
index 0000000..ec54605
--- /dev/null
+++ b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveScopeTest.java
@@ -0,0 +1,200 @@
+/*
+ * 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 buildtests;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.gradle.testkit.runner.TaskOutcome.SUCCESS;
+
+import java.io.File;
+import java.io.IOException;
+import org.gradle.testkit.runner.BuildResult;
+import org.gradle.testkit.runner.GradleRunner;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+// This is a regression test for https://github.com/google/dagger/issues/3136
+@RunWith(JUnit4.class)
+public class TransitiveScopeTest {
+  @Rule public TemporaryFolder folder = new TemporaryFolder();
+
+  @Test
+  public void testTransitiveScope_WithImplementation() throws IOException {
+    BuildResult result = setupRunnerWith("implementation").buildAndFail();
+    assertThat(result.getOutput()).contains("Task :app:compileJava FAILED");
+    assertThat(result.getOutput())
+        .contains(
+            "ComponentProcessingStep was unable to process 'app.MyComponent' because "
+                + "'library2.MyScope' could not be resolved."
+                + "\n  "
+                + "\n  Dependency trace:"
+                // Note: this fails on the subcomponent rather than Foo because the subcomponent is
+                // validated before any of its dependencies.
+                + "\n      => element (INTERFACE): library1.MySubcomponent"
+                + "\n      => annotation: @library2.MyScope");
+  }
+
+  @Test
+  public void testTransitiveScope_WithApi() throws IOException {
+    BuildResult result = setupRunnerWith("api").build();
+    assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
+    assertThat(result.getOutput()).contains("@Inject library1.Foo(): SCOPED");
+  }
+
+  private GradleRunner setupRunnerWith(String dependencyType) throws IOException {
+    File projectDir = folder.getRoot();
+    GradleModule.create(projectDir)
+        .addSettingsFile(
+            "include 'app'",
+            "include 'library1'",
+            "include 'library2'",
+            "include 'spi-plugin'")
+        .addBuildFile(
+            "buildscript {",
+            "  ext {",
+            String.format("dagger_version = \"%s\"", System.getProperty("dagger_version")),
+            "  }",
+            "}",
+            "",
+            "allprojects {",
+            "  repositories {",
+            "    mavenCentral()",
+            "    mavenLocal()",
+            "  }",
+            "}");
+
+    GradleModule.create(projectDir, "app")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'application'",
+            "}",
+            "dependencies {",
+            "  implementation project(':library1')",
+            "  annotationProcessor project(':spi-plugin')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "MyComponent.java",
+            "package app;",
+            "",
+            "import dagger.Component;",
+            "import library1.MySubcomponent;",
+            "",
+            "@Component",
+            "public interface MyComponent {",
+            "  MySubcomponent subcomponent();",
+            "}");
+
+    GradleModule.create(projectDir, "library1")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            dependencyType + " project(':library2')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "Foo.java",
+            "package library1;",
+            "",
+            "import javax.inject.Inject;",
+            "import library2.MyScope;",
+            "",
+            "@MyScope",
+            "public class Foo {",
+            "  @Inject Foo() {}",
+            "}")
+        // Note: In order to repro the issue we place MyScope on a subcomponent so that it can be a
+        // transitive dependency of the component. If MyScope was placed on directly on the
+        // component, it would need to be a direct dependency of the component.
+        .addSrcFile(
+            "MySubcomponent.java",
+            "package library1;",
+            "",
+            "import dagger.Subcomponent;",
+            "import library2.MyScope;",
+            "",
+            "@MyScope",
+            "@Subcomponent",
+            "public interface MySubcomponent {",
+            "  Foo foo();",
+            "}");
+
+    GradleModule.create(projectDir, "library2")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            "  implementation 'javax.inject:javax.inject:1'",
+            "}")
+        .addSrcFile(
+            "MyScope.java",
+            "package library2;",
+            "",
+            "import javax.inject.Scope;",
+            "",
+            "@Scope",
+            "public @interface MyScope {}");
+
+    // This plugin is used to print output about bindings that we can assert on in tests.
+    GradleModule.create(projectDir, "spi-plugin")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "}",
+            "dependencies {",
+            "  implementation \"com.google.dagger:dagger-spi:$dagger_version\"",
+            "  implementation 'com.google.auto.service:auto-service-annotations:1.0.1'",
+            "  annotationProcessor 'com.google.auto.service:auto-service:1.0.1'",
+            "}")
+        .addSrcFile(
+            "TestBindingGraphPlugin.java",
+            "package spiplugin;",
+            "",
+            "import com.google.auto.service.AutoService;",
+            "import dagger.model.BindingGraph;",
+            "import dagger.spi.BindingGraphPlugin;",
+            "import dagger.spi.DiagnosticReporter;",
+            "",
+            "@AutoService(BindingGraphPlugin.class)",
+            "public class TestBindingGraphPlugin implements BindingGraphPlugin {",
+            "  @Override",
+            "  public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter"
+                + " diagnosticReporter) {",
+            "    bindingGraph.bindings().stream()",
+            "        .filter(binding -> binding.scope().isPresent())",
+            "        .forEach(binding -> System.out.println(binding + \": SCOPED\"));",
+            "    bindingGraph.bindings().stream()",
+            "        .filter(binding -> !binding.scope().isPresent())",
+            "        .forEach(binding -> System.out.println(binding + \": UNSCOPED\"));",
+            "  }",
+            "}");
+
+    return GradleRunner.create()
+        .withArguments("--stacktrace", "build")
+        .withProjectDir(projectDir);
+  }
+}
diff --git a/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveSubcomponentModulesTest.java b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveSubcomponentModulesTest.java
new file mode 100644
index 0000000..31376c0
--- /dev/null
+++ b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveSubcomponentModulesTest.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package buildtests;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.gradle.testkit.runner.TaskOutcome.SUCCESS;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import org.gradle.testkit.runner.BuildResult;
+import org.gradle.testkit.runner.GradleRunner;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// This is a regression test for https://github.com/google/dagger/issues/3136
+@RunWith(Parameterized.class)
+public class TransitiveSubcomponentModulesTest {
+  @Parameters(name = "{0}")
+  public static Collection<Object[]> parameters() {
+    return Arrays.asList(new Object[][] {{"implementation"}, {"api"}});
+  }
+
+  @Rule public TemporaryFolder folder = new TemporaryFolder();
+
+  private final String transitiveDependencyType;
+
+  public TransitiveSubcomponentModulesTest(String transitiveDependencyType) {
+    this.transitiveDependencyType = transitiveDependencyType;
+  }
+
+  @Test
+  public void testSubcomponentAnnotationWithTransitiveModule() throws IOException {
+    GradleRunner runner =
+        setupRunner(
+            GradleFile.create(
+                "MySubcomponent.java",
+                "package library1;",
+                "",
+                "import dagger.Subcomponent;",
+                "import library2.TransitiveModule;",
+                "",
+                "@Subcomponent(modules = TransitiveModule.class)",
+                "public abstract class MySubcomponent {",
+                "  public abstract int getInt();",
+                "}"));
+    BuildResult result;
+    switch (transitiveDependencyType) {
+      case "implementation":
+        result = runner.buildAndFail();
+        assertThat(result.getOutput()).contains("Task :app:compileJava FAILED");
+        String expectedErrorMsg =
+            "error: ComponentProcessingStep was unable to process 'app.MyComponent' because"
+                + " 'library2.TransitiveModule' could not be resolved."
+                + "\n  "
+                + "\n  Dependency trace:"
+                + "\n      => element (CLASS): library1.MySubcomponent"
+                + "\n      => annotation:"
+                + " @dagger.Subcomponent(modules = library2.TransitiveModule.class)"
+                + "\n      => annotation method: java.lang.Class<?>[] modules()"
+                + "\n      => annotation value (ARRAY):"
+                + " value 'library2.TransitiveModule.class' with expected type java.lang.Class<?>[]"
+                + "\n      => annotation value (TYPE):"
+                + " value 'library2.TransitiveModule' with expected type java.lang.Class<?>"
+                + "\n      => type (ERROR annotation value type): library2.TransitiveModule";
+        assertThat(result.getOutput()).contains(expectedErrorMsg);
+        break;
+      case "api":
+        result = runner.build();
+        assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
+        break;
+    }
+  }
+
+  @Test
+  public void testSubcomponentAnnotationWithModuleIncludesTransitiveModuleDependencies()
+      throws IOException {
+    GradleRunner runner =
+        setupRunner(
+            GradleFile.create(
+                "MySubcomponent.java",
+                "package library1;",
+                "",
+                "import dagger.Subcomponent;",
+                "",
+                "@Subcomponent(modules = IncludesTransitiveModule.class)",
+                "public abstract class MySubcomponent {",
+                "  public abstract int getInt();",
+                "}"));
+    BuildResult result;
+    switch (transitiveDependencyType) {
+      case "implementation":
+        result = runner.buildAndFail();
+        assertThat(result.getOutput()).contains("Task :app:compileJava FAILED");
+        String expectedErrorMsg =
+            "error: ComponentProcessingStep was unable to process 'app.MyComponent' because"
+                + " 'library2.TransitiveModule' could not be resolved."
+                + "\n  "
+                + "\n  Dependency trace:"
+                + "\n      => element (INTERFACE): library1.IncludesTransitiveModule"
+                + "\n      => annotation:"
+                + " @dagger.Module(includes = library2.TransitiveModule.class)"
+                + "\n      => annotation method: java.lang.Class<?>[] includes()"
+                + "\n      => annotation value (ARRAY):"
+                + " value 'library2.TransitiveModule.class' with expected type java.lang.Class<?>[]"
+                + "\n      => annotation value (TYPE):"
+                + " value 'library2.TransitiveModule' with expected type java.lang.Class<?>";
+        assertThat(result.getOutput()).contains(expectedErrorMsg);
+        break;
+      case "api":
+        result = runner.build();
+        assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
+        break;
+    }
+  }
+
+  private GradleRunner setupRunner(GradleFile subcomponent) throws IOException {
+    File projectDir = folder.getRoot();
+    GradleModule.create(projectDir)
+        .addSettingsFile("include 'app'", "include 'library1'", "include 'library2'")
+        .addBuildFile(
+            "buildscript {",
+            "  ext {",
+            String.format("dagger_version = \"%s\"", System.getProperty("dagger_version")),
+            "  }",
+            "}",
+            "",
+            "allprojects {",
+            "  repositories {",
+            "    mavenCentral()",
+            "    mavenLocal()",
+            "  }",
+            "}");
+
+    GradleModule.create(projectDir, "app")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'application'",
+            "}",
+            "tasks.withType(JavaCompile) {",
+            "    options.compilerArgs += '-Adagger.experimentalDaggerErrorMessages=ENABLED'",
+            "}",
+            "dependencies {",
+            "  implementation project(':library1')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "MyComponent.java",
+            "package app;",
+            "",
+            "import dagger.Component;",
+            "import library1.MySubcomponent;",
+            "",
+            "@Component",
+            "public interface MyComponent {",
+            "  MySubcomponent mySubcomponent();",
+            "}");
+
+    GradleModule.create(projectDir, "library1")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            transitiveDependencyType + " project(':library2')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "IncludesTransitiveModule.java",
+            "package library1;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "import library2.TransitiveModule;",
+            "",
+            "@Module(includes = TransitiveModule.class)",
+            "public interface IncludesTransitiveModule {}")
+        .addSrcFile(subcomponent);
+
+    GradleModule.create(projectDir, "library2")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "TransitiveModule.java",
+            "package library2;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "",
+            "@Module",
+            "public interface TransitiveModule {",
+            "  @Provides",
+            "  static int provideInt() {",
+            "    return 0;",
+            "  }",
+            "}");
+
+    return GradleRunner.create().withArguments("--stacktrace", "build").withProjectDir(projectDir);
+  }
+}
diff --git a/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveSubcomponentQualifierTest.java b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveSubcomponentQualifierTest.java
new file mode 100644
index 0000000..c135d74
--- /dev/null
+++ b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveSubcomponentQualifierTest.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package buildtests;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.gradle.testkit.runner.TaskOutcome.SUCCESS;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import org.gradle.testkit.runner.BuildResult;
+import org.gradle.testkit.runner.GradleRunner;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// This is a regression test for https://github.com/google/dagger/issues/3136
+@RunWith(Parameterized.class)
+public class TransitiveSubcomponentQualifierTest {
+  @Parameters(name = "{0}")
+  public static Collection<Object[]> parameters() {
+    return Arrays.asList(new Object[][] {{"implementation"}, {"api"}});
+  }
+
+  @Rule public TemporaryFolder folder = new TemporaryFolder();
+
+  private final String transitiveDependencyType;
+
+  public TransitiveSubcomponentQualifierTest(String transitiveDependencyType) {
+    this.transitiveDependencyType = transitiveDependencyType;
+  }
+
+  @Test
+  public void testQualifierWithFactory() throws IOException {
+    GradleRunner runner =
+        setupRunnerWith(
+            GradleFile.create(
+                "MySubcomponent.java",
+                "package library1;",
+                "",
+                "import dagger.BindsInstance;",
+                "import dagger.Subcomponent;",
+                "import library2.MyQualifier;",
+                "",
+                "@Subcomponent",
+                "public abstract class MySubcomponent {",
+                "  @MyQualifier",
+                "  public abstract int getQualifiedInt();",
+                "",
+                "  @Subcomponent.Factory",
+                "  public abstract static class Creator {",
+                "    public abstract MySubcomponent create(",
+                "      @BindsInstance @MyQualifier int qualifiedInt);",
+                "  }",
+                "}"));
+    BuildResult result;
+    switch (transitiveDependencyType) {
+      case "implementation":
+        result = runner.buildAndFail();
+        assertThat(result.getOutput()).contains("Task :app:compileJava FAILED");
+        assertThat(result.getOutput())
+            .contains(
+                "error: ComponentProcessingStep was unable to process 'app.MyComponent' because "
+                    + "'library2.MyQualifier' could not be resolved."
+                    + "\n  "
+                    + "\n  Dependency trace:"
+                    + "\n      => element (CLASS): library1.MySubcomponent"
+                    + "\n      => element (METHOD): getQualifiedInt()"
+                    + "\n      => annotation: @library2.MyQualifier");
+        break;
+      case "api":
+        result = runner.build();
+        assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
+        assertThat(result.getOutput())
+            .contains("ENTRY_POINT_REQUEST: @library2.MyQualifier java.lang.Integer");
+        break;
+    }
+  }
+
+  @Test
+  public void testQualifierOnBaseClassWithFactory() throws IOException {
+    GradleRunner runner =
+        setupRunnerWith(
+            GradleFile.create(
+                "MySubcomponent.java",
+                "package library1;",
+                "",
+                "import dagger.Subcomponent;",
+                "import library2.MyQualifier;",
+                "",
+                "@Subcomponent",
+                "public abstract class MySubcomponent extends MyBaseSubcomponent {",
+                "  @Subcomponent.Factory",
+                "  public abstract static class Creator extends MyBaseSubcomponent.Creator {}",
+                "}"),
+            GradleFile.create(
+                "MyBaseSubcomponent.java",
+                "package library1;",
+                "",
+                "import dagger.BindsInstance;",
+                "import library2.MyQualifier;",
+                "",
+                "public abstract class MyBaseSubcomponent {",
+                "  @MyQualifier",
+                "  public abstract int getQualifiedInt();",
+                "",
+                "  public abstract static class Creator {",
+                "    public abstract MySubcomponent create(",
+                "        @BindsInstance @MyQualifier int qualifiedInt);",
+                "  }",
+                "}"));
+    BuildResult result;
+    switch (transitiveDependencyType) {
+      case "implementation":
+        result = runner.buildAndFail();
+        assertThat(result.getOutput()).contains("Task :app:compileJava FAILED");
+        assertThat(result.getOutput())
+            .contains(
+                "error: ComponentProcessingStep was unable to process 'app.MyComponent' because "
+                    + "'library2.MyQualifier' could not be resolved."
+                    + "\n  "
+                    + "\n  Dependency trace:"
+                    + "\n      => element (CLASS): library1.MyBaseSubcomponent"
+                    + "\n      => element (METHOD): getQualifiedInt()"
+                    + "\n      => annotation: @library2.MyQualifier");
+        break;
+      case "api":
+        result = runner.build();
+        assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
+        assertThat(result.getOutput())
+            .contains("ENTRY_POINT_REQUEST: @library2.MyQualifier java.lang.Integer");
+        break;
+    }
+  }
+
+  @Test
+  public void testQualifierWithBuilder() throws IOException {
+    GradleRunner runner =
+        setupRunnerWith(
+            GradleFile.create(
+                "MySubcomponent.java",
+                "package library1;",
+                "",
+                "import dagger.BindsInstance;",
+                "import dagger.Subcomponent;",
+                "import library2.MyQualifier;",
+                "",
+                "@Subcomponent",
+                "public abstract class MySubcomponent {",
+                "  @MyQualifier",
+                "  public abstract int getQualifiedInt();",
+                "",
+                "  @Subcomponent.Builder",
+                "  public abstract static class Creator {",
+                "    public abstract MySubcomponent build();",
+                "    public abstract Creator qualifiedInt(",
+                "        @BindsInstance @MyQualifier int qualifiedInt);",
+                "  }",
+                "}"));
+    BuildResult result;
+    switch (transitiveDependencyType) {
+      case "implementation":
+        result = runner.buildAndFail();
+        assertThat(result.getOutput()).contains("Task :app:compileJava FAILED");
+        assertThat(result.getOutput())
+            .contains(
+                "error: ComponentProcessingStep was unable to process 'app.MyComponent' because "
+                    + "'library2.MyQualifier' could not be resolved."
+                    + "\n  "
+                    + "\n  Dependency trace:"
+                    + "\n      => element (CLASS): library1.MySubcomponent"
+                    + "\n      => element (METHOD): getQualifiedInt()"
+                    + "\n      => annotation: @library2.MyQualifier");
+        break;
+      case "api":
+        result = runner.build();
+        assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
+        assertThat(result.getOutput())
+            .contains("ENTRY_POINT_REQUEST: @library2.MyQualifier java.lang.Integer");
+        break;
+    }
+  }
+
+  @Test
+  public void testQualifierOnBaseClassWithBuilder() throws IOException {
+    GradleRunner runner =
+        setupRunnerWith(
+            GradleFile.create(
+                "MySubcomponent.java",
+                "package library1;",
+                "",
+                "import dagger.Subcomponent;",
+                "import library2.MyQualifier;",
+                "",
+                "@Subcomponent",
+                "public abstract class MySubcomponent extends MyBaseSubcomponent {",
+                "  @Subcomponent.Builder",
+                "  public abstract static class Creator extends MyBaseSubcomponent.Creator {}",
+                "}"),
+            GradleFile.create(
+                "MyBaseSubcomponent.java",
+                "package library1;",
+                "",
+                "import dagger.BindsInstance;",
+                "import library2.MyQualifier;",
+                "",
+                "public abstract class MyBaseSubcomponent {",
+                "  @MyQualifier",
+                "  public abstract int getQualifiedInt();",
+                "",
+                "  public abstract static class Creator {",
+                "    public abstract MySubcomponent build();",
+                "    public abstract Creator qualifiedInt(",
+                "        @BindsInstance @MyQualifier int qualifiedInt);",
+                "  }",
+                "}"));
+    BuildResult result;
+    switch (transitiveDependencyType) {
+      case "implementation":
+        result = runner.buildAndFail();
+        assertThat(result.getOutput()).contains("Task :app:compileJava FAILED");
+        // TODO(bcorso): Give more context about what couldn't be resolved once we've fixed the
+        // issue described in https://github.com/google/dagger/issues/2208.
+        assertThat(result.getOutput())
+            .contains(
+                "error: ComponentProcessingStep was unable to process 'app.MyComponent' because "
+                    + "'library2.MyQualifier' could not be resolved."
+                    + "\n  "
+                    + "\n  Dependency trace:"
+                    + "\n      => element (CLASS): library1.MyBaseSubcomponent"
+                    + "\n      => element (METHOD): getQualifiedInt()"
+                    + "\n      => annotation: @library2.MyQualifier");
+        break;
+      case "api":
+        result = runner.build();
+        assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
+        assertThat(result.getOutput())
+            .contains("ENTRY_POINT_REQUEST: @library2.MyQualifier java.lang.Integer");
+        break;
+    }
+  }
+
+  private GradleRunner setupRunnerWith(GradleFile... library1Files) throws IOException {
+    File projectDir = folder.getRoot();
+    GradleModule.create(projectDir)
+        .addSettingsFile(
+            "include 'app'", "include 'library1'", "include 'library2'", "include 'spi-plugin'")
+        .addBuildFile(
+            "buildscript {",
+            "  ext {",
+            String.format("dagger_version = \"%s\"", System.getProperty("dagger_version")),
+            "  }",
+            "}",
+            "",
+            "allprojects {",
+            "  repositories {",
+            "    mavenCentral()",
+            "    mavenLocal()",
+            "  }",
+            "}");
+
+    GradleModule.create(projectDir, "app")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'application'",
+            "}",
+            "tasks.withType(JavaCompile) {",
+            "    options.compilerArgs += '-Adagger.experimentalDaggerErrorMessages=ENABLED'",
+            "}",
+            "dependencies {",
+            "  implementation project(':library1')",
+            "  annotationProcessor project(':spi-plugin')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "MyComponent.java",
+            "package app;",
+            "",
+            "import dagger.Component;",
+            "import library1.MySubcomponent;",
+            "",
+            "@Component",
+            "public interface MyComponent {",
+            "  MySubcomponent.Creator mySubcomponentCreator();",
+            "}");
+
+    GradleModule.create(projectDir, "library1")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            transitiveDependencyType + " project(':library2')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFiles(library1Files);
+
+    GradleModule.create(projectDir, "library2")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            "  implementation 'javax.inject:javax.inject:1'",
+            "}")
+        .addSrcFile(
+            "MyQualifier.java",
+            "package library2;",
+            "",
+            "import javax.inject.Qualifier;",
+            "",
+            "@Qualifier",
+            "public @interface MyQualifier {}");
+
+    // This plugin is used to print output about bindings that we can assert on in tests.
+    GradleModule.create(projectDir, "spi-plugin")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "}",
+            "dependencies {",
+            "  implementation \"com.google.dagger:dagger-spi:$dagger_version\"",
+            "  implementation 'com.google.auto.service:auto-service-annotations:1.0.1'",
+            "  annotationProcessor 'com.google.auto.service:auto-service:1.0.1'",
+            "}")
+        .addSrcFile(
+            "TestBindingGraphPlugin.java",
+            "package spiplugin;",
+            "",
+            "import com.google.auto.service.AutoService;",
+            "import dagger.model.BindingGraph;",
+            "import dagger.model.BindingGraph.DependencyEdge;",
+            "import dagger.model.DependencyRequest;",
+            "import dagger.spi.BindingGraphPlugin;",
+            "import dagger.spi.DiagnosticReporter;",
+            "",
+            "@AutoService(BindingGraphPlugin.class)",
+            "public class TestBindingGraphPlugin implements BindingGraphPlugin {",
+            "  @Override",
+            "  public void visitGraph(",
+            "      BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {",
+            "    bindingGraph.entryPointEdges().stream()",
+            "        .map(DependencyEdge::dependencyRequest)",
+            "        .map(DependencyRequest::key)",
+            "        .forEach(key -> System.out.println(\"ENTRY_POINT_REQUEST: \" + key));",
+            "  }",
+            "}");
+
+    return GradleRunner.create().withArguments("--stacktrace", "build").withProjectDir(projectDir);
+  }
+}
diff --git a/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveSubcomponentScopeTest.java b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveSubcomponentScopeTest.java
new file mode 100644
index 0000000..82d0895
--- /dev/null
+++ b/javatests/artifacts/dagger/build-tests/src/test/java/buildtests/TransitiveSubcomponentScopeTest.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package buildtests;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.gradle.testkit.runner.TaskOutcome.SUCCESS;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import org.gradle.testkit.runner.BuildResult;
+import org.gradle.testkit.runner.GradleRunner;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// This is a regression test for https://github.com/google/dagger/issues/3136
+@RunWith(Parameterized.class)
+public class TransitiveSubcomponentScopeTest {
+  @Parameters(name = "{0}")
+  public static Collection<Object[]> parameters() {
+    return Arrays.asList(new Object[][] {{"implementation"}, {"api"}});
+  }
+
+  @Rule public TemporaryFolder folder = new TemporaryFolder();
+
+  private final String transitiveDependencyType;
+
+  public TransitiveSubcomponentScopeTest(String transitiveDependencyType) {
+    this.transitiveDependencyType = transitiveDependencyType;
+  }
+
+  @Test
+  public void testScopeWithSubcomponent() throws IOException {
+    BuildResult result;
+    switch (transitiveDependencyType) {
+      case "implementation":
+        result = setupRunner().buildAndFail();
+        assertThat(result.getOutput()).contains("Task :app:compileJava FAILED");
+        assertThat(result.getOutput())
+            .contains(
+                "error: ComponentProcessingStep was unable to process 'app.MyComponent' because "
+                    + "'library2.MySubcomponentScope' could not be resolved."
+                    + "\n  "
+                    + "\n  Dependency trace:"
+                    + "\n      => element (INTERFACE): library1.MySubcomponent.MySubcomponentModule"
+                    + "\n      => element (METHOD): provideScopedInt()"
+                    + "\n      => annotation: @library2.MySubcomponentScope");
+        break;
+      case "api":
+        result = setupRunner().build();
+        assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
+        assertThat(result.getOutput())
+            .contains(
+                "@Provides @library2.MySubcomponentScope int"
+                    + " library1.MySubcomponent.MySubcomponentModule.provideScopedInt(): SCOPED");
+        break;
+    }
+  }
+
+  private GradleRunner setupRunner() throws IOException {
+    File projectDir = folder.getRoot();
+    GradleModule.create(projectDir)
+        .addSettingsFile(
+            "include 'app'", "include 'library1'", "include 'library2'", "include 'spi-plugin'")
+        .addBuildFile(
+            "buildscript {",
+            "  ext {",
+            String.format("dagger_version = \"%s\"", System.getProperty("dagger_version")),
+            "  }",
+            "}",
+            "",
+            "allprojects {",
+            "  repositories {",
+            "    mavenCentral()",
+            "    mavenLocal()",
+            "  }",
+            "}");
+
+    GradleModule.create(projectDir, "app")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'application'",
+            "}",
+            "tasks.withType(JavaCompile) {",
+            "    options.compilerArgs += '-Adagger.experimentalDaggerErrorMessages=ENABLED'",
+            "}",
+            "dependencies {",
+            "  implementation project(':library1')",
+            "  annotationProcessor project(':spi-plugin')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "MyComponent.java",
+            "package app;",
+            "",
+            "import dagger.Component;",
+            "import library1.MySubcomponent;",
+            "",
+            "@Component",
+            "public interface MyComponent {",
+            "  MySubcomponent mySubcomponent();",
+            "}");
+
+    GradleModule.create(projectDir, "library1")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            transitiveDependencyType + " project(':library2')",
+            "  implementation \"com.google.dagger:dagger:$dagger_version\"",
+            "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
+            "}")
+        .addSrcFile(
+            "MySubcomponent.java",
+            "package library1;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "import dagger.Subcomponent;",
+            "import library2.MySubcomponentScope;",
+            "",
+            "@MySubcomponentScope",
+            "@Subcomponent(modules = MySubcomponent.MySubcomponentModule.class)",
+            "public abstract class MySubcomponent {",
+            "  public abstract int getScopedInt();",
+            "",
+            "  @Module",
+            "  public interface MySubcomponentModule {",
+            "    @Provides",
+            "    @MySubcomponentScope",
+            "    static int provideScopedInt() {",
+            "      return 0;",
+            "    }",
+            "  }",
+            "}");
+
+    GradleModule.create(projectDir, "library2")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "  id 'java-library'",
+            "}",
+            "dependencies {",
+            "  implementation 'javax.inject:javax.inject:1'",
+            "}")
+        .addSrcFile(
+            "MySubcomponentScope.java",
+            "package library2;",
+            "",
+            "import javax.inject.Scope;",
+            "",
+            "@Scope",
+            "public @interface MySubcomponentScope {}");
+
+    // This plugin is used to print output about bindings that we can assert on in tests.
+    GradleModule.create(projectDir, "spi-plugin")
+        .addBuildFile(
+            "plugins {",
+            "  id 'java'",
+            "}",
+            "dependencies {",
+            "  implementation \"com.google.dagger:dagger-spi:$dagger_version\"",
+            "  implementation 'com.google.auto.service:auto-service-annotations:1.0.1'",
+            "  annotationProcessor 'com.google.auto.service:auto-service:1.0.1'",
+            "}")
+        .addSrcFile(
+            "TestBindingGraphPlugin.java",
+            "package spiplugin;",
+            "",
+            "import com.google.auto.service.AutoService;",
+            "import dagger.model.BindingGraph;",
+            "import dagger.spi.BindingGraphPlugin;",
+            "import dagger.spi.DiagnosticReporter;",
+            "",
+            "@AutoService(BindingGraphPlugin.class)",
+            "public class TestBindingGraphPlugin implements BindingGraphPlugin {",
+            "  @Override",
+            "  public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter"
+                + " diagnosticReporter) {",
+            "    bindingGraph.bindings().stream()",
+            "        .filter(binding -> binding.scope().isPresent())",
+            "        .forEach(binding -> System.out.println(binding + \": SCOPED\"));",
+            "    bindingGraph.bindings().stream()",
+            "        .filter(binding -> !binding.scope().isPresent())",
+            "        .forEach(binding -> System.out.println(binding + \": UNSCOPED\"));",
+            "  }",
+            "}");
+
+    return GradleRunner.create().withArguments("--stacktrace", "build").withProjectDir(projectDir);
+  }
+}
diff --git a/javatests/artifacts/dagger/build.gradle b/javatests/artifacts/dagger/build.gradle
new file mode 100644
index 0000000..89f9efe
--- /dev/null
+++ b/javatests/artifacts/dagger/build.gradle
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+buildscript {
+  ext {
+    dagger_version = "LOCAL-SNAPSHOT"
+    kotlin_version = "1.5.32"
+    junit_version = "4.13"
+    truth_version = "1.0.1"
+  }
+}
+
+allprojects {
+  repositories {
+    mavenCentral()
+    mavenLocal()
+  }
+
+  configurations.all {
+    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
+      if (details.requested.group == 'com.google.dagger'
+            && "$dagger_version" == 'LOCAL-SNAPSHOT') {
+        details.useVersion 'LOCAL-SNAPSHOT'
+        details.because 'LOCAL-SNAPSHOT should act as latest version.'
+      }
+    }
+  }
+}
diff --git a/javatests/artifacts/dagger/simple/gradle/wrapper/gradle-wrapper.jar b/javatests/artifacts/dagger/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
rename from javatests/artifacts/dagger/simple/gradle/wrapper/gradle-wrapper.jar
rename to javatests/artifacts/dagger/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/java/dagger/example/gradle/android/simple/gradle/wrapper/gradle-wrapper.properties b/javatests/artifacts/dagger/gradle/wrapper/gradle-wrapper.properties
similarity index 91%
rename from java/dagger/example/gradle/android/simple/gradle/wrapper/gradle-wrapper.properties
rename to javatests/artifacts/dagger/gradle/wrapper/gradle-wrapper.properties
index 4d9ca16..0f80bbf 100644
--- a/java/dagger/example/gradle/android/simple/gradle/wrapper/gradle-wrapper.properties
+++ b/javatests/artifacts/dagger/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/javatests/artifacts/dagger/simple/gradlew b/javatests/artifacts/dagger/gradlew
similarity index 100%
rename from javatests/artifacts/dagger/simple/gradlew
rename to javatests/artifacts/dagger/gradlew
diff --git a/javatests/artifacts/dagger/simple/build.gradle b/javatests/artifacts/dagger/java-app/build.gradle
similarity index 77%
rename from javatests/artifacts/dagger/simple/build.gradle
rename to javatests/artifacts/dagger/java-app/build.gradle
index 97c966e..e709d7d 100644
--- a/javatests/artifacts/dagger/simple/build.gradle
+++ b/javatests/artifacts/dagger/java-app/build.gradle
@@ -19,19 +19,14 @@
   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'
+  implementation "com.google.dagger:dagger:$dagger_version"
+  annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
 }
 
-mainClassName = 'dagger.simple.SimpleApplication'
\ No newline at end of file
+mainClassName = 'app.SimpleApplication'
\ No newline at end of file
diff --git a/javatests/artifacts/dagger/simple/src/main/java/dagger/simple/AssistedInjects.java b/javatests/artifacts/dagger/java-app/src/main/java/app/AssistedInjects.java
similarity index 98%
rename from javatests/artifacts/dagger/simple/src/main/java/dagger/simple/AssistedInjects.java
rename to javatests/artifacts/dagger/java-app/src/main/java/app/AssistedInjects.java
index eb4946f..246c541 100644
--- a/javatests/artifacts/dagger/simple/src/main/java/dagger/simple/AssistedInjects.java
+++ b/javatests/artifacts/dagger/java-app/src/main/java/app/AssistedInjects.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package dagger.simple;
+package app;
 
 import dagger.Component;
 import dagger.assisted.Assisted;
diff --git a/javatests/artifacts/dagger/simple/src/main/java/dagger/simple/SimpleApplication.java b/javatests/artifacts/dagger/java-app/src/main/java/app/SimpleApplication.java
similarity index 93%
rename from javatests/artifacts/dagger/simple/src/main/java/dagger/simple/SimpleApplication.java
rename to javatests/artifacts/dagger/java-app/src/main/java/app/SimpleApplication.java
index f136101..dfe95ca 100644
--- a/javatests/artifacts/dagger/simple/src/main/java/dagger/simple/SimpleApplication.java
+++ b/javatests/artifacts/dagger/java-app/src/main/java/app/SimpleApplication.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package dagger.simple;
+package app;
 
 import dagger.Component;
 import dagger.Module;
@@ -44,5 +44,8 @@
 
   public static void main(String[] args) {
     Foo foo = DaggerSimpleApplication_SimpleComponent.create().foo();
+
+    // Execute other classes
+    AssistedInjects.main(args);
   }
 }
diff --git a/javatests/artifacts/dagger/kotlin-app/build.gradle b/javatests/artifacts/dagger/kotlin-app/build.gradle
new file mode 100644
index 0000000..2f4f7f3
--- /dev/null
+++ b/javatests/artifacts/dagger/kotlin-app/build.gradle
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+plugins {
+    id 'application'
+    id 'org.jetbrains.kotlin.jvm' version "$kotlin_version"
+    id 'org.jetbrains.kotlin.kapt' version "$kotlin_version"
+}
+
+java {
+    // Make sure the generated source is compatible with Java 8.
+    sourceCompatibility = JavaVersion.VERSION_1_8
+}
+
+dependencies {
+  implementation project(path: ':kotlin-app:kotlin-library')
+
+  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+  implementation "com.google.dagger:dagger:$dagger_version"
+  kapt "com.google.dagger:dagger-compiler:$dagger_version"
+
+  // This is testImplementation rather than kaptTest because we're actually
+  // testing the reference to ComponentProcessor.
+  // See https://github.com/google/dagger/issues/2765
+  testImplementation "com.google.dagger:dagger-compiler:$dagger_version"
+  testImplementation "com.google.truth:truth:$truth_version"
+  testImplementation "junit:junit:$junit_version"
+}
+
+application {
+    mainClass = 'app.SimpleApplicationKt'
+}
\ No newline at end of file
diff --git a/javatests/artifacts/dagger/kotlin-app/kotlin-library/build.gradle b/javatests/artifacts/dagger/kotlin-app/kotlin-library/build.gradle
new file mode 100644
index 0000000..954bd9f
--- /dev/null
+++ b/javatests/artifacts/dagger/kotlin-app/kotlin-library/build.gradle
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+plugins {
+    id 'org.jetbrains.kotlin.jvm'
+    id 'org.jetbrains.kotlin.kapt'
+}
+
+java {
+    // Make sure the generated source is compatible with Java 8.
+    sourceCompatibility = JavaVersion.VERSION_1_8
+}
+
+dependencies {
+  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+  implementation "com.google.dagger:dagger:$dagger_version"
+  kapt "com.google.dagger:dagger-compiler:$dagger_version"
+
+  // This is testImplementation rather than kaptTest because we're actually
+  // testing the reference to ComponentProcessor.
+  // See https://github.com/google/dagger/issues/2765
+  testImplementation "com.google.dagger:dagger-compiler:$dagger_version"
+  testImplementation "com.google.truth:truth:$truth_version"
+  testImplementation "junit:junit:$junit_version"
+}
diff --git a/javatests/artifacts/dagger/kotlin-app/kotlin-library/src/main/kotlin/library/MySubcomponent.kt b/javatests/artifacts/dagger/kotlin-app/kotlin-library/src/main/kotlin/library/MySubcomponent.kt
new file mode 100644
index 0000000..70e9d3b
--- /dev/null
+++ b/javatests/artifacts/dagger/kotlin-app/kotlin-library/src/main/kotlin/library/MySubcomponent.kt
@@ -0,0 +1,35 @@
+/*
+ * 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 library
+
+import dagger.BindsInstance
+import dagger.Subcomponent
+
+/**
+ * This subcomponent reproduces a regression in https://github.com/google/dagger/issues/2997.
+ */
+@Subcomponent
+abstract class MySubcomponent {
+    abstract fun instance(): InstanceType
+
+    @Subcomponent.Factory
+    interface Factory {
+        fun create(@BindsInstance instance: InstanceType): MySubcomponent
+    }
+}
+
+class InstanceType
diff --git a/javatests/artifacts/dagger/kotlin-app/src/main/kotlin/app/AssistedInjects.kt b/javatests/artifacts/dagger/kotlin-app/src/main/kotlin/app/AssistedInjects.kt
new file mode 100644
index 0000000..0fe65fd
--- /dev/null
+++ b/javatests/artifacts/dagger/kotlin-app/src/main/kotlin/app/AssistedInjects.kt
@@ -0,0 +1,60 @@
+/*
+ * 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 app
+
+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. */
+class AssistedInjects {
+  @Component
+  interface MyComponent {
+    fun fooFactory(): FooFactory
+
+    fun parameterizedFooFactory(): ParameterizedFooFactory<Bar, String>
+  }
+
+  class Bar @Inject constructor()
+
+  class Foo @AssistedInject constructor(val bar: Bar, @Assisted val str: String)
+
+  @AssistedFactory
+  interface FooFactory {
+    fun create(str: String): Foo
+  }
+
+  class ParameterizedFoo<T1, T2> @AssistedInject constructor(val t1: T1, @Assisted val t2: T2)
+
+  @AssistedFactory
+  interface ParameterizedFooFactory<T1, T2> {
+    fun create(t2: T2): ParameterizedFoo<T1, T2>
+  }
+
+  companion object {
+    // Called from SimpleApplication.main()
+    fun main() {
+        val foo: Foo = DaggerAssistedInjects_MyComponent.create().fooFactory().create("")
+
+        val parameterizedFoo: ParameterizedFoo<Bar, String> =
+            DaggerAssistedInjects_MyComponent.create().parameterizedFooFactory().create("")
+    }
+  }
+}
diff --git a/javatests/artifacts/dagger/kotlin-app/src/main/kotlin/app/SimpleApplication.kt b/javatests/artifacts/dagger/kotlin-app/src/main/kotlin/app/SimpleApplication.kt
new file mode 100644
index 0000000..599d703
--- /dev/null
+++ b/javatests/artifacts/dagger/kotlin-app/src/main/kotlin/app/SimpleApplication.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 app
+
+import dagger.Component
+import dagger.Module
+import dagger.Provides
+import javax.inject.Inject
+import javax.inject.Singleton
+import library.MySubcomponent
+
+/** A simple, skeletal application that defines a simple component. */
+class SimpleApplication {
+  class Foo @Inject constructor()
+
+  @Module
+  object SimpleModule {
+    @Provides
+    fun provideFoo(): Foo {
+      return Foo()
+    }
+  }
+
+  @Singleton
+  @Component(modules = [SimpleModule::class])
+  interface SimpleComponent {
+    fun foo(): Foo
+
+    // Reproduces a regression in https://github.com/google/dagger/issues/2997.
+    fun mySubcomponentFactory(): MySubcomponent.Factory
+  }
+
+  companion object {
+    fun main() {
+      val foo: Foo = DaggerSimpleApplication_SimpleComponent.create().foo()
+    }
+  }
+}
+
+fun main() {
+  SimpleApplication.main()
+  AssistedInjects.main()
+}
diff --git a/javatests/artifacts/dagger/kotlin-app/src/test/kotlin/app/ComponentProcessorBuildTest.kt b/javatests/artifacts/dagger/kotlin-app/src/test/kotlin/app/ComponentProcessorBuildTest.kt
new file mode 100644
index 0000000..1b050f8
--- /dev/null
+++ b/javatests/artifacts/dagger/kotlin-app/src/test/kotlin/app/ComponentProcessorBuildTest.kt
@@ -0,0 +1,36 @@
+/*
+ * 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 app
+
+import com.google.common.truth.Truth.assertThat
+import dagger.internal.codegen.ComponentProcessor
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class ComponentProcessorBuildTest {
+
+  // This is a regression test for https://github.com/google/dagger/issues/2765
+  // to make sure ComponentProcessor builds in kotlin.
+  @Test
+  fun testComponentProcessor() {
+    val processor = ComponentProcessor.forTesting()
+
+    assertThat(processor).isNotNull()
+  }
+}
diff --git a/javatests/artifacts/dagger/settings.gradle b/javatests/artifacts/dagger/settings.gradle
new file mode 100644
index 0000000..6ba1bae
--- /dev/null
+++ b/javatests/artifacts/dagger/settings.gradle
@@ -0,0 +1,8 @@
+rootProject.name = 'Dagger Apps'
+include ':build-tests'
+include ':java-app'
+include ':kotlin-app'
+include ':kotlin-app:kotlin-library'
+include ':transitive-annotation-app'
+include ':transitive-annotation-app:library1'
+include ':transitive-annotation-app:library2'
diff --git a/javatests/artifacts/dagger/simple/gradle/wrapper/gradle-wrapper.properties b/javatests/artifacts/dagger/simple/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index 4d9ca16..0000000
--- a/javatests/artifacts/dagger/simple/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,5 +0,0 @@
-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/build.gradle b/javatests/artifacts/dagger/transitive-annotation-app/build.gradle
similarity index 68%
copy from javatests/artifacts/dagger/simple/build.gradle
copy to javatests/artifacts/dagger/transitive-annotation-app/build.gradle
index 97c966e..22933a4 100644
--- a/javatests/artifacts/dagger/simple/build.gradle
+++ b/javatests/artifacts/dagger/transitive-annotation-app/build.gradle
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Dagger Authors.
+ * 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.
@@ -19,19 +19,16 @@
   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'
-}
+  implementation project(":transitive-annotation-app:library1")
+  implementation "com.google.dagger:dagger:$dagger_version"
+  annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
 
-mainClassName = 'dagger.simple.SimpleApplication'
\ No newline at end of file
+  testImplementation "junit:junit:$junit_version"
+  testImplementation "com.google.truth:truth:$truth_version"
+}
diff --git a/javatests/artifacts/dagger/simple/build.gradle b/javatests/artifacts/dagger/transitive-annotation-app/library1/build.gradle
similarity index 71%
copy from javatests/artifacts/dagger/simple/build.gradle
copy to javatests/artifacts/dagger/transitive-annotation-app/library1/build.gradle
index 97c966e..2126f09 100644
--- a/javatests/artifacts/dagger/simple/build.gradle
+++ b/javatests/artifacts/dagger/transitive-annotation-app/library1/build.gradle
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Dagger Authors.
+ * 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.
@@ -16,12 +16,7 @@
 
 plugins {
   id 'java'
-  id 'application'
-}
-
-repositories {
-  mavenCentral()
-  mavenLocal()
+  id 'java-library'
 }
 
 java {
@@ -30,8 +25,7 @@
 }
 
 dependencies {
-  implementation 'com.google.dagger:dagger:LOCAL-SNAPSHOT'
-  annotationProcessor 'com.google.dagger:dagger-compiler:LOCAL-SNAPSHOT'
+  implementation project(":transitive-annotation-app:library2")
+  implementation "com.google.dagger:dagger:$dagger_version"
+  annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
 }
-
-mainClassName = 'dagger.simple.SimpleApplication'
\ No newline at end of file
diff --git a/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/AssistedFoo.java b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/AssistedFoo.java
new file mode 100644
index 0000000..8377d1c
--- /dev/null
+++ b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/AssistedFoo.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package library1;
+
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import javax.inject.Inject;
+import library2.MyTransitiveAnnotation;
+import library2.MyTransitiveType;
+
+/**
+ * A class used to test that Dagger won't fail when non-dagger related annotations cannot be
+ * resolved.
+ *
+ * <p>During the compilation of {@code :app}, {@link MyTransitiveAnnotation} will no longer be on
+ * the classpath. In most cases, Dagger shouldn't care that the annotation isn't on the classpath
+ */
+@MyTransitiveAnnotation
+@MyAnnotation(MyTransitiveType.VALUE)
+@MyOtherAnnotation(MyTransitiveType.class)
+public final class AssistedFoo extends FooBase {
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  MyTransitiveType nonDaggerField;
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  @Inject
+  @MyQualifier
+  Dep daggerField;
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  AssistedFoo(
+      @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          MyTransitiveType nonDaggerParameter) {
+    super(nonDaggerParameter);
+  }
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  @AssistedInject
+  AssistedFoo(
+      @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          @Assisted
+          int i,
+      @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          @MyQualifier
+          Dep dep) {
+    super(dep);
+  }
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  MyTransitiveType nonDaggerMethod(
+      @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          MyTransitiveType nonDaggerParameter) {
+    return nonDaggerParameter;
+  }
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  @Inject
+  void daggerMethod(
+      @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          @MyQualifier
+          Dep dep) {}
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  @AssistedFactory
+  public interface Factory {
+    @MyTransitiveAnnotation
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    AssistedFoo create(
+        @MyTransitiveAnnotation
+            @MyAnnotation(MyTransitiveType.VALUE)
+            @MyOtherAnnotation(MyTransitiveType.class)
+            int i);
+  }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/Dep.java
similarity index 79%
copy from java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
copy to javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/Dep.java
index 0fd3db3..fa64e34 100644
--- a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
+++ b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/Dep.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Dagger Authors.
+ * Copyright (C) 2022 The Dagger Authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package dagger.hilt.android.example.gradle.simple.feature
+package library1;
 
-class FeatureCounter(var count: Int)
+public final class Dep {}
diff --git a/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/Foo.java b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/Foo.java
new file mode 100644
index 0000000..062acbe
--- /dev/null
+++ b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/Foo.java
@@ -0,0 +1,93 @@
+/*
+ * 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 library1;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import library2.MyTransitiveAnnotation;
+import library2.MyTransitiveType;
+
+/**
+ * A class used to test that Dagger won't fail when non-dagger related annotations cannot be
+ * resolved.
+ *
+ * <p>During the compilation of {@code :app}, {@link MyTransitiveAnnotation} will no longer be on
+ * the classpath. In most cases, Dagger shouldn't care that the annotation isn't on the classpath
+ */
+@Singleton
+@MyTransitiveAnnotation
+@MyAnnotation(MyTransitiveType.VALUE)
+@MyOtherAnnotation(MyTransitiveType.class)
+public final class Foo extends FooBase {
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  MyTransitiveType nonDaggerField;
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  @Inject
+  @MyQualifier
+  Dep daggerField;
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  Foo(
+      @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          MyTransitiveType nonDaggerParameter) {
+    super(nonDaggerParameter);
+  }
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  @Inject
+  Foo(
+      @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          @MyQualifier
+          Dep dep) {
+    super(dep);
+  }
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  MyTransitiveType nonDaggerMethod(
+      @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          MyTransitiveType nonDaggerParameter) {
+    return nonDaggerParameter;
+  }
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  @Inject
+  void daggerMethod(
+      @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          @MyQualifier
+          Dep dep) {}
+}
diff --git a/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/FooBase.java b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/FooBase.java
new file mode 100644
index 0000000..ad113dd
--- /dev/null
+++ b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/FooBase.java
@@ -0,0 +1,79 @@
+/*
+ * 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 library1;
+
+import javax.inject.Inject;
+import library2.MyTransitiveBaseAnnotation;
+import library2.MyTransitiveType;
+
+/** A baseclass for {@link Foo}. */
+@MyTransitiveBaseAnnotation
+@MyAnnotation(MyTransitiveType.VALUE)
+@MyOtherAnnotation(MyTransitiveType.class)
+public class FooBase {
+  @MyTransitiveBaseAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  int baseNonDaggerField;
+
+  @MyTransitiveBaseAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  @Inject
+  @MyQualifier
+  Dep baseDaggerField;
+
+  @MyTransitiveBaseAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  FooBase(
+      @MyTransitiveBaseAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          MyTransitiveType nonDaggerParameter) {}
+
+  @MyTransitiveBaseAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  @Inject
+  FooBase(
+      @MyTransitiveBaseAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          @MyQualifier
+          Dep dep) {}
+
+  @MyTransitiveBaseAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  void baseNonDaggerMethod(
+      @MyTransitiveBaseAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          int i) {}
+
+  @MyTransitiveBaseAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  @Inject
+  void baseDaggerMethod(
+      @MyTransitiveBaseAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          @MyQualifier
+          Dep dep) {}
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MyAnnotation.java
similarity index 75%
copy from java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
copy to javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MyAnnotation.java
index 0fd3db3..58ba4e7 100644
--- a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
+++ b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MyAnnotation.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Dagger Authors.
+ * Copyright (C) 2022 The Dagger Authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
-package dagger.hilt.android.example.gradle.simple.feature
+package library1;
 
-class FeatureCounter(var count: Int)
+/** An annotation that is a direct dependency of the app. */
+public @interface MyAnnotation {
+  int value();
+}
diff --git a/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MyBaseComponent.java b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MyBaseComponent.java
new file mode 100644
index 0000000..d647f54
--- /dev/null
+++ b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MyBaseComponent.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package library1;
+
+import library2.MyTransitiveAnnotation;
+import library2.MyTransitiveType;
+
+/**
+ * A class used to test that Dagger won't fail on unresolvable transitive types used in non-dagger
+ * related elements and annotations.
+ */
+// @MyTransitiveAnnotation: Not yet supported
+@MyAnnotation(MyTransitiveType.VALUE)
+@MyOtherAnnotation(MyTransitiveType.class)
+public abstract class MyBaseComponent {
+  @MyQualifier
+  // @MyTransitiveAnnotation: Not yet supported
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public abstract MyComponentModule.UnscopedQualifiedBindsType unscopedQualifiedBindsTypeBase();
+
+  // @MyTransitiveAnnotation: Not yet supported
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public abstract MyComponentModule.UnscopedUnqualifiedBindsType unscopedUnqualifiedBindsTypeBase();
+
+  // @MyTransitiveAnnotation: Not yet supported
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public abstract void injectFooBase(
+      // @MyTransitiveAnnotation: Not yet supported
+      @MyAnnotation(MyTransitiveType.VALUE) @MyOtherAnnotation(MyTransitiveType.class) Foo binding);
+
+  // @MyTransitiveAnnotation: Not yet supported
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public abstract static class Factory {
+    @MyTransitiveAnnotation
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    public abstract MyBaseComponent create(
+        @MyTransitiveAnnotation
+            @MyAnnotation(MyTransitiveType.VALUE)
+            @MyOtherAnnotation(MyTransitiveType.class)
+            MyComponentModule myComponentModule,
+        @MyTransitiveAnnotation
+            @MyAnnotation(MyTransitiveType.VALUE)
+            @MyOtherAnnotation(MyTransitiveType.class)
+            MyComponentDependency myComponentDependency);
+
+    // Non-dagger factory code
+
+    @MyTransitiveAnnotation
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    public MyTransitiveType nonDaggerField = null;
+
+    @MyTransitiveAnnotation
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    public static MyTransitiveType nonDaggerStaticField = null;
+
+    @MyTransitiveAnnotation
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    public MyTransitiveType nonDaggerMethod(
+        @MyTransitiveAnnotation
+            @MyAnnotation(MyTransitiveType.VALUE)
+            @MyOtherAnnotation(MyTransitiveType.class)
+            MyTransitiveType nonDaggerParameter) {
+      return nonDaggerParameter;
+    }
+
+    @MyTransitiveAnnotation
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    public static MyTransitiveType nonDaggerStaticMethod(
+        @MyTransitiveAnnotation
+            @MyAnnotation(MyTransitiveType.VALUE)
+            @MyOtherAnnotation(MyTransitiveType.class)
+            MyTransitiveType nonDaggerParameter) {
+      return nonDaggerParameter;
+    }
+  }
+
+  // Non-dagger code
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public MyTransitiveType nonDaggerField = null;
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public static MyTransitiveType nonDaggerStaticField = null;
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public MyTransitiveType nonDaggerMethod(
+      @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          MyTransitiveType nonDaggerParameter) {
+    return nonDaggerParameter;
+  }
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public static MyTransitiveType nonDaggerStaticMethod(
+      @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          MyTransitiveType nonDaggerParameter) {
+    return nonDaggerParameter;
+  }
+}
diff --git a/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MyComponentDependency.java b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MyComponentDependency.java
new file mode 100644
index 0000000..bb17021
--- /dev/null
+++ b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MyComponentDependency.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package library1;
+
+import library2.MyTransitiveAnnotation;
+import library2.MyTransitiveType;
+
+/**
+ * A class used to test that Dagger won't fail on unresolvable transitive types used in non-dagger
+ * related elements and annotations.
+ */
+// @MyTransitiveAnnotation: Not yet supported
+@MyAnnotation(MyTransitiveType.VALUE)
+@MyOtherAnnotation(MyTransitiveType.class)
+public final class MyComponentDependency {
+  private final MyComponentDependencyBinding qualifiedMyComponentDependencyBinding =
+      new MyComponentDependencyBinding();
+  private final MyComponentDependencyBinding unqualifiedMyComponentDependencyBinding =
+      new MyComponentDependencyBinding();
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public MyComponentDependency() {}
+
+  @MyQualifier
+  // @MyTransitiveAnnotation: Not yet supported
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public MyComponentDependencyBinding qualifiedMyComponentDependencyBinding() {
+    return qualifiedMyComponentDependencyBinding;
+  }
+
+  // @MyTransitiveAnnotation: Not yet supported
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public MyComponentDependencyBinding unqualifiedMyComponentDependencyBinding() {
+    return unqualifiedMyComponentDependencyBinding;
+  }
+
+  // Non-dagger code
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public MyTransitiveType nonDaggerField = null;
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public static MyTransitiveType nonDaggerStaticField = null;
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public MyTransitiveType nonDaggerMethod(
+      @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          MyTransitiveType nonDaggerParameter) {
+    return nonDaggerParameter;
+  }
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public static MyTransitiveType nonDaggerStaticMethod(
+      @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          MyTransitiveType nonDaggerParameter) {
+    return nonDaggerParameter;
+  }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MyComponentDependencyBinding.java
similarity index 76%
copy from java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
copy to javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MyComponentDependencyBinding.java
index 0fd3db3..c5d6bc9 100644
--- a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
+++ b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MyComponentDependencyBinding.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Dagger Authors.
+ * Copyright (C) 2022 The Dagger Authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
-package dagger.hilt.android.example.gradle.simple.feature
+package library1;
 
-class FeatureCounter(var count: Int)
+/** Used as a binding in {@link MyComponentDependency}. */
+public final class MyComponentDependencyBinding {}
diff --git a/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MyComponentModule.java b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MyComponentModule.java
new file mode 100644
index 0000000..2c7a363
--- /dev/null
+++ b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MyComponentModule.java
@@ -0,0 +1,218 @@
+/*
+ * 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 library1;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import javax.inject.Singleton;
+import library2.MyTransitiveAnnotation;
+import library2.MyTransitiveType;
+
+/**
+ * A class used to test that Dagger won't fail when non-dagger related annotations cannot be
+ * resolved.
+ *
+ * <p>During the compilation of {@code :app}, {@link MyTransitiveAnnotation} will no longer be on
+ * the classpath. In most cases, Dagger shouldn't care that the annotation isn't on the classpath
+ */
+@MyTransitiveAnnotation
+@MyAnnotation(MyTransitiveType.VALUE)
+@MyOtherAnnotation(MyTransitiveType.class)
+@Module(includes = {MyComponentModule.MyAbstractModule.class})
+public final class MyComponentModule {
+  // Define bindings for each configuration: Scoped/Unscoped, Qualified/UnQualified, Provides/Binds
+  public static class ScopedQualifiedBindsType {}
+  public static final class ScopedQualifiedProvidesType extends ScopedQualifiedBindsType {}
+  public static class ScopedUnqualifiedBindsType {}
+  public static final class ScopedUnqualifiedProvidesType extends ScopedUnqualifiedBindsType {}
+  public static class UnscopedQualifiedBindsType {}
+  public static final class UnscopedQualifiedProvidesType extends UnscopedQualifiedBindsType {}
+  public static class UnscopedUnqualifiedBindsType {}
+  public static final class UnscopedUnqualifiedProvidesType extends UnscopedUnqualifiedBindsType {}
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  @Provides
+  @Singleton
+  @MyQualifier
+  ScopedQualifiedProvidesType scopedQualifiedProvidesType(
+      @MyQualifier
+          @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          Dep dep) {
+    return new ScopedQualifiedProvidesType();
+  }
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  @Provides
+  @Singleton
+  ScopedUnqualifiedProvidesType scopedUnqualifiedProvidesType(
+      @MyQualifier
+          @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          Dep dep) {
+    return new ScopedUnqualifiedProvidesType();
+  }
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  @Provides
+  @MyQualifier
+  UnscopedQualifiedProvidesType unscopedQualifiedProvidesType(
+      @MyQualifier
+          @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          Dep dep) {
+    return new UnscopedQualifiedProvidesType();
+  }
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  @Provides
+  UnscopedUnqualifiedProvidesType unscopedUnqualifiedProvidesType(
+      @MyQualifier
+          @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          Dep dep) {
+    return new UnscopedUnqualifiedProvidesType();
+  }
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  @Module
+  interface MyAbstractModule {
+    // @MyTransitiveAnnotation: Not yet supported
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    @Binds
+    @Singleton
+    @MyQualifier
+    ScopedQualifiedBindsType scopedQualifiedBindsType(
+        // @MyTransitiveAnnotation: Not yet supported
+        @MyQualifier
+            @MyAnnotation(MyTransitiveType.VALUE)
+            @MyOtherAnnotation(MyTransitiveType.class)
+            ScopedQualifiedProvidesType scopedQualifiedProvidesType);
+
+    // @MyTransitiveAnnotation: Not yet supported
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    @Binds
+    @Singleton
+    ScopedUnqualifiedBindsType scopedUnqualifiedBindsType(
+        // @MyTransitiveAnnotation: Not yet supported
+        @MyAnnotation(MyTransitiveType.VALUE) @MyOtherAnnotation(MyTransitiveType.class)
+            ScopedUnqualifiedProvidesType scopedUnqualifiedProvidesType);
+
+    // @MyTransitiveAnnotation: Not yet supported
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    @Binds
+    @MyQualifier
+    UnscopedQualifiedBindsType unscopedQualifiedBindsType(
+        // @MyTransitiveAnnotation: Not yet supported
+        @MyQualifier
+            @MyAnnotation(MyTransitiveType.VALUE)
+            @MyOtherAnnotation(MyTransitiveType.class)
+            UnscopedQualifiedProvidesType unscopedQualifiedProvidesType);
+
+    // @MyTransitiveAnnotation: Not yet supported
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    @Binds
+    UnscopedUnqualifiedBindsType unscopedUnqualifiedBindsType(
+        // @MyTransitiveAnnotation: Not yet supported
+        @MyAnnotation(MyTransitiveType.VALUE) @MyOtherAnnotation(MyTransitiveType.class)
+            UnscopedUnqualifiedProvidesType unscopedUnqualifiedProvidesType);
+  }
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  @Provides
+  @MyQualifier
+  Dep provideQualifiedDep() {
+    return new Dep();
+  }
+
+  // Provide an unqualified Dep to ensure that if we accidentally drop the qualifier
+  // we'll get a runtime exception.
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  @Provides
+  Dep provideDep() {
+    throw new UnsupportedOperationException();
+  }
+
+  // Non-Dagger elements
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  private Dep dep;
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  private MyTransitiveType nonDaggerField;
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public MyComponentModule(
+      @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          Dep dep) {
+    this.dep = dep;
+    this.nonDaggerField = new MyTransitiveType();
+  }
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  MyTransitiveType nonDaggerMethod(
+      @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          MyTransitiveType nonDaggerParameter) {
+    return nonDaggerParameter;
+  }
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  static MyTransitiveType nonDaggerStaticMethod(
+      @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          MyTransitiveType nonDaggerParameter) {
+    return nonDaggerParameter;
+  }
+}
diff --git a/java/dagger/internal/MemoizedSentinel.java b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MyOtherAnnotation.java
similarity index 75%
copy from java/dagger/internal/MemoizedSentinel.java
copy to javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MyOtherAnnotation.java
index dd24dcd..c1cb6ae 100644
--- a/java/dagger/internal/MemoizedSentinel.java
+++ b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MyOtherAnnotation.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Dagger Authors.
+ * Copyright (C) 2022 The Dagger Authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
-package dagger.internal;
+package library1;
 
-/** A sentinel used to memoize a scoped binding in a component. */
-public final class MemoizedSentinel {}
+/** An annotation that is a direct dependency of the app. */
+public @interface MyOtherAnnotation {
+  Class<?> value();
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MyQualifier.java
similarity index 79%
copy from java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
copy to javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MyQualifier.java
index 0fd3db3..c08ac98 100644
--- a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
+++ b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MyQualifier.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Dagger Authors.
+ * Copyright (C) 2022 The Dagger Authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
-package dagger.hilt.android.example.gradle.simple.feature
+package library1;
 
-class FeatureCounter(var count: Int)
+import javax.inject.Qualifier;
+
+@Qualifier
+public @interface MyQualifier {}
diff --git a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MySubcomponentBinding.java
similarity index 74%
copy from java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
copy to javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MySubcomponentBinding.java
index 0fd3db3..61bb3f7 100644
--- a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
+++ b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MySubcomponentBinding.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Dagger Authors.
+ * Copyright (C) 2022 The Dagger Authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
-package dagger.hilt.android.example.gradle.simple.feature
+package library1;
 
-class FeatureCounter(var count: Int)
+/** A simple binding that needs to be passed in when creating this component. */
+public final class MySubcomponentBinding {}
diff --git a/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MySubcomponentModule.java b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MySubcomponentModule.java
new file mode 100644
index 0000000..da8de7b
--- /dev/null
+++ b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MySubcomponentModule.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package library1;
+
+import dagger.Module;
+import library2.MyTransitiveAnnotation;
+import library2.MyTransitiveType;
+
+/** A simple module that needs to be passed in when creating this component. */
+@MyTransitiveAnnotation
+@MyAnnotation(MyTransitiveType.VALUE)
+@MyOtherAnnotation(MyTransitiveType.class)
+@Module
+public final class MySubcomponentModule {
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public MySubcomponentModule(
+      @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          int i) {}
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MySubcomponentScope.java
similarity index 75%
copy from java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
copy to javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MySubcomponentScope.java
index 0fd3db3..e76b0b6 100644
--- a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
+++ b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MySubcomponentScope.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Dagger Authors.
+ * Copyright (C) 2022 The Dagger Authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
-package dagger.hilt.android.example.gradle.simple.feature
+package library1;
 
-class FeatureCounter(var count: Int)
+import javax.inject.Scope;
+
+/** A scope for {@link MySubcomponent}. */
+@Scope
+public @interface MySubcomponentScope {}
diff --git a/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MySubcomponentWithBuilder.java b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MySubcomponentWithBuilder.java
new file mode 100644
index 0000000..456eb25
--- /dev/null
+++ b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MySubcomponentWithBuilder.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package library1;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+import library2.MyTransitiveAnnotation;
+import library2.MyTransitiveType;
+
+/**
+ * A class used to test that Dagger won't fail when non-dagger related annotations cannot be
+ * resolved.
+ *
+ * <p>During the compilation of {@code :app}, {@link MyTransitiveAnnotation} will no longer be on
+ * the classpath. In most cases, Dagger shouldn't care that the annotation isn't on the classpath
+ */
+// @MyTransitiveAnnotation: Not yet supported
+@MyAnnotation(MyTransitiveType.VALUE)
+@MyOtherAnnotation(MyTransitiveType.class)
+@MySubcomponentScope
+@Subcomponent(modules = MySubcomponentModule.class)
+public abstract class MySubcomponentWithBuilder {
+  @MyQualifier
+  // @MyTransitiveAnnotation: Not yet supported
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public abstract MySubcomponentBinding qualifiedMySubcomponentBinding();
+
+  // @MyTransitiveAnnotation: Not yet supported
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public abstract MySubcomponentBinding unqualifiedMySubcomponentBinding();
+
+  // @MyTransitiveAnnotation: Not yet supported
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public abstract void injectFoo(
+      // @MyTransitiveAnnotation: Not yet supported
+      @MyAnnotation(MyTransitiveType.VALUE) @MyOtherAnnotation(MyTransitiveType.class) Foo foo);
+
+  // @MyTransitiveAnnotation: Not yet supported
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  @Subcomponent.Builder
+  public abstract static class Builder {
+    @MyTransitiveAnnotation
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    public abstract Builder mySubcomponentModule(
+        @MyTransitiveAnnotation
+            @MyAnnotation(MyTransitiveType.VALUE)
+            @MyOtherAnnotation(MyTransitiveType.class)
+            MySubcomponentModule mySubcomponentModule);
+
+    @BindsInstance
+    @MyTransitiveAnnotation
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    public abstract Builder qualifiedMySubcomponentBinding(
+        @MyQualifier
+            // @MyTransitiveAnnotation: Not yet supported
+            @MyAnnotation(MyTransitiveType.VALUE)
+            @MyOtherAnnotation(MyTransitiveType.class)
+            MySubcomponentBinding subcomponentBinding);
+
+    @BindsInstance
+    @MyTransitiveAnnotation
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    public abstract Builder unqualifiedMySubcomponentBinding(
+        // @MyTransitiveAnnotation: Not yet supported
+        @MyAnnotation(MyTransitiveType.VALUE) @MyOtherAnnotation(MyTransitiveType.class)
+            MySubcomponentBinding subcomponentBinding);
+
+    @MyTransitiveAnnotation
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    public abstract MySubcomponentWithBuilder build();
+
+    // Non-dagger code
+
+    @MyTransitiveAnnotation
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    public String nonDaggerField = "";
+
+    @MyTransitiveAnnotation
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    public static String nonDaggerStaticField = "";
+
+    @MyTransitiveAnnotation
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    public void nonDaggerMethod(
+        @MyTransitiveAnnotation
+            @MyAnnotation(MyTransitiveType.VALUE)
+            @MyOtherAnnotation(MyTransitiveType.class)
+            String str) {}
+
+    @MyTransitiveAnnotation
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    public static void nonDaggerStaticMethod(
+        @MyTransitiveAnnotation
+            @MyAnnotation(MyTransitiveType.VALUE)
+            @MyOtherAnnotation(MyTransitiveType.class)
+            String str) {}
+  }
+
+  // Non-dagger code
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public MyTransitiveType nonDaggerField = null;
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public static MyTransitiveType nonDaggerStaticField = null;
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public MyTransitiveType nonDaggerMethod(
+      @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          MyTransitiveType nonDaggerParameter) {
+    return nonDaggerParameter;
+  }
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public static MyTransitiveType nonDaggerStaticMethod(
+      @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          MyTransitiveType nonDaggerParameter) {
+    return nonDaggerParameter;
+  }
+}
diff --git a/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MySubcomponentWithFactory.java b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MySubcomponentWithFactory.java
new file mode 100644
index 0000000..f3c6f93
--- /dev/null
+++ b/javatests/artifacts/dagger/transitive-annotation-app/library1/src/main/java/library1/MySubcomponentWithFactory.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package library1;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+import library2.MyTransitiveAnnotation;
+import library2.MyTransitiveType;
+
+/**
+ * A class used to test that Dagger won't fail when non-dagger related annotations cannot be
+ * resolved.
+ *
+ * <p>During the compilation of {@code :app}, {@link MyTransitiveAnnotation} will no longer be on
+ * the classpath. In most cases, Dagger shouldn't care that the annotation isn't on the classpath
+ */
+// @MyTransitiveAnnotation: Not yet supported
+@MyAnnotation(MyTransitiveType.VALUE)
+@MyOtherAnnotation(MyTransitiveType.class)
+@MySubcomponentScope
+@Subcomponent(modules = MySubcomponentModule.class)
+public abstract class MySubcomponentWithFactory {
+  // @MyTransitiveAnnotation: Not yet supported
+  @MyQualifier
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public abstract MySubcomponentBinding qualifiedMySubcomponentBinding();
+
+  // @MyTransitiveAnnotation: Not yet supported
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public abstract MySubcomponentBinding unqualifiedMySubcomponentBinding();
+
+  // @MyTransitiveAnnotation: Not yet supported
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public abstract void injectFoo(
+      // @MyTransitiveAnnotation: Not yet supported
+      @MyAnnotation(MyTransitiveType.VALUE) @MyOtherAnnotation(MyTransitiveType.class) Foo foo);
+
+  // @MyTransitiveAnnotation: Not yet supported
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  @Subcomponent.Factory
+  public abstract static class Factory {
+    @MyTransitiveAnnotation
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    public abstract MySubcomponentWithFactory create(
+        @MyTransitiveAnnotation
+            @MyAnnotation(MyTransitiveType.VALUE)
+            @MyOtherAnnotation(MyTransitiveType.class)
+            MySubcomponentModule mySubcomponentModule,
+        @BindsInstance
+            @MyQualifier
+            // @MyTransitiveAnnotation: Not yet supported
+            @MyAnnotation(MyTransitiveType.VALUE)
+            @MyOtherAnnotation(MyTransitiveType.class)
+            MySubcomponentBinding qualifiedSubcomponentBinding,
+        @BindsInstance
+            // @MyTransitiveAnnotation: Not yet supported
+            @MyAnnotation(MyTransitiveType.VALUE)
+            @MyOtherAnnotation(MyTransitiveType.class)
+            MySubcomponentBinding unqualifiedSubcomponentBinding);
+
+    // Non-dagger code
+
+    @MyTransitiveAnnotation
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    public MyTransitiveType nonDaggerField = null;
+
+    @MyTransitiveAnnotation
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    public static MyTransitiveType nonDaggerStaticField = null;
+
+    @MyTransitiveAnnotation
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    public MyTransitiveType nonDaggerMethod(
+        @MyTransitiveAnnotation
+            @MyAnnotation(MyTransitiveType.VALUE)
+            @MyOtherAnnotation(MyTransitiveType.class)
+            MyTransitiveType nonDaggerParameter) {
+      return nonDaggerParameter;
+    }
+
+    @MyTransitiveAnnotation
+    @MyAnnotation(MyTransitiveType.VALUE)
+    @MyOtherAnnotation(MyTransitiveType.class)
+    public static MyTransitiveType nonDaggerStaticMethod(
+        @MyTransitiveAnnotation
+            @MyAnnotation(MyTransitiveType.VALUE)
+            @MyOtherAnnotation(MyTransitiveType.class)
+            MyTransitiveType nonDaggerParameter) {
+      return nonDaggerParameter;
+    }
+  }
+
+  // Non-dagger code
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public MyTransitiveType nonDaggerField = null;
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public static MyTransitiveType nonDaggerStaticField = null;
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public MyTransitiveType nonDaggerMethod(
+      @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          MyTransitiveType nonDaggerParameter) {
+    return nonDaggerParameter;
+  }
+
+  @MyTransitiveAnnotation
+  @MyAnnotation(MyTransitiveType.VALUE)
+  @MyOtherAnnotation(MyTransitiveType.class)
+  public static MyTransitiveType nonDaggerStaticMethod(
+      @MyTransitiveAnnotation
+          @MyAnnotation(MyTransitiveType.VALUE)
+          @MyOtherAnnotation(MyTransitiveType.class)
+          MyTransitiveType nonDaggerParameter) {
+    return nonDaggerParameter;
+  }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt b/javatests/artifacts/dagger/transitive-annotation-app/library2/build.gradle
similarity index 72%
copy from java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
copy to javatests/artifacts/dagger/transitive-annotation-app/library2/build.gradle
index 0fd3db3..b4d45e4 100644
--- a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
+++ b/javatests/artifacts/dagger/transitive-annotation-app/library2/build.gradle
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Dagger Authors.
+ * 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.
@@ -14,6 +14,12 @@
  * limitations under the License.
  */
 
-package dagger.hilt.android.example.gradle.simple.feature
+plugins {
+  id 'java'
+  id 'java-library'
+}
 
-class FeatureCounter(var count: Int)
+java {
+    // Make sure the generated source is compatible with Java 7.
+    sourceCompatibility = JavaVersion.VERSION_1_7
+}
diff --git a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt b/javatests/artifacts/dagger/transitive-annotation-app/library2/src/main/java/library2/MyTransitiveAnnotation.java
similarity index 75%
copy from java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
copy to javatests/artifacts/dagger/transitive-annotation-app/library2/src/main/java/library2/MyTransitiveAnnotation.java
index 0fd3db3..5f1f4bc 100644
--- a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
+++ b/javatests/artifacts/dagger/transitive-annotation-app/library2/src/main/java/library2/MyTransitiveAnnotation.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Dagger Authors.
+ * Copyright (C) 2022 The Dagger Authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
-package dagger.hilt.android.example.gradle.simple.feature
+package library2;
 
-class FeatureCounter(var count: Int)
+/** A simple annotation that is a transitive dependency of the app. */
+public @interface MyTransitiveAnnotation {}
diff --git a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt b/javatests/artifacts/dagger/transitive-annotation-app/library2/src/main/java/library2/MyTransitiveBaseAnnotation.java
similarity index 75%
copy from java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
copy to javatests/artifacts/dagger/transitive-annotation-app/library2/src/main/java/library2/MyTransitiveBaseAnnotation.java
index 0fd3db3..dcc6739 100644
--- a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
+++ b/javatests/artifacts/dagger/transitive-annotation-app/library2/src/main/java/library2/MyTransitiveBaseAnnotation.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Dagger Authors.
+ * Copyright (C) 2022 The Dagger Authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
-package dagger.hilt.android.example.gradle.simple.feature
+package library2;
 
-class FeatureCounter(var count: Int)
+/** A simple annotation that is a transitive dependency of the app. */
+public @interface MyTransitiveBaseAnnotation {}
diff --git a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt b/javatests/artifacts/dagger/transitive-annotation-app/library2/src/main/java/library2/MyTransitiveType.java
similarity index 73%
copy from java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
copy to javatests/artifacts/dagger/transitive-annotation-app/library2/src/main/java/library2/MyTransitiveType.java
index 0fd3db3..c6ecd5d 100644
--- a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureCounter.kt
+++ b/javatests/artifacts/dagger/transitive-annotation-app/library2/src/main/java/library2/MyTransitiveType.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Dagger Authors.
+ * Copyright (C) 2022 The Dagger Authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
-package dagger.hilt.android.example.gradle.simple.feature
+package library2;
 
-class FeatureCounter(var count: Int)
+
+/** A class that is a transitive dependency of the app. */
+public final class MyTransitiveType {
+  public static final int VALUE = 3;
+}
diff --git a/javatests/artifacts/dagger/transitive-annotation-app/src/main/java/app/MyComponent.java b/javatests/artifacts/dagger/transitive-annotation-app/src/main/java/app/MyComponent.java
new file mode 100644
index 0000000..ee55d3a
--- /dev/null
+++ b/javatests/artifacts/dagger/transitive-annotation-app/src/main/java/app/MyComponent.java
@@ -0,0 +1,73 @@
+/*
+ * 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 app;
+
+import dagger.Component;
+import javax.inject.Singleton;
+import library1.AssistedFoo;
+import library1.Foo;
+import library1.MyBaseComponent;
+import library1.MyComponentDependency;
+import library1.MyComponentDependencyBinding;
+import library1.MyComponentModule;
+import library1.MyQualifier;
+import library1.MySubcomponentWithBuilder;
+import library1.MySubcomponentWithFactory;
+
+@Singleton
+@Component(dependencies = MyComponentDependency.class, modules = MyComponentModule.class)
+abstract class MyComponent extends MyBaseComponent {
+  abstract Foo foo();
+
+  abstract AssistedFoo.Factory assistedFooFactory();
+
+  @MyQualifier
+  abstract MyComponentModule.ScopedQualifiedBindsType scopedQualifiedBindsType();
+
+  abstract MyComponentModule.ScopedUnqualifiedBindsType scopedUnqualifiedBindsType();
+
+  @MyQualifier
+  abstract MyComponentModule.UnscopedQualifiedBindsType unscopedQualifiedBindsType();
+
+  abstract MyComponentModule.UnscopedUnqualifiedBindsType unscopedUnqualifiedBindsType();
+
+  @MyQualifier
+  abstract MyComponentModule.ScopedQualifiedProvidesType scopedQualifiedProvidesType();
+
+  abstract MyComponentModule.ScopedUnqualifiedProvidesType scopedUnqualifiedProvidesType();
+
+  @MyQualifier
+  abstract MyComponentModule.UnscopedQualifiedProvidesType unscopedQualifiedProvidesType();
+
+  abstract MyComponentModule.UnscopedUnqualifiedProvidesType unscopedUnqualifiedProvidesType();
+
+  abstract MySubcomponentWithFactory.Factory mySubcomponentWithFactory();
+
+  abstract MySubcomponentWithBuilder.Builder mySubcomponentWithBuilder();
+
+  @MyQualifier
+  abstract MyComponentDependencyBinding qualifiedMyComponentDependencyBinding();
+
+  abstract MyComponentDependencyBinding unqualifiedMyComponentDependencyBinding();
+
+  @Component.Factory
+  abstract static class Factory extends MyBaseComponent.Factory {
+    public abstract MyComponent create(
+        MyComponentModule myComponentModule,
+        MyComponentDependency myComponentDependency);
+  }
+}
diff --git a/javatests/artifacts/dagger/transitive-annotation-app/src/test/java/app/MyComponentTest.java b/javatests/artifacts/dagger/transitive-annotation-app/src/test/java/app/MyComponentTest.java
new file mode 100644
index 0000000..438d5db
--- /dev/null
+++ b/javatests/artifacts/dagger/transitive-annotation-app/src/test/java/app/MyComponentTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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 app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import library1.Dep;
+import library1.MyComponentDependency;
+import library1.MyComponentModule;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class MyComponentTest {
+  private MyComponent component;
+
+  @Before
+  public void setup() {
+    component = DaggerMyComponent.factory()
+        .create(new MyComponentModule(new Dep()), new MyComponentDependency());
+  }
+
+  @Test
+  public void testFooIsScoped() {
+    assertThat(component.foo()).isEqualTo(component.foo());
+  }
+
+  @Test
+  public void testAssistedFoo() {
+    assertThat(component.assistedFooFactory().create(5)).isNotNull();
+  }
+
+  @Test
+  public void testScopedQualifiedBindsTypeIsScoped() {
+    assertThat(component.scopedQualifiedBindsType())
+        .isEqualTo(component.scopedQualifiedBindsType());
+  }
+
+  @Test
+  public void testScopedUnqualifiedBindsTypeIsScoped() {
+    assertThat(component.scopedUnqualifiedBindsType())
+        .isEqualTo(component.scopedUnqualifiedBindsType());
+  }
+
+  @Test
+  public void testUnscopedQualifiedBindsTypeIsNotScoped() {
+    assertThat(component.unscopedQualifiedBindsType())
+        .isNotEqualTo(component.unscopedQualifiedBindsType());
+  }
+
+  @Test
+  public void testUnscopedUnqualifiedBindsTypeIsNotScoped() {
+    assertThat(component.unscopedUnqualifiedBindsType())
+        .isNotEqualTo(component.unscopedUnqualifiedBindsType());
+  }
+
+  @Test
+  public void testScopedQualifiedProvidesTypeIsScoped() {
+    assertThat(component.scopedQualifiedProvidesType())
+        .isEqualTo(component.scopedQualifiedProvidesType());
+  }
+
+  @Test
+  public void testScopedUnqualifiedProvidesTypeIsScoped() {
+    assertThat(component.scopedUnqualifiedProvidesType())
+        .isEqualTo(component.scopedUnqualifiedProvidesType());
+  }
+
+  @Test
+  public void testUnscopedQualifiedProvidesTypeIsNotScoped() {
+    assertThat(component.unscopedQualifiedProvidesType())
+        .isNotEqualTo(component.unscopedQualifiedProvidesType());
+  }
+
+  @Test
+  public void testUnscopedUnqualifiedProvidesTypeIsNotScoped() {
+    assertThat(component.unscopedUnqualifiedProvidesType())
+        .isNotEqualTo(component.unscopedUnqualifiedProvidesType());
+  }
+
+  @Test
+  public void testMyComponentDependencyBinding() {
+    assertThat(component.qualifiedMyComponentDependencyBinding())
+        .isNotEqualTo(component.unqualifiedMyComponentDependencyBinding());
+  }
+}
diff --git a/javatests/artifacts/dagger/transitive-annotation-app/src/test/java/app/MySubcomponentWithBuilderTest.java b/javatests/artifacts/dagger/transitive-annotation-app/src/test/java/app/MySubcomponentWithBuilderTest.java
new file mode 100644
index 0000000..3db241b
--- /dev/null
+++ b/javatests/artifacts/dagger/transitive-annotation-app/src/test/java/app/MySubcomponentWithBuilderTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import library1.Dep;
+import library1.MyComponentDependency;
+import library1.MyComponentModule;
+import library1.MySubcomponentBinding;
+import library1.MySubcomponentModule;
+import library1.MySubcomponentWithBuilder;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class MySubcomponentWithBuilderTest {
+  private MySubcomponentWithBuilder subcomponentWithBuilder;
+
+  @Before
+  public void setup() {
+    subcomponentWithBuilder =
+        DaggerMyComponent.factory()
+            .create(new MyComponentModule(new Dep()), new MyComponentDependency())
+            .mySubcomponentWithBuilder()
+            .mySubcomponentModule(new MySubcomponentModule(3))
+            .qualifiedMySubcomponentBinding(new MySubcomponentBinding())
+            .unqualifiedMySubcomponentBinding(new MySubcomponentBinding())
+            .build();
+  }
+
+  // Test that the qualified and unqualified bindings are two separate objects
+  @Test
+  public void testMySubcomponentBinding() {
+    assertThat(subcomponentWithBuilder.qualifiedMySubcomponentBinding())
+        .isNotEqualTo(subcomponentWithBuilder.unqualifiedMySubcomponentBinding());
+  }
+}
diff --git a/javatests/artifacts/dagger/transitive-annotation-app/src/test/java/app/MySubcomponentWithFactoryTest.java b/javatests/artifacts/dagger/transitive-annotation-app/src/test/java/app/MySubcomponentWithFactoryTest.java
new file mode 100644
index 0000000..a45dcc7
--- /dev/null
+++ b/javatests/artifacts/dagger/transitive-annotation-app/src/test/java/app/MySubcomponentWithFactoryTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import library1.Dep;
+import library1.MyComponentDependency;
+import library1.MyComponentModule;
+import library1.MySubcomponentBinding;
+import library1.MySubcomponentModule;
+import library1.MySubcomponentWithFactory;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class MySubcomponentWithFactoryTest {
+  private MySubcomponentWithFactory subcomponentWithFactory;
+
+  @Before
+  public void setup() {
+    subcomponentWithFactory =
+        DaggerMyComponent.factory()
+            .create(new MyComponentModule(new Dep()), new MyComponentDependency())
+            .mySubcomponentWithFactory()
+            .create(
+                new MySubcomponentModule(1),
+                new MySubcomponentBinding(),
+                new MySubcomponentBinding());
+  }
+
+  // Test that the qualified and unqualified bindings are two separate objects
+  @Test
+  public void testMySubcomponentBinding() {
+    assertThat(subcomponentWithFactory.qualifiedMySubcomponentBinding())
+        .isNotEqualTo(subcomponentWithFactory.unqualifiedMySubcomponentBinding());
+  }
+}
diff --git a/javatests/artifacts/hilt-android/gradleConfigCache/app/build.gradle b/javatests/artifacts/hilt-android/gradleConfigCache/app/build.gradle
deleted file mode 100644
index 4f21d0a..0000000
--- a/javatests/artifacts/hilt-android/gradleConfigCache/app/build.gradle
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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'
-    }
-    lintOptions {
-        checkReleaseBuilds = false
-    }
-    testOptions {
-        unitTests.includeAndroidResources = true
-    }
-}
-
-hilt {
-    enableTransformForLocalTests = true
-    enableExperimentalClasspathAggregation = 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
deleted file mode 100644
index 9099db4..0000000
--- a/javatests/artifacts/hilt-android/gradleConfigCache/app/src/androidTest/java/dagger/hilt/android/gradleConfigCache/EmulatorTest.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index 9fce7cd..0000000
--- a/javatests/artifacts/hilt-android/gradleConfigCache/app/src/androidTest/java/dagger/hilt/android/gradleConfigCache/TestRunner.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index ce1fb71..0000000
--- a/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?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/Foo.kt b/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/java/dagger/hilt/android/gradleConfigCache/Foo.kt
deleted file mode 100644
index 969e183..0000000
--- a/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/java/dagger/hilt/android/gradleConfigCache/Foo.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-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
deleted file mode 100644
index 0011bde..0000000
--- a/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/java/dagger/hilt/android/gradleConfigCache/MainActivity.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index cb5a8df..0000000
--- a/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?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/test/java/dagger/hilt/android/gradleConfigCache/LocalTest.kt b/javatests/artifacts/hilt-android/gradleConfigCache/app/src/test/java/dagger/hilt/android/gradleConfigCache/LocalTest.kt
deleted file mode 100644
index 527d887..0000000
--- a/javatests/artifacts/hilt-android/gradleConfigCache/app/src/test/java/dagger/hilt/android/gradleConfigCache/LocalTest.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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
deleted file mode 100644
index de0566c..0000000
--- a/javatests/artifacts/hilt-android/gradleConfigCache/build.gradle
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2020 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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()
-    }
-    // TODO(bcorso): Consider organizing all projects under a single project
-    // that share a build.gradle configuration to reduce the copy-paste.
-    // Local tests: Adds logs for individual local tests
-    tasks.withType(Test) {
-        testLogging {
-            exceptionFormat "full"
-            showCauses true
-            showExceptions true
-            showStackTraces true
-            showStandardStreams true
-            events = ["passed", "skipped", "failed", "standardOut", "standardError"]
-        }
-    }
-}
-
-// Instrumentation tests: Combines test reports for all modules
-apply plugin: 'android-reporting'
\ No newline at end of file
diff --git a/javatests/artifacts/hilt-android/gradleConfigCache/gradle.properties b/javatests/artifacts/hilt-android/gradleConfigCache/gradle.properties
deleted file mode 100644
index 1813493..0000000
--- a/javatests/artifacts/hilt-android/gradleConfigCache/gradle.properties
+++ /dev/null
@@ -1,8 +0,0 @@
-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
deleted file mode 100644
index 62d4c05..0000000
--- a/javatests/artifacts/hilt-android/gradleConfigCache/gradle/wrapper/gradle-wrapper.jar
+++ /dev/null
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
deleted file mode 100644
index 4d9ca16..0000000
--- a/javatests/artifacts/hilt-android/gradleConfigCache/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,5 +0,0 @@
-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
deleted file mode 100755
index fbd7c51..0000000
--- a/javatests/artifacts/hilt-android/gradleConfigCache/gradlew
+++ /dev/null
@@ -1,185 +0,0 @@
-#!/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
deleted file mode 100644
index a9f778a..0000000
--- a/javatests/artifacts/hilt-android/gradleConfigCache/gradlew.bat
+++ /dev/null
@@ -1,104 +0,0 @@
-@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
deleted file mode 100644
index d0d5fee..0000000
--- a/javatests/artifacts/hilt-android/gradleConfigCache/settings.gradle
+++ /dev/null
@@ -1,2 +0,0 @@
-rootProject.name='Gradle Configuration Cache App'
-include ':app'
diff --git a/javatests/artifacts/hilt-android/pluginMarker/app/build.gradle b/javatests/artifacts/hilt-android/pluginMarker/app/build.gradle
new file mode 100644
index 0000000..b288628
--- /dev/null
+++ b/javatests/artifacts/hilt-android/pluginMarker/app/build.gradle
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 'com.android.application' version '7.0.0'
+    id 'com.google.dagger.hilt.android' version 'LOCAL-SNAPSHOT'
+}
+
+android {
+    compileSdkVersion 30
+    buildToolsVersion "30.0.2"
+
+    defaultConfig {
+        applicationId "dagger.hilt.android.simple"
+        minSdkVersion 21
+        targetSdkVersion 30
+    }
+
+    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/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/res/values/strings.xml b/javatests/artifacts/hilt-android/pluginMarker/app/src/main/AndroidManifest.xml
similarity index 75%
copy from javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/res/values/strings.xml
copy to javatests/artifacts/hilt-android/pluginMarker/app/src/main/AndroidManifest.xml
index 05afdbf..60fb594 100644
--- a/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/res/values/strings.xml
+++ b/javatests/artifacts/hilt-android/pluginMarker/app/src/main/AndroidManifest.xml
@@ -1,5 +1,5 @@
 <!--
-  ~ Copyright (C) 2020 The Dagger Authors.
+  ~ Copyright (C) 2022 The Dagger Authors.
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
@@ -13,8 +13,6 @@
   ~ 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>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="dagger.hilt.android.simple">
+</manifest>
\ No newline at end of file
diff --git a/java/dagger/example/gradle/android/simple/gradle.properties b/javatests/artifacts/hilt-android/pluginMarker/gradle.properties
similarity index 95%
copy from java/dagger/example/gradle/android/simple/gradle.properties
copy to javatests/artifacts/hilt-android/pluginMarker/gradle.properties
index 2d8d1e4..5bac8ac 100644
--- a/java/dagger/example/gradle/android/simple/gradle.properties
+++ b/javatests/artifacts/hilt-android/pluginMarker/gradle.properties
@@ -1 +1 @@
-android.useAndroidX=true
\ No newline at end of file
+android.useAndroidX=true
diff --git a/javatests/artifacts/dagger/simple/gradle/wrapper/gradle-wrapper.jar b/javatests/artifacts/hilt-android/pluginMarker/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
copy from javatests/artifacts/dagger/simple/gradle/wrapper/gradle-wrapper.jar
copy to javatests/artifacts/hilt-android/pluginMarker/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/java/dagger/example/gradle/android/simple/gradle/wrapper/gradle-wrapper.properties b/javatests/artifacts/hilt-android/pluginMarker/gradle/wrapper/gradle-wrapper.properties
similarity index 91%
copy from java/dagger/example/gradle/android/simple/gradle/wrapper/gradle-wrapper.properties
copy to javatests/artifacts/hilt-android/pluginMarker/gradle/wrapper/gradle-wrapper.properties
index 4d9ca16..0f80bbf 100644
--- a/java/dagger/example/gradle/android/simple/gradle/wrapper/gradle-wrapper.properties
+++ b/javatests/artifacts/hilt-android/pluginMarker/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/javatests/artifacts/dagger/simple/gradlew b/javatests/artifacts/hilt-android/pluginMarker/gradlew
similarity index 100%
copy from javatests/artifacts/dagger/simple/gradlew
copy to javatests/artifacts/hilt-android/pluginMarker/gradlew
diff --git a/javatests/artifacts/hilt-android/pluginMarker/settings.gradle b/javatests/artifacts/hilt-android/pluginMarker/settings.gradle
new file mode 100644
index 0000000..f32d225
--- /dev/null
+++ b/javatests/artifacts/hilt-android/pluginMarker/settings.gradle
@@ -0,0 +1,18 @@
+pluginManagement {
+    repositories {
+        gradlePluginPortal()
+        google()
+        mavenCentral()
+        mavenLocal()
+    }
+}
+dependencyResolutionManagement {
+    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+    repositories {
+        google()
+        mavenCentral()
+        mavenLocal()
+    }
+}
+rootProject.name = "PluginMarkerCheck"
+include ':app'
\ No newline at end of file
diff --git a/javatests/artifacts/hilt-android/simple/app-java-only/build.gradle b/javatests/artifacts/hilt-android/simple/app-java-only/build.gradle
new file mode 100644
index 0000000..9d17cd9
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app-java-only/build.gradle
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+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 "androidx.test.runner.AndroidJUnitRunner"
+    }
+    compileOptions {
+        sourceCompatibility 1.8
+        targetCompatibility 1.8
+    }
+    testOptions {
+        unitTests.includeAndroidResources = true
+    }
+    lintOptions {
+        checkReleaseBuilds = false
+    }
+}
+
+hilt {
+    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 "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'
+}
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/debug/AndroidManifest.xml b/javatests/artifacts/hilt-android/simple/app-java-only/src/main/AndroidManifest.xml
similarity index 73%
copy from java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/debug/AndroidManifest.xml
copy to javatests/artifacts/hilt-android/simple/app-java-only/src/main/AndroidManifest.xml
index 081c7ad..cae4e88 100644
--- a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/debug/AndroidManifest.xml
+++ b/javatests/artifacts/hilt-android/simple/app-java-only/src/main/AndroidManifest.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2020 The Dagger Authors.
+  ~ 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.
@@ -14,11 +13,12 @@
   ~ 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">
+  package="dagger.hilt.android.simple">
 
-  <application>
-    <activity android:name=".SimpleTest$TestActivity" android:exported="false"/>
+  <application
+               android:name=".SimpleApplication"
+               android:label="Java-only Hilt Android"
+               android:taskAffinity="">
   </application>
 </manifest>
diff --git a/javatests/artifacts/hilt-android/simple/app-java-only/src/main/java/dagger/hilt/android/simple/SimpleApplication.java b/javatests/artifacts/hilt-android/simple/app-java-only/src/main/java/dagger/hilt/android/simple/SimpleApplication.java
new file mode 100644
index 0000000..2a0d8be
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app-java-only/src/main/java/dagger/hilt/android/simple/SimpleApplication.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.hilt.android.simple;
+
+import android.app.Application;
+import dagger.Module;
+import dagger.Provides;
+import dagger.hilt.EntryPoint;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.EntryPointAccessors;
+import dagger.hilt.android.HiltAndroidApp;
+import dagger.hilt.components.SingletonComponent;
+import javax.inject.Inject;
+
+/** A java-only application that uses Hilt. */
+@HiltAndroidApp
+public class SimpleApplication extends Application {
+  @Module
+  @InstallIn(SingletonComponent.class)
+  interface MyModule {
+    @Provides
+    static String provideString() {
+      return "some string";
+    }
+  }
+
+  @EntryPoint
+  @InstallIn(SingletonComponent.class)
+  interface MyEntryPoint {
+    String getString();
+  }
+
+  @Inject String str;
+
+  public String getStringEntryPoint() {
+    return EntryPointAccessors.fromApplication(this, MyEntryPoint.class).getString();
+  }
+}
diff --git a/javatests/artifacts/hilt-android/simple/app-java-only/src/test/java/dagger/hilt/android/simple/BuildTest.java b/javatests/artifacts/hilt-android/simple/app-java-only/src/test/java/dagger/hilt/android/simple/BuildTest.java
new file mode 100644
index 0000000..9411852
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app-java-only/src/test/java/dagger/hilt/android/simple/BuildTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.simple;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Build;
+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;
+
+/**
+ * Regression test for https://github.com/google/dagger/issues/3119
+ */
+@RunWith(AndroidJUnit4.class)
+// Robolectric requires Java9 to run API 29 and above, so use API 28 instead
+@Config(sdk = Build.VERSION_CODES.P, application = SimpleApplication.class)
+public class BuildTest {
+  @Test
+  public void useAppContext() {
+    assertThat((Object) ApplicationProvider.getApplicationContext())
+        .isInstanceOf(SimpleApplication.class);
+    SimpleApplication app = (SimpleApplication) ApplicationProvider.getApplicationContext();
+    assertThat(app.str).isNotNull();
+    assertThat(app.str).isEqualTo(app.getStringEntryPoint());
+  }
+}
diff --git a/javatests/artifacts/hilt-android/simple/app/build.gradle b/javatests/artifacts/hilt-android/simple/app/build.gradle
index 1d76ec2..34043ad 100644
--- a/javatests/artifacts/hilt-android/simple/app/build.gradle
+++ b/javatests/artifacts/hilt-android/simple/app/build.gradle
@@ -13,10 +13,35 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+import org.gradle.util.VersionNumber
 
 apply plugin: 'com.android.application'
 apply plugin: 'dagger.hilt.android.plugin'
 
+// Gets additional test directories to be added to test and androidTest source
+// sets. If the directory name is appended with '-agp-x.x.x' then the directory
+// is conditionally added based on the AGP version of the project.
+def getAdditionalTestDirs(String variant) {
+    def testDirs = [
+        'androidTest': [],
+        'sharedTest': ['src/sharedTest/java'],
+        'test': []
+    ]
+    def suffix = '-agp-'
+    def agpVersion = VersionNumber.parse(agp_version)
+    file("${getProjectDir().absolutePath}/src").eachFile { file ->
+        int indexOf = file.name.indexOf(suffix)
+        if (file.isDirectory() && indexOf != -1) {
+            def dirAgpVersion =
+                VersionNumber.parse(file.name.substring(indexOf + suffix.length()))
+            if (agpVersion >= dirAgpVersion) {
+                testDirs[file.name.substring(0, indexOf)].add("src/${file.name}/java")
+            }
+        }
+    }
+    return testDirs[variant] + testDirs['sharedTest']
+}
+
 android {
     compileSdkVersion 30
     buildToolsVersion "30.0.2"
@@ -40,19 +65,18 @@
         checkReleaseBuilds = false
     }
     sourceSets {
-        String sharedTestDir = 'src/sharedTest/java'
         test {
-            java.srcDirs += sharedTestDir
+            java.srcDirs += getAdditionalTestDirs("test")
         }
         androidTest {
-            java.srcDirs += sharedTestDir
+            java.srcDirs += getAdditionalTestDirs("androidTest")
         }
     }
 }
 
 hilt {
-    enableExperimentalClasspathAggregation = true
     enableTransformForLocalTests = true
+    enableAggregatingTask = true
 }
 
 configurations.all {
@@ -97,8 +121,8 @@
 
   // To help us catch version skew related issues in hilt extensions.
   // TODO(bcorso): Add examples testing the actual API.
-  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'
+  implementation 'androidx.hilt:hilt-work:1.0.0'
+  annotationProcessor 'androidx.hilt:hilt-compiler:1.0.0'
+  testAnnotationProcessor 'androidx.hilt:hilt-compiler:1.0.0'
+  androidTestAnnotationProcessor 'androidx.hilt:hilt-compiler:1.0.0'
 }
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/BroadcastReceiverTest.java b/javatests/artifacts/hilt-android/simple/app/src/androidTest-agp-4.2.0/java/dagger/hilt/android/simple/BroadcastReceiverTest.java
similarity index 94%
rename from java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/BroadcastReceiverTest.java
rename to javatests/artifacts/hilt-android/simple/app/src/androidTest-agp-4.2.0/java/dagger/hilt/android/simple/BroadcastReceiverTest.java
index 7585bbb..265869a 100644
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/sharedTest/java/dagger/hilt/android/example/gradle/simple/BroadcastReceiverTest.java
+++ b/javatests/artifacts/hilt-android/simple/app/src/androidTest-agp-4.2.0/java/dagger/hilt/android/simple/BroadcastReceiverTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package dagger.hilt.android.example.gradle.simple;
+package dagger.hilt.android.simple;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -111,7 +111,7 @@
 
   /** Test receiver */
   @AndroidEntryPoint
-  static class TestReceiverOne extends BroadcastReceiver {
+  public static class TestReceiverOne extends BroadcastReceiver {
 
     final CountDownLatch latch = new CountDownLatch(1);
 
@@ -126,7 +126,7 @@
 
   /** Test receiver */
   @AndroidEntryPoint
-  static class TestReceiverTwo extends BaseReceiverAbstractMethod {
+  public static class TestReceiverTwo extends BaseReceiverAbstractMethod {
 
     final CountDownLatch latch = new CountDownLatch(1);
 
@@ -141,7 +141,7 @@
 
   /** Test receiver */
   @AndroidEntryPoint
-  static class TestReceiverThree extends BaseReceiverConcreteMethod {
+  public static class TestReceiverThree extends BaseReceiverConcreteMethod {
 
     final CountDownLatch latch = new CountDownLatch(1);
 
@@ -157,7 +157,7 @@
 
   /** Complex-ish test receiver */
   @AndroidEntryPoint
-  static class TestReceiverFour extends BroadcastReceiver {
+  public static class TestReceiverFour extends BroadcastReceiver {
 
     final CountDownLatch latch = new CountDownLatch(1);
 
diff --git a/javatests/artifacts/hilt-android/simple/app/src/debug/AndroidManifest.xml b/javatests/artifacts/hilt-android/simple/app/src/debug/AndroidManifest.xml
index 14d33b0..76c6afa 100644
--- a/javatests/artifacts/hilt-android/simple/app/src/debug/AndroidManifest.xml
+++ b/javatests/artifacts/hilt-android/simple/app/src/debug/AndroidManifest.xml
@@ -18,6 +18,10 @@
 
   <application>
     <activity
+        android:name=".AliasOfMultipleScopesTest$TestActivity"
+        android:theme="@style/Theme.AppCompat.Light"
+        android:exported="false" />
+    <activity
         android:name=".Injection1Test$TestActivity"
         android:theme="@style/Theme.AppCompat.Light"
         android:exported="false" />
@@ -26,6 +30,10 @@
         android:theme="@style/Theme.AppCompat.Light"
         android:exported="false"/>
     <activity
+        android:name=".InvokeSpecialTransformTest$TestActivity"
+        android:theme="@style/Theme.AppCompat.Light"
+        android:exported="false"/>
+    <activity
         android:name=".ActivityScenarioRuleTest$TestActivity"
         android:theme="@style/Theme.AppCompat.Light"
         android:exported="false"/>
diff --git a/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/AliasOfMultipleScopesTest.java b/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/AliasOfMultipleScopesTest.java
new file mode 100644
index 0000000..7175f78
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/AliasOfMultipleScopesTest.java
@@ -0,0 +1,184 @@
+/*
+ * 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.simple;
+
+import static com.google.common.truth.Truth.assertThat;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import android.content.Context;
+import androidx.activity.ComponentActivity;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.Module;
+import dagger.Provides;
+import dagger.hilt.DefineComponent;
+import dagger.hilt.EntryPoint;
+import dagger.hilt.EntryPoints;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.AndroidEntryPoint;
+import dagger.hilt.android.components.ActivityComponent;
+import dagger.hilt.android.qualifiers.ApplicationContext;
+import dagger.hilt.android.scopes.ActivityScoped;
+import dagger.hilt.android.testing.HiltAndroidRule;
+import dagger.hilt.android.testing.HiltAndroidTest;
+import dagger.hilt.components.SingletonComponent;
+import dagger.hilt.migration.AliasOf;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Scope;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+public final class AliasOfMultipleScopesTest {
+
+  @Rule public final HiltAndroidRule rule = new HiltAndroidRule(this);
+
+  @Inject @ApplicationContext Context context;
+  @Inject CustomComponent.Builder customComponentBuilder;
+
+  @Scope
+  @Retention(CLASS)
+  @Target({ElementType.METHOD, ElementType.TYPE})
+  public @interface CustomScoped {}
+
+  @DefineComponent(parent = SingletonComponent.class)
+  @CustomScoped
+  public interface CustomComponent {
+    @DefineComponent.Builder
+    public interface Builder {
+      CustomComponent build();
+    }
+  }
+
+  @Scope
+  @AliasOf({ActivityScoped.class, CustomScoped.class})
+  public @interface AliasScoped {}
+
+  public interface UnscopedDep {}
+
+  public interface ActivityScopedDep {}
+
+  public interface CustomScopedDep {}
+
+  public interface AliasScopedDep {}
+
+  @Module
+  @InstallIn(SingletonComponent.class)
+  interface SingletonTestModule {
+    @Provides
+    static UnscopedDep unscopedDep() {
+      return new UnscopedDep() {};
+    }
+  }
+
+  @Module
+  @InstallIn(ActivityComponent.class)
+  interface ActivityTestModule {
+    @Provides
+    @ActivityScoped
+    static ActivityScopedDep activityScopedDep() {
+      return new ActivityScopedDep() {};
+    }
+
+    @Provides
+    @AliasScoped
+    static AliasScopedDep aliasScopedDep() {
+      return new AliasScopedDep() {};
+    }
+  }
+
+  @Module
+  @InstallIn(CustomComponent.class)
+  interface CustomTestModule {
+    @Provides
+    @CustomScoped
+    static CustomScopedDep customScopedDep() {
+      return new CustomScopedDep() {};
+    }
+
+    @Provides
+    @AliasScoped
+    static AliasScopedDep aliasScopedDep() {
+      return new AliasScopedDep() {};
+    }
+  }
+
+  /** An activity to test injection. */
+  @AndroidEntryPoint(ComponentActivity.class)
+  public static final class TestActivity extends Hilt_AliasOfMultipleScopesTest_TestActivity {
+    @Inject Provider<UnscopedDep> unscopedDep;
+    @Inject Provider<ActivityScopedDep> activityScopedDep;
+    @Inject Provider<AliasScopedDep> aliasScopedDep;
+  }
+
+  @EntryPoint
+  @InstallIn(SingletonComponent.class)
+  interface CustomComponentBuilderEntryPoint {
+    CustomComponent.Builder customComponentBuilder();
+  }
+
+  @EntryPoint
+  @InstallIn(CustomComponent.class)
+  interface CustomComponentEntryPoint {
+    Provider<UnscopedDep> unscopedDep();
+
+    Provider<CustomScopedDep> customScopedDep();
+
+    Provider<AliasScopedDep> aliasScopedDep();
+  }
+
+  @Before
+  public void setUp() {
+    rule.inject();
+  }
+
+  @Test
+  public void testActivityScoped() {
+    try (ActivityScenario<TestActivity> scenario = ActivityScenario.launch(TestActivity.class)) {
+      scenario.onActivity(
+          activity -> {
+            assertThat(activity.unscopedDep.get()).isNotSameInstanceAs(activity.unscopedDep.get());
+            assertThat(activity.activityScopedDep.get())
+                .isSameInstanceAs(activity.activityScopedDep.get());
+            assertThat(activity.aliasScopedDep.get())
+                .isSameInstanceAs(activity.aliasScopedDep.get());
+          });
+    }
+  }
+
+  @Test
+  public void testCustomScoped() {
+    CustomComponent customComponent =
+        EntryPoints.get(context, CustomComponentBuilderEntryPoint.class)
+            .customComponentBuilder()
+            .build();
+    CustomComponentEntryPoint entryPoint =
+        EntryPoints.get(customComponent, CustomComponentEntryPoint.class);
+    assertThat(entryPoint.unscopedDep().get()).isNotSameInstanceAs(entryPoint.unscopedDep().get());
+    assertThat(entryPoint.customScopedDep().get())
+        .isSameInstanceAs(entryPoint.customScopedDep().get());
+    assertThat(entryPoint.aliasScopedDep().get())
+        .isSameInstanceAs(entryPoint.aliasScopedDep().get());
+  }
+}
diff --git a/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/InvokeSpecialTransformTest.java b/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/InvokeSpecialTransformTest.java
new file mode 100644
index 0000000..9fed04a
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/app/src/sharedTest/java/dagger/hilt/android/simple/InvokeSpecialTransformTest.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.android.simple;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.widget.FrameLayout;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.lifecycle.Lifecycle.State;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+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;
+
+/** Test that verifies edge cases of invokespecial instructions transformation. */
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+public class InvokeSpecialTransformTest {
+
+  @Rule
+  public HiltAndroidRule rule = new HiltAndroidRule(this);
+
+  @Test
+  public void constructorCallOfOldSuperclass() {
+    try (ActivityScenario<TestActivity> scenario = ActivityScenario.launch(TestActivity.class)) {
+      scenario.moveToState(State.DESTROYED);
+    }
+  }
+
+  /** A test activity */
+  @AndroidEntryPoint
+  public static final class TestActivity extends AppCompatActivity {
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+      super.onCreate(savedInstanceState);
+      setContentView(new CustomView(this).createBrother());
+    }
+  }
+
+  /** A custom view for testing */
+  @AndroidEntryPoint
+  public static class CustomView extends FrameLayout {
+
+    public CustomView(@NonNull Context context) {
+      // This super call is an invokespecial that should be transformed
+      super(context);
+      // This invokespecial that should not be transformed.
+      FrameLayout secondInvokeSpecial = new FrameLayout(getContext());
+    }
+
+    FrameLayout createBrother() {
+      // This invokespecial that should not be transformed.
+      return new FrameLayout(getContext());
+    }
+  }
+}
\ No newline at end of file
diff --git a/javatests/artifacts/hilt-android/simple/build.gradle b/javatests/artifacts/hilt-android/simple/build.gradle
index 59336bc..d869b39 100644
--- a/javatests/artifacts/hilt-android/simple/build.gradle
+++ b/javatests/artifacts/hilt-android/simple/build.gradle
@@ -17,12 +17,12 @@
 buildscript {
     ext {
         dagger_version = 'LOCAL-SNAPSHOT'
-        kotlin_version = '1.3.61'
-        agp_version = System.getenv('AGP_VERSION') ?: "4.2.0-beta04"
+        kotlin_version = '1.5.32'
+        agp_version = System.getenv('AGP_VERSION') ?: "4.2.0"
     }
     repositories {
         google()
-        jcenter()
+        mavenCentral()
         mavenLocal()
     }
     dependencies {
@@ -35,7 +35,6 @@
 allprojects {
     repositories {
       google()
-      jcenter()
       mavenCentral()
       mavenLocal()
     }
diff --git a/javatests/artifacts/hilt-android/simple/earlyentrypoint/build.gradle b/javatests/artifacts/hilt-android/simple/earlyentrypoint/build.gradle
new file mode 100644
index 0000000..1428fb1
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/earlyentrypoint/build.gradle
@@ -0,0 +1,68 @@
+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
+    }
+    testOptions {
+        unitTests.includeAndroidResources = true
+    }
+}
+
+hilt {
+    enableTransformForLocalTests = true
+    enableAggregatingTask = true
+}
+
+// This is a regression test for https://github.com/google/dagger/issues/2789.
+// Reproducing this issue requires that we don't have unexpected tests, so this
+// check validates that. In particular, if we accidentally add a test with no
+// test-specific bindings the EarlyEntryPoints will use the component for that
+// test instead of generating a component just for the EarlyEntryPoints, which
+// causes this issue.
+task checkSourceSetTask(){
+    sourceSets {
+        test {
+            def actualSrcs = allSource.files.name as Set
+            def expectedSrcs = [
+                'EarlyEntryPointWithBindValueTest.java',
+                'EarlyEntryPointWithBindValueObjects.java'
+            ] as Set
+            if (!actualSrcs.equals(expectedSrcs)) {
+                throw new StopExecutionException(
+                    'Unexpected test sources: ' + allSource.files.name)
+            }
+        }
+    }
+}
+
+gradle.projectsEvaluated {
+    preBuild.dependsOn checkSourceSetTask
+}
+
+dependencies {
+    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 "com.google.dagger:hilt-android-testing:$dagger_version"
+    testAnnotationProcessor "com.google.dagger:hilt-compiler:$dagger_version"
+}
\ No newline at end of file
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/debug/AndroidManifest.xml b/javatests/artifacts/hilt-android/simple/earlyentrypoint/src/main/AndroidManifest.xml
similarity index 80%
rename from java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/debug/AndroidManifest.xml
rename to javatests/artifacts/hilt-android/simple/earlyentrypoint/src/main/AndroidManifest.xml
index 081c7ad..7197111 100644
--- a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/debug/AndroidManifest.xml
+++ b/javatests/artifacts/hilt-android/simple/earlyentrypoint/src/main/AndroidManifest.xml
@@ -16,9 +16,6 @@
   -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="dagger.hilt.android.example.gradle.simpleKotlin">
+    package="dagger.hilt.android.simple.earlyentrypoint">
 
-  <application>
-    <activity android:name=".SimpleTest$TestActivity" android:exported="false"/>
-  </application>
 </manifest>
diff --git a/javatests/artifacts/hilt-android/simple/earlyentrypoint/src/test/java/dagger/hilt/android/simple/earlyentrypoint/EarlyEntryPointWithBindValueObjects.java b/javatests/artifacts/hilt-android/simple/earlyentrypoint/src/test/java/dagger/hilt/android/simple/earlyentrypoint/EarlyEntryPointWithBindValueObjects.java
new file mode 100644
index 0000000..c86f900
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/earlyentrypoint/src/test/java/dagger/hilt/android/simple/earlyentrypoint/EarlyEntryPointWithBindValueObjects.java
@@ -0,0 +1,41 @@
+/*
+ * 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.simple;
+
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.EarlyEntryPoint;
+import dagger.hilt.components.SingletonComponent;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+// This is defined outside of the tests since EarlyEntryPoint cannot be nested in tests.
+public final class EarlyEntryPointWithBindValueObjects {
+  private EarlyEntryPointWithBindValueObjects() {}
+
+  @Singleton
+  public static final class Foo {
+    @Inject
+    Foo() {}
+  }
+
+  /** Used to test {@link EarlyEntryPoint} */
+  @EarlyEntryPoint
+  @InstallIn(SingletonComponent.class)
+  public interface FooEarlyEntryPoint {
+    Foo getEarlyFoo();
+  }
+}
diff --git a/javatests/artifacts/hilt-android/simple/earlyentrypoint/src/test/java/dagger/hilt/android/simple/earlyentrypoint/EarlyEntryPointWithBindValueTest.java b/javatests/artifacts/hilt-android/simple/earlyentrypoint/src/test/java/dagger/hilt/android/simple/earlyentrypoint/EarlyEntryPointWithBindValueTest.java
new file mode 100644
index 0000000..0b2acfd
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simple/earlyentrypoint/src/test/java/dagger/hilt/android/simple/earlyentrypoint/EarlyEntryPointWithBindValueTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.simple;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.Build;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.hilt.EntryPoint;
+import dagger.hilt.EntryPoints;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.EarlyEntryPoints;
+import dagger.hilt.android.simple.EarlyEntryPointWithBindValueObjects.Foo;
+import dagger.hilt.android.simple.EarlyEntryPointWithBindValueObjects.FooEarlyEntryPoint;
+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.components.SingletonComponent;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+@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 EarlyEntryPointWithBindValueTest {
+  @EntryPoint
+  @InstallIn(SingletonComponent.class)
+  interface FooEntryPoint {
+    Foo foo();
+  }
+
+  @Rule public HiltAndroidRule rule = new HiltAndroidRule(this);
+
+  @BindValue String bindString = "TestValue";
+
+  @Test
+  public void testBindValue() throws Exception {
+    rule.inject();
+    assertThat(bindString).isNotNull();
+    assertThat(bindString).isEqualTo("TestValue");
+  }
+
+  @Test
+  public void testEarlyEntryPoint() throws Exception {
+    Context appContext = getApplicationContext();
+
+    // Assert that all scoped Foo instances from EarlyEntryPoint are equal
+    Foo earlyFoo1 = EarlyEntryPoints.get(appContext, FooEarlyEntryPoint.class).getEarlyFoo();
+    Foo earlyFoo2 = EarlyEntryPoints.get(appContext, FooEarlyEntryPoint.class).getEarlyFoo();
+    assertThat(earlyFoo1).isNotNull();
+    assertThat(earlyFoo2).isNotNull();
+    assertThat(earlyFoo1).isEqualTo(earlyFoo2);
+
+    // Assert that all scoped Foo instances from EntryPoint are equal
+    Foo foo1 = EntryPoints.get(appContext, FooEntryPoint.class).foo();
+    Foo foo2 = EntryPoints.get(appContext, FooEntryPoint.class).foo();
+    assertThat(foo1).isNotNull();
+    assertThat(foo2).isNotNull();
+    assertThat(foo1).isEqualTo(foo2);
+
+    // Assert that scoped Foo instances from EarlyEntryPoint and EntryPoint are not equal
+    assertThat(foo1).isNotEqualTo(earlyFoo1);
+  }
+}
diff --git a/javatests/artifacts/hilt-android/simple/gradle.properties b/javatests/artifacts/hilt-android/simple/gradle.properties
index 646c51b..cc9cc55 100644
--- a/javatests/artifacts/hilt-android/simple/gradle.properties
+++ b/javatests/artifacts/hilt-android/simple/gradle.properties
@@ -1,2 +1,9 @@
 android.useAndroidX=true
 android.enableJetifier=true
+
+# Enable and fail the build 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-problems=fail
+org.gradle.unsafe.configuration-cache.max-problems=0
diff --git a/javatests/artifacts/hilt-android/simple/gradle/wrapper/gradle-wrapper.properties b/javatests/artifacts/hilt-android/simple/gradle/wrapper/gradle-wrapper.properties
index 4d9ca16..0f80bbf 100644
--- a/javatests/artifacts/hilt-android/simple/gradle/wrapper/gradle-wrapper.properties
+++ b/javatests/artifacts/hilt-android/simple/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/javatests/artifacts/hilt-android/simple/settings.gradle b/javatests/artifacts/hilt-android/simple/settings.gradle
index bad895d..9dc59db 100644
--- a/javatests/artifacts/hilt-android/simple/settings.gradle
+++ b/javatests/artifacts/hilt-android/simple/settings.gradle
@@ -1,6 +1,8 @@
 rootProject.name='Simple Hilt Android'
 include ':app'
+include ':app-java-only'
 include ':feature'
 include ':lib'
 include ':deep-android-lib'
-include ':deep-lib'
\ No newline at end of file
+include ':deep-lib'
+include ':earlyentrypoint'
\ 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
index 383c4be..f682247 100644
--- a/javatests/artifacts/hilt-android/simpleKotlin/android-library/build.gradle
+++ b/javatests/artifacts/hilt-android/simpleKotlin/android-library/build.gradle
@@ -26,6 +26,7 @@
 dependencies {
     implementation project(':deep-android-lib')
     implementation project(':deep-kotlin-lib')
+    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
     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/app/build.gradle b/javatests/artifacts/hilt-android/simpleKotlin/app/build.gradle
index ecfaf6e..d8104d7 100644
--- a/javatests/artifacts/hilt-android/simpleKotlin/app/build.gradle
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/build.gradle
@@ -62,8 +62,8 @@
 }
 
 hilt {
-    enableExperimentalClasspathAggregation = true
     enableTransformForLocalTests = true
+    enableAggregatingTask = true
 }
 
 dependencies {
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/app/src/sharedTest/java/dagger/hilt/android/simpleKotlin/KotlinSuppressClasses.kt b/javatests/artifacts/hilt-android/simpleKotlin/app/src/sharedTest/java/dagger/hilt/android/simpleKotlin/KotlinSuppressClasses.kt
new file mode 100644
index 0000000..c9bc671
--- /dev/null
+++ b/javatests/artifacts/hilt-android/simpleKotlin/app/src/sharedTest/java/dagger/hilt/android/simpleKotlin/KotlinSuppressClasses.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
+
+import androidx.fragment.app.FragmentActivity
+import dagger.hilt.android.AndroidEntryPoint
+
+// Regression test for https://github.com/google/dagger/issues/2898
+// TODO(bcorso):Replace this with a Kotlin compiler test and verify the SuppressWarnings is
+// propagated to the generated Hilt class.
+class KotlinSuppressClasses {
+  @Suppress("deprecation")
+  @AndroidEntryPoint
+  class MyActivity : FragmentActivity()
+}
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/build.gradle b/javatests/artifacts/hilt-android/simpleKotlin/build.gradle
index e7eb9b2..249387c 100644
--- a/javatests/artifacts/hilt-android/simpleKotlin/build.gradle
+++ b/javatests/artifacts/hilt-android/simpleKotlin/build.gradle
@@ -16,12 +16,12 @@
 
 buildscript {
     ext {
-        kotlin_version = '1.3.61'
-        agp_version = System.getenv('AGP_VERSION') ?: "4.2.0-beta04"
+        kotlin_version = '1.5.32'
+        agp_version = System.getenv('AGP_VERSION') ?: "4.2.0"
     }
     repositories {
         google()
-        jcenter()
+        mavenCentral()
         mavenLocal()
     }
     dependencies {
@@ -34,7 +34,6 @@
 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
index 3a1923a..8bfadf0 100644
--- a/javatests/artifacts/hilt-android/simpleKotlin/deep-android-lib/build.gradle
+++ b/javatests/artifacts/hilt-android/simpleKotlin/deep-android-lib/build.gradle
@@ -30,6 +30,8 @@
 }
 
 dependencies {
+    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+
     implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'
     kapt 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'
 
@@ -42,5 +44,5 @@
 }
 
 hilt {
-    enableExperimentalClasspathAggregation = true
+    enableAggregatingTask = true
 }
\ No newline at end of file
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/gradle.properties b/javatests/artifacts/hilt-android/simpleKotlin/gradle.properties
index 646c51b..cc9cc55 100644
--- a/javatests/artifacts/hilt-android/simpleKotlin/gradle.properties
+++ b/javatests/artifacts/hilt-android/simpleKotlin/gradle.properties
@@ -1,2 +1,9 @@
 android.useAndroidX=true
 android.enableJetifier=true
+
+# Enable and fail the build 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-problems=fail
+org.gradle.unsafe.configuration-cache.max-problems=0
diff --git a/javatests/artifacts/hilt-android/simpleKotlin/gradle/wrapper/gradle-wrapper.properties b/javatests/artifacts/hilt-android/simpleKotlin/gradle/wrapper/gradle-wrapper.properties
index 4d9ca16..0f80bbf 100644
--- a/javatests/artifacts/hilt-android/simpleKotlin/gradle/wrapper/gradle-wrapper.properties
+++ b/javatests/artifacts/hilt-android/simpleKotlin/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/javatests/dagger/BUILD b/javatests/dagger/BUILD
index c6ab360..f3153d9 100644
--- a/javatests/dagger/BUILD
+++ b/javatests/dagger/BUILD
@@ -27,10 +27,10 @@
     javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
     deps = [
         "//java/dagger:core",
-        "//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",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/util/concurrent",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
 )
diff --git a/javatests/dagger/android/BUILD b/javatests/dagger/android/BUILD
index 38efb3a..09dc4ae 100644
--- a/javatests/dagger/android/BUILD
+++ b/javatests/dagger/android/BUILD
@@ -29,8 +29,8 @@
     deps = [
         "//:dagger_with_compiler",
         "//java/dagger/android",
-        "//java/dagger/internal/guava:collect",
-        "@google_bazel_common//third_party/java/junit",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/guava/collect",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
 )
diff --git a/javatests/dagger/android/processor/BUILD b/javatests/dagger/android/processor/BUILD
index c639a41..4dbd260 100644
--- a/javatests/dagger/android/processor/BUILD
+++ b/javatests/dagger/android/processor/BUILD
@@ -30,12 +30,12 @@
         "//java/dagger/android",
         "//java/dagger/android/processor",
         "//java/dagger/internal/codegen:processor",
-        "//java/dagger/internal/guava:base",
-        "//java/dagger/internal/guava:collect",
+        "//third_party/java/compile_testing",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
         "@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_activity_activity",
         "@maven//:androidx_fragment_fragment",
         "@maven//:androidx_lifecycle_lifecycle_common",
diff --git a/javatests/dagger/android/support/BUILD b/javatests/dagger/android/support/BUILD
index 2ef1ece..de1b2d3 100644
--- a/javatests/dagger/android/support/BUILD
+++ b/javatests/dagger/android/support/BUILD
@@ -32,11 +32,11 @@
         "//:dagger_with_compiler",
         "//java/dagger/android",
         "//java/dagger/android/support",
-        "//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",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/util/concurrent",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
         "@maven//:androidx_activity_activity",
         "@maven//:androidx_appcompat_appcompat",
         "@maven//:androidx_fragment_fragment",
diff --git a/javatests/dagger/android/support/functional/BUILD b/javatests/dagger/android/support/functional/BUILD
index 459d55f..0281ea1 100644
--- a/javatests/dagger/android/support/functional/BUILD
+++ b/javatests/dagger/android/support/functional/BUILD
@@ -40,7 +40,7 @@
         "//:android",
         "//:android-support",
         # TODO(ronshapiro): figure out why strict deps is failing without this
-        "@google_bazel_common//third_party/java/jsr250_annotations",
+        "//third_party/java/jsr250_annotations",
     ],
 )
 
@@ -55,7 +55,7 @@
         "//:android",
         "//:android-support",
         "//:dagger_with_compiler",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/truth",
         "@maven//:androidx_activity_activity",
         "@maven//:androidx_fragment_fragment",
         "@maven//:androidx_lifecycle_lifecycle_common",
diff --git a/javatests/dagger/functional/BUILD b/javatests/dagger/functional/BUILD
index 8e6a555..81469a2 100644
--- a/javatests/dagger/functional/BUILD
+++ b/javatests/dagger/functional/BUILD
@@ -28,19 +28,19 @@
     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",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/util/concurrent",
+        "//third_party/java/guava:testlib",
+        "//third_party/java/truth",
+        "//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",
-        "@google_bazel_common//third_party/java/auto:value",
-        "@google_bazel_common//third_party/java/jsr330_inject",
+        "//third_party/java/auto:factory",
+        "//third_party/java/auto:value",
+        "//third_party/java/jsr330_inject",
     ],
 )
diff --git a/javatests/dagger/functional/ComponentNestedTypeTest.java b/javatests/dagger/functional/ComponentNestedTypeTest.java
new file mode 100644
index 0000000..e39c429
--- /dev/null
+++ b/javatests/dagger/functional/ComponentNestedTypeTest.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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.Component;
+import dagger.Module;
+import dagger.Provides;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Regression test for b/212604806. */
+@RunWith(JUnit4.class)
+public final class ComponentNestedTypeTest {
+  @Component(modules = TestModule.class)
+  interface TestComponent {
+
+    // Dagger generated component implementation that extends TestComponent will implement this
+    // method, so the component implementation will keep a reference to the {@link
+    // dagger.functional.sub.NestedType}. The reference to {@link dagger.functional.sub.NestedType}
+    // may collide with the NestedType defined inside of TestComponent, because javapoet may strip
+    // the package prefix of the type as it does not have enough information about the super
+    // class/interfaces.
+    dagger.functional.sub.NestedType nestedType();
+
+    interface NestedType {}
+  }
+
+  public static final class SomeType implements dagger.functional.sub.NestedType {}
+
+  @Module
+  static final class TestModule {
+    @Provides
+    static dagger.functional.sub.NestedType provideSomeType() {
+      return new SomeType();
+    }
+  }
+
+  @Test
+  public void typeNameWontClashWithNestedTypeName() {
+    TestComponent component =
+        DaggerComponentNestedTypeTest_TestComponent.builder().testModule(new TestModule()).build();
+    assertThat(component.nestedType()).isNotNull();
+  }
+}
diff --git a/javatests/dagger/functional/assisted/AssistedFactoryDuplicatedParamNamesTest.java b/javatests/dagger/functional/assisted/AssistedFactoryDuplicatedParamNamesTest.java
new file mode 100644
index 0000000..af9b40e
--- /dev/null
+++ b/javatests/dagger/functional/assisted/AssistedFactoryDuplicatedParamNamesTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.BindsInstance;
+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 AssistedFactoryDuplicatedParamNamesTest {
+  static final class Foo {
+    private final String arg;
+    private final Bar bar;
+
+    @AssistedInject
+    Foo(@Assisted String arg, Bar bar) {
+      this.arg = arg;
+      this.bar = bar;
+    }
+
+    Bar getBar() {
+      return bar;
+    }
+
+    String getArg() {
+      return arg;
+    }
+  }
+
+  static final class Bar {}
+
+  @AssistedFactory
+  interface FooFactory {
+    Foo create(String arg);
+  }
+
+  @Component
+  interface TestComponent {
+    @Component.Factory
+    interface Factory {
+      TestComponent create(@BindsInstance Bar arg);
+    }
+
+    FooFactory fooFactory();
+  }
+
+  @Test
+  public void duplicatedParameterNames_doesNotConflict() {
+    String str = "test";
+    Bar bar = new Bar();
+
+    Foo foo =
+        DaggerAssistedFactoryDuplicatedParamNamesTest_TestComponent.factory()
+            .create(bar)
+            .fooFactory()
+            .create(str);
+
+    assertThat(foo.getArg()).isEqualTo(str);
+    assertThat(foo.getBar()).isEqualTo(bar);
+  }
+}
diff --git a/javatests/dagger/functional/assisted/AssistedFactoryTest.java b/javatests/dagger/functional/assisted/AssistedFactoryTest.java
index 3176add..d8ef53a 100644
--- a/javatests/dagger/functional/assisted/AssistedFactoryTest.java
+++ b/javatests/dagger/functional/assisted/AssistedFactoryTest.java
@@ -35,6 +35,8 @@
     // Simple factory using a nested factory.
     SimpleFoo.Factory nestedSimpleFooFactory();
 
+    Provider<SimpleFoo.Factory> nestedSimpleFooFactoryProvider();
+
     // Simple factory using a non-nested factory.
     SimpleFooFactory nonNestedSimpleFooFactory();
 
@@ -58,6 +60,7 @@
   static class SomeEntryPoint {
     private final SimpleFoo.Factory nestedSimpleFooFactory;
     private final SimpleFooFactory nonNestedSimpleFooFactory;
+    private final Provider<SimpleFoo.Factory> nestedSimpleFooFactoryProvider;
     private final ExtendedSimpleFooFactory extendedSimpleFooFactory;
     private final FooFactory fooFactory;
     private final AbstractFooFactory abstractFooFactory;
@@ -66,12 +69,14 @@
     @Inject
     SomeEntryPoint(
         SimpleFoo.Factory nestedSimpleFooFactory,
+        Provider<SimpleFoo.Factory> nestedSimpleFooFactoryProvider,
         SimpleFooFactory nonNestedSimpleFooFactory,
         ExtendedSimpleFooFactory extendedSimpleFooFactory,
         FooFactory fooFactory,
         AbstractFooFactory abstractFooFactory,
         NoAssistedParametersFooFactory noAssistedParametersFooFactory) {
       this.nestedSimpleFooFactory = nestedSimpleFooFactory;
+      this.nestedSimpleFooFactoryProvider = nestedSimpleFooFactoryProvider;
       this.nonNestedSimpleFooFactory = nonNestedSimpleFooFactory;
       this.extendedSimpleFooFactory = extendedSimpleFooFactory;
       this.fooFactory = fooFactory;
@@ -265,6 +270,25 @@
   }
 
   @Test
+  public void testNestedSimpleFooFactoryProvider() {
+    AssistedDep1 assistedDep1 = new AssistedDep1();
+    SimpleFoo simpleFoo1 =
+        DaggerAssistedFactoryTest_ParentComponent.create()
+            .nestedSimpleFooFactoryProvider()
+            .get()
+            .createSimpleFoo(assistedDep1);
+    assertThat(simpleFoo1.assistedDep).isEqualTo(assistedDep1);
+
+    AssistedDep2 assistedDep2 = new AssistedDep2();
+    SimpleFoo simpleFoo2 =
+        DaggerAssistedFactoryTest_ParentComponent.create()
+            .nestedSimpleFooFactoryProvider()
+            .get()
+            .createSimpleFoo(assistedDep2);
+    assertThat(simpleFoo2.assistedDep).isEqualTo(assistedDep2);
+  }
+
+  @Test
   public void testNonNestedSimpleFooFactory() {
     AssistedDep1 assistedDep1 = new AssistedDep1();
     SimpleFoo simpleFoo =
@@ -323,6 +347,7 @@
     SomeEntryPoint someEntryPoint =
         DaggerAssistedFactoryTest_ParentComponent.create().someEntryPoint();
     assertThat(someEntryPoint.nestedSimpleFooFactory).isNotNull();
+    assertThat(someEntryPoint.nestedSimpleFooFactoryProvider).isNotNull();
     assertThat(someEntryPoint.nonNestedSimpleFooFactory).isNotNull();
     assertThat(someEntryPoint.extendedSimpleFooFactory).isNotNull();
     assertThat(someEntryPoint.fooFactory).isNotNull();
diff --git a/javatests/dagger/functional/assisted/AssistedFactoryWithMultibindingsTest.java b/javatests/dagger/functional/assisted/AssistedFactoryWithMultibindingsTest.java
new file mode 100644
index 0000000..1e4bd60
--- /dev/null
+++ b/javatests/dagger/functional/assisted/AssistedFactoryWithMultibindingsTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+import dagger.multibindings.IntoSet;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class AssistedFactoryWithMultibindingsTest {
+  @Component(modules = ParentModule.class)
+  interface ParentComponent {
+    // Factory for assisted injection binding with multi binding contribution.
+    MultibindingFooFactory multibindingFooFactory();
+
+    ChildComponent.Builder childComponent();
+  }
+
+  static final class AssistedDep {}
+
+  static final class MultibindingFoo {
+    private final AssistedDep assistedDep;
+    private final Set<String> stringSet;
+
+    @AssistedInject
+    MultibindingFoo(@Assisted AssistedDep assistedDep, Set<String> stringSet) {
+      this.assistedDep = assistedDep;
+      this.stringSet = stringSet;
+    }
+
+    AssistedDep assistedDep() {
+      return assistedDep;
+    }
+
+    Set<String> stringSet() {
+      return stringSet;
+    }
+  }
+
+  @Subcomponent(modules = ChildModule.class)
+  static interface ChildComponent {
+    MultibindingFooFactory multibindingFooFactory();
+
+    @Subcomponent.Builder
+    interface Builder {
+      ChildComponent build();
+    }
+  }
+
+  @Module(subcomponents = ChildComponent.class)
+  static class ParentModule {
+    @Provides
+    @IntoSet
+    String parentString() {
+      return "parent";
+    }
+  }
+
+  @Module
+  static class ChildModule {
+    @Provides
+    @IntoSet
+    String childString() {
+      return "child";
+    }
+  }
+
+  @AssistedFactory
+  interface MultibindingFooFactory {
+    MultibindingFoo createFoo(AssistedDep factoryAssistedDep1);
+  }
+
+  @Test
+  public void testAssistedFactoryWithMultibinding() {
+    AssistedDep assistedDep1 = new AssistedDep();
+    ParentComponent parent = DaggerAssistedFactoryWithMultibindingsTest_ParentComponent.create();
+    ChildComponent child = parent.childComponent().build();
+    MultibindingFoo foo1 = parent.multibindingFooFactory().createFoo(assistedDep1);
+    MultibindingFoo foo2 = child.multibindingFooFactory().createFoo(assistedDep1);
+    assertThat(foo1.assistedDep()).isEqualTo(foo2.assistedDep);
+    assertThat(foo1.stringSet()).containsExactly("parent");
+    assertThat(foo2.stringSet()).containsExactly("child", "parent");
+  }
+}
diff --git a/javatests/dagger/functional/assisted/BUILD b/javatests/dagger/functional/assisted/BUILD
index 5e663e7..b94b357 100644
--- a/javatests/dagger/functional/assisted/BUILD
+++ b/javatests/dagger/functional/assisted/BUILD
@@ -26,20 +26,20 @@
     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",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/util/concurrent",
+        "//third_party/java/guava:testlib",
+        "//third_party/java/truth",
+        "//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",
+        "//third_party/java/auto:factory",
+        "//third_party/java/auto:value",
+        "//third_party/java/jsr330_inject",
     ],
 )
diff --git a/javatests/dagger/functional/assisted/kotlin/BUILD b/javatests/dagger/functional/assisted/kotlin/BUILD
index 3c3421d..efb785e 100644
--- a/javatests/dagger/functional/assisted/kotlin/BUILD
+++ b/javatests/dagger/functional/assisted/kotlin/BUILD
@@ -25,6 +25,7 @@
     srcs = ["KotlinAssistedInjectionClasses.kt"],
     deps = [
         "//:dagger_with_compiler",
+        "//third_party/java/auto:factory",
     ],
 )
 
@@ -38,8 +39,8 @@
     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",
+        "//third_party/java/junit",
+        "//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.
diff --git a/javatests/dagger/functional/assisted/kotlin/KotlinAssistedInjectionClasses.kt b/javatests/dagger/functional/assisted/kotlin/KotlinAssistedInjectionClasses.kt
index e78873d..18cc43d 100644
--- a/javatests/dagger/functional/assisted/kotlin/KotlinAssistedInjectionClasses.kt
+++ b/javatests/dagger/functional/assisted/kotlin/KotlinAssistedInjectionClasses.kt
@@ -21,6 +21,7 @@
 import dagger.assisted.AssistedInject
 import javax.inject.Inject
 
+
 class Dep @Inject constructor()
 
 class AssistedDep
@@ -29,7 +30,9 @@
 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)
+data class FooData
+@AssistedInject
+constructor(val dep: Dep, @Assisted val assistedDep: AssistedDep)
 
 /** Assisted factory for a kotlin class */
 @AssistedFactory
@@ -42,3 +45,15 @@
 interface FooDataFactory {
   fun create(assistedDep: AssistedDep): FooData
 }
+
+/** Kotlin classes for regression test of https://github.com/google/dagger/issues/3065. */
+class BarManager
+@AssistedInject
+internal constructor(@Assisted val bar: Bar, @Assisted val name: String) {
+  @AssistedFactory
+  interface Factory {
+    operator fun Bar.invoke(name: String): BarManager
+  }
+}
+
+class Bar
diff --git a/javatests/dagger/functional/assisted/kotlin/KotlinAssistedInjectionTest.java b/javatests/dagger/functional/assisted/kotlin/KotlinAssistedInjectionTest.java
index 7c597f8..b7a0a1b 100644
--- a/javatests/dagger/functional/assisted/kotlin/KotlinAssistedInjectionTest.java
+++ b/javatests/dagger/functional/assisted/kotlin/KotlinAssistedInjectionTest.java
@@ -31,6 +31,9 @@
     FooFactory fooFactory();
 
     FooDataFactory fooDataFactory();
+
+    BarManager.Factory barManagerFactory();
+
   }
 
   @Test
@@ -49,4 +52,15 @@
     FooData fooData = fooDataFactory.create(assistedDep);
     assertThat(fooData.getAssistedDep()).isEqualTo(assistedDep);
   }
+
+  @Test
+  public void testBarManager() {
+    BarManager.Factory barManagerFactory =
+        DaggerKotlinAssistedInjectionTest_TestComponent.create().barManagerFactory();
+    Bar bar = new Bar();
+    String name = "someName";
+    BarManager barManager = barManagerFactory.invoke(bar, name);
+    assertThat(barManager.getBar()).isEqualTo(bar);
+    assertThat(barManager.getName()).isEqualTo(name);
+  }
 }
diff --git a/javatests/dagger/functional/assisted/subpackage/BUILD b/javatests/dagger/functional/assisted/subpackage/BUILD
index 644cf44..8f06984 100644
--- a/javatests/dagger/functional/assisted/subpackage/BUILD
+++ b/javatests/dagger/functional/assisted/subpackage/BUILD
@@ -22,6 +22,6 @@
     srcs = glob(["*.java"]),
     deps = [
         "//:dagger_with_compiler",
-        "@google_bazel_common//third_party/java/jsr330_inject",
+        "//third_party/java/jsr330_inject",
     ],
 )
diff --git a/javatests/dagger/functional/binds/AccessesExposedComponent.java b/javatests/dagger/functional/binds/AccessesExposedComponent.java
index 61952b6..075ae96 100644
--- a/javatests/dagger/functional/binds/AccessesExposedComponent.java
+++ b/javatests/dagger/functional/binds/AccessesExposedComponent.java
@@ -29,7 +29,7 @@
  * accessible from the component, but the left-hand-side is. If the right-hand-side is represented
  * as a Provider (e.g. because it is scoped), then the raw {@code Provider.get()} will return {@link
  * Object}, which must be downcasted to the type accessible from the component. See {@code
- * instanceRequiresCast()} in {@link dagger.internal.codegen.DelegateBindingExpression}.
+ * instanceRequiresCast()} in {@link dagger.internal.codegen.DelegateRequestRepresentation}.
  */
 @Singleton
 @Component(modules = ExposedModule.class)
diff --git a/javatests/dagger/functional/binds/BindsTest.java b/javatests/dagger/functional/binds/BindsTest.java
index 999c303..e89dee6 100644
--- a/javatests/dagger/functional/binds/BindsTest.java
+++ b/javatests/dagger/functional/binds/BindsTest.java
@@ -52,6 +52,7 @@
     assertThat(component.foosOfNumbers()).hasSize(2);
     assertThat(component.objects()).hasSize(3);
     assertThat(component.charSequences()).hasSize(5);
+    assertThat(component.notExposedString()).isEqualTo("not exposed");
 
     assertThat(component.integerObjectMap())
         .containsExactly(123, "123-string", 456, "456-string", 789, "789-string");
diff --git a/javatests/dagger/functional/binds/ScopedBindsTest.java b/javatests/dagger/functional/binds/ScopedBindsTest.java
new file mode 100644
index 0000000..90feb8b
--- /dev/null
+++ b/javatests/dagger/functional/binds/ScopedBindsTest.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.binds;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.Binds;
+import dagger.Component;
+import dagger.Module;
+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 class ScopedBindsTest {
+  interface Foo {}
+
+  static final class FooImpl implements Foo {
+    @Inject FooImpl() {}
+  }
+
+  @Module
+  interface FooModule {
+    @Binds
+    @Singleton
+    Foo bindFoo(FooImpl impl);
+  }
+
+  @Component(modules = FooModule.class)
+  @Singleton
+  interface TestComponent {
+    Foo foo();
+
+    Provider<Foo> fooProvider();
+  }
+
+  @Test
+  public void fooVsFooProvider_sameInstanceTest() {
+    TestComponent component = DaggerScopedBindsTest_TestComponent.create();
+
+    // These should be the same instance because Foo is scoped
+    assertThat(component.foo()).isSameInstanceAs(component.fooProvider().get());
+  }
+}
diff --git a/javatests/dagger/functional/binds/TestComponent.java b/javatests/dagger/functional/binds/TestComponent.java
index 3299dc8..5e459be 100644
--- a/javatests/dagger/functional/binds/TestComponent.java
+++ b/javatests/dagger/functional/binds/TestComponent.java
@@ -18,16 +18,19 @@
 
 import dagger.Component;
 import dagger.functional.SomeQualifier;
+import dagger.functional.binds.subpackage.ExposedModule;
 import java.util.Map;
 import java.util.Set;
 import javax.inject.Provider;
 import javax.inject.Singleton;
 
 @Singleton
-@Component(modules = SimpleBindingModule.class)
+@Component(modules = {SimpleBindingModule.class, ExposedModule.class})
 public interface TestComponent {
   Object object();
 
+  String notExposedString();
+
   @SomeQualifier
   Object reusableObject();
 
diff --git a/javatests/dagger/functional/binds/subpackage/ExposedModule.java b/javatests/dagger/functional/binds/subpackage/ExposedModule.java
index a2660d9..5f428d9 100644
--- a/javatests/dagger/functional/binds/subpackage/ExposedModule.java
+++ b/javatests/dagger/functional/binds/subpackage/ExposedModule.java
@@ -19,8 +19,14 @@
 import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
+import dagger.multibindings.ElementsIntoSet;
+import dagger.multibindings.IntoSet;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
+import java.util.Set;
+import javax.inject.Provider;
 import javax.inject.Singleton;
 
 @Module
@@ -40,4 +46,24 @@
   @Binds
   abstract ExposedInjectsMembers bindExposedInjectsMembers(
       NotExposedInjectsMembers notExposedInjectsMembers);
+
+  @Provides
+  static Collection<NotExposed> provideNotExposedCollection(NotExposed notExposed) {
+    return Arrays.<NotExposed>asList(notExposed);
+  }
+
+  @Provides
+  @IntoSet // This is needed to ensure a provider field gets created for providing the Collection.
+  static NotExposed provideNotExposed(Provider<Collection<NotExposed>> collectionProvider) {
+    return collectionProvider.get().iterator().next();
+  }
+
+  @Binds
+  @ElementsIntoSet
+  abstract Set<NotExposed> bindCollectionOfNotExposeds(Collection<NotExposed> collection);
+
+  @Provides
+  static String provideString(Set<NotExposed> setOfFoo) {
+    return "not exposed";
+  }
 }
diff --git a/javatests/dagger/functional/guava/BUILD b/javatests/dagger/functional/guava/BUILD
index ed8efd3..157fd55 100644
--- a/javatests/dagger/functional/guava/BUILD
+++ b/javatests/dagger/functional/guava/BUILD
@@ -26,12 +26,12 @@
     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/jsr305_annotations",
-        "@google_bazel_common//third_party/java/jsr330_inject",
-        "@google_bazel_common//third_party/java/junit",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/auto:value",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/jsr305_annotations",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
 )
diff --git a/javatests/dagger/functional/jakarta/BUILD b/javatests/dagger/functional/jakarta/BUILD
new file mode 100644
index 0000000..7fc1ec2
--- /dev/null
+++ b/javatests/dagger/functional/jakarta/BUILD
@@ -0,0 +1,34 @@
+# Copyright (C) 2022 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 that depend on jakarta.inject.
+
+load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
+load("//:test_defs.bzl", "GenJavaTests")
+
+package(default_visibility = ["//:src"])
+
+# TODO(b/203233586): Replace with GenJavaTest
+GenJavaTests(
+    name = "SimpleJakartaTest",
+    srcs = ["SimpleJakartaTest.java"],
+    javacopts = DOCLINT_HTML_AND_SYNTAX,
+    deps = [
+        "//:dagger_with_compiler",
+        "@maven//:jakarta_inject_jakarta_inject_api",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
+    ],
+)
diff --git a/javatests/dagger/functional/jakarta/SimpleJakartaTest.java b/javatests/dagger/functional/jakarta/SimpleJakartaTest.java
new file mode 100644
index 0000000..e943eb0
--- /dev/null
+++ b/javatests/dagger/functional/jakarta/SimpleJakartaTest.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.functional.jakarta;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.Binds;
+import dagger.Component;
+import dagger.Module;
+import jakarta.inject.Inject;
+import jakarta.inject.Qualifier;
+import jakarta.inject.Scope;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class SimpleJakartaTest {
+
+  @Scope
+  public @interface TestScope {}
+
+  @Qualifier
+  public @interface TestQualifier {}
+
+  @TestScope
+  @Component(modules = TestModule.class)
+  interface TestComponent {
+    @TestQualifier Foo getQualifiedFoo();
+  }
+
+  public static final class Foo {
+    @Inject Foo() {}
+  }
+
+  @Module
+  interface TestModule {
+    // By binding this to itself, if the qualifier annotation isn't picked up, it will created a
+    // cycle.
+    @Binds
+    @TestScope
+    @TestQualifier
+    Foo bind(Foo impl);
+  }
+
+  @Test
+  public void testFooFactory() {
+    TestComponent testComponent = DaggerSimpleJakartaTest_TestComponent.create();
+    Foo foo = testComponent.getQualifiedFoo();
+
+    assertThat(foo).isSameInstanceAs(testComponent.getQualifiedFoo());
+  }
+}
diff --git a/javatests/dagger/functional/jdk8/BUILD b/javatests/dagger/functional/jdk8/BUILD
index 5e35c75..cc0c722 100644
--- a/javatests/dagger/functional/jdk8/BUILD
+++ b/javatests/dagger/functional/jdk8/BUILD
@@ -20,19 +20,53 @@
 
 package(default_visibility = ["//:src"])
 
+java_library(
+    name = "optional_binding_components",
+    srcs = ["OptionalBindingComponents.java"],
+    javacopts = DOCLINT_HTML_AND_SYNTAX,
+    deps = [
+        "//third_party/java/auto:value",
+        "//:dagger_with_compiler",
+        "//third_party/java/jsr305_annotations",
+    ],
+)
+
+# TODO(b/203233586): Replace with GenJavaTest
 GenJavaTests(
-    name = "jdk8_tests",
-    srcs = glob(["**/*.java"]),
+    name = "OptionalBindingComponentsEmptyTest",
+    srcs = ["OptionalBindingComponentsEmptyTest.java"],
     javacopts = DOCLINT_HTML_AND_SYNTAX,
     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",
+        ":optional_binding_components",
+        
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
     deps = [
+        "//third_party/java/auto:value",
         "//:dagger_with_compiler",
-        "@google_bazel_common//third_party/java/auto:value",
-        "@google_bazel_common//third_party/java/jsr305_annotations",
+        "//third_party/java/jsr305_annotations",
+    ],
+)
+
+# TODO(b/203233586): Replace with GenJavaTest
+GenJavaTests(
+    name = "OptionalBindingComponentsPresentTest",
+    srcs = ["OptionalBindingComponentsPresentTest.java"],
+    javacopts = DOCLINT_HTML_AND_SYNTAX,
+    test_only_deps = [
+        ":optional_binding_components",
+        
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
+    ],
+    deps = [
+        "//third_party/java/auto:value",
+        "//:dagger_with_compiler",
+        "//third_party/java/jsr305_annotations",
     ],
 )
diff --git a/javatests/dagger/functional/jdk8/a/BUILD b/javatests/dagger/functional/jdk8/a/BUILD
new file mode 100644
index 0000000..f364174
--- /dev/null
+++ b/javatests/dagger/functional/jdk8/a/BUILD
@@ -0,0 +1,41 @@
+# 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.
+
+load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
+load("//:test_defs.bzl", "GenJavaTests")
+
+package(default_visibility = ["//:src"])
+
+# TODO(b/203233586): Replace with GenJavaTest
+GenJavaTests(
+    name = "OptionalBindingComponentsEmptyTest",
+    srcs = [
+        "OptionalBindingComponentsWithInaccessibleTypes.java",
+        "OptionalBindingComponentsWithInaccessibleTypesTest.java",
+    ],
+    javacopts = DOCLINT_HTML_AND_SYNTAX,
+    test_only_deps = [
+        
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
+    ],
+    deps = [
+        "//third_party/java/auto:value",
+        "//:dagger_with_compiler",
+        "//third_party/java/jsr305_annotations",
+        "//javatests/dagger/functional/jdk8:optional_binding_components",
+    ],
+)
diff --git a/javatests/dagger/functional/kotlin/BUILD b/javatests/dagger/functional/kotlin/BUILD
index 529eb69..2741c8e 100644
--- a/javatests/dagger/functional/kotlin/BUILD
+++ b/javatests/dagger/functional/kotlin/BUILD
@@ -41,6 +41,7 @@
         ":java_qualifier",
         "//:dagger_with_compiler",
         "//javatests/dagger/functional/kotlin/processor:annotation",
+        "//third_party/java/auto:factory",
     ],
 )
 
@@ -66,10 +67,10 @@
     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",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
     deps = [
         ":foo_with_injected_qualifier",
diff --git a/javatests/dagger/functional/kotlin/DependsOnGeneratedCodeClasses.kt b/javatests/dagger/functional/kotlin/DependsOnGeneratedCodeClasses.kt
new file mode 100644
index 0000000..6fc8e24
--- /dev/null
+++ b/javatests/dagger/functional/kotlin/DependsOnGeneratedCodeClasses.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.kotlin
+
+import com.google.auto.factory.AutoFactory
+import dagger.Component
+import javax.inject.Inject
+
+// TODO(bcorso): Merge this into the test once we support kt_jvm_test
+/** Defines kotlin classes for the associated test. */
+object DependsOnGeneratedCodeClasses {
+  @Component
+  abstract class TestComponent {
+    abstract fun bar(): Bar
+  }
+
+  class Bar @Inject constructor(
+  )
+
+  @AutoFactory class SomeClass
+}
diff --git a/javatests/dagger/functional/kotlin/DependsOnGeneratedCodeTest.java b/javatests/dagger/functional/kotlin/DependsOnGeneratedCodeTest.java
new file mode 100644
index 0000000..d85aee8
--- /dev/null
+++ b/javatests/dagger/functional/kotlin/DependsOnGeneratedCodeTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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.kotlin;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * @see <a href="https://github.com/google/dagger/issues/3075">Issue #3075</a>
+ */
+@RunWith(JUnit4.class)
+public class DependsOnGeneratedCodeTest {
+  @Test
+  public void testComponentDependsOnGeneratedCode() {
+    assertThat(DaggerDependsOnGeneratedCodeClasses_TestComponent.create().bar()).isNotNull();
+  }
+}
diff --git a/javatests/dagger/functional/kotlin/processor/BUILD b/javatests/dagger/functional/kotlin/processor/BUILD
index 31a45c7..25273e2 100644
--- a/javatests/dagger/functional/kotlin/processor/BUILD
+++ b/javatests/dagger/functional/kotlin/processor/BUILD
@@ -26,8 +26,8 @@
     name = "processor",
     srcs = ["TestGeneratedTypeProcessor.kt"],
     deps = [
-        "@google_bazel_common//third_party/java/auto:service",
-        "@google_bazel_common//third_party/java/javapoet",
+        "//third_party/java/auto:service",
+        "//third_party/java/javapoet",
     ],
 )
 
diff --git a/javatests/dagger/functional/names/BUILD b/javatests/dagger/functional/names/BUILD
new file mode 100644
index 0000000..5de7510
--- /dev/null
+++ b/javatests/dagger/functional/names/BUILD
@@ -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.
+
+# Description:
+#   Functional tests for Dagger testing various name conflicts.
+
+load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
+load("//:test_defs.bzl", "GenJavaTests")
+
+package(default_visibility = ["//:src"])
+
+# TODO(b/203233586): Replace with GenJavaTest
+GenJavaTests(
+    name = "ComponentFactoryNameConflictsTest",
+    srcs = ["ComponentFactoryNameConflictsTest.java"],
+    javacopts = DOCLINT_HTML_AND_SYNTAX,
+    deps = [
+        "//:dagger_with_compiler",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
+    ],
+)
diff --git a/javatests/dagger/functional/names/ComponentFactoryNameConflictsTest.java b/javatests/dagger/functional/names/ComponentFactoryNameConflictsTest.java
new file mode 100644
index 0000000..14acd0e
--- /dev/null
+++ b/javatests/dagger/functional/names/ComponentFactoryNameConflictsTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.names;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import dagger.Component;
+import javax.inject.Inject;
+import javax.inject.Provider;
+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/3069. */
+@RunWith(JUnit4.class)
+public final class ComponentFactoryNameConflictsTest {
+
+  // A class name "Create" so the private method name in fastInit mode conflicts with the create()
+  // factory method.
+  public static final class Create {
+    // Take a dependency so we don't just inline this directly.
+    @Inject Create(Provider<CreateUsage> provider) {}
+  }
+
+  // Use another class to make sure the create class is used but not a direct component entry point.
+  public static final class CreateUsage {
+    @Inject CreateUsage(Create create) {}
+  }
+
+  @Component
+  interface CreateComponent {
+    CreateUsage getCreateUsage();
+  }
+
+  @Test
+  public void testCreate() {
+    CreateComponent testComponent =
+        DaggerComponentFactoryNameConflictsTest_CreateComponent.create();
+    CreateUsage createUsage = testComponent.getCreateUsage();
+    assertThat(createUsage).isNotNull();
+  }
+
+  // A class name "Builder" so the private method name in fastInit mode conflicts with the builder()
+  // factory method.
+  public static final class Builder {
+    // Take a dependency so we don't just inline this directly.
+    @Inject Builder(Provider<BuilderUsage> provider) {}
+  }
+
+  public static final class BuilderUsage {
+    @Inject BuilderUsage(Builder create) {}
+  }
+
+  @Component
+  interface BuilderComponent {
+    BuilderUsage getBuilderUsage();
+
+    @Component.Builder
+    interface OtherBuilder {
+      BuilderComponent build();
+    }
+  }
+
+  // Technically this test passes without claiming the name "builder" when we add the method (even
+  // though we do anyway for safety) because KeyVariableNamer actually hardcodes a list of common
+  // names to avoid which includes "builder".
+  @Test
+  public void testBuilder() {
+    BuilderComponent testComponent =
+        DaggerComponentFactoryNameConflictsTest_BuilderComponent.builder().build();
+    BuilderUsage builderUsage = testComponent.getBuilderUsage();
+    assertThat(builderUsage).isNotNull();
+  }
+}
diff --git a/javatests/dagger/functional/producers/BUILD b/javatests/dagger/functional/producers/BUILD
index f69b3cb..6f2551e 100644
--- a/javatests/dagger/functional/producers/BUILD
+++ b/javatests/dagger/functional/producers/BUILD
@@ -32,15 +32,15 @@
     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/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",
+        "//third_party/java/auto:value",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/util/concurrent",
+        "//third_party/java/jsr305_annotations",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/junit",
+        "//third_party/java/mockito",
+        "//third_party/java/truth",
     ],
 )
 
diff --git a/javatests/dagger/functional/spi/BUILD b/javatests/dagger/functional/spi/BUILD
index 8119bdd..dad3de6 100644
--- a/javatests/dagger/functional/spi/BUILD
+++ b/javatests/dagger/functional/spi/BUILD
@@ -24,11 +24,11 @@
     name = "test_plugin",
     srcs = ["TestPlugin.java"],
     deps = [
-        "//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/javapoet",
+        "//third_party/java/auto:service",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/javapoet",
     ],
 )
 
@@ -45,12 +45,12 @@
     ),
     functional = 0,
     test_only_deps = [
-        "@google_bazel_common//third_party/java/truth",
-        "@google_bazel_common//third_party/java/junit",
+        "//third_party/java/truth",
+        "//third_party/java/junit",
     ],
     deps = [
         ":test_lib",
         "//:dagger_with_compiler",
-        "@google_bazel_common//third_party/java/guava",
+        "//third_party/java/guava",
     ],
 )
diff --git a/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/java/dagger/hilt/android/gradleConfigCache/App.kt b/javatests/dagger/functional/sub/NestedType.java
similarity index 69%
copy from javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/java/dagger/hilt/android/gradleConfigCache/App.kt
copy to javatests/dagger/functional/sub/NestedType.java
index 1cbd4b0..999017a 100644
--- a/javatests/artifacts/hilt-android/gradleConfigCache/app/src/main/java/dagger/hilt/android/gradleConfigCache/App.kt
+++ b/javatests/dagger/functional/sub/NestedType.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Dagger Authors.
+ * 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.
@@ -14,10 +14,8 @@
  * limitations under the License.
  */
 
-package dagger.hilt.android.gradleConfigCache
+package dagger.functional.sub;
 
-import androidx.multidex.MultiDexApplication
-import dagger.hilt.android.HiltAndroidApp
-
-@HiltAndroidApp
-class App : MultiDexApplication()
+// Move this back to dagger.functional package once the javapoet clash bug gets fixed.
+// https://github.com/square/javapoet/issues/860
+public interface NestedType {}
diff --git a/javatests/dagger/functional/tck/BUILD b/javatests/dagger/functional/tck/BUILD
index 9cad2a0..ede9759 100644
--- a/javatests/dagger/functional/tck/BUILD
+++ b/javatests/dagger/functional/tck/BUILD
@@ -35,8 +35,8 @@
     ],
     deps = [
         "//:dagger_with_compiler",
-        "@google_bazel_common//third_party/java/jsr330_inject",
-        "@google_bazel_common//third_party/java/jsr330_inject:tck",
-        "@google_bazel_common//third_party/java/junit",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/jsr330_inject:tck",
+        "//third_party/java/junit",
     ],
 )
diff --git a/javatests/dagger/hilt/android/ActivityInjectedSavedStateViewModelTest.java b/javatests/dagger/hilt/android/ActivityInjectedSavedStateViewModelTest.java
index 03aa324..e8ba046 100644
--- a/javatests/dagger/hilt/android/ActivityInjectedSavedStateViewModelTest.java
+++ b/javatests/dagger/hilt/android/ActivityInjectedSavedStateViewModelTest.java
@@ -19,13 +19,13 @@
 import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.lifecycle.SavedStateHandle;
-import androidx.lifecycle.ViewModel;
-import androidx.lifecycle.ViewModelProvider;
 import android.content.Intent;
 import android.os.Build;
 import android.os.Bundle;
 import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.SavedStateHandle;
+import androidx.lifecycle.ViewModel;
+import androidx.lifecycle.ViewModelProvider;
 import androidx.test.core.app.ActivityScenario;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import dagger.Module;
diff --git a/javatests/dagger/hilt/android/ActivityInjectedViewModelTest.java b/javatests/dagger/hilt/android/ActivityInjectedViewModelTest.java
index 0e75741..5f88ca6 100644
--- a/javatests/dagger/hilt/android/ActivityInjectedViewModelTest.java
+++ b/javatests/dagger/hilt/android/ActivityInjectedViewModelTest.java
@@ -19,11 +19,11 @@
 import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.lifecycle.ViewModel;
-import androidx.lifecycle.ViewModelProvider;
 import android.content.Intent;
 import android.os.Build;
 import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.ViewModel;
+import androidx.lifecycle.ViewModelProvider;
 import androidx.test.core.app.ActivityScenario;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import dagger.Module;
diff --git a/javatests/dagger/hilt/android/ActivityRetainedClearedListenerTest.java b/javatests/dagger/hilt/android/ActivityRetainedClearedListenerTest.java
index 286998c..4530a72 100644
--- a/javatests/dagger/hilt/android/ActivityRetainedClearedListenerTest.java
+++ b/javatests/dagger/hilt/android/ActivityRetainedClearedListenerTest.java
@@ -19,9 +19,9 @@
 import static com.google.common.truth.Truth.assertThat;
 import static org.junit.Assert.fail;
 
-import androidx.lifecycle.Lifecycle.State;
 import android.os.Build;
 import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.Lifecycle.State;
 import androidx.test.core.app.ActivityScenario;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import dagger.hilt.android.ActivityRetainedLifecycle.OnClearedListener;
diff --git a/javatests/dagger/hilt/android/AliasOfMultipleScopesTest.java b/javatests/dagger/hilt/android/AliasOfMultipleScopesTest.java
new file mode 100644
index 0000000..032d546
--- /dev/null
+++ b/javatests/dagger/hilt/android/AliasOfMultipleScopesTest.java
@@ -0,0 +1,187 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import android.content.Context;
+import android.os.Build;
+import androidx.activity.ComponentActivity;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.Module;
+import dagger.Provides;
+import dagger.hilt.DefineComponent;
+import dagger.hilt.EntryPoint;
+import dagger.hilt.EntryPoints;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.components.ActivityComponent;
+import dagger.hilt.android.qualifiers.ApplicationContext;
+import dagger.hilt.android.scopes.ActivityScoped;
+import dagger.hilt.android.testing.HiltAndroidRule;
+import dagger.hilt.android.testing.HiltAndroidTest;
+import dagger.hilt.android.testing.HiltTestApplication;
+import dagger.hilt.components.SingletonComponent;
+import dagger.hilt.migration.AliasOf;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Scope;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+@HiltAndroidTest
+@RunWith(AndroidJUnit4.class)
+@Config(sdk = Build.VERSION_CODES.P, application = HiltTestApplication.class)
+public final class AliasOfMultipleScopesTest {
+
+  @Rule public final HiltAndroidRule rule = new HiltAndroidRule(this);
+
+  @Inject @ApplicationContext Context context;
+  @Inject CustomComponent.Builder customComponentBuilder;
+
+  @Scope
+  @Retention(CLASS)
+  @Target({ElementType.METHOD, ElementType.TYPE})
+  public @interface CustomScoped {}
+
+  @DefineComponent(parent = SingletonComponent.class)
+  @CustomScoped
+  public interface CustomComponent {
+    @DefineComponent.Builder
+    public interface Builder {
+      CustomComponent build();
+    }
+  }
+
+  @Scope
+  @AliasOf({ActivityScoped.class, CustomScoped.class})
+  public @interface AliasScoped {}
+
+  public interface UnscopedDep {}
+
+  public interface ActivityScopedDep {}
+
+  public interface CustomScopedDep {}
+
+  public interface AliasScopedDep {}
+
+  @Module
+  @InstallIn(SingletonComponent.class)
+  interface SingletonTestModule {
+    @Provides
+    static UnscopedDep unscopedDep() {
+      return new UnscopedDep() {};
+    }
+  }
+
+  @Module
+  @InstallIn(ActivityComponent.class)
+  interface ActivityTestModule {
+    @Provides
+    @ActivityScoped
+    static ActivityScopedDep activityScopedDep() {
+      return new ActivityScopedDep() {};
+    }
+
+    @Provides
+    @AliasScoped
+    static AliasScopedDep aliasScopedDep() {
+      return new AliasScopedDep() {};
+    }
+  }
+
+  @Module
+  @InstallIn(CustomComponent.class)
+  interface CustomTestModule {
+    @Provides
+    @CustomScoped
+    static CustomScopedDep customScopedDep() {
+      return new CustomScopedDep() {};
+    }
+
+    @Provides
+    @AliasScoped
+    static AliasScopedDep aliasScopedDep() {
+      return new AliasScopedDep() {};
+    }
+  }
+
+  /** An activity to test injection. */
+  @AndroidEntryPoint(ComponentActivity.class)
+  public static final class TestActivity extends Hilt_AliasOfMultipleScopesTest_TestActivity {
+    @Inject Provider<UnscopedDep> unscopedDep;
+    @Inject Provider<ActivityScopedDep> activityScopedDep;
+    @Inject Provider<AliasScopedDep> aliasScopedDep;
+  }
+
+  @EntryPoint
+  @InstallIn(SingletonComponent.class)
+  interface CustomComponentBuilderEntryPoint {
+    CustomComponent.Builder customComponentBuilder();
+  }
+
+  @EntryPoint
+  @InstallIn(CustomComponent.class)
+  interface CustomComponentEntryPoint {
+    Provider<UnscopedDep> unscopedDep();
+
+    Provider<CustomScopedDep> customScopedDep();
+
+    Provider<AliasScopedDep> aliasScopedDep();
+  }
+
+  @Before
+  public void setUp() {
+    rule.inject();
+  }
+
+  @Test
+  public void testActivityScoped() {
+    try (ActivityScenario<TestActivity> scenario = ActivityScenario.launch(TestActivity.class)) {
+      scenario.onActivity(
+          activity -> {
+            assertThat(activity.unscopedDep.get()).isNotSameInstanceAs(activity.unscopedDep.get());
+            assertThat(activity.activityScopedDep.get())
+                .isSameInstanceAs(activity.activityScopedDep.get());
+            assertThat(activity.aliasScopedDep.get())
+                .isSameInstanceAs(activity.aliasScopedDep.get());
+          });
+    }
+  }
+
+  @Test
+  public void testCustomScoped() {
+    CustomComponent customComponent =
+        EntryPoints.get(context, CustomComponentBuilderEntryPoint.class)
+            .customComponentBuilder()
+            .build();
+    CustomComponentEntryPoint entryPoint =
+        EntryPoints.get(customComponent, CustomComponentEntryPoint.class);
+    assertThat(entryPoint.unscopedDep().get()).isNotSameInstanceAs(entryPoint.unscopedDep().get());
+    assertThat(entryPoint.customScopedDep().get())
+        .isSameInstanceAs(entryPoint.customScopedDep().get());
+    assertThat(entryPoint.aliasScopedDep().get())
+        .isSameInstanceAs(entryPoint.aliasScopedDep().get());
+  }
+}
diff --git a/javatests/dagger/hilt/android/AndroidManifest.xml b/javatests/dagger/hilt/android/AndroidManifest.xml
index 7f6ede6..5195ca6 100644
--- a/javatests/dagger/hilt/android/AndroidManifest.xml
+++ b/javatests/dagger/hilt/android/AndroidManifest.xml
@@ -1,4 +1,5 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     package="dagger.hilt.android">
 
   <uses-sdk android:minSdkVersion="14" />
@@ -6,42 +7,71 @@
   <application>
     <activity
         android:name=".ActivityInjectedSavedStateViewModelTest$TestActivity"
-        android:exported="false"/>
+        android:exported="false"
+        tools:ignore="MissingClass"/>
     <activity
         android:name=".ActivityInjectedSavedStateViewModelTest$TestActivityWithSuperActivity"
-        android:exported="false"/>
+        android:exported="false"
+        tools:ignore="MissingClass"/>
     <activity
         android:name=".ActivityInjectedViewModelTest$TestActivity"
-        android:exported="false"/>
+        android:exported="false"
+        tools:ignore="MissingClass"/>
     <activity
         android:name=".ActivityRetainedClearedListenerTest$TestActivity"
-        android:exported="false"/>
+        android:exported="false"
+        tools:ignore="MissingClass"/>
     <activity
         android:name=".ActivityScenarioRuleTest$TestActivity"
-        android:exported="false"/>
+        android:exported="false"
+        tools:ignore="MissingClass"/>
+    <activity
+        android:name=".AliasOfMultipleScopesTest$TestActivity"
+        android:exported="false"
+        tools:ignore="MissingClass"/>
     <activity
         android:name=".DefaultViewModelFactoryTest$TestActivity"
-        android:exported="false"/>
+        android:exported="false"
+        tools:ignore="MissingClass"/>
+    <activity
+        android:name=".OptionalInjectTestClasses$TestActivity"
+        android:exported="false"
+        tools:ignore="MissingClass"/>
+    <activity
+        android:name=".OptionalInjectTestClasses$NonOptionalSubclassActivity"
+        android:exported="false"
+        tools:ignore="MissingClass"/>
+    <activity
+        android:name=".OptionalInjectTestClasses$OptionalSubclassActivity"
+        android:exported="false"
+        tools:ignore="MissingClass"/>
     <activity
         android:name=".QualifierInKotlinFieldsTest$TestActivity"
-        android:exported="false"/>
+        android:exported="false"
+        tools:ignore="MissingClass"/>
     <activity
         android:name=".UsesSharedComponent1Test$TestActivity"
-        android:exported="false"/>
+        android:exported="false"
+        tools:ignore="MissingClass"/>
     <activity
         android:name=".UsesSharedComponent2Test$TestActivity"
-        android:exported="false"/>
+        android:exported="false"
+        tools:ignore="MissingClass"/>
     <activity
         android:name=".UsesSharedComponentEnclosedTest$EnclosedTest$TestActivity"
-        android:exported="false"/>
+        android:exported="false"
+        tools:ignore="MissingClass"/>
     <activity
         android:name=".ViewModelScopedTest$TestActivity"
-        android:exported="false"/>
+        android:exported="false"
+        tools:ignore="MissingClass"/>
     <activity
         android:name=".ViewModelWithBaseTest$TestActivity"
-        android:exported="false"/>
+        android:exported="false"
+        tools:ignore="MissingClass"/>
     <activity
         android:name="dagger.hilt.android.testsubpackage.UsesSharedComponent1Test$TestActivity"
-        android:exported="false"/>
+        android:exported="false"
+        tools:ignore="MissingClass"/>
   </application>
 </manifest>
diff --git a/javatests/dagger/hilt/android/BUILD b/javatests/dagger/hilt/android/BUILD
index 1f9bd08..2aadb7d 100644
--- a/javatests/dagger/hilt/android/BUILD
+++ b/javatests/dagger/hilt/android/BUILD
@@ -30,6 +30,7 @@
         "MultiTestRootExternalModules.java",
     ],
     exports_manifest = 1,
+    javacopts = ["-Adagger.hilt.shareTestComponents=true"],
     manifest = "AndroidManifest.xml",
     deps = [
         "//:android_local_test_exports",
@@ -42,7 +43,7 @@
         "//java/dagger/hilt/android/testing:custom_test_application",
         "//java/dagger/hilt/android/testing:hilt_android_test",
         "//java/dagger/hilt/android/testing:uninstall_modules",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/truth",
         "@maven//:androidx_test_core",
         "@maven//:androidx_test_ext_junit",
         "@maven//:junit_junit",
@@ -91,6 +92,57 @@
 )
 
 android_local_test(
+    name = "AliasOfMultipleScopesTest",
+    srcs = ["AliasOfMultipleScopesTest.java"],
+    manifest = "AndroidManifest.xml",
+    manifest_values = {
+        "minSdkVersion": "14",
+    },
+    deps = [
+        "//:android_local_test_exports",
+        "//java/dagger/hilt:define_component",
+        "//java/dagger/hilt:entry_point",
+        "//java/dagger/hilt:install_in",
+        "//java/dagger/hilt/android:android_entry_point",
+        "//java/dagger/hilt/android:package_info",
+        "//java/dagger/hilt/android/qualifiers",
+        "//java/dagger/hilt/android/scopes",
+        "//java/dagger/hilt/android/testing:hilt_android_rule",
+        "//java/dagger/hilt/android/testing:hilt_android_test",
+        "//java/dagger/hilt/migration:alias_of",
+        "//third_party/java/truth",
+    ],
+)
+
+android_library(
+    name = "custom_inject_classes",
+    srcs = ["CustomInjectClasses.java"],
+    deps = [
+        "//:dagger_with_compiler",
+        "//java/dagger/hilt:install_in",
+        "//java/dagger/hilt/android:hilt_android_app",
+        "//java/dagger/hilt/android:package_info",
+        "//java/dagger/hilt/android/migration:custom_inject",
+        "//third_party/java/jsr330_inject",
+    ],
+)
+
+android_local_test(
+    name = "CustomInjectTest",
+    size = "small",
+    srcs = ["CustomInjectTest.java"],
+    manifest_values = {
+        "minSdkVersion": "14",
+    },
+    deps = [
+        ":custom_inject_classes",
+        "//:android_local_test_exports",
+        "//java/dagger/hilt/android:package_info",
+        "//third_party/java/truth",
+    ],
+)
+
+android_local_test(
     name = "EarlyEntryPointHiltAndroidAppRuntimeTest",
     size = "small",
     srcs = ["EarlyEntryPointHiltAndroidAppRuntimeTest.java"],
@@ -103,7 +155,7 @@
         "//java/dagger/hilt:entry_point",
         "//java/dagger/hilt/android:early_entry_point",
         "//java/dagger/hilt/android:package_info",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/truth",
         "@maven//:junit_junit",
     ],
 )
@@ -138,7 +190,7 @@
         "//java/dagger/hilt/android:early_entry_point",
         "//java/dagger/hilt/android:package_info",
         "//java/dagger/hilt/android/testing:hilt_android_test",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/truth",
     ],
 )
 
@@ -161,7 +213,7 @@
         "//java/dagger/hilt/android:package_info",
         "//java/dagger/hilt/android/testing:custom_test_application",
         "//java/dagger/hilt/android/testing:hilt_android_test",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/truth",
     ],
 )
 
@@ -180,7 +232,27 @@
         "//java/dagger/hilt/android:early_entry_point",
         "//java/dagger/hilt/android:package_info",
         "//java/dagger/hilt/android/testing:hilt_android_test",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/truth",
+    ],
+)
+
+android_local_test(
+    name = "FragmentContextOnAttachTest",
+    size = "small",
+    srcs = ["FragmentContextOnAttachTest.java"],
+    manifest_values = {
+        "minSdkVersion": "14",
+    },
+    deps = [
+        "//:android_local_test_exports",
+        "//:dagger_with_compiler",
+        "//java/dagger/hilt:install_in",
+        "//java/dagger/hilt/android:android_entry_point",
+        "//java/dagger/hilt/android:package_info",
+        "//java/dagger/hilt/android/flags:fragment_get_context_fix",
+        "//java/dagger/hilt/android/testing:bind_value",
+        "//java/dagger/hilt/android/testing:hilt_android_test",
+        "//third_party/java/truth",
     ],
 )
 
@@ -203,11 +275,10 @@
     },
     deps = [
         "//:android_local_test_exports",
-        "//:dagger_with_compiler",
         "//java/dagger/hilt:install_in",
         "//java/dagger/hilt/android:android_entry_point",
         "//java/dagger/hilt/android:package_info",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/truth",
     ],
 )
 
@@ -223,8 +294,8 @@
         "//java/dagger/hilt:install_in",
         "//java/dagger/hilt/android:package_info",
         "//java/dagger/hilt/android/testing:hilt_android_test",
-        "@google_bazel_common//third_party/java/jsr330_inject",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/truth",
     ],
 )
 
@@ -242,8 +313,8 @@
         "//java/dagger/hilt/android:android_entry_point",
         "//java/dagger/hilt/android:package_info",
         "//java/dagger/hilt/android/testing:hilt_android_test",
-        "@google_bazel_common//third_party/java/jsr330_inject",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/truth",
     ],
 )
 
@@ -261,8 +332,8 @@
         "//java/dagger/hilt/android:android_entry_point",
         "//java/dagger/hilt/android:package_info",
         "//java/dagger/hilt/android/testing:hilt_android_test",
-        "@google_bazel_common//third_party/java/junit",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
 )
 
@@ -288,12 +359,11 @@
     deps = [
         "//:android_local_test_exports",
         "//:dagger_with_compiler",
-        "//java/dagger/hilt:entry_point",
         "//java/dagger/hilt:install_in",
         "//java/dagger/hilt/android:package_info",
         "//java/dagger/hilt/android/testing:hilt_android_test",
         "//javatests/dagger/hilt/testmodules",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/truth",
     ],
 )
 
@@ -307,15 +377,14 @@
     deps = [
         "//:android_local_test_exports",
         "//:dagger_with_compiler",
-        "//java/dagger/hilt:entry_point",
         "//java/dagger/hilt:install_in",
         "//java/dagger/hilt/android:android_entry_point",
         "//java/dagger/hilt/android:package_info",
         "//java/dagger/hilt/android/lifecycle",
         "//java/dagger/hilt/android/testing:bind_value",
         "//java/dagger/hilt/android/testing:hilt_android_test",
-        "@google_bazel_common//third_party/java/jsr330_inject",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/truth",
         "@maven//:androidx_activity_activity",
         "@maven//:androidx_fragment_fragment",
         "@maven//:androidx_lifecycle_lifecycle_common",
@@ -339,8 +408,8 @@
         "//java/dagger/hilt/android:android_entry_point",
         "//java/dagger/hilt/android:package_info",
         "//java/dagger/hilt/android/testing:hilt_android_test",
-        "@google_bazel_common//third_party/java/jsr330_inject",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/truth",
         "@maven//:androidx_activity_activity",
         "@maven//:androidx_fragment_fragment",
         "@maven//:androidx_lifecycle_lifecycle_common",
@@ -361,6 +430,75 @@
 )
 
 android_local_test(
+    name = "OptionalInjectWithHiltTest",
+    size = "small",
+    srcs = [
+        "OptionalInjectWithHiltTest.java",
+    ],
+    manifest = "AndroidManifest.xml",
+    manifest_values = {
+        "minSdkVersion": "14",
+    },
+    deps = [
+        ":OptionalInjectTestClasses",
+        "//:android_local_test_exports",
+        "//java/dagger/hilt/android:android_entry_point",
+        "//java/dagger/hilt/android:package_info",
+        "//java/dagger/hilt/android/migration:optional_inject",
+        "//java/dagger/hilt/android/testing:hilt_android_test",
+        "//third_party/java/truth",
+        "@maven//:androidx_activity_activity",
+        "@maven//:androidx_fragment_fragment",
+        "@maven//:androidx_lifecycle_lifecycle_common",
+        "@maven//:androidx_lifecycle_lifecycle_viewmodel",
+        "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate",
+    ],
+)
+
+android_local_test(
+    name = "OptionalInjectWithoutHiltTest",
+    size = "small",
+    srcs = [
+        "OptionalInjectWithoutHiltTest.java",
+    ],
+    manifest = "AndroidManifest.xml",
+    manifest_values = {
+        "minSdkVersion": "14",
+    },
+    deps = [
+        ":OptionalInjectTestClasses",
+        "//:android_local_test_exports",
+        "//java/dagger/hilt/android:package_info",
+        "//java/dagger/hilt/android/migration:optional_inject",
+        "//third_party/java/truth",
+        "@maven//:androidx_activity_activity",
+        "@maven//:androidx_fragment_fragment",
+        "@maven//:androidx_lifecycle_lifecycle_common",
+        "@maven//:androidx_lifecycle_lifecycle_viewmodel",
+        "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate",
+    ],
+)
+
+android_library(
+    name = "OptionalInjectTestClasses",
+    srcs = ["OptionalInjectTestClasses.java"],
+    manifest = "AndroidManifest.xml",
+    deps = [
+        "//:dagger_with_compiler",
+        "//java/dagger/hilt:install_in",
+        "//java/dagger/hilt/android:android_entry_point",
+        "//java/dagger/hilt/android:package_info",
+        "//java/dagger/hilt/android/migration:optional_inject",
+        "//third_party/java/jsr330_inject",
+        "@maven//:androidx_activity_activity",
+        "@maven//:androidx_fragment_fragment",
+        "@maven//:androidx_lifecycle_lifecycle_common",
+        "@maven//:androidx_lifecycle_lifecycle_viewmodel",
+        "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate",
+    ],
+)
+
+android_local_test(
     name = "ActivityRetainedClearedListenerTest",
     srcs = ["ActivityRetainedClearedListenerTest.java"],
     manifest = "AndroidManifest.xml",
@@ -370,14 +508,13 @@
     deps = [
         "//:android_local_test_exports",
         "//:dagger_with_compiler",
-        "//java/dagger/hilt:entry_point",
         "//java/dagger/hilt:install_in",
         "//java/dagger/hilt/android:activity_retained_lifecycle",
         "//java/dagger/hilt/android:android_entry_point",
         "//java/dagger/hilt/android:package_info",
         "//java/dagger/hilt/android/testing:hilt_android_test",
-        "@google_bazel_common//third_party/java/jsr330_inject",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/truth",
         "@maven//:androidx_activity_activity",
         "@maven//:androidx_fragment_fragment",
         "@maven//:androidx_lifecycle_lifecycle_common",
@@ -397,13 +534,12 @@
     deps = [
         "//:android_local_test_exports",
         "//:dagger_with_compiler",
-        "//java/dagger/hilt:entry_point",
         "//java/dagger/hilt:install_in",
         "//java/dagger/hilt/android:android_entry_point",
         "//java/dagger/hilt/android:package_info",
         "//java/dagger/hilt/android/testing:hilt_android_test",
-        "@google_bazel_common//third_party/java/jsr330_inject",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/truth",
         "@maven//:androidx_activity_activity",
         "@maven//:androidx_fragment_fragment",
         "@maven//:androidx_lifecycle_lifecycle_common",
@@ -423,15 +559,14 @@
     deps = [
         "//:android_local_test_exports",
         "//:dagger_with_compiler",
-        "//java/dagger/hilt:entry_point",
         "//java/dagger/hilt:install_in",
         "//java/dagger/hilt/android:android_entry_point",
         "//java/dagger/hilt/android:package_info",
         "//java/dagger/hilt/android/lifecycle",
         "//java/dagger/hilt/android/scopes",
         "//java/dagger/hilt/android/testing:hilt_android_test",
-        "@google_bazel_common//third_party/java/jsr330_inject",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/truth",
         "@maven//:androidx_activity_activity",
         "@maven//:androidx_fragment_fragment",
         "@maven//:androidx_lifecycle_lifecycle_common",
@@ -451,14 +586,13 @@
     deps = [
         "//:android_local_test_exports",
         "//:dagger_with_compiler",
-        "//java/dagger/hilt:entry_point",
         "//java/dagger/hilt:install_in",
         "//java/dagger/hilt/android:android_entry_point",
         "//java/dagger/hilt/android:package_info",
         "//java/dagger/hilt/android/lifecycle",
         "//java/dagger/hilt/android/testing:hilt_android_test",
-        "@google_bazel_common//third_party/java/jsr330_inject",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/truth",
         "@maven//:androidx_activity_activity",
         "@maven//:androidx_fragment_fragment",
         "@maven//:androidx_lifecycle_lifecycle_common",
@@ -478,14 +612,12 @@
     deps = [
         "//:android_local_test_exports",
         "//:dagger_with_compiler",
-        "//java/dagger/hilt:entry_point",
         "//java/dagger/hilt:install_in",
         "//java/dagger/hilt/android:android_entry_point",
         "//java/dagger/hilt/android:package_info",
         "//java/dagger/hilt/android/testing:hilt_android_test",
-        "//java/dagger/internal/guava:base-android",
-        "@google_bazel_common//third_party/java/jsr330_inject",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/truth",
         "@maven//:junit_junit",
     ],
 )
@@ -502,7 +634,7 @@
         "//java/dagger/hilt:entry_point",
         "//java/dagger/hilt:install_in",
         "//java/dagger/hilt/android/components",
-        "@google_bazel_common//third_party/java/jsr330_inject",
+        "//third_party/java/jsr330_inject",
     ],
 )
 
@@ -548,8 +680,8 @@
         "//java/dagger/hilt/android/testing:custom_test_application",
         "//java/dagger/hilt/android/testing:hilt_android_test",
         "//java/dagger/hilt/android/testing:uninstall_modules",
-        "@google_bazel_common//third_party/java/jsr330_inject",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/truth",
         "@maven//:androidx_test_core",
         "@maven//:androidx_test_ext_junit",
         "@maven//:junit_junit",
@@ -578,8 +710,8 @@
         "//java/dagger/hilt/android/testing:bind_value",
         "//java/dagger/hilt/android/testing:hilt_android_test",
         "//java/dagger/hilt/testing:test_install_in",
-        "@google_bazel_common//third_party/java/jsr330_inject",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/truth",
         "@maven//:androidx_test_core",
         "@maven//:androidx_test_ext_junit",
         "@maven//:junit_junit",
diff --git a/javatests/dagger/hilt/android/CustomInjectClasses.java b/javatests/dagger/hilt/android/CustomInjectClasses.java
new file mode 100644
index 0000000..482b70b
--- /dev/null
+++ b/javatests/dagger/hilt/android/CustomInjectClasses.java
@@ -0,0 +1,54 @@
+/*
+ * 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;
+
+import android.app.Application;
+import dagger.Module;
+import dagger.Provides;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.migration.CustomInject;
+import dagger.hilt.android.migration.CustomInjection;
+import dagger.hilt.components.SingletonComponent;
+import javax.inject.Inject;
+
+/**
+ * Classes for CustomInjectTest. This is in a separate build target because otherwise
+ * robolectric does not recognize the application class as extending application due to order of
+ * class generation.
+ */
+final class CustomInjectClasses {
+
+  @Module
+  @InstallIn(SingletonComponent.class)
+  static final class TestModule {
+    @Provides
+    static Integer provideInt() {
+      return 9;
+    }
+  }
+
+  @CustomInject
+  @HiltAndroidApp(Application.class)
+  static final class TestApplication extends Hilt_CustomInjectClasses_TestApplication {
+
+    @Inject Integer intValue;
+
+    void inject() {
+      CustomInjection.inject(this);
+    }
+  }
+}
diff --git a/javatests/dagger/hilt/android/CustomInjectTest.java b/javatests/dagger/hilt/android/CustomInjectTest.java
new file mode 100644
index 0000000..38512a0
--- /dev/null
+++ b/javatests/dagger/hilt/android/CustomInjectTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Build;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.hilt.android.CustomInjectClasses.TestApplication;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+/** Tests for @CustomInject. */
+@RunWith(AndroidJUnit4.class)
+// Robolectric requires Java9 to run API 29 and above, so use API 28 instead
+@Config(
+    sdk = Build.VERSION_CODES.P,
+    application = TestApplication.class)
+public class CustomInjectTest {
+
+  @Test
+  public void testInjection() {
+    TestApplication app = (TestApplication) ApplicationProvider.getApplicationContext();
+
+    assertThat(app.intValue).isNull();
+    app.inject();
+    assertThat(app.intValue).isEqualTo(9);
+  }
+}
diff --git a/javatests/dagger/hilt/android/DefaultViewModelFactoryTest.java b/javatests/dagger/hilt/android/DefaultViewModelFactoryTest.java
index 78cd568..0d59e24 100644
--- a/javatests/dagger/hilt/android/DefaultViewModelFactoryTest.java
+++ b/javatests/dagger/hilt/android/DefaultViewModelFactoryTest.java
@@ -18,11 +18,11 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.lifecycle.ViewModel;
-import androidx.lifecycle.ViewModelProvider;
 import android.os.Build;
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.ViewModel;
+import androidx.lifecycle.ViewModelProvider;
 import androidx.test.core.app.ActivityScenario;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import dagger.hilt.android.lifecycle.HiltViewModel;
diff --git a/javatests/dagger/hilt/android/EntryPointAccessorsTest.kt b/javatests/dagger/hilt/android/EntryPointAccessorsTest.kt
new file mode 100644
index 0000000..fb4afc0
--- /dev/null
+++ b/javatests/dagger/hilt/android/EntryPointAccessorsTest.kt
@@ -0,0 +1,193 @@
+/*
+ * 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
+
+import android.content.Context
+import android.os.Build
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentActivity
+import android.view.View
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.EntryPoint
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.ActivityComponent
+import dagger.hilt.android.components.FragmentComponent
+import dagger.hilt.android.components.ViewComponent
+import dagger.hilt.android.testing.HiltAndroidRule
+import dagger.hilt.android.testing.HiltAndroidTest
+import dagger.hilt.android.testing.HiltTestApplication
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Qualifier
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.Robolectric
+import org.robolectric.annotation.Config
+
+@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)
+class EntryPointAccessorsTest {
+
+  companion object {
+    const val APPLICATION_STRING = "APPLICATION_STRING"
+    const val ACTIVITY_STRING = "ACTIVITY_STRING"
+    const val FRAGMENT_STRING = "FRAGMENT_STRING"
+    const val VIEW_STRING = "VIEW_STRING"
+  }
+
+  @get:Rule
+  var rule = HiltAndroidRule(this)
+
+  @Qualifier
+  @Retention(AnnotationRetention.BINARY)
+  annotation class ApplicationLevel
+
+  @Qualifier
+  @Retention(AnnotationRetention.BINARY)
+  annotation class ActivityLevel
+
+  @Qualifier
+  @Retention(AnnotationRetention.BINARY)
+  annotation class FragmentLevel
+
+  @Qualifier
+  @Retention(AnnotationRetention.BINARY)
+  annotation class ViewLevel
+
+  @Module
+  @InstallIn(SingletonComponent::class)
+  internal object ApplicationModule {
+    @ApplicationLevel
+    @Provides
+    fun provideString(): String {
+      return APPLICATION_STRING
+    }
+  }
+
+  @Module
+  @InstallIn(ActivityComponent::class)
+  internal object ActivityModule {
+    @ActivityLevel
+    @Provides
+    fun provideString(): String {
+      return ACTIVITY_STRING
+    }
+  }
+
+  @Module
+  @InstallIn(FragmentComponent::class)
+  internal object FragmentModule {
+    @FragmentLevel
+    @Provides
+    fun provideString(): String {
+      return FRAGMENT_STRING
+    }
+  }
+
+  @Module
+  @InstallIn(ViewComponent::class)
+  internal object ViewModule {
+    @ViewLevel
+    @Provides
+    fun provideString(): String {
+      return VIEW_STRING
+    }
+  }
+
+  @EntryPoint
+  @InstallIn(SingletonComponent::class)
+  internal interface ApplicationEntryPoint {
+    @ApplicationLevel
+    fun getString(): String
+  }
+
+  @EntryPoint
+  @InstallIn(ActivityComponent::class)
+  internal interface ActivityEntryPoint {
+    @ActivityLevel
+    fun getString(): String
+  }
+
+  @EntryPoint
+  @InstallIn(FragmentComponent::class)
+  internal interface FragmentEntryPoint {
+    @FragmentLevel
+    fun getString(): String
+  }
+
+  @EntryPoint
+  @InstallIn(ViewComponent::class)
+  internal interface ViewEntryPoint {
+    @ViewLevel
+    fun getString(): String
+  }
+
+  @Test
+  fun testApplicationEntryPoint() {
+    val app = getApplicationContext<HiltTestApplication>()
+    val entryPoint = EntryPointAccessors.fromApplication<ApplicationEntryPoint>(app)
+    Truth.assertThat(entryPoint.getString())
+      .isEqualTo(APPLICATION_STRING)
+
+    val activity = Robolectric.buildActivity(TestActivity::class.java).setup().get()
+    val applicationEntryPoint = EntryPointAccessors.fromApplication<ApplicationEntryPoint>(activity)
+    Truth.assertThat(applicationEntryPoint.getString())
+      .isEqualTo(APPLICATION_STRING)
+  }
+
+  @Test
+  fun testActivityEntryPoint() {
+    val activity = Robolectric.buildActivity(TestActivity::class.java).setup().get()
+    val entryPoint = EntryPointAccessors.fromActivity<ActivityEntryPoint>(activity)
+    Truth.assertThat(entryPoint.getString())
+      .isEqualTo(ACTIVITY_STRING)
+  }
+
+  @Test
+  fun testFragmentEntryPoint() {
+    val activity = Robolectric.buildActivity(TestActivity::class.java).setup().get()
+    val fragment = TestFragment()
+    activity.supportFragmentManager.beginTransaction().add(fragment, "").commitNow()
+    val entryPoint = EntryPointAccessors.fromFragment<FragmentEntryPoint>(fragment)
+    Truth.assertThat(entryPoint.getString())
+      .isEqualTo(FRAGMENT_STRING)
+  }
+
+  @Test
+  fun testViewEntryPoint() {
+    val activity = Robolectric.buildActivity(TestActivity::class.java).setup().get()
+    val view = TestView(activity)
+    val entryPoint = EntryPointAccessors.fromView<ViewEntryPoint>(view)
+    Truth.assertThat(entryPoint.getString())
+      .isEqualTo(VIEW_STRING)
+  }
+
+  @AndroidEntryPoint(FragmentActivity::class)
+  class TestActivity : Hilt_EntryPointAccessorsTest_TestActivity()
+
+  @AndroidEntryPoint(Fragment::class)
+  class TestFragment : Hilt_EntryPointAccessorsTest_TestFragment()
+
+  @AndroidEntryPoint(View::class)
+  class TestView(context: Context) : Hilt_EntryPointAccessorsTest_TestView(context)
+}
diff --git a/javatests/dagger/hilt/android/FragmentContextOnAttachTest.java b/javatests/dagger/hilt/android/FragmentContextOnAttachTest.java
new file mode 100644
index 0000000..19b3d05
--- /dev/null
+++ b/javatests/dagger/hilt/android/FragmentContextOnAttachTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Build;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.hilt.android.flags.FragmentGetContextFix;
+import dagger.hilt.android.testing.BindValueIntoSet;
+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.Robolectric;
+import org.robolectric.annotation.Config;
+
+@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 FragmentContextOnAttachTest {
+
+  @Rule public final HiltAndroidRule rule = new HiltAndroidRule(this);
+
+  @BindValueIntoSet
+  @FragmentGetContextFix.DisableFragmentGetContextFix
+  boolean disableGetContextFix = false;
+
+  /** Hilt Activity */
+  @AndroidEntryPoint(FragmentActivity.class)
+  public static final class TestActivity extends Hilt_FragmentContextOnAttachTest_TestActivity {}
+
+  /** Hilt Fragment */
+  @AndroidEntryPoint(Fragment.class)
+  public static final class TestFragment extends Hilt_FragmentContextOnAttachTest_TestFragment {
+    Context onAttachContextContext = null;
+    Context onAttachActivityContext = null;
+
+    @Override
+    public void onAttach(Context context) {
+      // Test that getContext() can be called at this point
+      onAttachContextContext = getContext();
+      super.onAttach(context);
+    }
+
+    @Override
+    public void onAttach(Activity activity) {
+      // Test that getContext() can be called at this point
+      onAttachActivityContext = getContext();
+      super.onAttach(activity);
+    }
+  }
+
+  @Test
+  public void testGetContextAvailableBeforeSuperOnAttach() throws Exception {
+    FragmentActivity activity = Robolectric.setupActivity(TestActivity.class);
+    TestFragment fragment = new TestFragment();
+    activity.getSupportFragmentManager().beginTransaction().add(fragment, "").commitNow();
+    assertThat(fragment.onAttachContextContext).isNotNull();
+    assertThat(fragment.onAttachActivityContext).isNotNull();
+  }
+
+  // Tests the behavior when using the useFragmentGetContextFix flag.
+  @Test
+  public void testGetContextReturnsNullAfterRemoval() throws Exception {
+    FragmentActivity activity = Robolectric.setupActivity(TestActivity.class);
+    TestFragment fragment = new TestFragment();
+    activity.getSupportFragmentManager().beginTransaction().add(fragment, "").commitNow();
+    assertThat(fragment.getContext()).isNotNull();
+    activity.getSupportFragmentManager().beginTransaction().remove(fragment).commitNow();
+    // This should be null since the fix was enabled by the compiler flag and runtime flag
+    assertThat(fragment.getContext()).isNull();
+
+    // Flip the flag so that we now disable the fix
+    disableGetContextFix = true;
+    TestFragment fragment2 = new TestFragment();
+    activity.getSupportFragmentManager().beginTransaction().add(fragment2, "").commitNow();
+    assertThat(fragment2.getContext()).isNotNull();
+    activity.getSupportFragmentManager().beginTransaction().remove(fragment2).commitNow();
+    // This should not be null since the fix was disabled by the runtime flag
+    assertThat(fragment2.getContext()).isNotNull();
+  }
+}
diff --git a/javatests/dagger/hilt/android/ModuleTest.java b/javatests/dagger/hilt/android/ModuleTest.java
index 0dadfe0..2a24623 100644
--- a/javatests/dagger/hilt/android/ModuleTest.java
+++ b/javatests/dagger/hilt/android/ModuleTest.java
@@ -79,7 +79,7 @@
   // constructor exists.
   @Module
   @InstallIn(SingletonComponent.class)
-  static final class TestModule3 {
+  public static final class TestModule3 {
     TestModule3() {
       this("");
     }
diff --git a/javatests/dagger/hilt/android/MultiTestRoot1Test.java b/javatests/dagger/hilt/android/MultiTestRoot1Test.java
index 49c03bc..36719b4 100644
--- a/javatests/dagger/hilt/android/MultiTestRoot1Test.java
+++ b/javatests/dagger/hilt/android/MultiTestRoot1Test.java
@@ -150,7 +150,8 @@
     }
   }
 
-  static class Bar {
+  // Must be public due to b/183636779
+  public static class Bar {
     final String value;
 
     Bar(String value) {
@@ -166,7 +167,8 @@
     }
   }
 
-  static class Qux {}
+  // Must be public due to b/183636779
+  public static class Qux {}
 
   @Module
   @InstallIn(SingletonComponent.class)
@@ -277,10 +279,11 @@
             ClassCastException.class,
             () -> EntryPoints.get(getApplicationContext(), MultiTestRoot2Test.BarEntryPoint.class));
     assertThat(exception)
-          .hasMessageThat()
-          .isEqualTo(
-              "Cannot cast dagger.hilt.android.DaggerMultiTestRoot1Test_HiltComponents_SingletonC"
-              + " to dagger.hilt.android.MultiTestRoot2Test$BarEntryPoint");
+        .hasMessageThat()
+        .isEqualTo(
+            "Cannot cast dagger.hilt.android.internal.testing.root."
+                + "DaggerMultiTestRoot1Test_HiltComponents_SingletonC"
+                + " to dagger.hilt.android.MultiTestRoot2Test$BarEntryPoint");
   }
 
   @Test
diff --git a/javatests/dagger/hilt/android/MultiTestRoot2Test.java b/javatests/dagger/hilt/android/MultiTestRoot2Test.java
index 39aecfa..ec660c4 100644
--- a/javatests/dagger/hilt/android/MultiTestRoot2Test.java
+++ b/javatests/dagger/hilt/android/MultiTestRoot2Test.java
@@ -121,7 +121,8 @@
     }
   }
 
-  static class Bar {
+  // Must be public due to b/183636779
+  public static class Bar {
     final String value;
 
     Bar(String value) {
@@ -137,7 +138,8 @@
     }
   }
 
-  static class Qux {}
+  // Must be public due to b/183636779
+  public static class Qux {}
 
   @Module
   @InstallIn(SingletonComponent.class)
@@ -255,10 +257,11 @@
             ClassCastException.class,
             () -> EntryPoints.get(getApplicationContext(), MultiTestRoot1Test.BarEntryPoint.class));
     assertThat(exception)
-          .hasMessageThat()
-          .isEqualTo(
-              "Cannot cast dagger.hilt.android.DaggerMultiTestRoot2Test_HiltComponents_SingletonC"
-              + " to dagger.hilt.android.MultiTestRoot1Test$BarEntryPoint");
+        .hasMessageThat()
+        .isEqualTo(
+            "Cannot cast dagger.hilt.android.internal.testing.root."
+                + "DaggerMultiTestRoot2Test_HiltComponents_SingletonC"
+                + " to dagger.hilt.android.MultiTestRoot1Test$BarEntryPoint");
   }
 
   @Test
diff --git a/javatests/dagger/hilt/android/OptionalInjectTestClasses.java b/javatests/dagger/hilt/android/OptionalInjectTestClasses.java
new file mode 100644
index 0000000..78b52da
--- /dev/null
+++ b/javatests/dagger/hilt/android/OptionalInjectTestClasses.java
@@ -0,0 +1,170 @@
+/*
+ * 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;
+
+import android.app.IntentService;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import android.widget.LinearLayout;
+import androidx.lifecycle.ViewModel;
+import dagger.Module;
+import dagger.Provides;
+import dagger.hilt.InstallIn;
+import dagger.hilt.android.lifecycle.HiltViewModel;
+import dagger.hilt.android.migration.OptionalInject;
+import dagger.hilt.components.SingletonComponent;
+import javax.inject.Inject;
+import javax.inject.Qualifier;
+
+/** Test classes for optional injection. */
+public final class OptionalInjectTestClasses {
+  public static final String APP_BINDING = "app binding";
+  public static final String ACTIVITY_BINDING = "activity binding";
+  public static final String FRAGMENT_BINDING = "fragment binding";
+  public static final String VIEW_BINDING = "view binding";
+
+  @Qualifier
+  @interface ActivityLevel {}
+
+  @Qualifier
+  @interface FragmentLevel {}
+
+  @Qualifier
+  @interface ViewLevel {}
+
+  @AndroidEntryPoint(FragmentActivity.class)
+  @OptionalInject
+  public static class TestActivity extends Hilt_OptionalInjectTestClasses_TestActivity {
+    @Inject @ActivityLevel String testActivityBinding;
+  }
+
+  @AndroidEntryPoint(TestActivity.class)
+  public static final class NonOptionalSubclassActivity
+      extends Hilt_OptionalInjectTestClasses_NonOptionalSubclassActivity {
+    @Inject @ActivityLevel String testActivitySubclassBinding;
+  }
+
+  @AndroidEntryPoint(TestActivity.class)
+  @OptionalInject
+  public static final class OptionalSubclassActivity
+      extends Hilt_OptionalInjectTestClasses_OptionalSubclassActivity {
+    @Inject @ActivityLevel String testActivitySubclassBinding;
+  }
+
+  @AndroidEntryPoint(Fragment.class)
+  @OptionalInject
+  public static final class TestFragment extends Hilt_OptionalInjectTestClasses_TestFragment {
+    @Inject @FragmentLevel String testFragmentBinding;
+  }
+
+  @AndroidEntryPoint(LinearLayout.class)
+  @OptionalInject
+  public static final class TestView extends Hilt_OptionalInjectTestClasses_TestView {
+    @Inject @ViewLevel String testViewBinding;
+
+    TestView(Context context) {
+      super(context);
+    }
+  }
+
+  @WithFragmentBindings
+  @AndroidEntryPoint(LinearLayout.class)
+  @OptionalInject
+  public static final class TestWithFragmentBindingsView
+      extends Hilt_OptionalInjectTestClasses_TestWithFragmentBindingsView {
+    @Inject @ViewLevel String testViewBinding;
+
+    TestWithFragmentBindingsView(Context context) {
+      super(context);
+    }
+  }
+
+  @HiltViewModel
+  public static final class TestViewModel extends ViewModel {
+    final String appBinding;
+
+    @Inject TestViewModel(String appBinding) {
+      this.appBinding = appBinding;
+    }
+  }
+
+  public static final class NonHiltViewModel extends ViewModel {}
+
+  @AndroidEntryPoint(Service.class)
+  @OptionalInject
+  public static final class TestService extends Hilt_OptionalInjectTestClasses_TestService {
+    @Inject String testAppBinding;
+
+    @Override
+    public IBinder onBind(Intent intent) {
+      return null;
+    }
+  }
+
+  @AndroidEntryPoint(IntentService.class)
+  @OptionalInject
+  public static final class TestIntentService
+      extends Hilt_OptionalInjectTestClasses_TestIntentService {
+    TestIntentService() {
+      super("TestIntentService");
+    }
+
+    @Inject String testAppBinding;
+
+    @Override
+    public void onHandleIntent(Intent intent) {}
+  }
+
+  @AndroidEntryPoint(BroadcastReceiver.class)
+  @OptionalInject
+  public static final class TestBroadcastReceiver
+      extends Hilt_OptionalInjectTestClasses_TestBroadcastReceiver {
+    @Inject String testAppBinding;
+  }
+
+  @Module
+  @InstallIn(SingletonComponent.class)
+  static final class AppModule {
+    @Provides
+    static String provideAppString() {
+      return APP_BINDING;
+    }
+
+    @Provides
+    @ActivityLevel
+    static String provideActivityString() {
+      return ACTIVITY_BINDING;
+    }
+
+    @Provides
+    @FragmentLevel
+    static String provideFragmentString() {
+      return FRAGMENT_BINDING;
+    }
+
+    @Provides
+    @ViewLevel
+    static String provideViewString() {
+      return VIEW_BINDING;
+    }
+  }
+}
diff --git a/javatests/dagger/hilt/android/OptionalInjectWithHiltTest.java b/javatests/dagger/hilt/android/OptionalInjectWithHiltTest.java
new file mode 100644
index 0000000..a8fedf6
--- /dev/null
+++ b/javatests/dagger/hilt/android/OptionalInjectWithHiltTest.java
@@ -0,0 +1,230 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+import static dagger.hilt.android.OptionalInjectTestClasses.ACTIVITY_BINDING;
+import static dagger.hilt.android.OptionalInjectTestClasses.APP_BINDING;
+import static dagger.hilt.android.OptionalInjectTestClasses.FRAGMENT_BINDING;
+import static dagger.hilt.android.OptionalInjectTestClasses.VIEW_BINDING;
+import static dagger.hilt.android.migration.OptionalInjectCheck.wasInjectedByHilt;
+import static org.junit.Assert.assertThrows;
+
+import android.content.Intent;
+import android.os.Build;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.hilt.android.OptionalInjectTestClasses.NonHiltViewModel;
+import dagger.hilt.android.OptionalInjectTestClasses.NonOptionalSubclassActivity;
+import dagger.hilt.android.OptionalInjectTestClasses.OptionalSubclassActivity;
+import dagger.hilt.android.OptionalInjectTestClasses.TestActivity;
+import dagger.hilt.android.OptionalInjectTestClasses.TestBroadcastReceiver;
+import dagger.hilt.android.OptionalInjectTestClasses.TestFragment;
+import dagger.hilt.android.OptionalInjectTestClasses.TestIntentService;
+import dagger.hilt.android.OptionalInjectTestClasses.TestService;
+import dagger.hilt.android.OptionalInjectTestClasses.TestView;
+import dagger.hilt.android.OptionalInjectTestClasses.TestViewModel;
+import dagger.hilt.android.OptionalInjectTestClasses.TestWithFragmentBindingsView;
+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.Robolectric;
+import org.robolectric.annotation.Config;
+
+/** Tests that optional inject works with a Hilt root. */
+@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 OptionalInjectWithHiltTest {
+  @Rule public final HiltAndroidRule rules = new HiltAndroidRule(this);
+
+  @Test
+  public void testActivityInjection() throws Exception {
+    TestActivity testActivity = Robolectric.setupActivity(TestActivity.class);
+    assertThat(testActivity.testActivityBinding).isEqualTo(ACTIVITY_BINDING);
+    assertThat(testActivity.wasInjectedByHilt()).isTrue();
+    assertThat(wasInjectedByHilt(testActivity)).isTrue();
+  }
+
+  @Test
+  public void testNonOptionalSubclassActivityInjection() throws Exception {
+    NonOptionalSubclassActivity testActivity = Robolectric.setupActivity(
+        NonOptionalSubclassActivity.class);
+    assertThat(testActivity.testActivityBinding).isEqualTo(ACTIVITY_BINDING);
+    assertThat(testActivity.testActivitySubclassBinding).isEqualTo(ACTIVITY_BINDING);
+    assertThat(testActivity.wasInjectedByHilt()).isTrue();
+    assertThat(wasInjectedByHilt(testActivity)).isTrue();
+  }
+
+  @Test
+  public void testOptionalSubclassActivityInjection() throws Exception {
+    OptionalSubclassActivity testActivity = Robolectric.setupActivity(
+        OptionalSubclassActivity.class);
+    assertThat(testActivity.testActivityBinding).isEqualTo(ACTIVITY_BINDING);
+    assertThat(testActivity.testActivitySubclassBinding).isEqualTo(ACTIVITY_BINDING);
+    assertThat(testActivity.wasInjectedByHilt()).isTrue();
+    assertThat(wasInjectedByHilt(testActivity)).isTrue();
+  }
+
+  @Test
+  public void testFragmentInjection() throws Exception {
+    TestActivity testActivity = Robolectric.setupActivity(TestActivity.class);
+    TestFragment testFragment = new TestFragment();
+    testActivity.getSupportFragmentManager()
+        .beginTransaction()
+        .add(testFragment, null)
+        .commitNow();
+    assertThat(testFragment.testFragmentBinding).isEqualTo(FRAGMENT_BINDING);
+    assertThat(testFragment.wasInjectedByHilt()).isTrue();
+    assertThat(wasInjectedByHilt(testFragment)).isTrue();
+  }
+
+  @Test
+  public void testFragmentInjectionWithNonHiltActivityWithHiltRoot() throws Exception {
+    FragmentActivity testActivity = Robolectric.setupActivity(FragmentActivity.class);
+    TestFragment testFragment = new TestFragment();
+    testActivity.getSupportFragmentManager()
+        .beginTransaction()
+        .add(testFragment, null)
+        .commitNow();
+    assertThat(testFragment.testFragmentBinding).isNull();
+    assertThat(testFragment.wasInjectedByHilt()).isFalse();
+    assertThat(wasInjectedByHilt(testFragment)).isFalse();
+  }
+
+  @Test
+  public void testViewInjection() throws Exception {
+    TestActivity testActivity = Robolectric.setupActivity(TestActivity.class);
+    TestView testView = new TestView(testActivity);
+    assertThat(testView.testViewBinding).isEqualTo(VIEW_BINDING);
+    assertThat(testView.wasInjectedByHilt()).isTrue();
+    assertThat(wasInjectedByHilt(testView)).isTrue();
+  }
+
+  @Test
+  public void testViewInjectionWithNonHiltActivityWithHiltRoot() throws Exception {
+    FragmentActivity testActivity = Robolectric.setupActivity(FragmentActivity.class);
+    TestView testView = new TestView(testActivity);
+    assertThat(testView.testViewBinding).isNull();
+    assertThat(testView.wasInjectedByHilt()).isFalse();
+    assertThat(wasInjectedByHilt(testView)).isFalse();
+  }
+
+  @Test
+  public void testViewWithFragmentBindingsInjection() throws Exception {
+    TestActivity testActivity = Robolectric.setupActivity(TestActivity.class);
+    TestFragment testFragment = new TestFragment();
+    testActivity.getSupportFragmentManager()
+        .beginTransaction()
+        .add(testFragment, null)
+        .commitNow();
+
+    TestWithFragmentBindingsView testView = new TestWithFragmentBindingsView(
+        testFragment.getLayoutInflater().getContext());
+    assertThat(testView.testViewBinding).isEqualTo(VIEW_BINDING);
+    assertThat(testView.wasInjectedByHilt()).isTrue();
+    assertThat(wasInjectedByHilt(testView)).isTrue();
+  }
+
+  @Test
+  public void testViewWithFragmentBindingsInjectionWithNonHiltFragmentWithHiltRoot()
+      throws Exception {
+    TestActivity testActivity = Robolectric.setupActivity(TestActivity.class);
+    Fragment testFragment = new Fragment();
+    testActivity.getSupportFragmentManager()
+        .beginTransaction()
+        .add(testFragment, null)
+        .commitNow();
+
+    TestWithFragmentBindingsView testView = new TestWithFragmentBindingsView(
+        testFragment.getLayoutInflater().getContext());
+    assertThat(testView.testViewBinding).isNull();
+    assertThat(testView.wasInjectedByHilt()).isFalse();
+    assertThat(wasInjectedByHilt(testView)).isFalse();
+  }
+
+  @Test
+  public void testHiltViewModels() {
+    TestActivity testActivity = Robolectric.setupActivity(TestActivity.class);
+    TestFragment testFragment = new TestFragment();
+    testActivity.getSupportFragmentManager()
+        .beginTransaction()
+        .add(testFragment, null)
+        .commitNow();
+    assertThat(new ViewModelProvider(testActivity).get(TestViewModel.class).appBinding)
+        .isEqualTo(APP_BINDING);
+    assertThat(new ViewModelProvider(testActivity).get(NonHiltViewModel.class)).isNotNull();
+    assertThat(new ViewModelProvider(testFragment).get(TestViewModel.class).appBinding)
+        .isEqualTo(APP_BINDING);
+    assertThat(new ViewModelProvider(testFragment).get(NonHiltViewModel.class)).isNotNull();
+  }
+
+  @Test
+  public void testHiltViewModelsWithNonHiltActivityWithHiltRoot() throws Exception {
+    FragmentActivity testActivity = Robolectric.setupActivity(FragmentActivity.class);
+    TestFragment testFragment = new TestFragment();
+    testActivity.getSupportFragmentManager()
+        .beginTransaction()
+        .add(testFragment, null)
+        .commitNow();
+    assertThat(new ViewModelProvider(testActivity).get(NonHiltViewModel.class)).isNotNull();
+    assertThat(new ViewModelProvider(testFragment).get(NonHiltViewModel.class)).isNotNull();
+
+    // Hilt View Models aren't usable in this case, so check that it throws. We only test with the
+    // owner as the fragment since the activity is just a plain FragmentActivity.
+    RuntimeException exception =
+        assertThrows(
+            RuntimeException.class,
+            () -> new ViewModelProvider(testFragment).get(TestViewModel.class));
+    assertThat(exception)
+        .hasMessageThat()
+        .contains("TestViewModel");
+  }
+
+  @Test
+  public void testServiceInjection() throws Exception {
+    TestService testService = Robolectric.setupService(TestService.class);
+    assertThat(testService.testAppBinding).isEqualTo(APP_BINDING);
+    assertThat(testService.wasInjectedByHilt()).isTrue();
+    assertThat(wasInjectedByHilt(testService)).isTrue();
+  }
+
+  @Test
+  public void testIntentServiceInjection() throws Exception {
+    TestIntentService testIntentService = Robolectric.setupService(TestIntentService.class);
+    assertThat(testIntentService.testAppBinding).isEqualTo(APP_BINDING);
+    assertThat(testIntentService.wasInjectedByHilt()).isTrue();
+    assertThat(wasInjectedByHilt(testIntentService)).isTrue();
+  }
+
+  @Test
+  public void testBroadcastReceiverInjection() throws Exception {
+    TestBroadcastReceiver testBroadcastReceiver = new TestBroadcastReceiver();
+    Intent intent = new Intent();
+    testBroadcastReceiver.onReceive(ApplicationProvider.getApplicationContext(), intent);
+    assertThat(testBroadcastReceiver.testAppBinding).isEqualTo(APP_BINDING);
+    assertThat(testBroadcastReceiver.wasInjectedByHilt()).isTrue();
+    assertThat(wasInjectedByHilt(testBroadcastReceiver)).isTrue();
+  }
+}
diff --git a/javatests/dagger/hilt/android/OptionalInjectWithoutHiltTest.java b/javatests/dagger/hilt/android/OptionalInjectWithoutHiltTest.java
new file mode 100644
index 0000000..fb51648
--- /dev/null
+++ b/javatests/dagger/hilt/android/OptionalInjectWithoutHiltTest.java
@@ -0,0 +1,157 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+import static dagger.hilt.android.migration.OptionalInjectCheck.wasInjectedByHilt;
+import static org.junit.Assert.assertThrows;
+
+import android.content.Intent;
+import android.os.Build;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import dagger.hilt.android.OptionalInjectTestClasses.NonHiltViewModel;
+import dagger.hilt.android.OptionalInjectTestClasses.OptionalSubclassActivity;
+import dagger.hilt.android.OptionalInjectTestClasses.TestActivity;
+import dagger.hilt.android.OptionalInjectTestClasses.TestBroadcastReceiver;
+import dagger.hilt.android.OptionalInjectTestClasses.TestFragment;
+import dagger.hilt.android.OptionalInjectTestClasses.TestIntentService;
+import dagger.hilt.android.OptionalInjectTestClasses.TestService;
+import dagger.hilt.android.OptionalInjectTestClasses.TestView;
+import dagger.hilt.android.OptionalInjectTestClasses.TestViewModel;
+import dagger.hilt.android.OptionalInjectTestClasses.TestWithFragmentBindingsView;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.annotation.Config;
+
+/** Tests that optional inject work without a Hilt root. */
+@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 OptionalInjectWithoutHiltTest {
+  @Test
+  public void testActivityInjection() throws Exception {
+    TestActivity testActivity = Robolectric.setupActivity(TestActivity.class);
+    assertThat(testActivity.testActivityBinding).isNull();
+    assertThat(testActivity.wasInjectedByHilt()).isFalse();
+    assertThat(wasInjectedByHilt(testActivity)).isFalse();
+  }
+
+  @Test
+  public void testOptionalSubclassActivityInjection() throws Exception {
+    OptionalSubclassActivity testActivity = Robolectric.setupActivity(
+        OptionalSubclassActivity.class);
+    assertThat(testActivity.testActivityBinding).isNull();
+    assertThat(testActivity.testActivitySubclassBinding).isNull();
+    assertThat(testActivity.wasInjectedByHilt()).isFalse();
+    assertThat(wasInjectedByHilt(testActivity)).isFalse();
+  }
+
+  @Test
+  public void testFragmentInjection() throws Exception {
+    TestActivity testActivity = Robolectric.setupActivity(TestActivity.class);
+    TestFragment testFragment = new TestFragment();
+    testActivity.getSupportFragmentManager()
+        .beginTransaction()
+        .add(testFragment, null)
+        .commitNow();
+    assertThat(testFragment.testFragmentBinding).isNull();
+    assertThat(testFragment.wasInjectedByHilt()).isFalse();
+    assertThat(wasInjectedByHilt(testFragment)).isFalse();
+  }
+
+  @Test
+  public void testViewInjection() throws Exception {
+    TestActivity testActivity = Robolectric.setupActivity(TestActivity.class);
+    TestView testView = new TestView(testActivity);
+    assertThat(testView.testViewBinding).isNull();
+    assertThat(testView.wasInjectedByHilt()).isFalse();
+    assertThat(wasInjectedByHilt(testView)).isFalse();
+  }
+
+  @Test
+  public void testViewWithFragmentBindingsInjection() throws Exception {
+    TestActivity testActivity = Robolectric.setupActivity(TestActivity.class);
+    TestFragment testFragment = new TestFragment();
+    testActivity.getSupportFragmentManager()
+        .beginTransaction()
+        .add(testFragment, null)
+        .commitNow();
+
+    TestWithFragmentBindingsView testView = new TestWithFragmentBindingsView(
+        testFragment.getLayoutInflater().getContext());
+    assertThat(testView.testViewBinding).isNull();
+    assertThat(testView.wasInjectedByHilt()).isFalse();
+    assertThat(wasInjectedByHilt(testView)).isFalse();
+  }
+
+  @Test
+  public void testViewModels() {
+    TestActivity testActivity = Robolectric.setupActivity(TestActivity.class);
+    TestFragment testFragment = new TestFragment();
+    testActivity.getSupportFragmentManager()
+        .beginTransaction()
+        .add(testFragment, null)
+        .commitNow();
+    assertThat(new ViewModelProvider(testActivity).get(NonHiltViewModel.class)).isNotNull();
+    assertThat(new ViewModelProvider(testFragment).get(NonHiltViewModel.class)).isNotNull();
+
+    // Hilt View Models aren't usable in this case, so check that it throws.
+    RuntimeException activityException =
+        assertThrows(
+            RuntimeException.class,
+            () -> new ViewModelProvider(testFragment).get(TestViewModel.class));
+    assertThat(activityException)
+        .hasMessageThat()
+        .contains("TestViewModel");
+    RuntimeException fragmentException =
+        assertThrows(
+            RuntimeException.class,
+            () -> new ViewModelProvider(testFragment).get(TestViewModel.class));
+    assertThat(fragmentException)
+        .hasMessageThat()
+        .contains("TestViewModel");
+  }
+
+  @Test
+  public void testServiceInjection() throws Exception {
+    TestService testService = Robolectric.setupService(TestService.class);
+    assertThat(testService.testAppBinding).isNull();
+    assertThat(testService.wasInjectedByHilt()).isFalse();
+    assertThat(wasInjectedByHilt(testService)).isFalse();
+  }
+
+  @Test
+  public void testIntentServiceInjection() throws Exception {
+    TestIntentService testIntentService = Robolectric.setupService(TestIntentService.class);
+    assertThat(testIntentService.testAppBinding).isNull();
+    assertThat(testIntentService.wasInjectedByHilt()).isFalse();
+    assertThat(wasInjectedByHilt(testIntentService)).isFalse();
+  }
+
+  @Test
+  public void testBroadcastReceiverInjection() throws Exception {
+    TestBroadcastReceiver testBroadcastReceiver = new TestBroadcastReceiver();
+    Intent intent = new Intent();
+    testBroadcastReceiver.onReceive(ApplicationProvider.getApplicationContext(), intent);
+    assertThat(testBroadcastReceiver.testAppBinding).isNull();
+    assertThat(testBroadcastReceiver.wasInjectedByHilt()).isFalse();
+    assertThat(wasInjectedByHilt(testBroadcastReceiver)).isFalse();
+  }
+}
diff --git a/javatests/dagger/hilt/android/ViewModelScopedTest.java b/javatests/dagger/hilt/android/ViewModelScopedTest.java
index e37e42a..bc82126 100644
--- a/javatests/dagger/hilt/android/ViewModelScopedTest.java
+++ b/javatests/dagger/hilt/android/ViewModelScopedTest.java
@@ -18,13 +18,13 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.lifecycle.ViewModel;
-import androidx.lifecycle.ViewModelProvider;
 import android.os.Build;
 import android.os.Bundle;
 import androidx.annotation.Nullable;
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.ViewModel;
+import androidx.lifecycle.ViewModelProvider;
 import androidx.test.core.app.ActivityScenario;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import dagger.hilt.android.lifecycle.HiltViewModel;
diff --git a/javatests/dagger/hilt/android/ViewModelWithBaseTest.java b/javatests/dagger/hilt/android/ViewModelWithBaseTest.java
index 4e4a47c..292fd16 100644
--- a/javatests/dagger/hilt/android/ViewModelWithBaseTest.java
+++ b/javatests/dagger/hilt/android/ViewModelWithBaseTest.java
@@ -18,13 +18,13 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import androidx.lifecycle.SavedStateHandle;
-import androidx.lifecycle.ViewModel;
-import androidx.lifecycle.ViewModelProvider;
 import android.os.Build;
 import android.os.Bundle;
 import androidx.fragment.app.FragmentActivity;
 import androidx.annotation.Nullable;
+import androidx.lifecycle.SavedStateHandle;
+import androidx.lifecycle.ViewModel;
+import androidx.lifecycle.ViewModelProvider;
 import androidx.test.core.app.ActivityScenario;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import dagger.hilt.android.lifecycle.HiltViewModel;
diff --git a/javatests/dagger/hilt/android/internal/managers/BUILD b/javatests/dagger/hilt/android/internal/managers/BUILD
index ff4f432..b7fe47c 100644
--- a/javatests/dagger/hilt/android/internal/managers/BUILD
+++ b/javatests/dagger/hilt/android/internal/managers/BUILD
@@ -27,10 +27,8 @@
     deps = [
         "//:android_local_test_exports",
         "//:dagger_with_compiler",
-        "@google_bazel_common//third_party/java/jsr330_inject",
         "@maven//:junit_junit",
-        "@google_bazel_common//third_party/java/truth",
-        "//java/dagger/hilt:entry_point",
+        "//third_party/java/truth",
         "//java/dagger/hilt:install_in",
         "//java/dagger/hilt/android:android_entry_point",
         "//java/dagger/hilt/android/lifecycle",
diff --git a/javatests/dagger/hilt/android/internal/managers/FragmentContextWrapperLeakTest.java b/javatests/dagger/hilt/android/internal/managers/FragmentContextWrapperLeakTest.java
index 38086c6..116d2d9 100644
--- a/javatests/dagger/hilt/android/internal/managers/FragmentContextWrapperLeakTest.java
+++ b/javatests/dagger/hilt/android/internal/managers/FragmentContextWrapperLeakTest.java
@@ -19,10 +19,10 @@
 import static com.google.common.truth.Truth.assertThat;
 import static org.junit.Assert.assertThrows;
 
-import androidx.lifecycle.Lifecycle;
 import android.os.Build;
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.Lifecycle;
 import androidx.test.core.app.ActivityScenario;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import dagger.hilt.android.AndroidEntryPoint;
diff --git a/javatests/dagger/hilt/android/processor/internal/BUILD b/javatests/dagger/hilt/android/processor/internal/BUILD
index 9a0a8f2..513173f 100644
--- a/javatests/dagger/hilt/android/processor/internal/BUILD
+++ b/javatests/dagger/hilt/android/processor/internal/BUILD
@@ -28,9 +28,9 @@
         "@maven//:androidx_annotation_annotation",
     ],
     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",
+        "//third_party/java/compile_testing",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
+        "//java/dagger/hilt/android/testing/compile",
     ],
 )
diff --git a/javatests/dagger/hilt/android/processor/internal/GeneratorsTest.java b/javatests/dagger/hilt/android/processor/internal/GeneratorsTest.java
index ecfd1f6..4dfa87c 100644
--- a/javatests/dagger/hilt/android/processor/internal/GeneratorsTest.java
+++ b/javatests/dagger/hilt/android/processor/internal/GeneratorsTest.java
@@ -17,7 +17,7 @@
 package dagger.hilt.android.processor.internal;
 
 import static com.google.testing.compile.CompilationSubject.assertThat;
-import static dagger.hilt.android.processor.AndroidCompilers.compiler;
+import static dagger.hilt.android.testing.compile.HiltCompilerTests.compiler;
 
 import com.google.testing.compile.Compilation;
 import com.google.testing.compile.JavaFileObjects;
@@ -174,7 +174,6 @@
             " public MyView(Context context, AttributeSet attributeSet){",
             "   super(context, attributeSet);",
             " }",
-            "",
             "}");
     Compilation compilation = compiler().compile(myView);
     assertThat(compilation).succeeded();
@@ -247,7 +246,6 @@
                 "",
                 "@Generated(\"dagger.hilt.android.processor.internal.androidentrypoint.FragmentGenerator\")",
                 "@TargetApi(24)",
-                "@SuppressWarnings(\"deprecation\")",
                 "abstract class Hilt_MyFragment extends Fragment implements"
                     + " GeneratedComponentManagerHolder {}"));
   }
@@ -315,4 +313,193 @@
                 "abstract class Hilt_MyService extends Service implements"
                     + " GeneratedComponentManagerHolder{}"));
   }
+
+  @Test
+  public void copySuppressWarningsAnnotationActivity_annotationCopied() {
+    JavaFileObject myActivity =
+        JavaFileObjects.forSourceLines(
+            "test.MyActivity",
+            "package test;",
+            "",
+            "import android.annotation.TargetApi;",
+            "import androidx.fragment.app.FragmentActivity;",
+            "import dagger.hilt.android.AndroidEntryPoint;",
+            "",
+            "@SuppressWarnings(\"deprecation\")",
+            "@AndroidEntryPoint(FragmentActivity.class)",
+            "public class MyActivity extends Hilt_MyActivity {}");
+    Compilation compilation = compiler().compile(myActivity);
+    assertThat(compilation).succeeded();
+    assertThat(compilation)
+        .generatedSourceFile("test/Hilt_MyActivity")
+        .containsElementsIn(
+            JavaFileObjects.forSourceLines(
+                "test.Hilt_MyActivity",
+                " package test;",
+                "",
+                "@Generated(\"dagger.hilt.android.processor.internal.androidentrypoint.ActivityGenerator\")",
+                "@SuppressWarnings(\"deprecation\")",
+                "abstract class Hilt_MyActivity extends FragmentActivity ",
+                "implements GeneratedComponentManagerHolder {",
+                "}"));
+  }
+
+  @Test
+  public void copySuppressWarningsAnnotation_onView_annotationCopied() {
+    JavaFileObject myView =
+        JavaFileObjects.forSourceLines(
+            "test.MyView",
+            "package test;",
+            "",
+            "import android.annotation.TargetApi;",
+            "import android.widget.LinearLayout;",
+            "import android.content.Context;",
+            "import android.util.AttributeSet;",
+            "import dagger.hilt.android.AndroidEntryPoint;",
+            "",
+            "@SuppressWarnings(\"deprecation\")",
+            "@AndroidEntryPoint(LinearLayout.class)",
+            "public class MyView extends Hilt_MyView {",
+            " public MyView(Context context, AttributeSet attributeSet){",
+            "   super(context, attributeSet);",
+            " }",
+            "}");
+    Compilation compilation = compiler().compile(myView);
+    assertThat(compilation).succeeded();
+    assertThat(compilation)
+        .generatedSourceFile("test/Hilt_MyView")
+        .containsElementsIn(
+            JavaFileObjects.forSourceLines(
+                "test.Hilt_MyView",
+                "",
+                "package test;",
+                "",
+                "@Generated(\"dagger.hilt.android.processor.internal.androidentrypoint.ViewGenerator\")",
+                "@SuppressWarnings(\"deprecation\")",
+                "abstract class Hilt_MyView extends LinearLayout implements"
+                    + " GeneratedComponentManagerHolder {",
+                "}"));
+  }
+
+  @Test
+  public void copySuppressWarningsAnnotation_onApplication_annotationCopied() {
+    JavaFileObject myApplication =
+        JavaFileObjects.forSourceLines(
+            "test.MyApplication",
+            "package test;",
+            "",
+            "import android.annotation.TargetApi;",
+            "import android.app.Application;",
+            "import dagger.hilt.android.HiltAndroidApp;",
+            "",
+            "@SuppressWarnings(\"deprecation\")",
+            "@HiltAndroidApp(Application.class)",
+            "public class MyApplication extends Hilt_MyApplication {}");
+    Compilation compilation = compiler().compile(myApplication);
+    assertThat(compilation).succeeded();
+    assertThat(compilation)
+        .generatedSourceFile("test/Hilt_MyApplication")
+        .containsElementsIn(
+            JavaFileObjects.forSourceLines(
+                "test.Hilt_MyApplication",
+                " package test;",
+                "",
+                "@Generated(\"dagger.hilt.android.processor.internal.androidentrypoint.ApplicationGenerator\")",
+                "@SuppressWarnings(\"deprecation\")",
+                "abstract class Hilt_MyApplication extends Application implements"
+                    + " GeneratedComponentManagerHolder {}"));
+  }
+
+  @Test
+  public void copySuppressWarningsAnnotation_onFragment_annotationCopied() {
+    JavaFileObject myApplication =
+        JavaFileObjects.forSourceLines(
+            "test.MyFragment",
+            "package test;",
+            "",
+            "import android.annotation.TargetApi;",
+            "import androidx.fragment.app.Fragment;",
+            "import dagger.hilt.android.AndroidEntryPoint;",
+            "",
+            "@SuppressWarnings(\"rawtypes\")",
+            "@AndroidEntryPoint(Fragment.class)",
+            "public class MyFragment extends Hilt_MyFragment {}");
+    Compilation compilation = compiler().compile(myApplication);
+    assertThat(compilation).succeeded();
+    assertThat(compilation)
+        .generatedSourceFile("test/Hilt_MyFragment")
+        .containsElementsIn(
+            JavaFileObjects.forSourceLines(
+                "test.Hilt_MyFragment",
+                "package test;",
+                "",
+                "@Generated(\"dagger.hilt.android.processor.internal.androidentrypoint.FragmentGenerator\")",
+                "@SuppressWarnings(\"rawtypes\")",
+                "abstract class Hilt_MyFragment extends Fragment implements"
+                    + " GeneratedComponentManagerHolder {}"));
+  }
+
+  @Test
+  public void copySuppressWarnings_onBroadcastRecieverGenerator_annotationCopied() {
+    JavaFileObject myBroadcastReceiver =
+        JavaFileObjects.forSourceLines(
+            "test.MyBroadcastReceiver",
+            "package test;",
+            "",
+            "import android.content.BroadcastReceiver;",
+            "import android.annotation.TargetApi;",
+            "import dagger.hilt.android.AndroidEntryPoint;",
+            "",
+            "@SuppressWarnings(\"deprecation\")",
+            "@AndroidEntryPoint(BroadcastReceiver.class)",
+            "public class MyBroadcastReceiver extends Hilt_MyBroadcastReceiver {}");
+    Compilation compilation = compiler().compile(myBroadcastReceiver);
+    assertThat(compilation).succeeded();
+    assertThat(compilation)
+        .generatedSourceFile("test/Hilt_MyBroadcastReceiver")
+        .containsElementsIn(
+            JavaFileObjects.forSourceLines(
+                "test.Hilt_MyBroadcastReceiver",
+                "package test;",
+                "",
+                "@Generated(\"dagger.hilt.android.processor.internal.androidentrypoint.BroadcastReceiverGenerator\")",
+                "@SuppressWarnings(\"deprecation\")",
+                "abstract class Hilt_MyBroadcastReceiver extends BroadcastReceiver {}"));
+  }
+
+  @Test
+  public void copySuppressWarnings_onServiceGenerator_annotationCopied() {
+    JavaFileObject myService =
+        JavaFileObjects.forSourceLines(
+            "test.MyService",
+            "package test;",
+            "",
+            "import android.annotation.TargetApi;",
+            "import android.content.Intent;",
+            "import android.app.Service;",
+            "import android.os.IBinder;",
+            "import dagger.hilt.android.AndroidEntryPoint;",
+            "",
+            "@SuppressWarnings(\"deprecation\")",
+            "@AndroidEntryPoint(Service.class)",
+            "public class MyService extends Hilt_MyService {",
+            "   @Override",
+            "   public IBinder onBind(Intent intent){",
+            "     return null;",
+            "   }",
+            "}");
+    Compilation compilation = compiler().compile(myService);
+    assertThat(compilation).succeeded();
+    assertThat(compilation)
+        .generatedSourceFile("test/Hilt_MyService")
+        .containsElementsIn(
+            JavaFileObjects.forSourceLines(
+                "test.Hilt_MyService",
+                "package test;",
+                "",
+                "@Generated(\"dagger.hilt.android.processor.internal.androidentrypoint.ServiceGenerator\")",
+                "@SuppressWarnings(\"deprecation\")",
+                "abstract class Hilt_MyService extends Service implements"
+                    + " GeneratedComponentManagerHolder{}"));
+  }
 }
diff --git a/javatests/dagger/hilt/android/processor/internal/aggregateddeps/BUILD b/javatests/dagger/hilt/android/processor/internal/aggregateddeps/BUILD
index 654b573..79a45c4 100644
--- a/javatests/dagger/hilt/android/processor/internal/aggregateddeps/BUILD
+++ b/javatests/dagger/hilt/android/processor/internal/aggregateddeps/BUILD
@@ -33,10 +33,10 @@
         "@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",
+        "//third_party/java/compile_testing",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
+        "//java/dagger/hilt/android/testing/compile",
     ],
 )
 
@@ -52,10 +52,10 @@
         "@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",
+        "//third_party/java/compile_testing",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
+        "//java/dagger/hilt/android/testing/compile",
     ],
 )
 
diff --git a/javatests/dagger/hilt/android/processor/internal/aggregateddeps/EarlyEntryPointProcessorTest.java b/javatests/dagger/hilt/android/processor/internal/aggregateddeps/EarlyEntryPointProcessorTest.java
index 3bf3a31..b39cd6b 100644
--- a/javatests/dagger/hilt/android/processor/internal/aggregateddeps/EarlyEntryPointProcessorTest.java
+++ b/javatests/dagger/hilt/android/processor/internal/aggregateddeps/EarlyEntryPointProcessorTest.java
@@ -17,7 +17,7 @@
 package dagger.hilt.android.processor.internal.aggregateddeps;
 
 import static com.google.testing.compile.CompilationSubject.assertThat;
-import static dagger.hilt.android.processor.AndroidCompilers.compiler;
+import static dagger.hilt.android.testing.compile.HiltCompilerTests.compiler;
 
 import com.google.testing.compile.Compilation;
 import com.google.testing.compile.JavaFileObjects;
diff --git a/javatests/dagger/hilt/android/processor/internal/aggregateddeps/TestInstallInTest.java b/javatests/dagger/hilt/android/processor/internal/aggregateddeps/TestInstallInTest.java
index 3af5be6..e4e9f67 100644
--- a/javatests/dagger/hilt/android/processor/internal/aggregateddeps/TestInstallInTest.java
+++ b/javatests/dagger/hilt/android/processor/internal/aggregateddeps/TestInstallInTest.java
@@ -17,7 +17,7 @@
 package dagger.hilt.android.processor.internal.aggregateddeps;
 
 import static com.google.testing.compile.CompilationSubject.assertThat;
-import static dagger.hilt.android.processor.AndroidCompilers.compiler;
+import static dagger.hilt.android.testing.compile.HiltCompilerTests.compiler;
 
 import com.google.testing.compile.Compilation;
 import com.google.testing.compile.JavaFileObjects;
diff --git a/javatests/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGeneratorTest.java b/javatests/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGeneratorTest.java
index 25a1abd..b6674d4 100644
--- a/javatests/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGeneratorTest.java
+++ b/javatests/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGeneratorTest.java
@@ -17,7 +17,7 @@
 package dagger.hilt.android.processor.internal.androidentrypoint;
 
 import static com.google.testing.compile.CompilationSubject.assertThat;
-import static dagger.hilt.android.processor.AndroidCompilers.compiler;
+import static dagger.hilt.android.testing.compile.HiltCompilerTests.compiler;
 
 import com.google.testing.compile.Compilation;
 import com.google.testing.compile.JavaFileObjects;
diff --git a/javatests/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointProcessorTest.java b/javatests/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointProcessorTest.java
index c024a9c..a5f85a2 100644
--- a/javatests/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointProcessorTest.java
+++ b/javatests/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointProcessorTest.java
@@ -17,7 +17,7 @@
 package dagger.hilt.android.processor.internal.androidentrypoint;
 
 import static com.google.testing.compile.CompilationSubject.assertThat;
-import static dagger.hilt.android.processor.AndroidCompilers.compiler;
+import static dagger.hilt.android.testing.compile.HiltCompilerTests.compiler;
 
 import com.google.testing.compile.Compilation;
 import com.google.testing.compile.JavaFileObjects;
diff --git a/javatests/dagger/hilt/android/processor/internal/androidentrypoint/BUILD b/javatests/dagger/hilt/android/processor/internal/androidentrypoint/BUILD
index e53d9e2..c1f9fd2 100644
--- a/javatests/dagger/hilt/android/processor/internal/androidentrypoint/BUILD
+++ b/javatests/dagger/hilt/android/processor/internal/androidentrypoint/BUILD
@@ -26,10 +26,10 @@
         "@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",
+        "//third_party/java/compile_testing",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
+        "//java/dagger/hilt/android/testing/compile",
     ],
 )
 
@@ -42,10 +42,10 @@
         "@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",
+        "//third_party/java/compile_testing",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
+        "//java/dagger/hilt/android/testing/compile",
     ],
 )
 
@@ -58,10 +58,10 @@
         "@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",
+        "//third_party/java/guava/collect",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
+        "//java/dagger/hilt/android/testing/compile",
         "@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
index 739c87c..42875b1 100644
--- a/javatests/dagger/hilt/android/processor/internal/androidentrypoint/KotlinAndroidEntryPointProcessorTest.java
+++ b/javatests/dagger/hilt/android/processor/internal/androidentrypoint/KotlinAndroidEntryPointProcessorTest.java
@@ -16,7 +16,7 @@
 
 package dagger.hilt.android.processor.internal.androidentrypoint;
 
-import static dagger.hilt.android.processor.AndroidCompilers.kotlinCompiler;
+import static dagger.hilt.android.testing.compile.HiltCompilerTests.kotlinCompiler;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
diff --git a/javatests/dagger/hilt/android/processor/internal/customtestapplication/BUILD b/javatests/dagger/hilt/android/processor/internal/customtestapplication/BUILD
index 6fc39e4..2e564ec 100644
--- a/javatests/dagger/hilt/android/processor/internal/customtestapplication/BUILD
+++ b/javatests/dagger/hilt/android/processor/internal/customtestapplication/BUILD
@@ -28,9 +28,9 @@
         "@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",
+        "//third_party/java/compile_testing",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
+        "//java/dagger/hilt/android/testing/compile",
     ],
 )
diff --git a/javatests/dagger/hilt/android/processor/internal/customtestapplication/CustomTestApplicationProcessorTest.java b/javatests/dagger/hilt/android/processor/internal/customtestapplication/CustomTestApplicationProcessorTest.java
index f0372ab..beadecf 100644
--- a/javatests/dagger/hilt/android/processor/internal/customtestapplication/CustomTestApplicationProcessorTest.java
+++ b/javatests/dagger/hilt/android/processor/internal/customtestapplication/CustomTestApplicationProcessorTest.java
@@ -17,7 +17,7 @@
 package dagger.hilt.android.processor.internal.customtestapplication;
 
 import static com.google.testing.compile.CompilationSubject.assertThat;
-import static dagger.hilt.android.processor.AndroidCompilers.compiler;
+import static dagger.hilt.android.testing.compile.HiltCompilerTests.compiler;
 
 import com.google.testing.compile.Compilation;
 import com.google.testing.compile.JavaFileObjects;
diff --git a/javatests/dagger/hilt/android/processor/internal/viewmodel/BUILD b/javatests/dagger/hilt/android/processor/internal/viewmodel/BUILD
index 030efd9..86342bd 100644
--- a/javatests/dagger/hilt/android/processor/internal/viewmodel/BUILD
+++ b/javatests/dagger/hilt/android/processor/internal/viewmodel/BUILD
@@ -25,9 +25,9 @@
     runtime_deps = [
         ":ViewModelProcessorTestLib",
         "//java/dagger/hilt/android/lifecycle",
+        "//third_party/java/compile_testing",
+        "//third_party/java/truth",
         "@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",
     ],
@@ -41,9 +41,9 @@
     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",
+        "//third_party/java/compile_testing",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
 )
 
@@ -52,9 +52,9 @@
     runtime_deps = [
         ":ViewModelGeneratorTestLib",
         "//java/dagger/hilt/android/lifecycle",
+        "//third_party/java/compile_testing",
+        "//third_party/java/truth",
         "@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",
     ],
@@ -68,9 +68,9 @@
     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",
+        "//third_party/java/compile_testing",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
 )
 
@@ -83,8 +83,8 @@
         "@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",
+        "//third_party/java/compile_testing",
+        "//third_party/java/truth",
         "//java/dagger/hilt/android/lifecycle",
         "//java/dagger/hilt/android:android_entry_point",
         "//java/dagger/hilt/android:hilt_android_app",
@@ -94,10 +94,10 @@
         "//: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",
+        "//java/dagger/hilt/android/testing/compile",
+        "//third_party/java/compile_testing",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
 )
 
@@ -107,6 +107,6 @@
         "TestUtils.kt",
     ],
     deps = [
-        "@google_bazel_common//third_party/java/compile_testing",
+        "//third_party/java/compile_testing",
     ],
 )
diff --git a/javatests/dagger/hilt/android/processor/internal/viewmodel/ViewModelGeneratorTest.kt b/javatests/dagger/hilt/android/processor/internal/viewmodel/ViewModelGeneratorTest.kt
index df020ff..c2378e0 100644
--- a/javatests/dagger/hilt/android/processor/internal/viewmodel/ViewModelGeneratorTest.kt
+++ b/javatests/dagger/hilt/android/processor/internal/viewmodel/ViewModelGeneratorTest.kt
@@ -46,21 +46,6 @@
     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
@@ -102,7 +87,7 @@
     assertThat(compilation).apply {
       succeeded()
       generatedSourceFile("dagger.hilt.android.test.MyViewModel_HiltModules")
-        .hasSourceEquivalentTo(expected)
+        .containsElementsIn(expected)
     }
   }
 
@@ -126,21 +111,6 @@
     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
@@ -182,7 +152,7 @@
     assertThat(compilation).apply {
       succeeded()
       generatedSourceFile("dagger.hilt.android.test.MyViewModel_HiltModules")
-        .hasSourceEquivalentTo(expected)
+        .containsElementsIn(expected)
     }
   }
 
@@ -213,21 +183,6 @@
     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
@@ -269,7 +224,7 @@
     assertThat(compilation).apply {
       succeeded()
       generatedSourceFile("dagger.hilt.android.test.MyViewModel_HiltModules")
-        .hasSourceEquivalentTo(expected)
+        .containsElementsIn(expected)
     }
   }
 
@@ -301,21 +256,6 @@
     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
@@ -357,7 +297,7 @@
     assertThat(compilation).apply {
       succeeded()
       generatedSourceFile("dagger.hilt.android.test.MyViewModel_HiltModules")
-        .hasSourceEquivalentTo(expected)
+        .containsElementsIn(expected)
     }
   }
 
@@ -396,21 +336,6 @@
     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
@@ -452,7 +377,7 @@
     assertThat(compilation).apply {
       succeeded()
       generatedSourceFile("dagger.hilt.android.test.MyViewModel_HiltModules")
-        .hasSourceEquivalentTo(expected)
+        .containsElementsIn(expected)
     }
   }
 
@@ -477,21 +402,6 @@
     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
@@ -533,7 +443,7 @@
     assertThat(compilation).apply {
       succeeded()
       generatedSourceFile("dagger.hilt.android.test.Outer_InnerViewModel_HiltModules")
-        .hasSourceEquivalentTo(expectedModule)
+        .containsElementsIn(expectedModule)
     }
   }
 }
diff --git a/javatests/dagger/hilt/android/processor/internal/viewmodel/ViewModelValidationPluginTest.kt b/javatests/dagger/hilt/android/processor/internal/viewmodel/ViewModelValidationPluginTest.kt
index b5c22c1..5fe40dd 100644
--- a/javatests/dagger/hilt/android/processor/internal/viewmodel/ViewModelValidationPluginTest.kt
+++ b/javatests/dagger/hilt/android/processor/internal/viewmodel/ViewModelValidationPluginTest.kt
@@ -18,7 +18,7 @@
 
 import com.google.testing.compile.CompilationSubject.assertThat
 import com.google.testing.compile.Compiler
-import dagger.hilt.android.processor.AndroidCompilers.compiler
+import dagger.hilt.android.testing.compile.HiltCompilerTests.compiler
 import dagger.internal.codegen.ComponentProcessor
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/javatests/dagger/hilt/android/testing/BUILD b/javatests/dagger/hilt/android/testing/BUILD
index e336777..4b3cac9 100644
--- a/javatests/dagger/hilt/android/testing/BUILD
+++ b/javatests/dagger/hilt/android/testing/BUILD
@@ -25,8 +25,8 @@
     },
     deps = [
         "//:android_local_test_exports",
-        "@google_bazel_common//third_party/java/jsr330_inject",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/truth",
         "//java/dagger/hilt:entry_point",
         "//java/dagger/hilt/android:android_entry_point",
         "//java/dagger/hilt/android/testing:bind_value",
@@ -44,10 +44,10 @@
     },
     deps = [
         "//:android_local_test_exports",
-        "@google_bazel_common//third_party/java/auto:value",
+        "//third_party/java/auto:value",
         "//:dagger_with_compiler",
-        "@google_bazel_common//third_party/java/jsr330_inject",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/truth",
         "//java/dagger/hilt:entry_point",
         "//java/dagger/hilt/android:android_entry_point",
         "//java/dagger/hilt/android/testing:bind_value",
@@ -66,8 +66,8 @@
     deps = [
         "//:android_local_test_exports",
         "//:dagger_with_compiler",
-        "@google_bazel_common//third_party/java/jsr330_inject",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/truth",
         "//java/dagger/hilt:entry_point",
         "//java/dagger/hilt/android:android_entry_point",
         "//java/dagger/hilt/android/testing:bind_value",
@@ -85,10 +85,10 @@
     },
     deps = [
         "//:android_local_test_exports",
-        "//java/dagger/internal/guava:collect",
+        "//third_party/java/guava/collect",
         "//:dagger_with_compiler",
-        "@google_bazel_common//third_party/java/jsr330_inject",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/truth",
         "//java/dagger/hilt:entry_point",
         "//java/dagger/hilt/android:android_entry_point",
         "//java/dagger/hilt/android/testing:bind_value",
@@ -107,7 +107,7 @@
     deps = [
         "//:android_local_test_exports",
         "//:dagger_with_compiler",
-        "@google_bazel_common//third_party/java/jsr330_inject",
+        "//third_party/java/jsr330_inject",
         "//java/dagger/hilt:install_in",
         "//java/dagger/hilt/android:android_entry_point",
         "//java/dagger/hilt/android/testing:hilt_android_test",
@@ -125,11 +125,9 @@
     deps = [
         ":HiltAndroidRuleTestApp",
         "//:android_local_test_exports",
-        "//java/dagger/internal/guava:collect",
+        "//third_party/java/guava/collect",
         "//:dagger_with_compiler",
-        "@google_bazel_common//third_party/java/jsr330_inject",
-        "@google_bazel_common//third_party/java/truth",
-        "//java/dagger/hilt/android/qualifiers",
+        "//third_party/java/truth",
         "//java/dagger/hilt/android/testing:hilt_android_rule",
         "//java/dagger/hilt/android/testing:hilt_android_test",
     ],
@@ -152,8 +150,7 @@
     },
     deps = [
         "//:android_local_test_exports",
-        "@google_bazel_common//third_party/java/jsr330_inject",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/truth",
         "//java/dagger/hilt:entry_point",
         "//java/dagger/hilt/android:android_entry_point",
         "//java/dagger/hilt/android/testing:bind_value",
diff --git a/javatests/dagger/hilt/android/testing/HiltAndroidRuleTestApp.java b/javatests/dagger/hilt/android/testing/HiltAndroidRuleTestApp.java
index 9ec7d08..9ec5d75 100644
--- a/javatests/dagger/hilt/android/testing/HiltAndroidRuleTestApp.java
+++ b/javatests/dagger/hilt/android/testing/HiltAndroidRuleTestApp.java
@@ -21,4 +21,4 @@
 
 /** A Hilt application used to test errors in {@link HiltAndroidRuleTest}. */
 @HiltAndroidApp(Application.class)
-final class HiltAndroidRuleTestApp extends Hilt_HiltAndroidRuleTestApp {}
+public final class HiltAndroidRuleTestApp extends Hilt_HiltAndroidRuleTestApp {}
diff --git a/javatests/dagger/hilt/android/testing/testinstallin/BUILD b/javatests/dagger/hilt/android/testing/testinstallin/BUILD
index 63d0bca..67f3c35 100644
--- a/javatests/dagger/hilt/android/testing/testinstallin/BUILD
+++ b/javatests/dagger/hilt/android/testing/testinstallin/BUILD
@@ -33,7 +33,7 @@
                         "@maven//:androidx_test_ext_junit",
                         "@maven//:androidx_test_core",
                         
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/truth",
         "//java/dagger/hilt/android/testing:hilt_android_test",
     ],
 )
@@ -55,7 +55,7 @@
                         "@maven//:androidx_test_ext_junit",
                         "@maven//:androidx_test_core",
                         
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/truth",
         "//java/dagger/hilt/android/testing:hilt_android_test",
         "//java/dagger/hilt/android/testing:uninstall_modules",
         "//java/dagger/hilt/testing:test_install_in",
@@ -79,7 +79,7 @@
                         "@maven//:androidx_test_ext_junit",
                         "@maven//:androidx_test_core",
                         
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/truth",
     ],
 )
 
diff --git a/javatests/dagger/hilt/processor/internal/BUILD b/javatests/dagger/hilt/processor/internal/BUILD
index e6e1cfb..3dfe1ea 100644
--- a/javatests/dagger/hilt/processor/internal/BUILD
+++ b/javatests/dagger/hilt/processor/internal/BUILD
@@ -23,9 +23,9 @@
     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",
+        "//third_party/java/javapoet",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
 )
 
diff --git a/javatests/dagger/hilt/processor/internal/aggregateddeps/BUILD b/javatests/dagger/hilt/processor/internal/aggregateddeps/BUILD
index f52b051..180bba1 100644
--- a/javatests/dagger/hilt/processor/internal/aggregateddeps/BUILD
+++ b/javatests/dagger/hilt/processor/internal/aggregateddeps/BUILD
@@ -27,7 +27,7 @@
         "//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",
+        "//third_party/java/jsr250_annotations",
         "//java/dagger/hilt:entry_point",
         "//java/dagger/hilt:install_in",
         "//java/dagger/hilt/android/testing:hilt_android_test",
@@ -35,11 +35,11 @@
     ],
     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",
+        "//third_party/java/compile_testing",
+        "//third_party/java/guava/base",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
 )
 
diff --git a/javatests/dagger/hilt/processor/internal/aliasof/AliasOfProcessorTest.java b/javatests/dagger/hilt/processor/internal/aliasof/AliasOfProcessorTest.java
new file mode 100644
index 0000000..f04dc76
--- /dev/null
+++ b/javatests/dagger/hilt/processor/internal/aliasof/AliasOfProcessorTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.processor.internal.aliasof;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.hilt.android.testing.compile.HiltCompilerTests.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;
+
+/** Tests for failure on alias scope used on DefineComponent. */
+@RunWith(JUnit4.class)
+public final class AliasOfProcessorTest {
+  @Test
+  public void fails_componentScopedWithAliasScope() {
+    JavaFileObject scope =
+        JavaFileObjects.forSourceLines(
+            "test.AliasScope",
+            "package test;",
+            "",
+            "import javax.inject.Scope;",
+            "import javax.inject.Singleton;",
+            "import dagger.hilt.migration.AliasOf;",
+            "",
+            "@Scope",
+            "@AliasOf(Singleton.class)",
+            "public @interface AliasScope{}");
+
+    JavaFileObject root =
+        JavaFileObjects.forSourceLines(
+            "test.MyApp",
+            "package test;",
+            "",
+            "import android.app.Application;",
+            "import dagger.hilt.android.HiltAndroidApp;",
+            "",
+            "@HiltAndroidApp(Application.class)",
+            "public final class MyApp extends Hilt_MyApp {}");
+
+    JavaFileObject defineComponent =
+        JavaFileObjects.forSourceLines(
+            "test.ChildComponent",
+            "package test;",
+            "",
+            "import dagger.hilt.DefineComponent;",
+            "import dagger.hilt.components.SingletonComponent;",
+            "",
+            "@DefineComponent(parent = SingletonComponent.class)",
+            "@AliasScope",
+            "public interface ChildComponent {}");
+
+    Compilation compilation =
+        compiler()
+            .withOptions("-Xlint:-processing") // Suppresses unclaimed annotation warning
+            .compile(root, defineComponent, scope);
+
+    assertThat(compilation).failed();
+    // One extra error for the missing Hilt_MyApp reference
+    assertThat(compilation).hadErrorCount(2);
+    assertThat(compilation)
+        .hadErrorContaining(
+            "@DefineComponent test.ChildComponent, references invalid scope(s) annotated with"
+                + " @AliasOf. @DefineComponent scopes cannot be aliases of other scopes:"
+                + " [@test.AliasScope]");
+  }
+
+  @Test
+  public void fails_conflictingAliasScope() {
+    JavaFileObject scope =
+        JavaFileObjects.forSourceLines(
+            "test.AliasScope",
+            "package test;",
+            "",
+            "import javax.inject.Scope;",
+            "import javax.inject.Singleton;",
+            "import dagger.hilt.android.scopes.ActivityScoped;",
+            "import dagger.hilt.migration.AliasOf;",
+            "",
+            "@Scope",
+            "@AliasOf({Singleton.class, ActivityScoped.class})",
+            "public @interface AliasScope{}");
+
+    JavaFileObject root =
+        JavaFileObjects.forSourceLines(
+            "test.MyApp",
+            "package test;",
+            "",
+            "import android.app.Application;",
+            "import dagger.hilt.android.HiltAndroidApp;",
+            "",
+            "@HiltAndroidApp(Application.class)",
+            "public final class MyApp extends Hilt_MyApp {}");
+
+    Compilation compilation =
+        compiler()
+            .withOptions("-Xlint:-processing") // Suppresses unclaimed annotation warning
+            .compile(root, scope);
+
+    assertThat(compilation).failed();
+    assertThat(compilation).hadErrorCount(1);
+    assertThat(compilation).hadErrorContaining("has conflicting scopes");
+  }
+}
diff --git a/javatests/dagger/hilt/processor/internal/aliasof/BUILD b/javatests/dagger/hilt/processor/internal/aliasof/BUILD
new file mode 100644
index 0000000..3a7b4af
--- /dev/null
+++ b/javatests/dagger/hilt/processor/internal/aliasof/BUILD
@@ -0,0 +1,40 @@
+# 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:
+#  Builds and run tests related to AliasOfProcessor.
+
+load("//java/dagger/testing/compile:macros.bzl", "compiler_test")
+
+package(default_visibility = ["//:src"])
+
+compiler_test(
+    name = "AliasOfProcessorTest",
+    size = "small",
+    srcs = ["AliasOfProcessorTest.java"],
+    compiler_deps = [
+        "@androidsdk//:platforms/android-30/android.jar",
+        "//third_party/java/jsr330_inject",
+        "//java/dagger/hilt/android:hilt_android_app",
+        "//java/dagger/hilt/android/components",
+        "//java/dagger/hilt:define_component",
+        "//java/dagger/hilt/migration:alias_of",
+    ],
+    deps = [
+        "//third_party/java/compile_testing",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
+        "//java/dagger/hilt/android/testing/compile",
+    ],
+)
diff --git a/javatests/dagger/hilt/processor/internal/definecomponent/BUILD b/javatests/dagger/hilt/processor/internal/definecomponent/BUILD
index ce5cc0a..9de94ba 100644
--- a/javatests/dagger/hilt/processor/internal/definecomponent/BUILD
+++ b/javatests/dagger/hilt/processor/internal/definecomponent/BUILD
@@ -34,9 +34,9 @@
         "//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",
+        "//third_party/java/compile_testing",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
 )
 
diff --git a/javatests/dagger/hilt/processor/internal/definecomponent/DefineComponentProcessorTest.java b/javatests/dagger/hilt/processor/internal/definecomponent/DefineComponentProcessorTest.java
index a8c5fbe..373061c 100644
--- a/javatests/dagger/hilt/processor/internal/definecomponent/DefineComponentProcessorTest.java
+++ b/javatests/dagger/hilt/processor/internal/definecomponent/DefineComponentProcessorTest.java
@@ -22,7 +22,6 @@
 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;
@@ -67,9 +66,6 @@
             "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() + "\")",
             "public class _test_FooComponent {}");
@@ -79,9 +75,6 @@
             "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() + "\")",
             "public class _test_FooComponentBuilder {}");
@@ -90,10 +83,10 @@
     assertThat(compilation).succeeded();
     assertThat(compilation)
         .generatedSourceFile(sourceName(componentOutput))
-        .hasSourceEquivalentTo(componentOutput);
+        .containsElementsIn(componentOutput);
     assertThat(compilation)
         .generatedSourceFile(sourceName(builderOutput))
-        .hasSourceEquivalentTo(builderOutput);
+        .containsElementsIn(builderOutput);
   }
 
   private static String sourceName(JavaFileObject fileObject) {
diff --git a/javatests/dagger/hilt/processor/internal/disableinstallincheck/BUILD b/javatests/dagger/hilt/processor/internal/disableinstallincheck/BUILD
index 90b0596..11252a6 100644
--- a/javatests/dagger/hilt/processor/internal/disableinstallincheck/BUILD
+++ b/javatests/dagger/hilt/processor/internal/disableinstallincheck/BUILD
@@ -26,14 +26,14 @@
     compiler_deps = [
         "//java/dagger/hilt/migration:disable_install_in_check",
         "//:dagger_with_compiler",
-        "@google_bazel_common//third_party/java/jsr250_annotations",
+        "//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",
+        "//third_party/java/compile_testing",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
 )
 
diff --git a/javatests/dagger/hilt/processor/internal/generatesrootinput/BUILD b/javatests/dagger/hilt/processor/internal/generatesrootinput/BUILD
index c5dacd9..a815ecd 100644
--- a/javatests/dagger/hilt/processor/internal/generatesrootinput/BUILD
+++ b/javatests/dagger/hilt/processor/internal/generatesrootinput/BUILD
@@ -31,11 +31,11 @@
         "//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",
+        "//third_party/java/auto:common",
+        "//third_party/java/compile_testing",
+        "//third_party/java/javapoet",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
 )
 
diff --git a/javatests/dagger/hilt/processor/internal/generatesrootinput/GeneratesRootInputProcessorTest.java b/javatests/dagger/hilt/processor/internal/generatesrootinput/GeneratesRootInputProcessorTest.java
index fb62c3d..48818d4 100644
--- a/javatests/dagger/hilt/processor/internal/generatesrootinput/GeneratesRootInputProcessorTest.java
+++ b/javatests/dagger/hilt/processor/internal/generatesrootinput/GeneratesRootInputProcessorTest.java
@@ -74,7 +74,7 @@
   }
 
   @Test
-  public void succeeds_ComponentProcessorWaitsForAnnotationsWithgeneratesstinginput() {
+  public void succeeds_ComponentProcessorWaitsForAnnotationsWithGeneratesRootInput() {
     JavaFileObject testAnnotation =
         JavaFileObjects.forSourceLines(
             "test.TestAnnotation",
diff --git a/javatests/dagger/hilt/processor/internal/originatingelement/BUILD b/javatests/dagger/hilt/processor/internal/originatingelement/BUILD
index 3135159..c45cfb6 100644
--- a/javatests/dagger/hilt/processor/internal/originatingelement/BUILD
+++ b/javatests/dagger/hilt/processor/internal/originatingelement/BUILD
@@ -28,10 +28,10 @@
         "@maven//:androidx_annotation_annotation",
     ],
     deps = [
-        "//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",
+        "//java/dagger/hilt/android/testing/compile",
+        "//third_party/java/compile_testing",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
 )
 
diff --git a/javatests/dagger/hilt/processor/internal/originatingelement/OriginatingElementProcessorTest.java b/javatests/dagger/hilt/processor/internal/originatingelement/OriginatingElementProcessorTest.java
index 444fb1d..5dc7a11 100644
--- a/javatests/dagger/hilt/processor/internal/originatingelement/OriginatingElementProcessorTest.java
+++ b/javatests/dagger/hilt/processor/internal/originatingelement/OriginatingElementProcessorTest.java
@@ -17,7 +17,7 @@
 package dagger.hilt.processor.internal.originatingelement;
 
 import static com.google.testing.compile.CompilationSubject.assertThat;
-import static dagger.hilt.android.processor.AndroidCompilers.compiler;
+import static dagger.hilt.android.testing.compile.HiltCompilerTests.compiler;
 
 import com.google.testing.compile.Compilation;
 import com.google.testing.compile.JavaFileObjects;
diff --git a/javatests/dagger/hilt/processor/internal/root/BUILD b/javatests/dagger/hilt/processor/internal/root/BUILD
index 0dba875..ef2344c 100644
--- a/javatests/dagger/hilt/processor/internal/root/BUILD
+++ b/javatests/dagger/hilt/processor/internal/root/BUILD
@@ -40,11 +40,11 @@
         "@maven//:androidx_test_core",
     ],
     deps = [
-        "//java/dagger/internal/guava:collect",
-        "//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",
+        "//java/dagger/hilt/android/testing/compile",
+        "//third_party/java/compile_testing",
+        "//third_party/java/guava/collect",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
 )
 
@@ -71,11 +71,11 @@
         "@maven//:androidx_test_core",
     ],
     deps = [
-        "//java/dagger/internal/guava:collect",
-        "//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",
+        "//java/dagger/hilt/android/testing/compile",
+        "//third_party/java/compile_testing",
+        "//third_party/java/guava/collect",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
 )
 
@@ -92,11 +92,11 @@
         "@maven//:androidx_test_core",
     ],
     deps = [
-        "//java/dagger/internal/guava:collect",
-        "//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",
+        "//java/dagger/hilt/android/testing/compile",
+        "//third_party/java/compile_testing",
+        "//third_party/java/guava/collect",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
 )
 
@@ -113,11 +113,11 @@
         "@maven//:androidx_test_core",
     ],
     deps = [
-        "//java/dagger/internal/guava:base",
-        "//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",
+        "//java/dagger/hilt/android/testing/compile",
+        "//third_party/java/compile_testing",
+        "//third_party/java/guava/base",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
 )
 
diff --git a/javatests/dagger/hilt/processor/internal/root/MyAppPreviousCompilationTest.java b/javatests/dagger/hilt/processor/internal/root/MyAppPreviousCompilationTest.java
index adf6d9b..e4aa5ab 100644
--- a/javatests/dagger/hilt/processor/internal/root/MyAppPreviousCompilationTest.java
+++ b/javatests/dagger/hilt/processor/internal/root/MyAppPreviousCompilationTest.java
@@ -23,7 +23,7 @@
 import com.google.testing.compile.Compilation;
 import com.google.testing.compile.Compiler;
 import com.google.testing.compile.JavaFileObjects;
-import dagger.hilt.android.processor.AndroidCompilers;
+import dagger.hilt.android.testing.compile.HiltCompilerTests;
 import javax.tools.JavaFileObject;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -45,7 +45,7 @@
   }
 
   private Compiler compiler() {
-    return AndroidCompilers.compiler()
+    return HiltCompilerTests.compiler()
         .withOptions(
             String.format(
                 "-Adagger.hilt.disableCrossCompilationRootValidation=%s",
@@ -90,11 +90,11 @@
       assertThat(compilation).hadErrorCount(1);
       assertThat(compilation)
           .hadErrorContaining(
-              "Cannot process app roots in this compilation unit since there are app roots in a "
+              "Cannot process new app roots when there are app roots from a "
                   + "previous compilation unit:"
-                  + "\n  \tApp roots in previous compilation unit: ["
-                  + "dagger.hilt.processor.internal.root.MyAppPreviousCompilation.MyApp]"
-                  + "\n  \tApp roots in this compilation unit: [test.AppRoot]");
+                  + "\n    App roots in previous compilation unit: "
+                  + "dagger.hilt.processor.internal.root.MyAppPreviousCompilation.MyApp"
+                  + "\n    App roots in this compilation unit: test.AppRoot");
     }
   }
 }
diff --git a/javatests/dagger/hilt/processor/internal/root/MyTestPreviousCompilationTest.java b/javatests/dagger/hilt/processor/internal/root/MyTestPreviousCompilationTest.java
index 9e9fae5..82251bd 100644
--- a/javatests/dagger/hilt/processor/internal/root/MyTestPreviousCompilationTest.java
+++ b/javatests/dagger/hilt/processor/internal/root/MyTestPreviousCompilationTest.java
@@ -23,7 +23,7 @@
 import com.google.testing.compile.Compilation;
 import com.google.testing.compile.Compiler;
 import com.google.testing.compile.JavaFileObjects;
-import dagger.hilt.android.processor.AndroidCompilers;
+import dagger.hilt.android.testing.compile.HiltCompilerTests;
 import javax.tools.JavaFileObject;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -45,7 +45,7 @@
   }
 
   private Compiler compiler() {
-    return AndroidCompilers.compiler()
+    return HiltCompilerTests.compiler()
         .withOptions(
             String.format(
                 "-Adagger.hilt.disableCrossCompilationRootValidation=%s",
@@ -73,9 +73,9 @@
       assertThat(compilation)
           .hadErrorContaining(
               "Cannot process new roots when there are test roots from a previous compilation unit:"
-                  + "\n  \tTest roots from previous compilation unit: "
-                  + "[dagger.hilt.processor.internal.root.MyTestPreviousCompilation.MyTest]"
-                  + "\n  \tAll roots from this compilation unit: [test.TestRoot]");
+                  + "\n    Test roots from previous compilation unit: "
+                  + "dagger.hilt.processor.internal.root.MyTestPreviousCompilation.MyTest"
+                  + "\n    All roots from this compilation unit: test.TestRoot");
     }
   }
 
@@ -101,9 +101,9 @@
       assertThat(compilation)
           .hadErrorContaining(
               "Cannot process new roots when there are test roots from a previous compilation unit:"
-                  + "\n  \tTest roots from previous compilation unit: "
-                  + "[dagger.hilt.processor.internal.root.MyTestPreviousCompilation.MyTest]"
-                  + "\n  \tAll roots from this compilation unit: [test.AppRoot]");
+                  + "\n    Test roots from previous compilation unit: "
+                  + "dagger.hilt.processor.internal.root.MyTestPreviousCompilation.MyTest"
+                  + "\n    All roots from this compilation unit: test.AppRoot");
     }
   }
 }
diff --git a/javatests/dagger/hilt/processor/internal/root/RootFileFormatterTest.java b/javatests/dagger/hilt/processor/internal/root/RootFileFormatterTest.java
index 6d598c8..53ef42c 100644
--- a/javatests/dagger/hilt/processor/internal/root/RootFileFormatterTest.java
+++ b/javatests/dagger/hilt/processor/internal/root/RootFileFormatterTest.java
@@ -17,7 +17,7 @@
 package dagger.hilt.processor.internal.root;
 
 import static com.google.testing.compile.CompilationSubject.assertThat;
-import static dagger.hilt.android.processor.AndroidCompilers.compiler;
+import static dagger.hilt.android.testing.compile.HiltCompilerTests.compiler;
 
 import com.google.common.base.Joiner;
 import com.google.testing.compile.Compilation;
@@ -83,6 +83,7 @@
   public void testTestComponents() {
     Compilation compilation =
         compiler()
+            .withOptions("-Adagger.hilt.shareTestComponents=false")
             .compile(
                 JavaFileObjects.forSourceLines(
                     "test.MyTest",
diff --git a/javatests/dagger/hilt/processor/internal/root/RootProcessorErrorsTest.java b/javatests/dagger/hilt/processor/internal/root/RootProcessorErrorsTest.java
index e9ed8c4..c07ee53 100644
--- a/javatests/dagger/hilt/processor/internal/root/RootProcessorErrorsTest.java
+++ b/javatests/dagger/hilt/processor/internal/root/RootProcessorErrorsTest.java
@@ -23,7 +23,7 @@
 import com.google.testing.compile.Compilation;
 import com.google.testing.compile.Compiler;
 import com.google.testing.compile.JavaFileObjects;
-import dagger.hilt.android.processor.AndroidCompilers;
+import dagger.hilt.android.testing.compile.HiltCompilerTests;
 import javax.tools.JavaFileObject;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -45,7 +45,7 @@
   }
 
   private Compiler compiler() {
-    return AndroidCompilers.compiler()
+    return HiltCompilerTests.compiler()
         .withOptions(
             String.format(
                 "-Adagger.hilt.disableCrossCompilationRootValidation=%s",
@@ -83,7 +83,7 @@
     assertThat(compilation)
         .hadErrorContaining(
             "Cannot process multiple app roots in the same compilation unit: "
-                + "[test.AppRoot1, test.AppRoot2]");
+                + "test.AppRoot1, test.AppRoot2");
   }
 
   @Test
@@ -116,7 +116,7 @@
     assertThat(compilation)
         .hadErrorContaining(
             "Cannot process test roots and app roots in the same compilation unit:"
-                + "\n  \tApp root in this compilation unit: [test.AppRoot]"
-                + "\n  \tTest roots in this compilation unit: [test.TestRoot]");
+                + "\n    App root in this compilation unit: test.AppRoot"
+                + "\n    Test roots in this compilation unit: test.TestRoot");
   }
 }
diff --git a/javatests/dagger/hilt/processor/internal/uninstallmodules/BUILD b/javatests/dagger/hilt/processor/internal/uninstallmodules/BUILD
index caceb7c..e407193 100644
--- a/javatests/dagger/hilt/processor/internal/uninstallmodules/BUILD
+++ b/javatests/dagger/hilt/processor/internal/uninstallmodules/BUILD
@@ -31,10 +31,10 @@
         "@maven//:androidx_annotation_annotation",
     ],
     deps = [
-        "//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",
+        "//java/dagger/hilt/android/testing/compile",
+        "//third_party/java/compile_testing",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
 )
 
diff --git a/javatests/dagger/hilt/processor/internal/uninstallmodules/UninstallModulesProcessorTest.java b/javatests/dagger/hilt/processor/internal/uninstallmodules/UninstallModulesProcessorTest.java
index 2be9ab6..04b5a24 100644
--- a/javatests/dagger/hilt/processor/internal/uninstallmodules/UninstallModulesProcessorTest.java
+++ b/javatests/dagger/hilt/processor/internal/uninstallmodules/UninstallModulesProcessorTest.java
@@ -17,7 +17,7 @@
 package dagger.hilt.processor.internal.uninstallmodules;
 
 import static com.google.testing.compile.CompilationSubject.assertThat;
-import static dagger.hilt.android.processor.AndroidCompilers.compiler;
+import static dagger.hilt.android.testing.compile.HiltCompilerTests.compiler;
 
 import com.google.testing.compile.Compilation;
 import com.google.testing.compile.JavaFileObjects;
diff --git a/javatests/dagger/hilt/testmodules/BUILD b/javatests/dagger/hilt/testmodules/BUILD
index 7acd3d1..f7aef80 100644
--- a/javatests/dagger/hilt/testmodules/BUILD
+++ b/javatests/dagger/hilt/testmodules/BUILD
@@ -27,7 +27,7 @@
         "//:dagger_with_compiler",
         "//java/dagger/hilt:install_in",
         "//java/dagger/hilt/components",
-        "@google_bazel_common//third_party/java/jsr330_inject",
+        "//third_party/java/jsr330_inject",
     ],
 )
 
diff --git a/javatests/dagger/internal/codegen/AssistedFactoryErrorsTest.java b/javatests/dagger/internal/codegen/AssistedFactoryErrorsTest.java
index 42b8f8a..09f9fab 100644
--- a/javatests/dagger/internal/codegen/AssistedFactoryErrorsTest.java
+++ b/javatests/dagger/internal/codegen/AssistedFactoryErrorsTest.java
@@ -620,7 +620,7 @@
   }
 
   @Test
-  public void testInjectsProviderOfAssistedFactory() {
+  public void testInjectsLazyOfAssistedFactory() {
     JavaFileObject foo =
         JavaFileObjects.forSourceLines(
             "test.Foo",
@@ -644,12 +644,12 @@
             "test.Bar",
             "package test;",
             "",
+            "import dagger.Lazy;",
             "import javax.inject.Inject;",
-            "import javax.inject.Provider;",
             "",
             "class Bar {",
             "  @Inject",
-            "  Bar(Foo.Factory fooFactory, Provider<Foo.Factory> fooFactoryProvider) {}",
+            "  Bar(Foo.Factory fooFactory, Lazy<Foo.Factory> fooFactoryLazy) {}",
             "}");
 
     Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(foo, bar);
@@ -657,7 +657,7 @@
     assertThat(compilation).hadErrorCount(1);
     assertThat(compilation)
         .hadErrorContaining(
-            "Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, or Produced<T> "
+            "Dagger does not support injecting Lazy<T>, Producer<T>, or Produced<T> "
                 + "when T is an @AssistedFactory-annotated type such as test.Foo.Factory")
         .inFile(bar)
         .onLine(8);
diff --git a/javatests/dagger/internal/codegen/AssistedFactoryTest.java b/javatests/dagger/internal/codegen/AssistedFactoryTest.java
index cffd42e..7691e1e 100644
--- a/javatests/dagger/internal/codegen/AssistedFactoryTest.java
+++ b/javatests/dagger/internal/codegen/AssistedFactoryTest.java
@@ -99,19 +99,39 @@
             .addLinesIn(
                 FAST_INIT_MODE,
                 "final class DaggerTestComponent implements TestComponent {",
+                "  private final DaggerTestComponent testComponent = this;",
+                "  private Provider<FooFactory> fooFactoryProvider;",
                 "",
-                "  private Foo foo(String str) {",
-                "    return new Foo(str, new Bar());",
+                "  @SuppressWarnings(\"unchecked\")",
+                "  private void initialize() {",
+                "    this.fooFactoryProvider = SingleCheck.provider(new"
+                    + " SwitchingProvider<FooFactory>(testComponent, 0));",
                 "  }",
                 "",
                 "  @Override",
                 "  public FooFactory fooFactory() {",
-                "    return new FooFactory() {",
-                "      @Override",
-                "      public Foo create(String str) {",
-                "        return DaggerTestComponent.this.foo(str);",
+                "    return fooFactoryProvider.get();",
+                "  }",
+                "",
+                "  private static final class SwitchingProvider<T> implements Provider<T> {",
+                "    private final DaggerTestComponent testComponent;",
+                "    private final int id;",
+                "",
+                "    @SuppressWarnings(\"unchecked\")",
+                "    @Override",
+                "    public T get() {",
+                "      switch (id) {",
+                "        case 0:",
+                "        return (T) new FooFactory() {",
+                "          @Override",
+                "          public Foo create(String str) {",
+                "            return new Foo(str, new Bar());",
+                "          }",
+                "        };",
+                "",
+                "        default: throw new AssertionError(id);",
                 "      }",
-                "    };",
+                "    }",
                 "  }",
                 "}")
             .addLinesIn(
@@ -195,23 +215,43 @@
             .addLinesIn(
                 FAST_INIT_MODE,
                 "final class DaggerTestComponent implements TestComponent {",
+                "  private final DaggerTestComponent testComponent = this;",
+                "  private Provider<FooFactory> fooFactoryProvider;",
                 "",
                 "  private Bar bar() {",
-                "    return new Bar(fooFactory());",
+                "    return new Bar(fooFactoryProvider.get());",
                 "  }",
                 "",
-                "  private Foo foo(String str) {",
-                "    return new Foo(str, bar());",
+                "  @SuppressWarnings(\"unchecked\")",
+                "  private void initialize() {",
+                "    this.fooFactoryProvider = SingleCheck.provider(new"
+                    + " SwitchingProvider<FooFactory>(testComponent, 0));",
                 "  }",
                 "",
                 "  @Override",
                 "  public FooFactory fooFactory() {",
-                "    return new FooFactory() {",
-                "      @Override",
-                "      public Foo create(String str) {",
-                "        return DaggerTestComponent.this.foo(str);",
+                "    return fooFactoryProvider.get();",
+                "  }",
+                "",
+                "  private static final class SwitchingProvider<T> implements Provider<T> {",
+                "    private final DaggerTestComponent testComponent;",
+                "    private final int id;",
+                "",
+                "    @SuppressWarnings(\"unchecked\")",
+                "    @Override",
+                "    public T get() {",
+                "      switch (id) {",
+                "        case 0:",
+                "        return (T) new FooFactory() {",
+                "          @Override",
+                "          public Foo create(String str) {",
+                "            return new Foo(str, testComponent.bar())",
+                "          }",
+                "        };",
+                "",
+                "        default: throw new AssertionError(id);",
                 "      }",
-                "    };",
+                "    }",
                 "  }",
                 "}")
             .addLinesIn(
@@ -243,4 +283,344 @@
         .generatedSourceFile("test.DaggerTestComponent")
         .containsElementsIn(generatedComponent);
   }
+
+  @Test
+  public void assistedParamConflictsWithComponentFieldName_successfulyDeduped() {
+    JavaFileObject foo =
+        JavaFileObjects.forSourceLines(
+            "test.Foo",
+            "package test;",
+            "",
+            "import dagger.assisted.Assisted;",
+            "import dagger.assisted.AssistedInject;",
+            "import javax.inject.Provider;",
+            "",
+            "class Foo {",
+            "  @AssistedInject",
+            "  Foo(@Assisted String testComponent, Provider<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();",
+            "}");
+    JavaFileObject generatedComponent =
+        compilerMode
+            .javaFileBuilder("test.DaggerTestComponent")
+            .addLines("package test;", "", GeneratedLines.generatedAnnotations())
+            .addLinesIn(
+                FAST_INIT_MODE,
+                "final class DaggerTestComponent implements TestComponent {",
+                "  private final DaggerTestComponent testComponent = this;",
+                "  private Provider<FooFactory> fooFactoryProvider;",
+                "",
+                "  @SuppressWarnings(\"unchecked\")",
+                "  private void initialize() {",
+                "    this.barProvider = new SwitchingProvider<>(testComponent, 1);",
+                "    this.fooFactoryProvider = SingleCheck.provider(",
+                "      new SwitchingProvider<FooFactory>(testComponent, 0));",
+                "  }",
+                "",
+                "  @Override",
+                "  public FooFactory fooFactory() {",
+                "    return fooFactoryProvider.get();",
+                "  }",
+                "",
+                "  private static final class SwitchingProvider<T> implements Provider<T> {",
+                "    private final DaggerTestComponent testComponent;",
+                "    private final int id;",
+                "",
+                "    @SuppressWarnings(\"unchecked\")",
+                "    @Override",
+                "    public T get() {",
+                "      switch (id) {",
+                "        case 0:",
+                "        return (T) new FooFactory() {",
+                "          @Override",
+                "          public Foo create(String testComponent2) {",
+                "            return new Foo(testComponent2, testComponent.barProvider);",
+                "          }",
+                "        };",
+                "        case 1: return (T) new Bar();",
+                "        default: throw new AssertionError(id);",
+                "      }",
+                "    }",
+                "  }",
+                "}")
+            .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();
+
+    Compilation compilation =
+        compilerWithOptions(compilerMode.javacopts()).compile(foo, bar, fooFactory, component);
+
+    assertThat(compilation).succeeded();
+    assertThat(compilation)
+        .generatedSourceFile("test.DaggerTestComponent")
+        .containsElementsIn(generatedComponent);
+  }
+
+  @Test
+  public void testFactoryGeneratorDuplicatedParamNames() {
+    JavaFileObject componentSrc =
+        JavaFileObjects.forSourceLines(
+            "test.TestComponent",
+            "package test;",
+            "",
+            "import dagger.BindsInstance;",
+            "import dagger.Component;",
+            "",
+            "@Component",
+            "interface TestComponent {",
+            "  @Component.Factory",
+            "  interface Factory {",
+            "    TestComponent create(@BindsInstance Bar arg);",
+            "}",
+            "  FooFactory getFooFactory();",
+            "}");
+    JavaFileObject factorySrc =
+        JavaFileObjects.forSourceLines(
+            "test.FooFactory",
+            "package test;",
+            "",
+            "import dagger.assisted.AssistedFactory;",
+            "",
+            "@AssistedFactory",
+            "public interface FooFactory {",
+            "  Foo create(Integer arg);",
+            "}");
+    JavaFileObject barSrc =
+        JavaFileObjects.forSourceLines("test.Bar", "package test;", "", "interface Bar {}");
+    JavaFileObject injectSrc =
+        JavaFileObjects.forSourceLines(
+            "test.Foo",
+            "package test;",
+            "",
+            "import dagger.assisted.Assisted;",
+            "import dagger.assisted.AssistedInject;",
+            "",
+            "class Foo {",
+            "  @AssistedInject",
+            "  Foo(Bar arg, @Assisted Integer argProvider) {}",
+            "}");
+    JavaFileObject generatedSrc =
+        compilerMode
+            .javaFileBuilder("test.DaggerTestComponent")
+            .addLines(
+                "package test;",
+                "",
+                "@ScopeMetadata",
+                "@QualifierMetadata",
+                GeneratedLines.generatedAnnotations())
+            .addLinesIn(
+                FAST_INIT_MODE,
+                "public final class Foo_Factory {",
+                "  private final Provider<Bar> argProvider;",
+                "",
+                "  public Foo_Factory(Provider<Bar> argProvider) {",
+                "    this.argProvider = argProvider;",
+                "  }",
+                "",
+                "  public Foo get(Integer argProvider2) {",
+                "    return newInstance(argProvider.get(), argProvider2);",
+                "  }",
+                "",
+                "  public static Foo_Factory create(Provider<Bar> argProvider) {",
+                "    return new Foo_Factory(argProvider);",
+                "  }",
+                "",
+                "  public static Foo newInstance(Object arg, Integer argProvider) {",
+                "    return new Foo((Bar) arg, argProvider);",
+                "  }",
+                "}")
+            .addLinesIn(
+                DEFAULT_MODE,
+                "public final class Foo_Factory {",
+                "  private final Provider<Bar> argProvider;",
+                "",
+                "  public Foo_Factory(Provider<Bar> argProvider) {",
+                "    this.argProvider = argProvider;",
+                "  }",
+                "",
+                "  public Foo get(Integer argProvider2) {",
+                "    return newInstance(argProvider.get(), argProvider2);",
+                "  }",
+                "",
+                "  public static Foo_Factory create(Provider<Bar> argProvider) {",
+                "    return new Foo_Factory(argProvider);",
+                "  }",
+                "",
+                "  public static Foo newInstance(Object arg, Integer argProvider) {",
+                "    return new Foo((Bar) arg, argProvider);",
+                "  }",
+                "}")
+            .build();
+    Compilation compilation =
+        compilerWithOptions(compilerMode.javacopts())
+            .compile(componentSrc, factorySrc, barSrc, injectSrc);
+    assertThat(compilation).succeeded();
+    assertThat(compilation)
+        .generatedSourceFile("test.Foo_Factory")
+        .containsElementsIn(generatedSrc);
+  }
+
+  @Test
+  public void testParameterizedAssistParam() {
+    JavaFileObject componentSrc =
+        JavaFileObjects.forSourceLines(
+            "test.TestComponent",
+            "package test;",
+            "",
+            "import dagger.Component;",
+            "",
+            "@Component",
+            "interface TestComponent {",
+            "  FooFactory<String> getFooFactory();",
+            "}");
+    JavaFileObject factorySrc =
+        JavaFileObjects.forSourceLines(
+            "test.FooFactory",
+            "package test;",
+            "",
+            "import dagger.assisted.AssistedFactory;",
+            "",
+            "@AssistedFactory",
+            "public interface FooFactory<T> {",
+            "  Foo<T> create(T arg);",
+            "}");
+    JavaFileObject injectSrc =
+        JavaFileObjects.forSourceLines(
+            "test.Foo",
+            "package test;",
+            "",
+            "import dagger.assisted.Assisted;",
+            "import dagger.assisted.AssistedInject;",
+            "",
+            "class Foo<T> {",
+            "  @AssistedInject",
+            "  Foo(@Assisted T arg) {}",
+            "}");
+    JavaFileObject generatedSrc =
+        compilerMode
+            .javaFileBuilder("test.DaggerTestComponent")
+            .addLines("package test;", "", GeneratedLines.generatedAnnotations())
+            .addLinesIn(
+                FAST_INIT_MODE,
+                "final class DaggerTestComponent implements TestComponent {",
+                "  private final DaggerTestComponent testComponent = this;",
+                "  private Provider<FooFactory<String>> fooFactoryProvider;",
+                "",
+                "  @SuppressWarnings(\"unchecked\")",
+                "  private void initialize() {",
+                "    this.fooFactoryProvider = SingleCheck.provider(new"
+                    + " SwitchingProvider<FooFactory<String>>(testComponent, 0));",
+                "  }",
+                "",
+                "  @Override",
+                "  public FooFactory<String> getFooFactory() {",
+                "    return fooFactoryProvider.get();",
+                "  }",
+                "  ",
+                "  private static final class SwitchingProvider<T> implements Provider<T> {",
+                "    private final DaggerTestComponent testComponent;",
+                "    private final int id;",
+                "",
+                "    @SuppressWarnings(\"unchecked\")",
+                "    @Override",
+                "    public T get() {",
+                "      switch (id) {",
+                "        case 0: return (T) new FooFactory<String>() {",
+                "          @Override",
+                "          public Foo<String> create(String arg) {",
+                "            return new Foo<String>(arg)",
+                "          }",
+                "        };",
+                "",
+                "        default: throw new AssertionError(id);",
+                "      }",
+                "    }",
+                "  }",
+                "}")
+            .addLinesIn(
+                DEFAULT_MODE,
+                "final class DaggerTestComponent implements TestComponent {",
+                "  private final DaggerTestComponent testComponent = this;",
+                "  private Foo_Factory<String> fooProvider;",
+                "  private Provider<FooFactory<String>> fooFactoryProvider;",
+                "",
+                "  private DaggerTestComponent() {",
+                "    initialize();",
+                "  }",
+                "",
+                "  public static Builder builder() {",
+                "    return new Builder();",
+                "  }",
+                "",
+                "  public static TestComponent create() {",
+                "    return new Builder().build();",
+                "  }",
+                "",
+                "  @SuppressWarnings(\"unchecked\")",
+                "  private void initialize() {",
+                "    this.fooProvider = Foo_Factory.create();",
+                "    this.fooFactoryProvider = FooFactory_Impl.create(fooProvider);",
+                "  }",
+                "",
+                "  @Override",
+                "  public FooFactory<String> getFooFactory() {",
+                "    return fooFactoryProvider.get();",
+                "  }",
+                "}")
+            .build();
+    Compilation compilation =
+        compilerWithOptions(compilerMode.javacopts()).compile(componentSrc, factorySrc, injectSrc);
+    assertThat(compilation).succeeded();
+    assertThat(compilation)
+        .generatedSourceFile("test.DaggerTestComponent")
+        .containsElementsIn(generatedSrc);
+  }
 }
diff --git a/javatests/dagger/internal/codegen/BUILD b/javatests/dagger/internal/codegen/BUILD
index 979ae71..f0a65bf 100644
--- a/javatests/dagger/internal/codegen/BUILD
+++ b/javatests/dagger/internal/codegen/BUILD
@@ -18,6 +18,7 @@
 load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_library")
 load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX")
 load("//:test_defs.bzl", "GenJavaTests")
+load("//java/dagger/testing/compile:macros.bzl", "compiler_test")
 
 package(default_visibility = ["//:src"])
 
@@ -43,10 +44,19 @@
     deps = [
         "//java/dagger/internal/codegen:package_info",
         "//java/dagger/internal/codegen:processor",
-        "//java/dagger/internal/guava:base",
-        "//java/dagger/internal/guava:collect",
+        "//third_party/java/compile_testing",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
         "@com_google_auto_value_auto_value//jar",
-        "@google_bazel_common//third_party/java/compile_testing",
+    ],
+)
+
+java_library(
+    name = "InvalidInjectConstructor",
+    srcs = ["InvalidInjectConstructor.java"],
+    # Note: We purposely leave off the dagger processor here.
+    deps = [
+        "//third_party/java/jsr330_inject",
     ],
 )
 
@@ -58,12 +68,15 @@
             "CompilerMode.java",
             "Compilers.java",
             "JavaFileBuilder.java",
+            "ComponentValidationKtTest.java",
+            "InvalidInjectConstructor.java",
         ],
     ),
     functional = False,
     javacopts = DOCLINT_HTML_AND_SYNTAX,
     plugins = ["//java/dagger/internal/codegen/bootstrap"],
     deps = [
+        ":InvalidInjectConstructor",
         ":compilers",
         ":kotlin_sources",
         "//java/dagger:core",
@@ -73,26 +86,54 @@
         "//java/dagger/internal/codegen/binding",
         "//java/dagger/internal/codegen/bindinggraphvalidation",
         "//java/dagger/internal/codegen/compileroption",
+        "//java/dagger/internal/codegen/javac",
         "//java/dagger/internal/codegen/javapoet",
         "//java/dagger/internal/codegen/kotlin",
         "//java/dagger/internal/codegen/langmodel",
         "//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/internal/codegen/xprocessing",
         "//java/dagger/model/testing",
         "//java/dagger/producers",
         "//java/dagger/spi",
+        "//third_party/java/auto:common",
+        "//third_party/java/auto:value",
+        "//third_party/java/compile_testing",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/util/concurrent",
+        "//third_party/java/javapoet",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/junit",
+        "//third_party/java/mockito",
+        "//third_party/java/truth",
         "@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/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",
-        "@maven//:com_google_auto_auto_common",
+    ],
+)
+
+compiler_test(
+    name = "ComponentValidationKtTest",
+    srcs = ["ComponentValidationKtTest.java"],
+    compiler_deps = [
+        "//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/langmodel",
+        "//java/dagger/internal/codegen/validation",
+        "//java/dagger/internal/codegen/writing",
+        "//java/dagger/model/testing",
+        "//java/dagger/producers",
+        "//java/dagger/spi",
+    ],
+    deps = [
+        "//third_party/java/guava/collect",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
+        "@maven//:com_github_tschuchortdev_kotlin_compile_testing",
     ],
 )
diff --git a/javatests/dagger/internal/codegen/BindsMethodValidationTest.java b/javatests/dagger/internal/codegen/BindsMethodValidationTest.java
index ab26010..8974e06 100644
--- a/javatests/dagger/internal/codegen/BindsMethodValidationTest.java
+++ b/javatests/dagger/internal/codegen/BindsMethodValidationTest.java
@@ -16,11 +16,15 @@
 
 package dagger.internal.codegen;
 
+import static com.google.testing.compile.CompilationSubject.assertThat;
+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 java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import com.google.common.collect.ImmutableList;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
 import dagger.Module;
 import dagger.multibindings.IntKey;
 import dagger.multibindings.LongKey;
@@ -29,6 +33,7 @@
 import java.lang.annotation.Retention;
 import java.util.Collection;
 import javax.inject.Qualifier;
+import javax.tools.JavaFileObject;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -41,10 +46,12 @@
     return ImmutableList.copyOf(new Object[][] {{Module.class}, {ProducerModule.class}});
   }
 
+  private final String moduleAnnotation;
   private final String moduleDeclaration;
 
   public BindsMethodValidationTest(Class<? extends Annotation> moduleAnnotation) {
-    moduleDeclaration = "@" + moduleAnnotation.getCanonicalName() + " abstract class %s { %s }";
+    this.moduleAnnotation = "@" + moduleAnnotation.getCanonicalName();
+    moduleDeclaration = this.moduleAnnotation + " abstract class %s { %s }";
   }
 
   @Test
@@ -141,6 +148,113 @@
         .hasError("may not have more than one map key");
   }
 
+  @Test
+  public void bindsMissingTypeInParameterHierarchy() {
+    JavaFileObject module =
+        JavaFileObjects.forSourceLines(
+            "test.TestComponent",
+            "package test;",
+            "",
+            "import dagger.Binds;",
+            "",
+            moduleAnnotation,
+            "interface TestModule {",
+            "  @Binds String bindObject(Child<String> child);",
+            "}");
+
+    JavaFileObject child =
+        JavaFileObjects.forSourceLines(
+            "test.Child",
+            "package test;",
+            "",
+            "class Child<T> extends Parent<T> {}");
+
+    JavaFileObject parent =
+        JavaFileObjects.forSourceLines(
+            "test.Parent",
+            "package test;",
+            "",
+            "class Parent<T> extends MissingType {}");
+
+    Compilation compilation = daggerCompiler().compile(module, child, parent);
+    assertThat(compilation).failed();
+    assertThat(compilation).hadErrorCount(3);
+    assertThat(compilation)
+        .hadErrorContaining(
+            "cannot find symbol"
+                + "\n  symbol: class MissingType");
+    assertThat(compilation)
+        .hadErrorContaining(
+            "ModuleProcessingStep was unable to process 'test.TestModule' because 'MissingType' "
+                + "could not be resolved.");
+    assertThat(compilation)
+        .hadErrorContaining(
+            "BindingMethodProcessingStep was unable to process"
+                + " 'bindObject(test.Child<java.lang.String>)' because 'MissingType' could not be"
+                + " resolved."
+                + "\n  "
+                + "\n  Dependency trace:"
+                + "\n      => element (INTERFACE): test.TestModule"
+                + "\n      => element (METHOD): bindObject(test.Child<java.lang.String>)"
+                + "\n      => element (PARAMETER): child"
+                + "\n      => type (DECLARED parameter): test.Child<java.lang.String>"
+                + "\n      => type (DECLARED supertype): test.Parent<java.lang.String>"
+                + "\n      => type (ERROR supertype): MissingType");
+  }
+
+
+  @Test
+  public void bindsMissingTypeInReturnTypeHierarchy() {
+    JavaFileObject module =
+        JavaFileObjects.forSourceLines(
+            "test.TestComponent",
+            "package test;",
+            "",
+            "import dagger.Binds;",
+            "",
+            moduleAnnotation,
+            "interface TestModule {",
+            "  @Binds Child<String> bindChild(String str);",
+            "}");
+
+    JavaFileObject child =
+        JavaFileObjects.forSourceLines(
+            "test.Child",
+            "package test;",
+            "",
+            "class Child<T> extends Parent<T> {}");
+
+    JavaFileObject parent =
+        JavaFileObjects.forSourceLines(
+            "test.Parent",
+            "package test;",
+            "",
+            "class Parent<T> extends MissingType {}");
+
+    Compilation compilation = daggerCompiler().compile(module, child, parent);
+    assertThat(compilation).failed();
+    assertThat(compilation).hadErrorCount(3);
+    assertThat(compilation)
+        .hadErrorContaining(
+            "cannot find symbol"
+                + "\n  symbol: class MissingType");
+    assertThat(compilation)
+        .hadErrorContaining(
+            "ModuleProcessingStep was unable to process 'test.TestModule' because 'MissingType' "
+                + "could not be resolved.");
+    assertThat(compilation)
+        .hadErrorContaining(
+            "BindingMethodProcessingStep was unable to process 'bindChild(java.lang.String)'"
+                + " because 'MissingType' could not be resolved."
+                + "\n  "
+                + "\n  Dependency trace:"
+                + "\n      => element (INTERFACE): test.TestModule"
+                + "\n      => element (METHOD): bindChild(java.lang.String)"
+                + "\n      => type (DECLARED return type): test.Child<java.lang.String>"
+                + "\n      => type (DECLARED supertype): test.Parent<java.lang.String>"
+                + "\n      => type (ERROR supertype): MissingType");
+  }
+
   private DaggerModuleMethodSubject assertThatMethod(String method) {
     return assertThatModuleMethod(method).withDeclaration(moduleDeclaration);
   }
diff --git a/javatests/dagger/internal/codegen/Compilers.java b/javatests/dagger/internal/codegen/Compilers.java
index 26796bf..af45f2c 100644
--- a/javatests/dagger/internal/codegen/Compilers.java
+++ b/javatests/dagger/internal/codegen/Compilers.java
@@ -44,7 +44,8 @@
           .collect(collectingAndThen(toList(), ImmutableList::copyOf));
 
   static final ImmutableList<String> DEFAULT_JAVACOPTS =
-      ImmutableList.of("-Adagger.experimentalDaggerErrorMessages=enabled");
+      ImmutableList.of(
+          "-Adagger.experimentalDaggerErrorMessages=enabled");
 
   /**
    * Returns a compiler that runs the Dagger and {@code @AutoAnnotation} processors, along with
diff --git a/javatests/dagger/internal/codegen/ComponentBuilderTest.java b/javatests/dagger/internal/codegen/ComponentBuilderTest.java
index c4e0e07..17c4900 100644
--- a/javatests/dagger/internal/codegen/ComponentBuilderTest.java
+++ b/javatests/dagger/internal/codegen/ComponentBuilderTest.java
@@ -18,7 +18,7 @@
 
 import static com.google.testing.compile.CompilationSubject.assertThat;
 import static dagger.internal.codegen.Compilers.compilerWithOptions;
-import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.COMPONENT_BUILDER;
+import static dagger.internal.codegen.base.ComponentCreatorAnnotation.COMPONENT_BUILDER;
 import static dagger.internal.codegen.binding.ErrorMessages.creatorMessagesFor;
 
 import com.google.testing.compile.Compilation;
diff --git a/javatests/dagger/internal/codegen/ComponentCreatorTest.java b/javatests/dagger/internal/codegen/ComponentCreatorTest.java
index 3cf05ac..be8066f 100644
--- a/javatests/dagger/internal/codegen/ComponentCreatorTest.java
+++ b/javatests/dagger/internal/codegen/ComponentCreatorTest.java
@@ -23,17 +23,17 @@
 import static dagger.internal.codegen.Compilers.compilerWithOptions;
 import static dagger.internal.codegen.Compilers.daggerCompiler;
 import static dagger.internal.codegen.ComponentCreatorTest.CompilerType.JAVAC;
-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.base.ComponentCreatorAnnotation.COMPONENT_BUILDER;
+import static dagger.internal.codegen.base.ComponentCreatorAnnotation.COMPONENT_FACTORY;
+import static dagger.internal.codegen.base.ComponentCreatorKind.BUILDER;
+import static dagger.internal.codegen.base.ComponentCreatorKind.FACTORY;
+import static dagger.internal.codegen.base.ComponentKind.COMPONENT;
 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 dagger.internal.codegen.base.ComponentCreatorAnnotation;
 import java.util.Collection;
 import javax.tools.JavaFileObject;
 import org.junit.Test;
@@ -163,6 +163,8 @@
             "final class DaggerTestComponent implements TestComponent {",
             "  private final TestModule testModule;",
             "",
+            "  private final DaggerTestComponent testComponent = this;",
+            "",
             "  private DaggerTestComponent(TestModule testModuleParam) {",
             "    this.testModule = testModuleParam;",
             "  }",
@@ -361,6 +363,8 @@
                 "final class DaggerSimpleComponent implements SimpleComponent {",
                 "  private final Object object;",
                 "",
+                "  private final DaggerSimpleComponent simpleComponent = this;",
+                "",
                 "  private DaggerSimpleComponent(Object objectParam) {",
                 "    this.object = objectParam;",
                 "  }",
diff --git a/javatests/dagger/internal/codegen/ComponentCreatorTestHelper.java b/javatests/dagger/internal/codegen/ComponentCreatorTestHelper.java
index 8ad4322..aaab5c3 100644
--- a/javatests/dagger/internal/codegen/ComponentCreatorTestHelper.java
+++ b/javatests/dagger/internal/codegen/ComponentCreatorTestHelper.java
@@ -17,14 +17,14 @@
 package dagger.internal.codegen;
 
 import static dagger.internal.codegen.Compilers.compilerWithOptions;
-import static dagger.internal.codegen.binding.ComponentCreatorKind.FACTORY;
+import static dagger.internal.codegen.base.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.base.ComponentCreatorAnnotation;
+import dagger.internal.codegen.base.ComponentCreatorKind;
 import dagger.internal.codegen.binding.ErrorMessages;
 import java.util.Arrays;
 import java.util.stream.Stream;
@@ -67,7 +67,7 @@
               line ->
                   line.replace("Builder", "Factory")
                       .replace("builder", "factory")
-                      .replace("build", "create"));
+                      .replace("build", "createComponent"));
     }
     return stream.collect(joining("\n"));
   }
diff --git a/javatests/dagger/internal/codegen/ComponentFactoryTest.java b/javatests/dagger/internal/codegen/ComponentFactoryTest.java
index 35cbb6a..a910f72 100644
--- a/javatests/dagger/internal/codegen/ComponentFactoryTest.java
+++ b/javatests/dagger/internal/codegen/ComponentFactoryTest.java
@@ -18,7 +18,7 @@
 
 import static com.google.testing.compile.CompilationSubject.assertThat;
 import static dagger.internal.codegen.Compilers.compilerWithOptions;
-import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.COMPONENT_FACTORY;
+import static dagger.internal.codegen.base.ComponentCreatorAnnotation.COMPONENT_FACTORY;
 import static dagger.internal.codegen.binding.ErrorMessages.creatorMessagesFor;
 
 import com.google.testing.compile.Compilation;
diff --git a/javatests/dagger/internal/codegen/ComponentProcessorTest.java b/javatests/dagger/internal/codegen/ComponentProcessorTest.java
index 3e514c8..480a449 100644
--- a/javatests/dagger/internal/codegen/ComponentProcessorTest.java
+++ b/javatests/dagger/internal/codegen/ComponentProcessorTest.java
@@ -175,74 +175,34 @@
             .addLines(
                 "package test;",
                 "",
-                GeneratedLines.generatedImports(
-                    "import dagger.Lazy;",
-                    "import dagger.internal.DoubleCheck;",
-                    "import javax.inject.Provider;"),
-                "",
                 GeneratedLines.generatedAnnotations(),
-                "final class DaggerSimpleComponent implements SimpleComponent {")
+                "final class DaggerSimpleComponent implements SimpleComponent {",
+                "  private final DaggerSimpleComponent simpleComponent = this;")
             .addLinesIn(
                 FAST_INIT_MODE,
-                "  private volatile Provider<SomeInjectableType> someInjectableTypeProvider;")
-            .addLines(
-                "  private DaggerSimpleComponent() {}",
-                "",
-                "  public static Builder builder() {",
-                "    return new Builder();",
-                "  }",
-                "",
-                "  public static SimpleComponent create() {",
-                "    return new Builder().build();",
+                "  @SuppressWarnings(\"unchecked\")",
+                "  private void initialize() {",
+                "    this.someInjectableTypeProvider =",
+                "        new SwitchingProvider<>(simpleComponent, 0);",
                 "  }",
                 "",
                 "  @Override",
                 "  public SomeInjectableType someInjectableType() {",
-                "    return new SomeInjectableType();",
+                "    return someInjectableTypeProvider.get();",
                 "  }",
                 "",
                 "  @Override",
-                "  public Lazy<SomeInjectableType> lazySomeInjectableType() {")
-            .addLinesIn(
-                DEFAULT_MODE, //
-                "    return DoubleCheck.lazy(SomeInjectableType_Factory.create());")
-            .addLinesIn(
-                FAST_INIT_MODE,
-                "    return DoubleCheck.lazy(someInjectableTypeProvider());")
-            .addLines(
+                "  public Lazy<SomeInjectableType> lazySomeInjectableType() {",
+                "    return DoubleCheck.lazy(someInjectableTypeProvider);",
                 "  }",
                 "",
                 "  @Override",
-                "  public Provider<SomeInjectableType> someInjectableTypeProvider() {")
-            .addLinesIn(
-                DEFAULT_MODE, //
-                "    return SomeInjectableType_Factory.create();")
-            .addLinesIn(
-                FAST_INIT_MODE, //
-                "    Object local = someInjectableTypeProvider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(0);",
-                "      someInjectableTypeProvider = (Provider<SomeInjectableType>) local;",
-                "    }",
-                "    return (Provider<SomeInjectableType>) local;")
-            .addLines(
+                "  public Provider<SomeInjectableType> someInjectableTypeProvider() {",
+                "    return someInjectableTypeProvider",
                 "  }",
                 "",
-                "  static final class Builder {",
-                "    private Builder() {}",
-                "",
-                "    public SimpleComponent build() {",
-                "      return new DaggerSimpleComponent();",
-                "    }",
-                "  }")
-            .addLinesIn(
-                FAST_INIT_MODE,
-                "  private final class SwitchingProvider<T> implements Provider<T> {",
-                "    private final int id;",
-                "",
-                "    SwitchingProvider(int id) {",
-                "      this.id = id;",
-                "    }",
+                "  private static final class SwitchingProvider<T> implements Provider<T> {",
+                "    private final DaggerSimpleComponent simpleComponent;",
                 "",
                 "    @SuppressWarnings(\"unchecked\")",
                 "    @Override",
@@ -251,8 +211,24 @@
                 "        case 0: return (T) new SomeInjectableType();",
                 "        default: throw new AssertionError(id);",
                 "      }",
-                "    }",
-                "  }")
+                "    }")
+            .addLinesIn(
+                DEFAULT_MODE,
+                "  @Override",
+                "  public SomeInjectableType someInjectableType() {",
+                "    return new SomeInjectableType();",
+                "  }",
+                "",
+                "  @Override",
+                "  public Lazy<SomeInjectableType> lazySomeInjectableType() {",
+                "    return DoubleCheck.lazy(SomeInjectableType_Factory.create());",
+                "  }",
+                "",
+                "  @Override",
+                "  public Provider<SomeInjectableType> someInjectableTypeProvider() {",
+                "    return SomeInjectableType_Factory.create();",
+                "  }",
+                "}")
             .build();
 
     Compilation compilation =
@@ -261,7 +237,7 @@
     assertThat(compilation).succeeded();
     assertThat(compilation)
         .generatedSourceFile("test.DaggerSimpleComponent")
-        .hasSourceEquivalentTo(generatedComponent);
+        .containsElementsIn(generatedComponent);
   }
 
   @Test public void componentWithScope() {
@@ -297,84 +273,46 @@
                 "package test;",
                 "",
                 GeneratedLines.generatedAnnotations(),
-                "final class DaggerSimpleComponent implements SimpleComponent {")
+                "final class DaggerSimpleComponent implements SimpleComponent {",
+                "  private Provider<SomeInjectableType> someInjectableTypeProvider;")
             .addLinesIn(
                 FAST_INIT_MODE,
-                "  private volatile Object someInjectableType = new MemoizedSentinel();",
-                "  private volatile Provider<SomeInjectableType> someInjectableTypeProvider;")
+                "  @SuppressWarnings(\"unchecked\")",
+                "  private void initialize() {",
+                "    this.someInjectableTypeProvider =",
+                "        DoubleCheck.provider(",
+                "            new SwitchingProvider<SomeInjectableType>(simpleComponent, 0));",
+                "  }")
             .addLinesIn(
                 DEFAULT_MODE,
-                "  private Provider<SomeInjectableType> someInjectableTypeProvider;",
-                "",
                 "  @SuppressWarnings(\"unchecked\")",
                 "  private void initialize() {",
                 "    this.someInjectableTypeProvider =",
                 "        DoubleCheck.provider(SomeInjectableType_Factory.create());",
-                "  }",
-                "")
+                "  }")
             .addLines(
-                "  @Override", //
-                "  public SomeInjectableType someInjectableType() {")
-            .addLinesIn(
-                FAST_INIT_MODE,
-                "    Object local = someInjectableType;",
-                "    if (local instanceof MemoizedSentinel) {",
-                "      synchronized (local) {",
-                "        local = someInjectableType;",
-                "        if (local instanceof MemoizedSentinel) {",
-                "          local = new SomeInjectableType();",
-                "          someInjectableType =",
-                "              DoubleCheck.reentrantCheck(someInjectableType, local);",
-                "        }",
-                "      }",
-                "    }",
-                "    return (SomeInjectableType) local;")
-            .addLinesIn(
-                DEFAULT_MODE, //
-                "    return someInjectableTypeProvider.get();")
-            .addLines(
+                "  @Override",
+                "  public SomeInjectableType someInjectableType() {",
+                "    return someInjectableTypeProvider.get();",
                 "  }",
                 "",
                 "  @Override",
-                "  public Lazy<SomeInjectableType> lazySomeInjectableType() {")
-            .addLinesIn(
-                DEFAULT_MODE, //
-                "    return DoubleCheck.lazy(someInjectableTypeProvider);")
-            .addLinesIn(
-                FAST_INIT_MODE,
-                "    return DoubleCheck.lazy(someInjectableTypeProvider());")
-            .addLines(
+                "  public Lazy<SomeInjectableType> lazySomeInjectableType() {",
+                "    return DoubleCheck.lazy(someInjectableTypeProvider);",
                 "  }",
                 "",
                 "  @Override",
-                "  public Provider<SomeInjectableType> someInjectableTypeProvider() {")
-            .addLinesIn(
-                FAST_INIT_MODE, //
-                "    Object local = someInjectableTypeProvider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(0);",
-                "      someInjectableTypeProvider = (Provider<SomeInjectableType>) local;",
-                "    }",
-                "    return (Provider<SomeInjectableType>) local;")
-            .addLinesIn(
-                DEFAULT_MODE, //
-                "    return someInjectableTypeProvider;")
-            .addLines( //
+                "  public Provider<SomeInjectableType> someInjectableTypeProvider() {",
+                "    return someInjectableTypeProvider;",
                 "  }")
             .addLinesIn(
                 FAST_INIT_MODE,
-                "  private final class SwitchingProvider<T> implements Provider<T> {",
-                "    private final int id;",
-                "",
-                "    SwitchingProvider(int id) {",
-                "      this.id = id;",
-                "    }",
-                "",
+                "  private static final class SwitchingProvider<T> implements Provider<T> {",
                 "    @SuppressWarnings(\"unchecked\")",
                 "    @Override",
                 "    public T get() {",
                 "      switch (id) {",
-                "        case 0: return (T) DaggerSimpleComponent.this.someInjectableType();",
+                "        case 0: return (T) new SomeInjectableType();",
                 "        default: throw new AssertionError(id);",
                 "      }",
                 "    }",
@@ -882,6 +820,7 @@
             "",
             GeneratedLines.generatedAnnotations(),
             "final class DaggerParent implements Parent {",
+            "  private final DaggerParent parent = this;",
             "",
             "  private DaggerParent() {}",
             "",
@@ -1044,29 +983,42 @@
         "  Provider<SimpleComponent> selfProvider();",
         "}");
     JavaFileObject generatedComponent =
-        JavaFileObjects.forSourceLines(
-            "test.DaggerSimpleComponent",
-            "package test;",
-            "",
-            GeneratedLines.generatedAnnotations(),
-            "final class DaggerSimpleComponent implements SimpleComponent {",
-            "  private Provider<SimpleComponent> simpleComponentProvider;",
-            "",
-            "  @SuppressWarnings(\"unchecked\")",
-            "  private void initialize() {",
-            "    this.simpleComponentProvider = InstanceFactory.create((SimpleComponent) this);",
-            "  }",
-            "",
-            "  @Override",
-            "  public SomeInjectableType someInjectableType() {",
-            "    return new SomeInjectableType(this)",
-            "  }",
-            "",
-            "  @Override",
-            "  public Provider<SimpleComponent> selfProvider() {",
-            "    return simpleComponentProvider;",
-            "  }",
-            "}");
+        compilerMode
+            .javaFileBuilder("test.DaggerSimpleComponent")
+            .addLines(
+                "package test;",
+                "",
+                GeneratedLines.generatedAnnotations(),
+                "final class DaggerSimpleComponent implements SimpleComponent {",
+                "  private final DaggerSimpleComponent simpleComponent = this;",
+                "",
+                "  private Provider<SimpleComponent> simpleComponentProvider;",
+                "",
+                "  @SuppressWarnings(\"unchecked\")",
+                "  private void initialize() {",
+                "    this.simpleComponentProvider =",
+                "        InstanceFactory.create((SimpleComponent) simpleComponent);",
+                "  }",
+                "")
+            .addLinesIn(
+                DEFAULT_MODE,
+                "  @Override",
+                "  public SomeInjectableType someInjectableType() {",
+                "    return new SomeInjectableType(this)",
+                "  }")
+            .addLinesIn(
+                FAST_INIT_MODE,
+                "  @Override",
+                "  public SomeInjectableType someInjectableType() {",
+                "    return new SomeInjectableType(simpleComponentProvider.get());",
+                "  }")
+            .addLines(
+                "  @Override",
+                "  public Provider<SimpleComponent> selfProvider() {",
+                "    return simpleComponentProvider;",
+                "  }",
+                "}")
+            .build();
     Compilation compilation =
         compilerWithOptions(compilerMode.javacopts())
             .compile(injectableTypeFile, componentFile);
@@ -1182,34 +1134,37 @@
                 "",
                 GeneratedLines.generatedAnnotations(),
                 "final class DaggerBComponent implements BComponent {")
-            .addLinesIn(DEFAULT_MODE, "  private Provider<A> aProvider;")
             .addLinesIn(
                 FAST_INIT_MODE,
                 "  private final AComponent aComponent;",
-                "  private volatile Provider<A> aProvider;",
+                "  private final DaggerBComponent bComponent = this;",
+                "  private Provider<A> aProvider;",
                 "",
                 "  private DaggerBComponent(AComponent aComponentParam) {",
                 "    this.aComponent = aComponentParam;",
+                "    initialize(aComponentParam);",
                 "  }",
                 "",
-                "  private Provider<A> aProvider() {",
-                "    Object local = aProvider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(0);",
-                "      aProvider = (Provider<A>) local;",
-                "    }",
-                "    return (Provider<A>) local;",
+                "  @SuppressWarnings(\"unchecked\")",
+                "  private void initialize(final AComponent aComponentParam) {",
+                "    this.aProvider = new SwitchingProvider<>(bComponent, 0);",
                 "  }")
             .addLinesIn(
                 DEFAULT_MODE,
+                "  private Provider<A> aProvider;",
+                "",
+                "  private DaggerBComponent(AComponent aComponentParam) {",
+                "    initialize(aComponentParam);",
+                "  }",
+                "",
                 "  @SuppressWarnings(\"unchecked\")",
                 "  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(aProvider());")
             .addLines(
+                "  @Override",
+                "  public B b() {",
+                "    return new B(aProvider);",
                 "  }",
                 "",
                 "  static final class Builder {",
@@ -1242,15 +1197,14 @@
                 "}")
             .addLinesIn(
                 FAST_INIT_MODE,
-                "  private final class SwitchingProvider<T> implements Provider<T> {",
+                "  private static final class SwitchingProvider<T> implements Provider<T> {",
                 "    @SuppressWarnings(\"unchecked\")",
                 "    @Override",
                 "    public T get() {",
                 "      switch (id) {",
                 "        case 0:",
                 "          return (T)",
-                "              Preconditions.checkNotNullFromComponent(",
-                "                  DaggerBComponent.this.aComponent.a());",
+                "              Preconditions.checkNotNullFromComponent(bComponent.aComponent.a());",
                 "        default:",
                 "          throw new AssertionError(id);",
                 "      }",
@@ -1320,9 +1274,9 @@
             "",
             "  private DaggerTestComponent(",
             "      TestModule testModuleParam,",
-            "      other.test.TestModule testModule2Param) {",
+            "      other.test.TestModule testModuleParam2) {",
             "    this.testModule = testModuleParam;",
-            "    this.testModule2 = testModule2Param;",
+            "    this.testModule2 = testModuleParam2;",
             "  }",
             "",
             "  @Override",
@@ -1601,6 +1555,8 @@
             "",
             GeneratedLines.generatedAnnotations(),
             "final class DaggerSimpleComponent implements SimpleComponent {",
+            "  private final DaggerSimpleComponent simpleComponent = this;",
+            "",
             "  private DaggerSimpleComponent() {}",
             "",
             "  public static Builder builder() {",
@@ -2042,8 +1998,9 @@
             "",
             GeneratedLines.generatedAnnotations(),
             "final class DaggerParent implements Parent {",
-            "  private DaggerParent() {",
-            "  }",
+            "  private final DaggerParent parent = this;",
+            "",
+            "  private DaggerParent() {}",
             "",
             "  public static Builder builder() {",
             "    return new Builder();",
@@ -2181,6 +2138,8 @@
                 "test.TestModule_NonNullableStringFactory",
                 "package test;",
                 "",
+                "@ScopeMetadata",
+                "@QualifierMetadata",
                 GeneratedLines.generatedAnnotations(),
                 "public final class TestModule_NonNullableStringFactory",
                 "    implements Factory<String> {",
@@ -2271,6 +2230,8 @@
                 "test.TestModule_PrimitiveIntegerFactory",
                 "package test;",
                 "",
+                "@ScopeMetadata",
+                "@QualifierMetadata",
                 GeneratedLines.generatedAnnotations(),
                 "public final class TestModule_PrimitiveIntegerFactory",
                 "    implements Factory<Integer> {",
@@ -2439,10 +2400,10 @@
             "package test;",
             GeneratedLines.generatedAnnotations(),
             "final class DaggerParent implements Parent {",
-            "  private final class ChildImpl implements Child {",
+            "  private static final class ChildImpl implements Child {",
             "    @Override",
             "    public String string() {",
-            "      return DaggerParent.this.string();",
+            "      return parent.string();",
             "    }",
             "  }",
             "}");
@@ -2633,6 +2594,8 @@
                 "",
                 GeneratedLines.generatedAnnotations(),
                 "public final class DaggerPublicComponent implements PublicComponent {",
+                "  private final DaggerPublicComponent publicComponent = this;",
+                "",
                 "  private DaggerPublicComponent() {}",
                 "",
                 "  public static Builder builder() {",
@@ -2653,6 +2616,219 @@
                 "}"));
   }
 
+  @Test
+  public void componentFactoryInterfaceTest() {
+    JavaFileObject parentInterface =
+        JavaFileObjects.forSourceLines(
+            "test.ParentInterface",
+            "package test;",
+            "",
+            "interface ParentInterface extends ChildInterface.Factory {}");
+
+    JavaFileObject childInterface =
+        JavaFileObjects.forSourceLines(
+            "test.ChildInterface",
+            "package test;",
+            "",
+            "interface ChildInterface {",
+            "  interface Factory {",
+            "    ChildInterface child(ChildModule childModule);",
+            "  }",
+            "}");
+
+    JavaFileObject parent =
+        JavaFileObjects.forSourceLines(
+            "test.Parent",
+            "package test;",
+            "",
+            "import dagger.Component;",
+            "",
+            "@Component",
+            "interface Parent extends ParentInterface, Child.Factory {}");
+
+    JavaFileObject child =
+        JavaFileObjects.forSourceLines(
+            "test.Child",
+            "package test;",
+            "",
+            "import dagger.Subcomponent;",
+            "",
+            "@Subcomponent(modules = ChildModule.class)",
+            "interface Child extends ChildInterface {",
+            "  interface Factory extends ChildInterface.Factory {",
+            "    @Override Child child(ChildModule childModule);",
+            "  }",
+            "}");
+
+    JavaFileObject childModule =
+        JavaFileObjects.forSourceLines(
+            "test.ChildModule",
+            "package test;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "",
+            "@Module",
+            "class ChildModule {",
+            "  @Provides",
+            "  int provideInt() {",
+            "    return 0;",
+            "  }",
+            "}");
+
+    Compilation compilation =
+        daggerCompiler().compile(parentInterface, childInterface, parent, child, childModule);
+    assertThat(compilation).succeeded();
+  }
+
+  @Test
+  public void providerComponentType() {
+    JavaFileObject entryPoint =
+        JavaFileObjects.forSourceLines(
+            "test.SomeEntryPoint",
+            "package test;",
+            "",
+            "import javax.inject.Inject;",
+            "import javax.inject.Provider;",
+            "",
+            "public class SomeEntryPoint {",
+            "  @Inject SomeEntryPoint(Foo foo, Provider<Foo> fooProvider) {}",
+            "}");
+    JavaFileObject foo =
+        JavaFileObjects.forSourceLines(
+            "test.Foo",
+            "package test;",
+            "",
+            "import javax.inject.Inject;",
+            "",
+            "public class Foo {",
+            "  @Inject Foo(Bar bar) {}",
+            "}");
+    JavaFileObject bar =
+        JavaFileObjects.forSourceLines(
+            "test.Bar",
+            "package test;",
+            "",
+            "import javax.inject.Inject;",
+            "",
+            "public class Bar {",
+            "  @Inject Bar() {}",
+            "}");
+    JavaFileObject component =
+        JavaFileObjects.forSourceLines(
+            "test.TestComponent",
+            "package test;",
+            "",
+            "import dagger.Component;",
+            "import javax.inject.Provider;",
+            "",
+            "@Component",
+            "public interface TestComponent {",
+            "  SomeEntryPoint someEntryPoint();",
+            "}");
+    Compilation compilation =
+        compilerWithOptions(compilerMode.javacopts()).compile(component, foo, bar, entryPoint);
+
+    assertThat(compilation)
+        .generatedSourceFile("test.DaggerTestComponent")
+        .containsElementsIn(
+            compilerMode
+                .javaFileBuilder("test.DaggerSimpleComponent")
+                .addLines(
+                    "package test;",
+                    "",
+                    GeneratedLines.generatedAnnotations(),
+                    "public final class DaggerTestComponent implements TestComponent {",
+                    "  private Provider<Foo> fooProvider;")
+                .addLinesIn(
+                    DEFAULT_MODE,
+                    "  private Foo foo() {",
+                    "    return new Foo(new Bar());",
+                    "  }",
+                    "",
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize() {",
+                    "    this.fooProvider = Foo_Factory.create(Bar_Factory.create());",
+                    "  }",
+                    "",
+                    "  @Override",
+                    "  public SomeEntryPoint someEntryPoint() {",
+                    "    return new SomeEntryPoint(foo(), fooProvider);",
+                    "  }")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize() {",
+                    "    this.fooProvider = new SwitchingProvider<>(testComponent, 0);",
+                    "  }",
+                    "",
+                    "  @Override",
+                    "  public SomeEntryPoint someEntryPoint() {",
+                    "    return new SomeEntryPoint(fooProvider.get(), fooProvider);",
+                    "  }",
+                    "",
+                    "  private static final class SwitchingProvider<T> implements Provider<T> {",
+                    "    @SuppressWarnings(\"unchecked\")",
+                    "    @Override",
+                    "    public T get() {",
+                    "      switch (id) {",
+                    "        case 0: return (T) new Foo(new Bar());",
+                    "",
+                    "        default: throw new AssertionError(id);",
+                    "      }",
+                    "    }",
+                    "  }",
+                    "}")
+                .build());
+  }
+
+  @Test
+  public void injectedTypeHasGeneratedParam() {
+    JavaFileObject foo =
+        JavaFileObjects.forSourceLines(
+            "test.Foo",
+            "package test;",
+            "",
+            "import javax.inject.Inject;",
+            "",
+            "public final class Foo {",
+            "",
+            "  @Inject",
+            "  public Foo(GeneratedParam param) {}",
+            "",
+            "  @Inject",
+            "  public void init() {}",
+            "}");
+    JavaFileObject component =
+        JavaFileObjects.forSourceLines(
+            "test.TestComponent",
+            "package test;",
+            "",
+            "import dagger.Component;",
+            "import java.util.Set;",
+            "",
+            "@Component",
+            "interface TestComponent {",
+            "  Foo foo();",
+            "}");
+
+    Compilation compilation =
+        daggerCompiler(
+                new GeneratingProcessor(
+                    "test.GeneratedParam",
+                    "package test;",
+                    "",
+                    "import javax.inject.Inject;",
+                    "",
+                    "public final class GeneratedParam {",
+                    "",
+                    "  @Inject",
+                    "  public GeneratedParam() {}",
+                    "}"))
+            .compile(foo, component);
+    assertThat(compilation).succeededWithoutWarnings();
+  }
+
   /**
    * A {@link ComponentProcessor} that excludes elements using a {@link Predicate}.
    */
diff --git a/javatests/dagger/internal/codegen/ComponentProtectedTypeTest.java b/javatests/dagger/internal/codegen/ComponentProtectedTypeTest.java
new file mode 100644
index 0000000..c67cbec
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ComponentProtectedTypeTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 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.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.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public final class ComponentProtectedTypeTest {
+  @Parameters(name = "{0}")
+  public static ImmutableList<Object[]> parameters() {
+    return CompilerMode.TEST_PARAMETERS;
+  }
+
+  private final CompilerMode compilerMode;
+
+  public ComponentProtectedTypeTest(CompilerMode compilerMode) {
+    this.compilerMode = compilerMode;
+  }
+
+  @Test
+  public void componentAccessesProtectedType_succeeds() {
+    JavaFileObject baseSrc =
+        JavaFileObjects.forSourceLines(
+            "test.sub.TestComponentBase",
+            "package test.sub;",
+            "",
+            "import javax.inject.Inject;",
+            "import javax.inject.Singleton;",
+            "",
+            "public abstract class TestComponentBase {",
+            "  static class Dep {",
+            "    @Inject",
+            "    Dep() {}",
+            "  }",
+            "",
+            "  @Singleton",
+            "  protected static final class ProtectedType {",
+            "    @Inject",
+            "    ProtectedType(Dep dep) {}",
+            "  }",
+            "}");
+    JavaFileObject componentSrc =
+        JavaFileObjects.forSourceLines(
+            "test.TestComponent",
+            "package test;",
+            "",
+            "import dagger.Component;",
+            "import javax.inject.Provider;",
+            "import javax.inject.Singleton;",
+            "import test.sub.TestComponentBase;",
+            "",
+            "@Singleton",
+            "@Component",
+            "public abstract class TestComponent extends TestComponentBase {",
+            // This component method will be implemented as:
+            // TestComponentBase.ProtectedType provideProtectedType() {
+            //   return protectedTypeProvider.get();
+            // }
+            // The protectedTypeProvider can't be a raw provider, otherwise it will have a type cast
+            // error. So protected accessibility should be evaluated when checking accessibility of
+            // a type.
+            "  abstract TestComponentBase.ProtectedType provideProtectedType();",
+            "}");
+    JavaFileObject generatedComponent =
+        JavaFileObjects.forSourceLines(
+            "test.DaggerTestComponent",
+            "package test;",
+            "",
+            GeneratedLines.generatedAnnotations(),
+            "public final class DaggerTestComponent extends TestComponent {",
+            "  private Provider<test.sub.TestComponentBase.ProtectedType> protectedTypeProvider;",
+            "",
+            "  @Override",
+            "  test.sub.TestComponentBase.ProtectedType provideProtectedType() {",
+            "    return protectedTypeProvider.get();",
+            "  }",
+            "}");
+
+    Compilation compilation =
+        compilerWithOptions(compilerMode.javacopts()).compile(baseSrc, componentSrc);
+
+    assertThat(compilation).succeeded();
+    assertThat(compilation)
+        .generatedSourceFile("test.DaggerTestComponent")
+        .containsElementsIn(generatedComponent);
+  }
+}
diff --git a/javatests/dagger/internal/codegen/ComponentRequirementFieldTest.java b/javatests/dagger/internal/codegen/ComponentRequirementFieldTest.java
index 05164a6..b0e30e8 100644
--- a/javatests/dagger/internal/codegen/ComponentRequirementFieldTest.java
+++ b/javatests/dagger/internal/codegen/ComponentRequirementFieldTest.java
@@ -17,6 +17,8 @@
 package dagger.internal.codegen;
 
 import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
+import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
 import static dagger.internal.codegen.Compilers.compilerWithOptions;
 
 import com.google.testing.compile.Compilation;
@@ -272,15 +274,15 @@
                 "    return Preconditions.checkNotNullFromComponent(dep.object());",
                 "  }",
                 "",
-                "  private final class TestSubcomponentImpl implements TestSubcomponent {",
+                "  private static final class TestSubcomponentImpl implements TestSubcomponent {",
                 "    @Override",
                 "    public TestComponent parent() {",
-                "      return DaggerTestComponent.this;",
+                "      return testComponent;",
                 "    }",
                 "",
                 "    @Override",
                 "    public Dep depFromSubcomponent() {",
-                "      return DaggerTestComponent.this.dep;",
+                "      return testComponent.dep;",
                 "    }",
                 "  }",
                 "}"));
@@ -348,83 +350,128 @@
             "interface TestSubcomponent {",
             "  Provider<Object> dependsOnMultibinding();",
             "}");
-    JavaFileObject generatedComponent;
-    switch (compilerMode) {
-      case FAST_INIT_MODE:
-        generatedComponent =
-            JavaFileObjects.forSourceLines(
-                "test.DaggerTestComponent",
-                "package test;",
-                "",
-                GeneratedLines.generatedAnnotations(),
-                "final class DaggerTestComponent implements TestComponent {",
-                "  private final ParentModule parentModule;",
-                "",
-                "  private DaggerTestComponent(ParentModule parentModuleParam) {",
-                "    this.parentModule = parentModuleParam;",
-                "  }",
-                "",
-                "  private final class TestSubcomponentImpl implements TestSubcomponent {",
-                "    private Set<Object> setOfObject() {",
-                "      return ImmutableSet.<Object>of(",
-                "          ParentModule_ContributionFactory.contribution(),",
-                "          ChildModule_ContributionFactory.contribution());",
-                "    }",
-                "",
-                "    private Object object() {",
-                "      return ParentModule_ReliesOnMultibindingFactory.reliesOnMultibinding(",
-                "          DaggerTestComponent.this.parentModule, setOfObject());",
-                "    }",
-                "  }",
-                "}");
-        break;
-      default:
-        generatedComponent =
-            JavaFileObjects.forSourceLines(
-                "test.DaggerTestComponent",
-                "package test;",
-                "",
-                GeneratedLines.generatedAnnotations(),
-                "final class DaggerTestComponent implements TestComponent {",
-                "  private final ParentModule parentModule;",
-                "",
-                "  private DaggerTestComponent(ParentModule parentModuleParam) {",
-                "    this.parentModule = parentModuleParam;",
-                "    initialize(parentModuleParam);",
-                "  }",
-                "",
-                "  @SuppressWarnings(\"unchecked\")",
-                "  private void initialize(final ParentModule parentModuleParam) {",
-                "    this.setOfObjectProvider =",
-                "        SetFactory.<Object>builder(1, 0)",
-                "            .addProvider(ParentModule_ContributionFactory.create())",
-                "            .build();",
-                "    this.reliesOnMultibindingProvider =",
-                "        ParentModule_ReliesOnMultibindingFactory.create(",
-                "            parentModuleParam, setOfObjectProvider);",
-                "  }",
-                "",
-                "  private final class TestSubcomponentImpl implements TestSubcomponent {",
-                "    @SuppressWarnings(\"unchecked\")",
-                "    private void initialize() {",
-                "      this.setOfObjectProvider =",
-                "          SetFactory.<Object>builder(2, 0)",
-                "              .addProvider(ParentModule_ContributionFactory.create())",
-                "              .addProvider(ChildModule_ContributionFactory.create())",
-                "              .build();",
-                "      this.reliesOnMultibindingProvider =",
-                "          ParentModule_ReliesOnMultibindingFactory.create(",
-                "              DaggerTestComponent.this.parentModule, setOfObjectProvider);",
-                "    }",
-                "  }",
-                "}");
-    }
+
     Compilation compilation =
         compilerWithOptions(compilerMode.javacopts())
             .compile(parentModule, childModule, component, subcomponent);
     assertThat(compilation).succeeded();
     assertThat(compilation)
         .generatedSourceFile("test.DaggerTestComponent")
-        .containsElementsIn(generatedComponent);
+        .containsElementsIn(
+            compilerMode
+                .javaFileBuilder("test.DaggerSimpleComponent")
+                .addLines(
+                    "package test;",
+                    "",
+                    GeneratedLines.generatedAnnotations(),
+                    "final class DaggerTestComponent implements TestComponent {",
+                    "  private final ParentModule parentModule;",
+                    "",
+                    "  private DaggerTestComponent(ParentModule parentModuleParam) {",
+                    "    this.parentModule = parentModuleParam;",
+                    "    initialize(parentModuleParam);",
+                    "  }")
+                .addLinesIn(
+                    DEFAULT_MODE,
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize(final ParentModule parentModuleParam) {",
+                    "    this.setOfObjectProvider =",
+                    "        SetFactory.<Object>builder(1, 0)",
+                    "            .addProvider(ParentModule_ContributionFactory.create())",
+                    "            .build();",
+                    "    this.reliesOnMultibindingProvider =",
+                    "        ParentModule_ReliesOnMultibindingFactory",
+                    "            .create(parentModuleParam, setOfObjectProvider);",
+                    "  }")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "",
+                    "  private Set<Object> setOfObject() {",
+                    "    return ImmutableSet.<Object>of(",
+                    "        ParentModule_ContributionFactory.contribution());",
+                    "  }",
+                    "",
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize(final ParentModule parentModuleParam) {",
+                    "    this.reliesOnMultibindingProvider =",
+                    "        new SwitchingProvider<>(testComponent, 0);",
+                    "  }")
+                .addLines(
+                    "  @Override",
+                    "  public Provider<Object> dependsOnMultibinding() {",
+                    "    return reliesOnMultibindingProvider;",
+                    "  }",
+                    "",
+                    "  @Override",
+                    "  public TestSubcomponent subcomponent() {",
+                    "    return new TestSubcomponentImpl(testComponent);",
+                    "  }",
+                    "",
+                    "  private static final class TestSubcomponentImpl",
+                    "      implements TestSubcomponent {")
+                .addLinesIn(
+                    DEFAULT_MODE,
+                    "    @SuppressWarnings(\"unchecked\")",
+                    "    private void initialize() {",
+                    "      this.setOfObjectProvider =",
+                    "          SetFactory.<Object>builder(2, 0)",
+                    "              .addProvider(ParentModule_ContributionFactory.create())",
+                    "              .addProvider(ChildModule_ContributionFactory.create())",
+                    "              .build();",
+                    "      this.reliesOnMultibindingProvider =",
+                    "          ParentModule_ReliesOnMultibindingFactory",
+                    "              .create(testComponent.parentModule, setOfObjectProvider);",
+                    "    }")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "    private Set<Object> setOfObject() {",
+                    "      return ImmutableSet.<Object>of(",
+                    "          ParentModule_ContributionFactory.contribution(),",
+                    "          ChildModule_ContributionFactory.contribution());",
+                    "    }",
+                    "",
+                    "    @SuppressWarnings(\"unchecked\")",
+                    "    private void initialize() {",
+                    "      this.reliesOnMultibindingProvider =",
+                    "          new SwitchingProvider<>(testComponent, testSubcomponentImpl, 0);",
+                    "    }")
+                .addLines(
+                    "    @Override",
+                    "    public Provider<Object> dependsOnMultibinding() {",
+                    "      return reliesOnMultibindingProvider;",
+                    "    }")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "    private static final class SwitchingProvider<T> implements Provider<T> {",
+                    "      @SuppressWarnings(\"unchecked\")",
+                    "      @Override",
+                    "      public T get() {",
+                    "        switch (id) {",
+                    "          case 0:",
+                    "          return (T) ParentModule_ReliesOnMultibindingFactory",
+                    "            .reliesOnMultibinding(",
+                    "              testComponent.parentModule,",
+                    "              testSubcomponentImpl.setOfObject());",
+                    "          default: throw new AssertionError(id);",
+                    "        }",
+                    "      }",
+                    "    }",
+                    "  }",
+                    "",
+                    "  private static final class SwitchingProvider<T> implements Provider<T> {",
+                    "    @SuppressWarnings(\"unchecked\")",
+                    "    @Override",
+                    "    public T get() {",
+                    "      switch (id) {",
+                    "        case 0:",
+                    "        return (T) ParentModule_ReliesOnMultibindingFactory",
+                    "          .reliesOnMultibinding(",
+                    "             testComponent.parentModule, testComponent.setOfObject());",
+                    "        default: throw new AssertionError(id);",
+                    "      }",
+                    "    }",
+                    "  }",
+                    "}")
+                .build());
   }
 }
diff --git a/javatests/dagger/internal/codegen/ComponentShardTest.java b/javatests/dagger/internal/codegen/ComponentShardTest.java
index ec7f499..015a8cf 100644
--- a/javatests/dagger/internal/codegen/ComponentShardTest.java
+++ b/javatests/dagger/internal/codegen/ComponentShardTest.java
@@ -17,9 +17,12 @@
 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.CompilerMode.DEFAULT_MODE;
+import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
 import static java.util.stream.Collectors.joining;
 
+import com.google.common.collect.ImmutableCollection;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.testing.compile.Compilation;
@@ -29,211 +32,268 @@
 import javax.tools.JavaFileObject;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
-@RunWith(JUnit4.class)
+@RunWith(Parameterized.class)
 public class ComponentShardTest {
-  private static final int BINDINGS_PER_SHARD = 10;
+  private static final int BINDINGS_PER_SHARD = 2;
+
+  @Parameters(name = "{0}")
+  public static ImmutableCollection<Object[]> parameters() {
+    return CompilerMode.TEST_PARAMETERS;
+  }
+
+  private final CompilerMode compilerMode;
+
+  public ComponentShardTest(CompilerMode compilerMode) {
+    this.compilerMode = compilerMode;
+  }
 
   @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;
+    // Add all bindings.
+    //
+    //     1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7
+    //          ^--------/
+    //
     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));
+    javaFileObjects
+        // Shard 2: Bindings (1)
+        .add(createBinding("Binding1", "Binding2 binding2"))
+        // Shard 1: Bindings (2, 3, 4, 5). Contains more than 2 bindings due to cycle.
+        .add(createBinding("Binding2", "Binding3 binding3"))
+        .add(createBinding("Binding3", "Binding4 binding4"))
+        .add(createBinding("Binding4", "Binding5 binding5, Provider<Binding2> binding2Provider"))
+        .add(createBinding("Binding5", "Binding6 binding6"))
+        // Component shard: Bindings (6, 7)
+        .add(createBinding("Binding6", "Binding7 binding7"))
+        .add(createBinding("Binding7"));
 
-      // 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 =
+    // Add the component with entry points for each binding and its provider.
+    javaFileObjects.add(
         JavaFileObjects.forSourceLines(
-            "dagger.internal.codegen.DaggerTestComponent",
-                "package dagger.internal.codegen;",
-            GeneratedLines.generatedAnnotations(),
-                "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;",
-                "    }",
-                "  }",
-                "}");
+            "dagger.internal.codegen.TestComponent",
+            "package dagger.internal.codegen;",
+            "",
+            "import dagger.Component;",
+            "import javax.inject.Provider;",
+            "import javax.inject.Singleton;",
+            "",
+            "@Singleton",
+            "@Component",
+            "interface TestComponent {",
+            "  Binding1 binding1();",
+            "  Binding2 binding2();",
+            "  Binding3 binding3();",
+            "  Binding4 binding4();",
+            "  Binding5 binding5();",
+            "  Binding6 binding6();",
+            "  Binding7 binding7();",
+            "  Provider<Binding1> providerBinding1();",
+            "  Provider<Binding2> providerBinding2();",
+            "  Provider<Binding3> providerBinding3();",
+            "  Provider<Binding4> providerBinding4();",
+            "  Provider<Binding5> providerBinding5();",
+            "  Provider<Binding6> providerBinding6();",
+            "  Provider<Binding7> providerBinding7();",
+            "}"));
 
-    Compilation compilation = compilerWithAndroidMode().compile(javaFileObjects.build());
+    Compilation compilation = compiler().compile(javaFileObjects.build());
     assertThat(compilation).succeededWithoutWarnings();
     assertThat(compilation)
         .generatedSourceFile("dagger.internal.codegen.DaggerTestComponent")
-        .containsElementsIn(generatedComponent);
+        .containsElementsIn(
+            compilerMode
+                .javaFileBuilder("dagger.internal.codegen.DaggerTestComponent")
+                .addLines(
+                    "package dagger.internal.codegen;",
+                    "",
+                    GeneratedLines.generatedAnnotations(),
+                    "final class DaggerTestComponent implements TestComponent {",
+                    "  private Shard1 shard1;",
+                    "  private Shard2 shard2;",
+                    "  private final DaggerTestComponent testComponent = this;",
+                    "  private Provider<Binding7> binding7Provider;",
+                    "  private Provider<Binding6> binding6Provider;",
+                    "",
+                    "  private DaggerTestComponent() {",
+                    "    initialize();",
+                    "    shard1 = new Shard1();",
+                    "    shard2 = new Shard2();",
+                    "  }")
+                .addLinesIn(
+                    DEFAULT_MODE,
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize() {",
+                    "    this.binding7Provider =",
+                    "        DoubleCheck.provider(Binding7_Factory.create());",
+                    "    this.binding6Provider =",
+                    "        DoubleCheck.provider(Binding6_Factory.create(binding7Provider));",
+                    "  }")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize() {",
+                    "    this.binding7Provider = DoubleCheck.provider(",
+                    "        new SwitchingProvider<Binding7>(testComponent, 6));",
+                    "    this.binding6Provider = DoubleCheck.provider(",
+                    "        new SwitchingProvider<Binding6>(testComponent, 5));",
+                    "  }")
+                .addLines(
+                    "  @Override",
+                    "  public Binding1 binding1() {",
+                    "    return testComponent.shard2.binding1Provider.get();",
+                    "  }",
+                    "",
+                    "  @Override",
+                    "  public Binding2 binding2() {",
+                    "    return testComponent.shard1.binding2Provider.get();",
+                    "  }",
+                    "",
+                    "  @Override",
+                    "  public Binding3 binding3() {",
+                    "    return testComponent.shard1.binding3Provider.get();",
+                    "  }",
+                    "",
+                    "  @Override",
+                    "  public Binding4 binding4() {",
+                    "    return testComponent.shard1.binding4Provider.get();",
+                    "  }",
+                    "",
+                    "  @Override",
+                    "  public Binding5 binding5() {",
+                    "    return testComponent.shard1.binding5Provider.get();",
+                    "  }",
+                    "",
+                    "  @Override",
+                    "  public Binding6 binding6() {",
+                    "    return binding6Provider.get();",
+                    "  }",
+                    "",
+                    "  @Override",
+                    "  public Binding7 binding7() {",
+                    "    return binding7Provider.get();",
+                    "  }",
+                    "",
+                    "  @Override",
+                    "  public Provider<Binding1> providerBinding1() {",
+                    "    return testComponent.shard2.binding1Provider;",
+                    "  }",
+                    "",
+                    "  @Override",
+                    "  public Provider<Binding2> providerBinding2() {",
+                    "    return testComponent.shard1.binding2Provider;",
+                    "  }",
+                    "",
+                    "  @Override",
+                    "  public Provider<Binding3> providerBinding3() {",
+                    "    return testComponent.shard1.binding3Provider;",
+                    "  }",
+                    "",
+                    "  @Override",
+                    "  public Provider<Binding4> providerBinding4() {",
+                    "    return testComponent.shard1.binding4Provider;",
+                    "  }",
+                    "",
+                    "  @Override",
+                    "  public Provider<Binding5> providerBinding5() {",
+                    "    return testComponent.shard1.binding5Provider;",
+                    "  }",
+                    "",
+                    "  @Override",
+                    "  public Provider<Binding6> providerBinding6() {",
+                    "    return binding6Provider;",
+                    "  }",
+                    "",
+                    "  @Override",
+                    "  public Provider<Binding7> providerBinding7() {",
+                    "    return binding7Provider;",
+                    "  }",
+                    "",
+                    "  private final class Shard1 {",
+                    "    private Provider<Binding5> binding5Provider;",
+                    "    private Provider<Binding2> binding2Provider;",
+                    "    private Provider<Binding4> binding4Provider;",
+                    "    private Provider<Binding3> binding3Provider;")
+                .addLinesIn(
+                    DEFAULT_MODE,
+                    "    @SuppressWarnings(\"unchecked\")",
+                    "    private void initialize() {",
+                    "      this.binding5Provider =",
+                    "          DoubleCheck.provider(",
+                    "              Binding5_Factory.create(testComponent.binding6Provider));",
+                    "      this.binding2Provider = new DelegateFactory<>();",
+                    "      this.binding4Provider =",
+                    "          DoubleCheck.provider(",
+                    "              Binding4_Factory.create(binding5Provider, binding2Provider));",
+                    "      this.binding3Provider =",
+                    "          DoubleCheck.provider(Binding3_Factory.create(binding4Provider));",
+                    "      DelegateFactory.setDelegate(",
+                    "          binding2Provider,",
+                    "          DoubleCheck.provider(Binding2_Factory.create(binding3Provider)));",
+                    "    }",
+                    "  }")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "    @SuppressWarnings(\"unchecked\")",
+                    "    private void initialize() {",
+                    "      this.binding5Provider = DoubleCheck.provider(",
+                    "          new SwitchingProvider<Binding5>(testComponent, 4));",
+                    "      this.binding4Provider = DoubleCheck.provider(",
+                    "          new SwitchingProvider<Binding4>(testComponent, 3));",
+                    "      this.binding3Provider = DoubleCheck.provider(",
+                    "          new SwitchingProvider<Binding3>(testComponent, 2));",
+                    "      this.binding2Provider = DoubleCheck.provider(",
+                    "          new SwitchingProvider<Binding2>(testComponent, 1));",
+                    "    }",
+                    "  }")
+                .addLines(
+                    "  private final class Shard2 {",
+                    "    private Provider<Binding1> binding1Provider;")
+                .addLinesIn(
+                    DEFAULT_MODE,
+                    "    @SuppressWarnings(\"unchecked\")",
+                    "    private void initialize() {",
+                    "      this.binding1Provider =",
+                    "          DoubleCheck.provider(",
+                    "              Binding1_Factory.create(",
+                    "                  testComponent.shard1.binding2Provider));",
+                    "    }",
+                    "  }")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "    @SuppressWarnings(\"unchecked\")",
+                    "    private void initialize() {",
+                    "      this.binding1Provider = DoubleCheck.provider(",
+                    "          new SwitchingProvider<Binding1>(testComponent, 0));",
+                    "    }",
+                    "  }",
+                    "",
+                    "  private static final class SwitchingProvider<T> implements Provider<T> {",
+                    "    @SuppressWarnings(\"unchecked\")",
+                    "    @Override",
+                    "    public T get() {",
+                    "      switch (id) {",
+                    "        case 0: return (T) new Binding1(",
+                    "            testComponent.shard1.binding2Provider.get());",
+                    "        case 1: return (T) new Binding2(",
+                    "            testComponent.shard1.binding3Provider.get());",
+                    "        case 2: return (T) new Binding3(",
+                    "            testComponent.shard1.binding4Provider.get());",
+                    "        case 3: return (T) new Binding4(",
+                    "            testComponent.shard1.binding5Provider.get(),",
+                    "            testComponent.shard1.binding2Provider);",
+                    "        case 4: return (T) new Binding5(",
+                    "            testComponent.binding6Provider.get());",
+                    "        case 5: return (T) new Binding6(",
+                    "            testComponent.binding7Provider.get());",
+                    "        case 6: return (T) new Binding7();",
+                    "        default: throw new AssertionError(id);",
+                    "      }",
+                    "    }",
+                    "  }")
+                .build());
   }
 
   private static JavaFileObject createBinding(String bindingName, String... deps) {
@@ -252,29 +312,11 @@
         "}");
   }
 
-  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());
+  private Compiler compiler() {
+    return compilerWithOptions(
+        ImmutableSet.<String>builder()
+            .add("-Adagger.keysPerComponentShard=" + BINDINGS_PER_SHARD)
+            .addAll(compilerMode.javacopts())
+            .build());
   }
 }
diff --git a/javatests/dagger/internal/codegen/ComponentValidationKtTest.java b/javatests/dagger/internal/codegen/ComponentValidationKtTest.java
new file mode 100644
index 0000000..4600f99
--- /dev/null
+++ b/javatests/dagger/internal/codegen/ComponentValidationKtTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.common.truth.Truth.assertThat;
+import static dagger.testing.compile.CompilerTests.kotlinCompiler;
+
+import com.google.common.collect.ImmutableList;
+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 final class ComponentValidationKtTest {
+  @Test
+  public void creatorMethodNameIsJavaKeyword_compilationError() {
+    SourceFile componentSrc =
+        SourceFile.Companion.kotlin(
+            "FooComponent.kt",
+            String.join(
+                "\n",
+                "package test",
+                "",
+                "import dagger.BindsInstance",
+                "import dagger.Component",
+                "",
+                "@Component",
+                "interface FooComponent {",
+                "  @Component.Builder",
+                "  interface Builder {",
+                "    @BindsInstance public fun int(str: Int): Builder",
+                "    public fun build(): FooComponent",
+                "  }",
+                "}"),
+            false);
+    KotlinCompilation compilation = kotlinCompiler();
+    compilation.setSources(ImmutableList.of(componentSrc));
+
+    KotlinCompilation.Result result = compilation.compile();
+
+    // TODO(b/192396673): Add error count when the feature request is fulfilled.
+    assertThat(result.getExitCode()).isEqualTo(ExitCode.COMPILATION_ERROR);
+    assertThat(result.getMessages())
+        .contains("Can not use a Java keyword as method name: int(I)Ltest/FooComponent$Builder");
+  }
+
+  @Test
+  public void componentMethodNameIsJavaKeyword_compilationError() {
+    SourceFile componentSrc =
+        SourceFile.Companion.kotlin(
+            "FooComponent.kt",
+            String.join(
+                "\n",
+                "package test",
+                "",
+                "import dagger.BindsInstance",
+                "import dagger.Component",
+                "",
+                "@Component(modules = [TestModule::class])",
+                "interface FooComponent {",
+                "  fun int(str: Int): String",
+                "}"),
+            false);
+    SourceFile moduleSrc =
+        SourceFile.Companion.kotlin(
+            "TestModule.kt",
+            String.join(
+                "\n",
+                "package test",
+                "",
+                "import dagger.Module",
+                "",
+                "@Module",
+                "interface TestModule {",
+                "  fun providesString(): String {",
+                "    return \"test\"",
+                "  }",
+                "}"),
+            false);
+    KotlinCompilation compilation = kotlinCompiler();
+    compilation.setSources(ImmutableList.of(componentSrc, moduleSrc));
+
+    KotlinCompilation.Result result = compilation.compile();
+
+    assertThat(result.getExitCode()).isEqualTo(ExitCode.COMPILATION_ERROR);
+    assertThat(result.getMessages())
+        .contains("Can not use a Java keyword as method name: int(I)Ljava/lang/String");
+  }
+}
diff --git a/javatests/dagger/internal/codegen/ComponentValidationTest.java b/javatests/dagger/internal/codegen/ComponentValidationTest.java
index 6c642a4..1575111 100644
--- a/javatests/dagger/internal/codegen/ComponentValidationTest.java
+++ b/javatests/dagger/internal/codegen/ComponentValidationTest.java
@@ -44,6 +44,118 @@
     assertThat(compilation).hadErrorContaining("interface");
   }
 
+  @Test
+  public void componentOnOverridingBuilder_failsWhenMethodNameConflictsWithStaticCreatorName() {
+    JavaFileObject componentFile =
+        JavaFileObjects.forSourceLines(
+            "test.TestComponent",
+            "package test;",
+            "",
+            "import dagger.Component;",
+            "",
+            "@Component(modules=TestModule.class)",
+            "interface TestComponent {",
+            "  String builder();",
+            "}");
+    JavaFileObject moduleFile =
+        JavaFileObjects.forSourceLines(
+            "test.TestModule",
+            "package test;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "",
+            "@Module",
+            "interface TestModule {",
+            "  @Provides",
+            "  static String provideString() { return \"test\"; }",
+            "}");
+
+    Compilation compilation = daggerCompiler().compile(componentFile, moduleFile);
+    assertThat(compilation).failed();
+    assertThat(compilation)
+        .hadErrorContaining("Cannot override generated method: TestComponent.builder()");
+  }
+
+  @Test
+  public void componentOnOverridingCreate_failsWhenGeneratedCreateMethod() {
+    JavaFileObject componentFile =
+        JavaFileObjects.forSourceLines(
+            "test.TestComponent",
+            "package test;",
+            "",
+            "import dagger.Component;",
+            "",
+            "@Component(modules=TestModule.class)",
+            "interface TestComponent {",
+            "  String create();",
+            "}");
+    JavaFileObject moduleFile =
+        JavaFileObjects.forSourceLines(
+            "test.TestModule",
+            "package test;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "",
+            "@Module",
+            "interface TestModule {",
+            "  @Provides",
+            "  static String provideString() { return \"test\"; }",
+            "}");
+
+    Compilation compilation = daggerCompiler().compile(componentFile, moduleFile);
+    assertThat(compilation).failed();
+    assertThat(compilation)
+        .hadErrorContaining("Cannot override generated method: TestComponent.create()");
+  }
+
+  @Test
+  public void subcomponentMethodNameBuilder_succeeds() {
+    JavaFileObject componentFile =
+        JavaFileObjects.forSourceLines(
+            "test.TestComponent",
+            "package test;",
+            "",
+            "import dagger.Component;",
+            "",
+            "@Component",
+            "interface TestComponent {",
+            "  TestSubcomponent.Builder subcomponent();",
+            "}");
+    JavaFileObject subcomponentFile =
+        JavaFileObjects.forSourceLines(
+            "test.TestSubcomponent",
+            "package test;",
+            "",
+            "import dagger.Subcomponent;",
+            "",
+            "@Subcomponent(modules=TestModule.class)",
+            "interface TestSubcomponent {",
+            "  String builder();",
+            "  @Subcomponent.Builder",
+            "  interface Builder {",
+            "    TestSubcomponent build();",
+            "  }",
+            "}");
+    JavaFileObject moduleFile =
+        JavaFileObjects.forSourceLines(
+            "test.TestModule",
+            "package test;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "",
+            "@Module",
+            "interface TestModule {",
+            "  @Provides",
+            "  static String provideString() { return \"test\"; }",
+            "}");
+
+    Compilation compilation = daggerCompiler().compile(componentFile, subcomponentFile, moduleFile);
+    assertThat(compilation).succeeded();
+  }
+
   @Test public void componentOnEnum() {
     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.NotAComponent",
         "package test;",
diff --git a/javatests/dagger/internal/codegen/DaggerSuperficialValidationTest.java b/javatests/dagger/internal/codegen/DaggerSuperficialValidationTest.java
new file mode 100644
index 0000000..a754108
--- /dev/null
+++ b/javatests/dagger/internal/codegen/DaggerSuperficialValidationTest.java
@@ -0,0 +1,615 @@
+/*
+ * 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.common.truth.Truth.assertAbout;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
+import static org.junit.Assert.assertThrows;
+
+import androidx.room.compiler.processing.XConstructorElement;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XTypeElement;
+import androidx.room.compiler.processing.XVariableElement;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableSet;
+import com.google.testing.compile.JavaFileObjects;
+import dagger.Component;
+import dagger.internal.codegen.base.DaggerSuperficialValidation;
+import dagger.internal.codegen.base.DaggerSuperficialValidation.ValidationException;
+import dagger.internal.codegen.javac.JavacPluginModule;
+import java.util.Set;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.inject.Singleton;
+import javax.lang.model.element.TypeElement;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class DaggerSuperficialValidationTest {
+  private static final Joiner NEW_LINES = Joiner.on("\n  ");
+
+  @Test
+  public void missingReturnType() {
+    JavaFileObject javaFileObject =
+        JavaFileObjects.forSourceLines(
+            "test.TestClass",
+            "package test;",
+            "",
+            "abstract class TestClass {",
+            "  abstract MissingType blah();",
+            "}");
+    assertAbout(javaSource())
+        .that(javaFileObject)
+        .processedWith(
+            new AssertingProcessor() {
+              @Override
+              void runAssertions(
+                  XProcessingEnv processingEnv, DaggerSuperficialValidation superficialValidation) {
+                XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass");
+                ValidationException exception =
+                    assertThrows(
+                        ValidationException.KnownErrorType.class,
+                        () -> superficialValidation.validateElement(testClassElement));
+                assertThat(exception)
+                    .hasMessageThat()
+                    .contains(
+                        NEW_LINES.join(
+                            "Validation trace:",
+                            "  => element (CLASS): test.TestClass",
+                            "  => element (METHOD): blah()",
+                            "  => type (EXECUTABLE method): ()MissingType",
+                            "  => type (ERROR return type): MissingType"));
+              }
+            })
+        .failsToCompile();
+  }
+
+  @Test
+  public void missingGenericReturnType() {
+    JavaFileObject javaFileObject =
+        JavaFileObjects.forSourceLines(
+            "test.TestClass",
+            "package test;",
+            "",
+            "abstract class TestClass {",
+            "  abstract MissingType<?> blah();",
+            "}");
+    assertAbout(javaSource())
+        .that(javaFileObject)
+        .processedWith(
+            new AssertingProcessor() {
+              @Override
+              void runAssertions(
+                  XProcessingEnv processingEnv, DaggerSuperficialValidation superficialValidation) {
+                XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass");
+                ValidationException exception =
+                    assertThrows(
+                        ValidationException.KnownErrorType.class,
+                        () -> superficialValidation.validateElement(testClassElement));
+                assertThat(exception)
+                    .hasMessageThat()
+                    .contains(
+                        NEW_LINES.join(
+                            "Validation trace:",
+                            "  => element (CLASS): test.TestClass",
+                            "  => element (METHOD): blah()",
+                            "  => type (EXECUTABLE method): ()<any>",
+                            "  => type (ERROR return type): <any>"));
+              }
+            })
+        .failsToCompile();
+  }
+
+  @Test
+  public void missingReturnTypeTypeParameter() {
+    JavaFileObject javaFileObject =
+        JavaFileObjects.forSourceLines(
+            "test.TestClass",
+            "package test;",
+            "",
+            "import java.util.Map;",
+            "import java.util.Set;",
+            "",
+            "abstract class TestClass {",
+            "  abstract Map<Set<?>, MissingType<?>> blah();",
+            "}");
+    assertAbout(javaSource())
+        .that(javaFileObject)
+        .processedWith(
+            new AssertingProcessor() {
+              @Override
+              void runAssertions(
+                  XProcessingEnv processingEnv, DaggerSuperficialValidation superficialValidation) {
+                XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass");
+                ValidationException exception =
+                    assertThrows(
+                        ValidationException.KnownErrorType.class,
+                        () -> superficialValidation.validateElement(testClassElement));
+                assertThat(exception)
+                    .hasMessageThat()
+                    .contains(
+                        NEW_LINES.join(
+                            "Validation trace:",
+                            "  => element (CLASS): test.TestClass",
+                            "  => element (METHOD): blah()",
+                            "  => type (EXECUTABLE method): "
+                                + "()java.util.Map<java.util.Set<?>,<any>>",
+                            "  => type (DECLARED return type): "
+                                + "java.util.Map<java.util.Set<?>,<any>>",
+                            "  => type (ERROR type argument): <any>"));
+              }
+            })
+        .failsToCompile();
+  }
+
+  @Test
+  public void missingTypeParameter() {
+    JavaFileObject javaFileObject =
+        JavaFileObjects.forSourceLines(
+            "test.TestClass", //
+            "package test;",
+            "",
+            "class TestClass<T extends MissingType> {}");
+    assertAbout(javaSource())
+        .that(javaFileObject)
+        .processedWith(
+            new AssertingProcessor() {
+              @Override
+              void runAssertions(
+                  XProcessingEnv processingEnv, DaggerSuperficialValidation superficialValidation) {
+                XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass");
+                ValidationException exception =
+                    assertThrows(
+                        ValidationException.KnownErrorType.class,
+                        () -> superficialValidation.validateElement(testClassElement));
+                assertThat(exception)
+                    .hasMessageThat()
+                    .contains(
+                        NEW_LINES.join(
+                            "Validation trace:",
+                            "  => element (CLASS): test.TestClass",
+                            "  => element (TYPE_PARAMETER): T",
+                            "  => type (ERROR bound type): MissingType"));
+              }
+            })
+        .failsToCompile();
+  }
+
+  @Test
+  public void missingParameterType() {
+    JavaFileObject javaFileObject =
+        JavaFileObjects.forSourceLines(
+            "test.TestClass",
+            "package test;",
+            "",
+            "abstract class TestClass {",
+            "  abstract void foo(MissingType x);",
+            "}");
+    assertAbout(javaSource())
+        .that(javaFileObject)
+        .processedWith(
+            new AssertingProcessor() {
+              @Override
+              void runAssertions(
+                  XProcessingEnv processingEnv, DaggerSuperficialValidation superficialValidation) {
+                XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass");
+                ValidationException exception =
+                    assertThrows(
+                        ValidationException.KnownErrorType.class,
+                        () -> superficialValidation.validateElement(testClassElement));
+                assertThat(exception)
+                    .hasMessageThat()
+                    .contains(
+                        NEW_LINES.join(
+                            "Validation trace:",
+                            "  => element (CLASS): test.TestClass",
+                            "  => element (METHOD): foo(MissingType)",
+                            "  => type (EXECUTABLE method): (MissingType)void",
+                            "  => type (ERROR parameter type): MissingType"));
+              }
+            })
+        .failsToCompile();
+  }
+
+  @Test
+  public void missingAnnotation() {
+    JavaFileObject javaFileObject =
+        JavaFileObjects.forSourceLines(
+            "test.TestClass", //
+            "package test;",
+            "",
+            "@MissingAnnotation",
+            "class TestClass {}");
+    assertAbout(javaSource())
+        .that(javaFileObject)
+        .processedWith(
+            new AssertingProcessor() {
+              @Override
+              void runAssertions(
+                  XProcessingEnv processingEnv, DaggerSuperficialValidation superficialValidation) {
+                XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass");
+                ValidationException exception =
+                    assertThrows(
+                        ValidationException.KnownErrorType.class,
+                        () -> superficialValidation.validateElement(testClassElement));
+                assertThat(exception)
+                    .hasMessageThat()
+                    .contains(
+                        NEW_LINES.join(
+                            "Validation trace:",
+                            "  => element (CLASS): test.TestClass",
+                            "  => annotation: @MissingAnnotation",
+                            "  => type (ERROR annotation type): MissingAnnotation"));
+              }
+            })
+        .failsToCompile();
+  }
+
+  @Test
+  public void handlesRecursiveTypeParams() {
+    JavaFileObject javaFileObject =
+        JavaFileObjects.forSourceLines(
+            "test.TestClass", //
+            "package test;",
+            "",
+            "class TestClass<T extends Comparable<T>> {}");
+    assertAbout(javaSource())
+        .that(javaFileObject)
+        .processedWith(
+            new AssertingProcessor() {
+              @Override
+              void runAssertions(
+                  XProcessingEnv processingEnv, DaggerSuperficialValidation superficialValidation) {
+                XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass");
+                superficialValidation.validateElement(testClassElement);
+              }
+            })
+        .compilesWithoutError();
+  }
+
+  @Test
+  public void handlesRecursiveType() {
+    JavaFileObject javaFileObject =
+        JavaFileObjects.forSourceLines(
+            "test.TestClass",
+            "package test;",
+            "",
+            "abstract class TestClass {",
+            "  abstract TestClass foo(TestClass x);",
+            "}");
+    assertAbout(javaSource())
+        .that(javaFileObject)
+        .processedWith(
+            new AssertingProcessor() {
+              @Override
+              void runAssertions(
+                  XProcessingEnv processingEnv, DaggerSuperficialValidation superficialValidation) {
+                XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass");
+                superficialValidation.validateElement(testClassElement);
+              }
+            })
+        .compilesWithoutError();
+  }
+
+  @Test
+  public void missingWildcardBound() {
+    JavaFileObject javaFileObject =
+        JavaFileObjects.forSourceLines(
+            "test.TestClass",
+            "package test;",
+            "",
+            "import java.util.Set;",
+            "",
+            "class TestClass {",
+            "  Set<? extends MissingType> extendsTest() {",
+            "    return null;",
+            "  }",
+            "",
+            "  Set<? super MissingType> superTest() {",
+            "    return null;",
+            "  }",
+            "}");
+    assertAbout(javaSource())
+        .that(javaFileObject)
+        .processedWith(
+            new AssertingProcessor() {
+              @Override
+              void runAssertions(
+                  XProcessingEnv processingEnv, DaggerSuperficialValidation superficialValidation) {
+                XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass");
+                ValidationException exception =
+                    assertThrows(
+                        ValidationException.KnownErrorType.class,
+                        () -> superficialValidation.validateElement(testClassElement));
+                assertThat(exception)
+                    .hasMessageThat()
+                    .contains(
+                        NEW_LINES.join(
+                            "Validation trace:",
+                            "  => element (CLASS): test.TestClass",
+                            "  => element (METHOD): extendsTest()",
+                            "  => type (EXECUTABLE method): ()java.util.Set<? extends MissingType>",
+                            "  => type (DECLARED return type): "
+                                + "java.util.Set<? extends MissingType>",
+                            "  => type (WILDCARD type argument): ? extends MissingType",
+                            "  => type (ERROR extends bound type): MissingType"));
+              }
+            })
+        .failsToCompile();
+  }
+
+  @Test
+  public void missingIntersection() {
+    JavaFileObject javaFileObject =
+        JavaFileObjects.forSourceLines(
+            "test.TestClass",
+            "package test;",
+            "",
+            "class TestClass<T extends Number & Missing> {}");
+    assertAbout(javaSource())
+        .that(javaFileObject)
+        .processedWith(
+            new AssertingProcessor() {
+              @Override
+              void runAssertions(
+                  XProcessingEnv processingEnv, DaggerSuperficialValidation superficialValidation) {
+                XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass");
+                ValidationException exception =
+                    assertThrows(
+                        ValidationException.KnownErrorType.class,
+                        () -> superficialValidation.validateElement(testClassElement));
+                assertThat(exception)
+                    .hasMessageThat()
+                    .contains(
+                        NEW_LINES.join(
+                            "Validation trace:",
+                            "  => element (CLASS): test.TestClass",
+                            "  => element (TYPE_PARAMETER): T",
+                            "  => type (ERROR bound type): Missing"));
+              }
+            })
+        .failsToCompile();
+  }
+
+  @Test
+  public void invalidAnnotationValue() {
+    JavaFileObject javaFileObject =
+        JavaFileObjects.forSourceLines(
+            "test.Outer",
+            "package test;",
+            "",
+            "final class Outer {",
+            "  @interface TestAnnotation {",
+            "    Class[] classes();",
+            "  }",
+            "",
+            "  @TestAnnotation(classes = Foo)",
+            "  static class TestClass {}",
+            "}");
+    assertAbout(javaSource())
+        .that(javaFileObject)
+        .processedWith(
+            new AssertingProcessor() {
+              @Override
+              void runAssertions(
+                  XProcessingEnv processingEnv, DaggerSuperficialValidation superficialValidation) {
+                XTypeElement testClassElement =
+                    processingEnv.findTypeElement("test.Outer.TestClass");
+                ValidationException exception =
+                    assertThrows(
+                        ValidationException.KnownErrorType.class,
+                        () -> superficialValidation.validateElement(testClassElement));
+                assertThat(exception)
+                    .hasMessageThat()
+                    .contains(
+                        NEW_LINES.join(
+                            "Validation trace:",
+                            "  => element (CLASS): test.Outer.TestClass",
+                            "  => annotation: @test.Outer.TestAnnotation(classes = \"<error>\")",
+                            "  => annotation method: java.lang.Class[] classes()",
+                            "  => annotation value (ARRAY): value '<error>' with expected type"
+                                + " java.lang.Class[]",
+                            "  => annotation value (STRING): value '<error>' with expected type"
+                                + " java.lang.Class"));
+              }
+            })
+        .failsToCompile();
+  }
+
+  @Test
+  public void invalidAnnotationValueOnParameter() {
+    JavaFileObject javaFileObject =
+        JavaFileObjects.forSourceLines(
+            "test.Outer",
+            "package test;",
+            "",
+            "final class Outer {",
+            "  @interface TestAnnotation {",
+            "    Class[] classes();",
+            "  }",
+            "",
+            "  static class TestClass {",
+            "    TestClass(@TestAnnotation(classes = Foo) String strParam) {}",
+            "  }",
+            "}");
+    assertAbout(javaSource())
+        .that(javaFileObject)
+        .processedWith(
+            new AssertingProcessor() {
+              @Override
+              void runAssertions(
+                  XProcessingEnv processingEnv, DaggerSuperficialValidation superficialValidation) {
+                XTypeElement testClassElement =
+                    processingEnv.findTypeElement("test.Outer.TestClass");
+                XConstructorElement constructor = testClassElement.getConstructors().get(0);
+                XVariableElement parameter = constructor.getParameters().get(0);
+                ValidationException exception =
+                    assertThrows(
+                        ValidationException.KnownErrorType.class,
+                        () -> superficialValidation.validateElement(parameter));
+                assertThat(exception)
+                    .hasMessageThat()
+                    .contains(
+                        NEW_LINES.join(
+                            "Validation trace:",
+                            "  => element (CLASS): test.Outer.TestClass",
+                            "  => element (CONSTRUCTOR): TestClass(java.lang.String)",
+                            "  => element (PARAMETER): strParam",
+                            "  => annotation: @test.Outer.TestAnnotation(classes = \"<error>\")",
+                            "  => annotation method: java.lang.Class[] classes()",
+                            "  => annotation value (ARRAY): value '<error>' with expected type"
+                                + " java.lang.Class[]",
+                            "  => annotation value (STRING): value '<error>' with expected type"
+                                + " java.lang.Class"));
+              }
+            })
+        .failsToCompile();
+  }
+
+  @Test
+  public void invalidSuperclassInTypeHierarchy() {
+    JavaFileObject javaFileObject =
+        JavaFileObjects.forSourceLines(
+            "test.Outer",
+            "package test;",
+            "",
+            "final class Outer {",
+            "  Child<Long> getChild() { return null; }",
+            "",
+            "  static class Child<T> extends Parent<T> {}",
+            "",
+            "  static class Parent<T> extends MissingType<T> {}",
+            "}");
+    assertAbout(javaSource())
+        .that(javaFileObject)
+        .processedWith(
+            new AssertingProcessor() {
+              @Override
+              void runAssertions(
+                  XProcessingEnv processingEnv, DaggerSuperficialValidation superficialValidation) {
+                XTypeElement outerElement = processingEnv.findTypeElement("test.Outer");
+                XMethodElement getChildMethod = outerElement.getDeclaredMethods().get(0);
+                ValidationException exception =
+                    assertThrows(
+                        ValidationException.KnownErrorType.class,
+                        () ->
+                            superficialValidation.validateTypeHierarchyOf(
+                                "return type", getChildMethod, getChildMethod.getReturnType()));
+                assertThat(exception)
+                    .hasMessageThat()
+                    .contains(
+                        NEW_LINES.join(
+                            "Validation trace:",
+                            "  => element (CLASS): test.Outer",
+                            "  => element (METHOD): getChild()",
+                            "  => type (DECLARED return type): test.Outer.Child<java.lang.Long>",
+                            "  => type (DECLARED supertype): test.Outer.Parent<java.lang.Long>",
+                            "  => type (ERROR supertype): MissingType<T>"));
+              }
+            })
+        .failsToCompile();
+  }
+
+  @Test
+  public void invalidSuperclassTypeParameterInTypeHierarchy() {
+    JavaFileObject javaFileObject =
+        JavaFileObjects.forSourceLines(
+            "test.Outer",
+            "package test;",
+            "",
+            "final class Outer {",
+            "  Child getChild() { return null; }",
+            "",
+            "  static class Child extends Parent<MissingType> {}",
+            "",
+            "  static class Parent<T> {}",
+            "}");
+    assertAbout(javaSource())
+        .that(javaFileObject)
+        .processedWith(
+            new AssertingProcessor() {
+              @Override
+              void runAssertions(
+                  XProcessingEnv processingEnv, DaggerSuperficialValidation superficialValidation) {
+                XTypeElement outerElement = processingEnv.findTypeElement("test.Outer");
+                XMethodElement getChildMethod = outerElement.getDeclaredMethods().get(0);
+                ValidationException exception =
+                    assertThrows(
+                        ValidationException.KnownErrorType.class,
+                        () ->
+                            superficialValidation.validateTypeHierarchyOf(
+                                "return type", getChildMethod, getChildMethod.getReturnType()));
+                assertThat(exception)
+                    .hasMessageThat()
+                    .contains(
+                        NEW_LINES.join(
+                            "Validation trace:",
+                            "  => element (CLASS): test.Outer",
+                            "  => element (METHOD): getChild()",
+                            "  => type (DECLARED return type): test.Outer.Child",
+                            "  => type (DECLARED supertype): test.Outer.Parent<MissingType>",
+                            "  => type (ERROR type argument): MissingType"));
+              }
+            })
+        .failsToCompile();
+  }
+
+  private abstract static class AssertingProcessor extends AbstractProcessor {
+    private boolean processed = false;
+
+    @Override
+    public Set<String> getSupportedAnnotationTypes() {
+      return ImmutableSet.of("*");
+    }
+
+    @Override
+    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+      if (!processed) {
+        processed = true; // only process once.
+        TestComponent component =
+            DaggerDaggerSuperficialValidationTest_TestComponent.builder()
+                .javacPluginModule(
+                    new JavacPluginModule(
+                        processingEnv.getElementUtils(), processingEnv.getTypeUtils()))
+                .build();
+        try {
+          runAssertions(component.processingEnv(), component.superficialValidation());
+        } catch (Exception e) {
+          throw new RuntimeException(e);
+        }
+      }
+      return false;
+    }
+
+    abstract void runAssertions(
+        XProcessingEnv processingEnv, DaggerSuperficialValidation superficialValidation)
+        throws Exception;
+  }
+
+  @Singleton
+  @Component(modules = JavacPluginModule.class)
+  interface TestComponent {
+    XProcessingEnv processingEnv();
+
+    DaggerSuperficialValidation superficialValidation();
+  }
+}
diff --git a/javatests/dagger/internal/codegen/DelegateBindingExpressionTest.java b/javatests/dagger/internal/codegen/DelegateRequestRepresentationTest.java
similarity index 71%
rename from javatests/dagger/internal/codegen/DelegateBindingExpressionTest.java
rename to javatests/dagger/internal/codegen/DelegateRequestRepresentationTest.java
index dc8b758..e24bebb 100644
--- a/javatests/dagger/internal/codegen/DelegateBindingExpressionTest.java
+++ b/javatests/dagger/internal/codegen/DelegateRequestRepresentationTest.java
@@ -32,7 +32,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class DelegateBindingExpressionTest {
+public class DelegateRequestRepresentationTest {
   @Parameters(name = "{0}")
   public static Collection<Object[]> parameters() {
     return CompilerMode.TEST_PARAMETERS;
@@ -40,7 +40,7 @@
 
   private final CompilerMode compilerMode;
 
-  public DelegateBindingExpressionTest(CompilerMode compilerMode) {
+  public DelegateRequestRepresentationTest(CompilerMode compilerMode) {
     this.compilerMode = compilerMode;
   }
 
@@ -147,32 +147,20 @@
                     "final class DaggerTestComponent implements TestComponent {")
                 .addLinesIn(
                     FAST_INIT_MODE,
-                    "  private volatile Object regularScoped = new MemoizedSentinel();",
-                    "  private volatile ReusableScoped reusableScoped;",
-                    "",
-                    "  private RegularScoped regularScoped() {",
-                    "    Object local = regularScoped;",
-                    "    if (local instanceof MemoizedSentinel) {",
-                    "      synchronized (local) {",
-                    "        local = regularScoped;",
-                    "        if (local instanceof MemoizedSentinel) {",
-                    "          local = new RegularScoped();",
-                    "          regularScoped = DoubleCheck.reentrantCheck(regularScoped, local);",
-                    "        }",
-                    "      }",
-                    "    }",
-                    "    return (RegularScoped) local;",
-                    "  }",
-                    "",
-                    "  private ReusableScoped reusableScoped() {",
-                    "    Object local = reusableScoped;",
-                    "    if (local == null) {",
-                    "      local = new ReusableScoped();",
-                    "      reusableScoped = (ReusableScoped) local;",
-                    "    }",
-                    "    return (ReusableScoped) local;",
-                    "  }",
-                    "")
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize() {",
+                    "    this.regularScopedProvider =",
+                    "        DoubleCheck.provider(",
+                    "            new SwitchingProvider<RegularScoped>(testComponent, 0));",
+                    "    this.reusableScopedProvider =",
+                    "        SingleCheck.provider(",
+                    "            new SwitchingProvider<ReusableScoped>(testComponent, 1));",
+                    "    this.reusableProvider =",
+                    "        DoubleCheck.provider((Provider) reusableScopedProvider);",
+                    "    this.unscopedProvider = new SwitchingProvider<>(testComponent, 2);",
+                    "    this.unscopedProvider2 =",
+                    "        DoubleCheck.provider((Provider) unscopedProvider);",
+                    "  }")
                 .addLinesIn(
                     DEFAULT_MODE,
                     "  @SuppressWarnings(\"unchecked\")",
@@ -186,8 +174,20 @@
                     "    this.unscopedProvider = DoubleCheck.provider(",
                     "        (Provider) Unscoped_Factory.create());",
                     "  }")
-                .addLines( //
-                    "}")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "  private static final class SwitchingProvider<T> implements Provider<T> {",
+                    "    @SuppressWarnings(\"unchecked\")",
+                    "    @Override",
+                    "    public T get() {",
+                    "      switch (id) {",
+                    "        case 0: return (T) new RegularScoped();",
+                    "        case 1: return (T) new ReusableScoped();",
+                    "        case 2: return (T) new Unscoped();",
+                    "        default: throw new AssertionError(id);",
+                    "      }",
+                    "    }",
+                    "  }")
                 .build());
   }
 
@@ -226,32 +226,18 @@
                     "final class DaggerTestComponent implements TestComponent {")
                 .addLinesIn(
                     FAST_INIT_MODE,
-                    "  private volatile Object regularScoped = new MemoizedSentinel();",
-                    "  private volatile ReusableScoped reusableScoped;",
-                    "",
-                    "  private RegularScoped regularScoped() {",
-                    "    Object local = regularScoped;",
-                    "    if (local instanceof MemoizedSentinel) {",
-                    "      synchronized (local) {",
-                    "        local = regularScoped;",
-                    "        if (local instanceof MemoizedSentinel) {",
-                    "          local = new RegularScoped();",
-                    "          regularScoped = DoubleCheck.reentrantCheck(regularScoped, local);",
-                    "        }",
-                    "      }",
-                    "    }",
-                    "    return (RegularScoped) local;",
-                    "  }",
-                    "",
-                    "  private ReusableScoped reusableScoped() {",
-                    "    Object local = reusableScoped;",
-                    "    if (local == null) {",
-                    "      local = new ReusableScoped();",
-                    "      reusableScoped = (ReusableScoped) local;",
-                    "    }",
-                    "    return (ReusableScoped) local;",
-                    "  }",
-                    "")
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize() {",
+                    "    this.regularScopedProvider =",
+                    "        DoubleCheck.provider(",
+                    "            new SwitchingProvider<RegularScoped>(testComponent, 0));",
+                    "    this.reusableScopedProvider =",
+                    "        SingleCheck.provider(",
+                    "            new SwitchingProvider<ReusableScoped>(testComponent, 1));",
+                    "    this.unscopedProvider = new SwitchingProvider<>(testComponent, 2);",
+                    "    this.unscopedProvider2 =",
+                    "        SingleCheck.provider((Provider) unscopedProvider);",
+                    "  }")
                 .addLinesIn(
                     DEFAULT_MODE,
                     "  @SuppressWarnings(\"unchecked\")",
@@ -263,8 +249,6 @@
                     "    this.unscopedProvider = SingleCheck.provider(",
                     "        (Provider) Unscoped_Factory.create());",
                     "  }")
-                .addLines( //
-                    "}")
                 .build());
   }
 
@@ -299,35 +283,9 @@
                     "package test;",
                     "",
                     GeneratedLines.generatedAnnotations(),
-                    "final class DaggerTestComponent implements TestComponent {")
-                .addLinesIn(
-                    FAST_INIT_MODE,
-                    "  private volatile Object regularScoped = new MemoizedSentinel();",
-                    "  private volatile ReusableScoped reusableScoped;",
-                    "",
-                    "  private RegularScoped regularScoped() {",
-                    "    Object local = regularScoped;",
-                    "    if (local instanceof MemoizedSentinel) {",
-                    "      synchronized (local) {",
-                    "        local = regularScoped;",
-                    "        if (local instanceof MemoizedSentinel) {",
-                    "          local = new RegularScoped();",
-                    "          regularScoped = DoubleCheck.reentrantCheck(regularScoped, local);",
-                    "        }",
-                    "      }",
-                    "    }",
-                    "    return (RegularScoped) local;",
-                    "  }",
-                    "",
-                    "  private ReusableScoped reusableScoped() {",
-                    "    Object local = reusableScoped;",
-                    "    if (local == null) {",
-                    "      local = new ReusableScoped();",
-                    "      reusableScoped = (ReusableScoped) local;",
-                    "    }",
-                    "    return (ReusableScoped) local;",
-                    "  }",
-                    "")
+                    "final class DaggerTestComponent implements TestComponent {",
+                    "  private Provider<RegularScoped> regularScopedProvider;",
+                    "  private Provider<ReusableScoped> reusableScopedProvider;")
                 .addLinesIn(
                     DEFAULT_MODE,
                     "  @SuppressWarnings(\"unchecked\")",
@@ -337,8 +295,17 @@
                     "    this.reusableScopedProvider = ",
                     "        SingleCheck.provider(ReusableScoped_Factory.create());",
                     "  }")
-                .addLines( //
-                    "}")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize() {",
+                    "  this.regularScopedProvider =",
+                    "      DoubleCheck.provider(",
+                    "          new SwitchingProvider<RegularScoped>(testComponent, 0));",
+                    "  this.reusableScopedProvider =",
+                    "      SingleCheck.provider(",
+                    "          new SwitchingProvider<ReusableScoped>(testComponent, 1));",
+                    "  }")
                 .build());
   }
 
@@ -401,43 +368,29 @@
                     "package test;",
                     "",
                     GeneratedLines.generatedAnnotations(),
-                    "final class DaggerTestComponent implements TestComponent {")
+                    "final class DaggerTestComponent implements TestComponent {",
+                    "  @SuppressWarnings(\"rawtypes\")",
+                    "  private Provider subtypeProvider;")
                 .addLinesIn(
                     DEFAULT_MODE,
-                    "  @SuppressWarnings(\"rawtypes\")",
-                    "  private Provider subtypeProvider;",
-                    "",
                     "  @SuppressWarnings(\"unchecked\")",
                     "  private void initialize() {",
                     "    this.subtypeProvider = DoubleCheck.provider(Subtype_Factory.create());",
-                    "  }",
-                    "",
-                    "  @Override",
-                    "  public Supertype supertype() {",
-                    "    return (Supertype) subtypeProvider.get();",
                     "  }")
                 .addLinesIn(
                     FAST_INIT_MODE,
-                    "  private volatile Object subtype = new MemoizedSentinel();",
-                    "",
-                    "  private Object subtype() {",
-                    "    Object local = subtype;",
-                    "    if (local instanceof MemoizedSentinel) {",
-                    "      synchronized (local) {",
-                    "        local = subtype;",
-                    "        if (local instanceof MemoizedSentinel) {",
-                    "          local = Subtype_Factory.newInstance();",
-                    "          subtype = DoubleCheck.reentrantCheck(subtype, local);",
-                    "        }",
-                    "      }",
-                    "    }",
-                    "    return (Object) local;",
-                    "  }",
-                    "",
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize() {",
+                    "    this.subtypeProvider =",
+                    "        DoubleCheck.provider(",
+                    "            new SwitchingProvider<Object>(testComponent, 0));",
+                    "  }")
+                .addLines(
                     "  @Override",
                     "  public Supertype supertype() {",
-                    "    return (Supertype) subtype();",
-                    "  }")
+                    "    return (Supertype) subtypeProvider.get();",
+                    "  }",
+                    "}")
                 .build());
   }
 
@@ -509,9 +462,7 @@
                     "package test;",
                     "",
                     GeneratedLines.generatedAnnotations(),
-                    "final class DaggerTestComponent implements TestComponent {")
-                .addLinesIn(
-                    DEFAULT_MODE,
+                    "final class DaggerTestComponent implements TestComponent {",
                     "  @SuppressWarnings(\"rawtypes\")",
                     "  private Provider subtypeProvider;",
                     "",
@@ -521,28 +472,6 @@
                     "    return UsesSupertype_Factory.newInstance(subtypeProvider.get());",
                     "  }",
                     "}")
-                .addLinesIn(
-                    FAST_INIT_MODE,
-                    "  private volatile Object subtype = new MemoizedSentinel();",
-                    "",
-                    "  private Object subtype() {",
-                    "    Object local = subtype;",
-                    "    if (local instanceof MemoizedSentinel) {",
-                    "      synchronized (local) {",
-                    "        local = subtype;",
-                    "        if (local instanceof MemoizedSentinel) {",
-                    "          local = Subtype_Factory.newInstance();",
-                    "          subtype = DoubleCheck.reentrantCheck(subtype, local);",
-                    "        }",
-                    "      }",
-                    "    }",
-                    "    return (Object) local;",
-                    "  }",
-                    "",
-                    "  @Override",
-                    "  public UsesSupertype usesSupertype() {",
-                    "    return UsesSupertype_Factory.newInstance(subtype());",
-                    "  }")
                 .build());
   }
 
@@ -582,6 +511,7 @@
             "@Component(modules = TestModule.class)",
             "interface TestComponent {",
             "  Provider<CharSequence> charSequence();",
+            "  CharSequence charSequenceInstance();",
             "",
             "  @Named(\"named\") Provider<String> namedString();",
             "}");
@@ -604,7 +534,12 @@
                     DEFAULT_MODE,
                     "  @Override",
                     "  public Provider<CharSequence> charSequence() {",
-                    "    return (Provider) TestModule_ProvideStringFactory.create();",
+                    "    return ((Provider) TestModule_ProvideStringFactory.create());",
+                    "  }",
+                    "",
+                    "  @Override",
+                    "  public CharSequence charSequenceInstance() {",
+                    "    return TestModule_ProvideStringFactory.provideString();",
                     "  }",
                     "",
                     "  @Override",
@@ -614,36 +549,30 @@
                     "}")
                 .addLinesIn(
                     FAST_INIT_MODE,
-                    "  private volatile Provider<String> provideStringProvider;",
-                    "",
-                    "  private Provider<String> stringProvider() {",
-                    "    Object local = provideStringProvider;",
-                    "    if (local == null) {",
-                    "      local = new SwitchingProvider<>(0);",
-                    "      provideStringProvider = (Provider<String>) local;",
-                    "    }",
-                    "    return (Provider<String>) local;",
-                    "  }",
+                    "  private Provider<String> provideStringProvider;",
                     "",
                     "  @Override",
                     "  public Provider<CharSequence> charSequence() {",
-                    "    return (Provider) stringProvider();",
+                    "    return ((Provider) provideStringProvider);",
+                    "  }",
+                    "",
+                    "  @Override",
+                    "  public CharSequence charSequenceInstance() {",
+                    "    return (CharSequence) ((Provider) provideStringProvider).get();",
                     "  }",
                     "",
                     "  @Override",
                     "  public Provider<String> namedString() {",
-                    "    return stringProvider();",
+                    "    return provideStringProvider;",
                     "  }",
                     "",
-                    "  private final class SwitchingProvider<T> implements Provider<T> {",
+                    "  private static final class SwitchingProvider<T> implements Provider<T> {",
                     "    @SuppressWarnings(\"unchecked\")",
                     "    @Override",
                     "    public T get() {",
                     "      switch (id) {",
-                    "        case 0:",
-                    "            return (T) TestModule_ProvideStringFactory.provideString();",
-                    "        default:",
-                    "            throw new AssertionError(id);",
+                    "        case 0: return (T) TestModule_ProvideStringFactory.provideString();",
+                    "        default: throw new AssertionError(id);",
                     "      }",
                     "    }",
                     "  }")
@@ -705,45 +634,39 @@
                     DEFAULT_MODE,
                     "  @Override",
                     "  public Provider<CharSequence> charSequence() {",
-                    "    return (Provider) TestModule_ProvideStringFactory.create();",
+                    "    return ((Provider) TestModule_ProvideStringFactory.create());",
                     "  }",
                     "  @Override",
                     "  public Provider<Object> object() {",
-                    "    return (Provider) TestModule_ProvideStringFactory.create();",
+                    "    return ((Provider) TestModule_ProvideStringFactory.create());",
                     "  }",
                     "}")
                 .addLinesIn(
                     FAST_INIT_MODE,
-                    "  private volatile Provider<String> provideStringProvider;",
+                    "  private Provider<String> provideStringProvider;",
                     "",
-                    "  private Provider<String> stringProvider() {",
-                    "    Object local = provideStringProvider;",
-                    "    if (local == null) {",
-                    "      local = new SwitchingProvider<>(0);",
-                    "      provideStringProvider = (Provider<String>) local;",
-                    "    }",
-                    "    return (Provider<String>) local;",
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize() {",
+                    "    this.provideStringProvider = new SwitchingProvider<>(testComponent, 0);",
                     "  }",
                     "",
                     "  @Override",
                     "  public Provider<CharSequence> charSequence() {",
-                    "    return (Provider) stringProvider();",
+                    "    return ((Provider) provideStringProvider);",
                     "  }",
                     "",
                     "  @Override",
                     "  public Provider<Object> object() {",
-                    "    return (Provider) stringProvider();",
+                    "    return ((Provider) provideStringProvider);",
                     "  }",
                     "",
-                    "  private final class SwitchingProvider<T> implements Provider<T> {",
+                    "  private static final class SwitchingProvider<T> implements Provider<T> {",
                     "    @SuppressWarnings(\"unchecked\")",
                     "    @Override",
                     "    public T get() {",
                     "      switch (id) {",
-                    "        case 0:",
-                    "            return (T) TestModule_ProvideStringFactory.provideString();",
-                    "        default:",
-                    "            throw new AssertionError(id);",
+                    "        case 0: return (T) TestModule_ProvideStringFactory.provideString();",
+                    "        default: throw new AssertionError(id);",
                     "      }",
                     "    }",
                     "  }")
@@ -811,28 +734,20 @@
                     DEFAULT_MODE,
                     "  @Override",
                     "  public Provider<Supertype> supertypeProvider() {",
-                    "    return (Provider) Subtype_Factory.create();",
+                    "    return ((Provider) Subtype_Factory.create());",
                     "  }",
                     "}")
                 .addLinesIn(
                     FAST_INIT_MODE,
-                    "  private volatile Provider subtypeProvider;",
-                    "",
-                    "  private Provider subtypeProvider() {",
-                    "    Object local = subtypeProvider;",
-                    "    if (local == null) {",
-                    "      local = new SwitchingProvider<>(0);",
-                    "      subtypeProvider = (Provider) local;",
-                    "    }",
-                    "    return (Provider) local;",
-                    "  }",
+                    "  @SuppressWarnings(\"rawtypes\")",
+                    "  private Provider subtypeProvider;",
                     "",
                     "  @Override",
                     "  public Provider<Supertype> supertypeProvider() {",
-                    "    return subtypeProvider();",
+                    "    return subtypeProvider;",
                     "  }",
                     "",
-                    "  private final class SwitchingProvider<T> implements Provider<T> {",
+                    "  private static final class SwitchingProvider<T> implements Provider<T> {",
                     "    @SuppressWarnings(\"unchecked\")",
                     "    @Override",
                     "    public T get() {",
@@ -897,70 +812,41 @@
                     "package test;",
                     "",
                     GeneratedLines.generatedAnnotations(),
-                    "final class DaggerTestComponent implements TestComponent {")
+                    "final class DaggerTestComponent implements TestComponent {",
+                    "  private Provider<String> provideStringProvider;",
+                    "  private Provider<Object> bindStringProvider;")
                 .addLinesIn(
                     DEFAULT_MODE,
-                    "  private Provider<String> provideStringProvider;",
-                    "  private Provider<Object> bindStringProvider;",
-                    "",
                     "  @SuppressWarnings(\"unchecked\")",
                     "  private void initialize() {",
                     "    this.provideStringProvider =",
                     "        SingleCheck.provider(TestModule_ProvideStringFactory.create());",
                     "    this.bindStringProvider =",
                     "        DoubleCheck.provider((Provider) provideStringProvider);",
-                    "  }",
-                    "",
+                    "  }")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize() {",
+                    "    this.provideStringProvider =",
+                    "        SingleCheck.provider(",
+                    "            new SwitchingProvider<String>(testComponent, 0));",
+                    "    this.bindStringProvider =",
+                    "        DoubleCheck.provider((Provider) provideStringProvider);",
+                    "  }")
+                .addLines(
                     "  @Override",
                     "  public Provider<Object> object() {",
                     "    return bindStringProvider;",
-                    "  }",
-                    "}")
+                    "  }")
                 .addLinesIn(
                     FAST_INIT_MODE,
-                    "  private volatile String string;",
-                    "  private volatile Object object = new MemoizedSentinel();",
-                    "  private volatile Provider<Object> bindStringProvider;",
-                    "",
-                    "  private String string() {",
-                    "    Object local = string;",
-                    "    if (local == null) {",
-                    "      local = TestModule_ProvideStringFactory.provideString();",
-                    "      string = (String) local;",
-                    "    }",
-                    "    return (String) local;",
-                    "  }",
-                    "",
-                    "  private Object object2() {",
-                    "    Object local = object;",
-                    "    if (local instanceof MemoizedSentinel) {",
-                    "      synchronized (local) {",
-                    "        local = object;",
-                    "        if (local instanceof MemoizedSentinel) {",
-                    "          local = string();",
-                    "          object = DoubleCheck.reentrantCheck(object, local);",
-                    "        }",
-                    "      }",
-                    "    }",
-                    "    return (Object) local;",
-                    "  }",
-                    "",
-                    "  @Override",
-                    "  public Provider<Object> object() {",
-                    "    Object local = bindStringProvider;",
-                    "    if (local == null) {",
-                    "      local = new SwitchingProvider<>(0);",
-                    "      bindStringProvider = (Provider<Object>) local;",
-                    "    }",
-                    "    return (Provider<Object>) local;",
-                    "  }",
-                    "",
-                    "  private final class SwitchingProvider<T> implements Provider<T> {",
+                    "  private static final class SwitchingProvider<T> implements Provider<T> {",
                     "    @SuppressWarnings(\"unchecked\")",
                     "    @Override",
                     "    public T get() {",
                     "      switch (id) {",
-                    "        case 0: return (T) DaggerTestComponent.this.object2();",
+                    "        case 0: return (T) TestModule_ProvideStringFactory.provideString();",
                     "        default: throw new AssertionError(id);",
                     "      }",
                     "    }",
diff --git a/javatests/dagger/internal/codegen/DependencyCycleValidationTest.java b/javatests/dagger/internal/codegen/DependencyCycleValidationTest.java
index 22547b5..2b7ae64 100644
--- a/javatests/dagger/internal/codegen/DependencyCycleValidationTest.java
+++ b/javatests/dagger/internal/codegen/DependencyCycleValidationTest.java
@@ -82,6 +82,11 @@
                 "        Outer.B(aParam)",
                 "    Outer.B is injected at",
                 "        Outer.C(bParam)",
+                "    Outer.C is injected at",
+                "        Outer.A(cParam)",
+                "    ...",
+                "",
+                "The cycle is requested via:",
                 "    Outer.C is requested at",
                 "        Outer.CComponent.getC()"))
         .inFile(SIMPLE_CYCLIC_DEPENDENCY)
@@ -104,6 +109,9 @@
             "        Outer.B(aParam)",
             "    Outer.B is injected at",
             "        Outer.C(bParam)",
+            "    Outer.C is injected at",
+            "        Outer.A(cParam)",
+            "    ...",
             "",
             "======================",
             "Full classname legend:",
@@ -178,6 +186,11 @@
                 "    Outer.B is injected at",
                 "        Outer.C(bParam)",
                 "    Outer.C is injected at",
+                "        Outer.A(cParam)",
+                "    ...",
+                "",
+                "The cycle is requested via:",
+                "    Outer.C is injected at",
                 "        Outer.D(cParam)",
                 "    Outer.D is requested at",
                 "        Outer.DComponent.getD()"))
@@ -242,6 +255,11 @@
                 "        Outer.B(aParam)",
                 "    Outer.B is injected at",
                 "        Outer.C(bParam)",
+                "    Outer.C is injected at",
+                "        Outer.CModule.c(c)",
+                "    ...",
+                "",
+                "The cycle is requested via:",
                 "    Outer.C is requested at",
                 "        Outer.CComponent.getC()"))
         .inFile(component)
@@ -303,6 +321,11 @@
                 "        Outer.B(aParam)",
                 "    Outer.B is injected at",
                 "        Outer.C(bParam)",
+                "    Outer.C is injected at",
+                "        Outer.CModule.c(c)",
+                "    ...",
+                "",
+                "The cycle is requested via:",
                 "    Outer.C is requested at",
                 "        Outer.CComponent.getC()"))
         .inFile(component)
@@ -357,6 +380,11 @@
                 "        Outer.B(aParam)",
                 "    Outer.B is injected at",
                 "        Outer.C(bParam)",
+                "    Outer.C is injected at",
+                "        Outer.A(cParam)",
+                "    ...",
+                "",
+                "The cycle is requested via:",
                 "    Provider<Outer.C> is injected at",
                 "        Outer.D(cParam)",
                 "    Outer.D is requested at",
@@ -439,6 +467,11 @@
                 "        CycleModule.object(string)",
                 "    Object is injected at",
                 "        CycleModule.string(object)",
+                "    String is injected at",
+                "        CycleModule.object(string)",
+                "    ...",
+                "",
+                "The cycle is requested via:",
                 "    String is requested at",
                 "        Grandchild.entry()"))
         .inFile(parent)
@@ -521,6 +554,11 @@
                 "        CycleModule.object(string)",
                 "    Object is injected at",
                 "        CycleModule.string(object)",
+                "    String is injected at",
+                "        CycleModule.object(string)",
+                "    ...",
+                "",
+                "The cycle is requested via:",
                 "    String is requested at",
                 "        Child.entry() [Parent → Child]"))
         .inFile(parent)
@@ -572,6 +610,11 @@
                 "        TestModule.bindQualified(unqualified)",
                 "    @SomeQualifier Object is injected at",
                 "        TestModule.bindUnqualified(qualified)",
+                "    Object is injected at",
+                "        TestModule.bindQualified(unqualified)",
+                "    ...",
+                "",
+                "The cycle is requested via:",
                 "    Object is requested at",
                 "        TestComponent.unqualified()"))
         .inFile(component)
@@ -612,6 +655,11 @@
                 "Found a dependency cycle:",
                 "    Object is injected at",
                 "        TestModule.bindToSelf(sameKey)",
+                "    Object is injected at",
+                "        TestModule.bindToSelf(sameKey)",
+                "    ...",
+                "",
+                "The cycle is requested via:",
                 "    Object is requested at",
                 "        TestComponent.selfReferential()"))
         .inFile(component)
@@ -666,6 +714,11 @@
                 "        test.B.a",
                 "    test.B is injected at",
                 "        test.A.b",
+                "    ...",
+                "",
+                "The cycle is requested via:",
+                "    test.B is injected at",
+                "        test.A.b",
                 "    test.A is injected at",
                 "        CycleComponent.inject(test.A)"))
         .inFile(component)
diff --git a/javatests/dagger/internal/codegen/DuplicateBindingsValidationTest.java b/javatests/dagger/internal/codegen/DuplicateBindingsValidationTest.java
index fbda59d..ecb1146 100644
--- a/javatests/dagger/internal/codegen/DuplicateBindingsValidationTest.java
+++ b/javatests/dagger/internal/codegen/DuplicateBindingsValidationTest.java
@@ -1096,4 +1096,74 @@
         .onLineContaining("interface Parent");
     assertThat(compilation).hadErrorCount(1);
   }
+
+  // Tests the format of the error for a somewhat complex binding method.
+  @Test
+  public void formatTest() {
+    JavaFileObject modules =
+        JavaFileObjects.forSourceLines(
+            "test.Modules",
+            "package test;",
+            "",
+            "import com.google.common.collect.ImmutableList;",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "import javax.inject.Singleton;",
+            "",
+            "interface Modules {",
+            "  @interface Foo {",
+            "    Class<?> bar();",
+            "  }",
+            "",
+            "  @Module",
+            "  interface Module1 {",
+            "    @Provides",
+            "    @Singleton",
+            "    @Foo(bar = String.class)",
+            "    static String foo(",
+            "        @SuppressWarnings(\"unused\") int a,",
+            "        @SuppressWarnings(\"unused\") ImmutableList<Boolean> blah) {",
+            "      return \"\";",
+            "    }",
+            "  }",
+            "",
+            "  @Module",
+            "  interface Module2 {",
+            "    @Provides",
+            "    @Singleton",
+            "    @Foo(bar = String.class)",
+            "    static String foo(",
+            "        @SuppressWarnings(\"unused\") int a,",
+            "        @SuppressWarnings(\"unused\") ImmutableList<Boolean> blah) {",
+            "      return \"\";",
+            "    }",
+            "  }",
+            "}");
+    JavaFileObject component =
+        JavaFileObjects.forSourceLines(
+            "test.TestComponent",
+            "package test;",
+            "",
+            "import dagger.BindsInstance;",
+            "import dagger.Component;",
+            "import javax.inject.Singleton;",
+            "",
+            "@Singleton",
+            "@Component(modules = {Modules.Module1.class, Modules.Module2.class})",
+            "interface TestComponent {",
+            "  @Modules.Foo(bar = String.class) String foo();",
+            "}");
+    Compilation compilation = daggerCompiler().compile(modules, component);
+    assertThat(compilation).failed();
+    assertThat(compilation).hadErrorCount(1);
+    assertThat(compilation)
+        .hadErrorContaining(
+            message(
+                "String is bound multiple times:",
+                "    @Provides @Singleton @Modules.Foo(bar = String.class) String "
+                    + "Modules.Module1.foo(int, ImmutableList<Boolean>)",
+                "    @Provides @Singleton @Modules.Foo(bar = String.class) String "
+                    + "Modules.Module2.foo(int, ImmutableList<Boolean>)"))
+        .inFile(component);
+  }
 }
diff --git a/javatests/dagger/internal/codegen/ElidedFactoriesTest.java b/javatests/dagger/internal/codegen/ElidedFactoriesTest.java
index 0c557c0..5180c48 100644
--- a/javatests/dagger/internal/codegen/ElidedFactoriesTest.java
+++ b/javatests/dagger/internal/codegen/ElidedFactoriesTest.java
@@ -17,6 +17,8 @@
 package dagger.internal.codegen;
 
 import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
+import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
 import static dagger.internal.codegen.Compilers.compilerWithOptions;
 
 import com.google.testing.compile.Compilation;
@@ -84,6 +86,8 @@
             "",
             GeneratedLines.generatedAnnotations(),
             "final class DaggerSimpleComponent implements SimpleComponent {",
+            "  private final DaggerSimpleComponent simpleComponent = this;",
+            "",
             "  private DaggerSimpleComponent() {}",
             "",
             "  public static Builder builder() {",
@@ -100,8 +104,7 @@
             "  }",
             "",
             "  static final class Builder {",
-            "    private Builder() {",
-            "    }",
+            "    private Builder() {}",
             "",
             "    public SimpleComponent build() {",
             "      return new DaggerSimpleComponent();",
@@ -169,147 +172,64 @@
             "interface SimpleComponent {",
             "  NeedsProvider needsProvider();",
             "}");
-    JavaFileObject generatedComponent;
-    switch (compilerMode) {
-      case FAST_INIT_MODE:
-        generatedComponent =
-            JavaFileObjects.forSourceLines(
-                "test.DaggerSimpleComponent",
-                "package test;",
-                "",
-                GeneratedLines.generatedImports(
-                    "import dagger.internal.DoubleCheck;",
-                    "import dagger.internal.MemoizedSentinel;",
-                    "import javax.inject.Provider;"),
-                "",
-                GeneratedLines.generatedAnnotations(),
-                "final class DaggerSimpleComponent implements SimpleComponent {",
-                "  private volatile Object scopedType = new MemoizedSentinel();",
-                "  private volatile Provider<DependsOnScoped> dependsOnScopedProvider;",
-                "",
-                "  private DaggerSimpleComponent() {}",
-                "",
-                "  public static Builder builder() {",
-                "    return new Builder();",
-                "  }",
-                "",
-                "  public static SimpleComponent create() {",
-                "    return new Builder().build();",
-                "  }",
-                "",
-                "  private ScopedType scopedType() {",
-                "    Object local = scopedType;",
-                "    if (local instanceof MemoizedSentinel) {",
-                "      synchronized (local) {",
-                "        local = scopedType;",
-                "        if (local instanceof MemoizedSentinel) {",
-                "          local = new ScopedType();",
-                "          scopedType = DoubleCheck.reentrantCheck(scopedType, local);",
-                "        }",
-                "      }",
-                "    }",
-                "    return (ScopedType) local;",
-                "  }",
-                "",
-                "  private DependsOnScoped dependsOnScoped() {",
-                "    return new DependsOnScoped(scopedType());",
-                "  }",
-                "",
-                "  private Provider<DependsOnScoped> dependsOnScopedProvider() {",
-                "    Object local = dependsOnScopedProvider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(0);",
-                "      dependsOnScopedProvider = (Provider<DependsOnScoped>) local;",
-                "    }",
-                "    return (Provider<DependsOnScoped>) local;",
-                "  }",
-                "",
-                "  @Override",
-                "  public NeedsProvider needsProvider() {",
-                "    return new NeedsProvider(dependsOnScopedProvider());",
-                "  }",
-                "",
-                "  static final class Builder {",
-                "    private Builder() {}",
-                "",
-                "    public SimpleComponent build() {",
-                "      return new DaggerSimpleComponent();",
-                "    }",
-                "  }",
-                "  private final class SwitchingProvider<T> implements Provider<T> {",
-                "    private final int id;",
-                "",
-                "    SwitchingProvider(int id) {",
-                "      this.id = id;",
-                "    }",
-                "",
-                "    @SuppressWarnings(\"unchecked\")",
-                "    @Override",
-                "    public T get() {",
-                "      switch (id) {",
-                "        case 0: return (T) DaggerSimpleComponent.this.dependsOnScoped();",
-                "        default: throw new AssertionError(id);",
-                "      }",
-                "    }",
-                "  }",
-                "}");
-        break;
-      default:
-        generatedComponent =
-            JavaFileObjects.forSourceLines(
-                "test.DaggerSimpleComponent",
-                "package test;",
-                "",
-                GeneratedLines.generatedImports(
-                    "import dagger.internal.DoubleCheck;",
-                    "import javax.inject.Provider;"),
-                "",
-                GeneratedLines.generatedAnnotations(),
-                "final class DaggerSimpleComponent implements SimpleComponent {",
-                "  private Provider<ScopedType> scopedTypeProvider;",
-                "  private Provider<DependsOnScoped> dependsOnScopedProvider;",
-                "",
-                "  private DaggerSimpleComponent() {",
-                "    initialize();",
-                "  }",
-                "",
-                "  public static Builder builder() {",
-                "    return new Builder();",
-                "  }",
-                "",
-                "  public static SimpleComponent create() {",
-                "    return new Builder().build();",
-                "  }",
-                "",
-                "  @SuppressWarnings(\"unchecked\")",
-                "  private void initialize() {",
-                "    this.scopedTypeProvider = DoubleCheck.provider(ScopedType_Factory.create());",
-                "    this.dependsOnScopedProvider = ",
-                "        DependsOnScoped_Factory.create(scopedTypeProvider);",
-                "  }",
-                "",
-                "  @Override",
-                "  public NeedsProvider needsProvider() {",
-                "    return new NeedsProvider(dependsOnScopedProvider);",
-                "  }",
-                "",
-                "  static final class Builder {",
-                "    private Builder() {",
-                "    }",
-                "",
-                "    public SimpleComponent build() {",
-                "      return new DaggerSimpleComponent();",
-                "    }",
-                "  }",
-                "}");
-    }
+
     Compilation compilation =
         compilerWithOptions(compilerMode.javacopts())
             .compile(scopedType, dependsOnScoped, componentFile, needsProvider);
     assertThat(compilation).succeeded();
     assertThat(compilation)
         .generatedSourceFile("test.DaggerSimpleComponent")
-        .hasSourceEquivalentTo(generatedComponent);
+        .containsElementsIn(
+            compilerMode
+                .javaFileBuilder("test.DaggerSimpleComponent")
+                .addLines(
+                    "package test;",
+                    "",
+                    GeneratedLines.generatedAnnotations(),
+                    "final class DaggerSimpleComponent implements SimpleComponent {",
+                    "  private final DaggerSimpleComponent simpleComponent = this;",
+                    "",
+                    "  private Provider<ScopedType> scopedTypeProvider;",
+                    "  private Provider<DependsOnScoped> dependsOnScopedProvider;")
+                .addLinesIn(
+                    DEFAULT_MODE,
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize() {",
+                    "    this.scopedTypeProvider =",
+                    "        DoubleCheck.provider(ScopedType_Factory.create());",
+                    "    this.dependsOnScopedProvider = ",
+                    "        DependsOnScoped_Factory.create(scopedTypeProvider);",
+                    "  }")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize() {",
+                    "    this.scopedTypeProvider =",
+                    "        DoubleCheck.provider(",
+                    "            new SwitchingProvider<ScopedType>(simpleComponent, 1));",
+                    "    this.dependsOnScopedProvider = ",
+                    "        new SwitchingProvider<>(simpleComponent, 0);",
+                    "  }")
+                .addLines(
+                    "  @Override",
+                    "  public NeedsProvider needsProvider() {",
+                    "    return new NeedsProvider(dependsOnScopedProvider);",
+                    "  }")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "  private static final class SwitchingProvider<T> implements Provider<T> {",
+                    "    @SuppressWarnings(\"unchecked\")",
+                    "    @Override",
+                    "    public T get() {",
+                    "      switch (id) {",
+                    "        case 0: return (T) new DependsOnScoped(",
+                    "          simpleComponent.scopedTypeProvider.get());",
+                    "        case 1: return (T) new ScopedType();",
+                    "        default: throw new AssertionError(id);",
+                    "      }",
+                    "    }",
+                    "  }")
+                .build());
   }
 
   @Test
@@ -363,130 +283,63 @@
             "  DependsOnScoped dependsOnScoped();",
             "}");
 
-    JavaFileObject generatedComponent;
-    switch (compilerMode) {
-      case FAST_INIT_MODE:
-        generatedComponent =
-            JavaFileObjects.forSourceLines(
-                "test.DaggerSimpleComponent",
-                "package test;",
-                "",
-                GeneratedLines.generatedImports(
-                    "import dagger.internal.DoubleCheck;",
-                    "import dagger.internal.MemoizedSentinel;"),
-                "",
-                GeneratedLines.generatedAnnotations(),
-                "final class DaggerSimpleComponent implements SimpleComponent {",
-                "  private volatile Object scopedType = new MemoizedSentinel();",
-                "",
-                "  private DaggerSimpleComponent() {}",
-                "",
-                "  public static Builder builder() {",
-                "    return new Builder();",
-                "  }",
-                "",
-                "  public static SimpleComponent create() {",
-                "    return new Builder().build();",
-                "  }",
-                "",
-                "  private ScopedType scopedType() {",
-                "    Object local = scopedType;",
-                "    if (local instanceof MemoizedSentinel) {",
-                "      synchronized (local) {",
-                "        local = scopedType;",
-                "        if (local instanceof MemoizedSentinel) {",
-                "          local = new ScopedType();",
-                "          scopedType = DoubleCheck.reentrantCheck(scopedType, local);",
-                "        }",
-                "      }",
-                "    }",
-                "    return (ScopedType) local;",
-                "  }",
-                "",
-                "  @Override",
-                "  public Sub sub() {",
-                "    return new SubImpl();",
-                "  }",
-                "",
-                "  static final class Builder {",
-                "    private Builder() {}",
-                "",
-                "    public SimpleComponent build() {",
-                "      return new DaggerSimpleComponent();",
-                "    }",
-                "  }",
-                "",
-                "  private final class SubImpl implements Sub {",
-                "    private SubImpl() {}",
-                "",
-                "    @Override",
-                "    public DependsOnScoped dependsOnScoped() {",
-                "      return new DependsOnScoped(DaggerSimpleComponent.this.scopedType());",
-                "    }",
-                "  }",
-                "}");
-        break;
-      default:
-        generatedComponent =
-            JavaFileObjects.forSourceLines(
-                "test.DaggerSimpleComponent",
-                "package test;",
-                "",
-                GeneratedLines.generatedImports(
-                    "import dagger.internal.DoubleCheck;",
-                    "import javax.inject.Provider;"),
-                "",
-                GeneratedLines.generatedAnnotations(),
-                "final class DaggerSimpleComponent implements SimpleComponent {",
-                "  private Provider<ScopedType> scopedTypeProvider;",
-                "",
-                "  private DaggerSimpleComponent() {",
-                "    initialize();",
-                "  }",
-                "",
-                "  public static Builder builder() {",
-                "    return new Builder();",
-                "  }",
-                "",
-                "  public static SimpleComponent create() {",
-                "    return new Builder().build();",
-                "  }",
-                "",
-                "  @SuppressWarnings(\"unchecked\")",
-                "  private void initialize() {",
-                "    this.scopedTypeProvider = DoubleCheck.provider(ScopedType_Factory.create());",
-                "  }",
-                "",
-                "  @Override",
-                "  public Sub sub() {",
-                "    return new SubImpl();",
-                "  }",
-                "",
-                "  static final class Builder {",
-                "    private Builder() {}",
-                "",
-                "    public SimpleComponent build() {",
-                "      return new DaggerSimpleComponent();",
-                "    }",
-                "  }",
-                "",
-                "  private final class SubImpl implements Sub {",
-                "    private SubImpl() {}",
-                "",
-                "    @Override",
-                "    public DependsOnScoped dependsOnScoped() {",
-                "      return new DependsOnScoped(",
-                "          DaggerSimpleComponent.this.scopedTypeProvider.get());",
-                "    }",
-                "  }",
-                "}");
-    }
     Compilation compilation =
         compilerWithOptions(compilerMode.javacopts())
             .compile(scopedType, dependsOnScoped, componentFile, subcomponentFile);
     assertThat(compilation).succeeded();
     assertThat(compilation)
         .generatedSourceFile("test.DaggerSimpleComponent")
-        .hasSourceEquivalentTo(generatedComponent);
+        .containsElementsIn(
+            compilerMode
+                .javaFileBuilder("test.DaggerSimpleComponent")
+                .addLines(
+                    "package test;",
+                    "",
+                    GeneratedLines.generatedAnnotations(),
+                    "final class DaggerSimpleComponent implements SimpleComponent {",
+                    "  private final DaggerSimpleComponent simpleComponent = this;",
+                    "  private Provider<ScopedType> scopedTypeProvider;")
+                .addLinesIn(
+                    DEFAULT_MODE,
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize() {",
+                    "    this.scopedTypeProvider = DoubleCheck.provider(",
+                    "        ScopedType_Factory.create());",
+                    "  }")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize() {",
+                    "    this.scopedTypeProvider = DoubleCheck.provider(",
+                    "        new SwitchingProvider<ScopedType>(simpleComponent, 0));",
+                    "  }")
+                .addLines(
+                    "  @Override",
+                    "  public Sub sub() {",
+                    "    return new SubImpl(simpleComponent);",
+                    "  }",
+                    "",
+                    "  private static final class SubImpl implements Sub {",
+                    "    private final DaggerSimpleComponent simpleComponent;",
+                    "    private final SubImpl subImpl = this;",
+                    "",
+                    "    @Override",
+                    "    public DependsOnScoped dependsOnScoped() {",
+                    "      return new DependsOnScoped(simpleComponent.scopedTypeProvider.get());",
+                    "    }",
+                    "  }")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "  private static final class SwitchingProvider<T> implements Provider<T> {",
+                    "    @SuppressWarnings(\"unchecked\")",
+                    "    @Override",
+                    "    public T get() {",
+                    "      switch (id) {",
+                    "        case 0: return (T) new ScopedType();",
+                    "        default: throw new AssertionError(id);",
+                    "      }",
+                    "    }",
+                    "  }")
+                .build());
   }
 }
diff --git a/javatests/dagger/internal/codegen/FrameworkTypeMapperTest.java b/javatests/dagger/internal/codegen/FrameworkTypeMapperTest.java
index e27534d..3e7992b 100644
--- a/javatests/dagger/internal/codegen/FrameworkTypeMapperTest.java
+++ b/javatests/dagger/internal/codegen/FrameworkTypeMapperTest.java
@@ -17,11 +17,11 @@
 package dagger.internal.codegen;
 
 import static com.google.common.truth.Truth.assertThat;
-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.spi.model.RequestKind.INSTANCE;
+import static dagger.spi.model.RequestKind.LAZY;
+import static dagger.spi.model.RequestKind.PRODUCED;
+import static dagger.spi.model.RequestKind.PRODUCER;
+import static dagger.spi.model.RequestKind.PROVIDER;
 
 import dagger.internal.codegen.binding.FrameworkType;
 import dagger.internal.codegen.binding.FrameworkTypeMapper;
diff --git a/javatests/dagger/internal/codegen/InjectConstructorFactoryGeneratorTest.java b/javatests/dagger/internal/codegen/InjectConstructorFactoryGeneratorTest.java
index 3e78d6b..1759033 100644
--- a/javatests/dagger/internal/codegen/InjectConstructorFactoryGeneratorTest.java
+++ b/javatests/dagger/internal/codegen/InjectConstructorFactoryGeneratorTest.java
@@ -134,8 +134,12 @@
             "",
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class GenericClass_Factory<T> implements Factory<GenericClass<T>> {",
             "  private final Provider<T> tProvider;",
@@ -183,8 +187,12 @@
             "",
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class GenericClass_Factory<A, B> implements",
             "    Factory<GenericClass<A, B>> {",
@@ -234,8 +242,13 @@
             "test.GenericClass_Factory",
             "package test;",
             "",
-            GeneratedLines.generatedImports("import dagger.internal.Factory;"),
+            GeneratedLines.generatedImports(
+                "import dagger.internal.Factory;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class GenericClass_Factory<T> implements Factory<GenericClass<T>> {",
             "  @Override",
@@ -279,8 +292,12 @@
             "",
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class GenericClass_Factory<A, B>",
             "    implements Factory<GenericClass<A, B>> {",
@@ -331,9 +348,13 @@
             "",
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;",
                 "import java.util.List;",
                 "import javax.inject.Provider;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class GenericClass_Factory<A extends Number & Comparable<A>,",
             "        B extends List<? extends String>,",
@@ -400,8 +421,12 @@
                 "import dagger.Lazy;",
                 "import dagger.internal.DoubleCheck;",
                 "import dagger.internal.Factory;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata(\"test.QualifierA\")",
             GeneratedLines.generatedAnnotations(),
             "public final class GenericClass_Factory<A, B>",
             "    implements Factory<GenericClass<A, B>> {",
@@ -547,14 +572,16 @@
         "}");
     Compilation compilation = daggerCompiler().compile(file);
     assertThat(compilation).failed();
+    assertThat(compilation).hadErrorCount(1);
     assertThat(compilation)
-        .hadErrorContaining("Types may only contain one injected constructor")
+        .hadErrorContaining(
+            "Type test.TooManyInjectConstructors may only contain one injected constructor. "
+                + "Found: ["
+                + "TooManyInjectConstructors(), "
+                + "TooManyInjectConstructors(java.lang.String)"
+                + "]")
         .inFile(file)
-        .onLine(6);
-    assertThat(compilation)
-        .hadErrorContaining("Types may only contain one injected constructor")
-        .inFile(file)
-        .onLine(8);
+        .onLine(5);
   }
 
   @Test public void multipleQualifiersOnInjectConstructorParameter() {
@@ -1067,8 +1094,12 @@
             "",
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class InjectConstructor_Factory ",
             "    implements Factory<InjectConstructor> {",
@@ -1114,8 +1145,12 @@
             "",
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class AllInjections_Factory implements Factory<AllInjections> {",
             "  private final Provider<String> sProvider;",
@@ -1174,9 +1209,13 @@
             "",
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;",
                 "import java.util.List;",
                 "import javax.inject.Provider;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class InjectConstructor_Factory ",
             "    implements Factory<InjectConstructor> {",
@@ -1227,8 +1266,12 @@
             "",
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class InjectConstructor_Factory ",
             "    implements Factory<InjectConstructor> {",
@@ -1283,9 +1326,13 @@
             "",
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;",
                 "import javax.inject.Provider;",
                 "import other.pkg.Outer;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class InjectConstructor_Factory ",
             "    implements Factory<InjectConstructor> {",
@@ -1342,8 +1389,12 @@
             "",
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class InjectConstructor_Factory ",
             "    implements Factory<InjectConstructor> {",
@@ -1395,8 +1446,13 @@
             "test.SimpleType_Factory",
             "package test;",
             "",
-            GeneratedLines.generatedImports("import dagger.internal.Factory;"),
+            GeneratedLines.generatedImports(
+                "import dagger.internal.Factory;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class SimpleType_Factory implements Factory<SimpleType> {",
             "  @Override public SimpleType get() {",
@@ -1442,8 +1498,13 @@
             "test.OuterType_A_Factory",
             "package test;",
             "",
-            GeneratedLines.generatedImports("import dagger.internal.Factory;"),
+            GeneratedLines.generatedImports(
+                "import dagger.internal.Factory;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class OuterType_A_Factory implements Factory<OuterType.A> {",
             "  @Override public OuterType.A get() {",
@@ -1467,4 +1528,452 @@
         .compilesWithoutError()
         .and().generatesSources(aFactory);
   }
+
+  @Test
+  public void testScopedMetadata() {
+    JavaFileObject scopedBinding =
+        JavaFileObjects.forSourceLines(
+            "test.ScopedBinding",
+            "package test;",
+            "",
+            "import javax.inject.Inject;",
+            "import javax.inject.Singleton;",
+            "",
+            "@Singleton",
+            "class ScopedBinding {",
+            "  @Inject",
+            "  ScopedBinding() {}",
+            "}");
+    Compilation compilation = daggerCompiler().compile(scopedBinding);
+    assertThat(compilation).succeeded();
+    assertThat(compilation)
+        .generatedSourceFile("test.ScopedBinding_Factory")
+        .containsElementsIn(
+            JavaFileObjects.forSourceLines(
+                "test.ScopedBinding_Factory",
+                "package test;",
+                "",
+                "@ScopeMetadata(\"javax.inject.Singleton\")",
+                "@QualifierMetadata",
+                GeneratedLines.generatedAnnotations(),
+                "public final class ScopedBinding_Factory implements Factory<ScopedBinding> {}"));
+  }
+
+  @Test
+  public void testScopedMetadataWithCustomScope() {
+    JavaFileObject customScope =
+        JavaFileObjects.forSourceLines(
+            "test.CustomScope",
+            "package test;",
+            "",
+            "import javax.inject.Scope;",
+            "",
+            "@Scope",
+            "@interface CustomScope {",
+            "  String value();",
+            "}");
+
+    JavaFileObject customAnnotation =
+        JavaFileObjects.forSourceLines(
+            "test.CustomAnnotation",
+            "package test;",
+            "",
+            "@interface CustomAnnotation {",
+            "  String value();",
+            "}");
+
+    JavaFileObject scopedBinding =
+        JavaFileObjects.forSourceLines(
+            "test.ScopedBinding",
+            "package test;",
+            "",
+            "import javax.inject.Inject;",
+            "import javax.inject.Singleton;",
+            "",
+            "@CustomAnnotation(\"someValue\")",
+            "@CustomScope(\"someOtherValue\")",
+            "class ScopedBinding {",
+            "  @Inject",
+            "  ScopedBinding() {}",
+            "}");
+    Compilation compilation =
+        daggerCompiler().compile(scopedBinding, customScope, customAnnotation);
+    assertThat(compilation).succeeded();
+    assertThat(compilation)
+        .generatedSourceFile("test.ScopedBinding_Factory")
+        .containsElementsIn(
+            JavaFileObjects.forSourceLines(
+                "test.ScopedBinding_Factory",
+                "package test;",
+                "",
+                "@ScopeMetadata(\"test.CustomScope\")",
+                "@QualifierMetadata",
+                GeneratedLines.generatedAnnotations(),
+                "public final class ScopedBinding_Factory implements Factory<ScopedBinding> {}"));
+  }
+
+  @Test
+  public void testQualifierMetadata() {
+    JavaFileObject someBinding =
+        JavaFileObjects.forSourceLines(
+            "test.SomeBinding",
+            "package test;",
+            "",
+            "import javax.inject.Inject;",
+            "import javax.inject.Singleton;",
+            "",
+            "@NonQualifier",
+            "@MisplacedQualifier",
+            "class SomeBinding {",
+            "  @NonQualifier @FieldQualifier @Inject String injectField;",
+            "  @NonQualifier @MisplacedQualifier String nonDaggerField;",
+            "",
+            "  @NonQualifier",
+            "  @Inject",
+            "  SomeBinding(@NonQualifier @ConstructorParameterQualifier Double d) {}",
+            "",
+            "  @NonQualifier",
+            "  @MisplacedQualifier",
+            "  SomeBinding(@NonQualifier @MisplacedQualifier Double d, int i) {}",
+            "",
+            "  @NonQualifier",
+            "  @MisplacedQualifier",
+            "  @Inject",
+            "  void injectMethod(@NonQualifier @MethodParameterQualifier Float f) {}",
+            "",
+            "  @NonQualifier",
+            "  @MisplacedQualifier",
+            "  void nonDaggerMethod(@NonQualifier @MisplacedQualifier Float f) {}",
+            "}");
+    JavaFileObject fieldQualifier =
+        JavaFileObjects.forSourceLines(
+            "test.FieldQualifier",
+            "package test;",
+            "",
+            "import javax.inject.Qualifier;",
+            "",
+            "@Qualifier",
+            "@interface FieldQualifier {}");
+    JavaFileObject constructorParameterQualifier =
+        JavaFileObjects.forSourceLines(
+            "test.ConstructorParameterQualifier",
+            "package test;",
+            "",
+            "import javax.inject.Qualifier;",
+            "",
+            "@Qualifier",
+            "@interface ConstructorParameterQualifier {}");
+    JavaFileObject methodParameterQualifier =
+        JavaFileObjects.forSourceLines(
+            "test.MethodParameterQualifier",
+            "package test;",
+            "",
+            "import javax.inject.Qualifier;",
+            "",
+            "@Qualifier",
+            "@interface MethodParameterQualifier {}");
+    JavaFileObject misplacedQualifier =
+        JavaFileObjects.forSourceLines(
+            "test.MisplacedQualifier",
+            "package test;",
+            "",
+            "import javax.inject.Qualifier;",
+            "",
+            "@Qualifier",
+            "@interface MisplacedQualifier {}");
+    JavaFileObject nonQualifier =
+        JavaFileObjects.forSourceLines(
+            "test.NonQualifier",
+            "package test;",
+            "",
+            "@interface NonQualifier {}");
+    Compilation compilation =
+        daggerCompiler().compile(
+            someBinding,
+            fieldQualifier,
+            constructorParameterQualifier,
+            methodParameterQualifier,
+            misplacedQualifier,
+            nonQualifier);
+    assertThat(compilation).succeeded();
+    assertThat(compilation)
+        .generatedSourceFile("test.SomeBinding_Factory")
+        .containsElementsIn(
+            JavaFileObjects.forSourceLines(
+                "test.SomeBinding_Factory",
+                "package test;",
+                "",
+                // Verifies that the @QualifierMetadata for the generated Factory does not contain
+                // @MisplacedQualifier, @NonQualifier, @MethodParameterQualifier or @FieldQualifier.
+                "@ScopeMetadata",
+                "@QualifierMetadata(\"test.ConstructorParameterQualifier\")",
+                GeneratedLines.generatedAnnotations(),
+                "public final class SomeBinding_Factory implements Factory<SomeBinding> {}"));
+    assertThat(compilation)
+        .generatedSourceFile("test.SomeBinding_MembersInjector")
+        .containsElementsIn(
+            JavaFileObjects.forSourceLines(
+                "test.SomeBinding_MembersInjector",
+                "package test;",
+                "",
+                // Verifies that the @QualifierMetadata for the generated MembersInjector does not
+                // contain @MisplacedQualifier, @NonQualifier, or @ConstructorParameterQualifier.
+                "@QualifierMetadata({",
+                "    \"test.FieldQualifier\",",
+                "    \"test.MethodParameterQualifier\"",
+                "})",
+                GeneratedLines.generatedAnnotations(),
+                "public final class SomeBinding_MembersInjector",
+                "    implements MembersInjector<SomeBinding> {}"));
+  }
+
+  @Test
+  public void testComplexQualifierMetadata() {
+    JavaFileObject someBinding =
+        JavaFileObjects.forSourceLines(
+            "test.SomeBinding",
+            "package test;",
+            "",
+            "import javax.inject.Inject;",
+            "import javax.inject.Inject;",
+            "",
+            "class SomeBinding {",
+            "  @QualifierWithValue(1) @Inject String injectField;",
+            "",
+            "  @Inject",
+            "  SomeBinding(",
+            "      @pkg1.SameNameQualifier String str1,",
+            "      @pkg2.SameNameQualifier String str2) {}",
+            "",
+            "  @Inject",
+            "  void injectMethod(@test.Outer.NestedQualifier Float f) {}",
+            "}");
+    JavaFileObject qualifierWithValue =
+        JavaFileObjects.forSourceLines(
+            "test.QualifierWithValue",
+            "package test;",
+            "",
+            "import javax.inject.Qualifier;",
+            "",
+            "@Qualifier",
+            "@interface QualifierWithValue {",
+            "  int value();",
+            "}");
+    JavaFileObject pkg1SameNameQualifier =
+        JavaFileObjects.forSourceLines(
+            "pkg1.SameNameQualifier",
+            "package pkg1;",
+            "",
+            "import javax.inject.Qualifier;",
+            "",
+            "@Qualifier",
+            "public @interface SameNameQualifier {}");
+    JavaFileObject pkg2SameNameQualifier =
+        JavaFileObjects.forSourceLines(
+            "pkg2.SameNameQualifier",
+            "package pkg2;",
+            "",
+            "import javax.inject.Qualifier;",
+            "",
+            "@Qualifier",
+            "public @interface SameNameQualifier {}");
+    JavaFileObject nestedQualifier =
+        JavaFileObjects.forSourceLines(
+            "test.Outer",
+            "package test;",
+            "",
+            "import javax.inject.Qualifier;",
+            "",
+            "interface Outer {",
+            "  @Qualifier",
+            "  @interface NestedQualifier {}",
+            "}");
+    Compilation compilation =
+        daggerCompiler().compile(
+            someBinding,
+            qualifierWithValue,
+            pkg1SameNameQualifier,
+            pkg2SameNameQualifier,
+            nestedQualifier);
+    assertThat(compilation).succeeded();
+    assertThat(compilation)
+        .generatedSourceFile("test.SomeBinding_Factory")
+        .containsElementsIn(
+            JavaFileObjects.forSourceLines(
+                "test.SomeBinding_Factory",
+                "package test;",
+                "",
+                "@ScopeMetadata",
+                "@QualifierMetadata({\"pkg1.SameNameQualifier\", \"pkg2.SameNameQualifier\"})",
+                GeneratedLines.generatedAnnotations(),
+                "public final class SomeBinding_Factory implements Factory<SomeBinding> {}"));
+    assertThat(compilation)
+        .generatedSourceFile("test.SomeBinding_MembersInjector")
+        .containsElementsIn(
+            JavaFileObjects.forSourceLines(
+                "test.SomeBinding_MembersInjector",
+                "package test;",
+                "",
+                "@QualifierMetadata({",
+                "    \"test.QualifierWithValue\",",
+                "    \"test.Outer.NestedQualifier\"",
+                "})",
+                GeneratedLines.generatedAnnotations(),
+                "public final class SomeBinding_MembersInjector",
+                "    implements MembersInjector<SomeBinding> {}"));
+  }
+
+  @Test
+  public void testBaseClassQualifierMetadata() {
+    JavaFileObject foo =
+        JavaFileObjects.forSourceLines(
+            "test.Foo",
+            "package test;",
+            "",
+            "import javax.inject.Inject;",
+            "import javax.inject.Singleton;",
+            "",
+            "class Foo extends FooBase {",
+            "  @FooFieldQualifier @Inject String injectField;",
+            "",
+            "  @Inject",
+            "  Foo(@FooConstructorQualifier int i) { super(i); }",
+            "",
+            "  @Inject",
+            "  void injectMethod(@FooMethodQualifier float f) {}",
+            "}");
+    JavaFileObject fooFieldQualifier =
+        JavaFileObjects.forSourceLines(
+            "test.FooFieldQualifier",
+            "package test;",
+            "",
+            "import javax.inject.Qualifier;",
+            "",
+            "@Qualifier",
+            "@interface FooFieldQualifier {}");
+    JavaFileObject fooConstructorQualifier =
+        JavaFileObjects.forSourceLines(
+            "test.FooConstructorQualifier",
+            "package test;",
+            "",
+            "import javax.inject.Qualifier;",
+            "",
+            "@Qualifier",
+            "@interface FooConstructorQualifier {}");
+    JavaFileObject fooMethodQualifier =
+        JavaFileObjects.forSourceLines(
+            "test.FooMethodQualifier",
+            "package test;",
+            "",
+            "import javax.inject.Qualifier;",
+            "",
+            "@Qualifier",
+            "@interface FooMethodQualifier {}");
+    JavaFileObject fooBase =
+        JavaFileObjects.forSourceLines(
+            "test.FooBase",
+            "package test;",
+            "",
+            "import javax.inject.Inject;",
+            "import javax.inject.Singleton;",
+            "",
+            "class FooBase {",
+            "  @FooBaseFieldQualifier @Inject String injectField;",
+            "",
+            "  @Inject",
+            "  FooBase(@FooBaseConstructorQualifier int i) {}",
+            "",
+            "  @Inject",
+            "  void injectMethod(@FooBaseMethodQualifier float f) {}",
+            "}");
+    JavaFileObject fooBaseFieldQualifier =
+        JavaFileObjects.forSourceLines(
+            "test.FooBaseFieldQualifier",
+            "package test;",
+            "",
+            "import javax.inject.Qualifier;",
+            "",
+            "@Qualifier",
+            "@interface FooBaseFieldQualifier {}");
+    JavaFileObject fooBaseConstructorQualifier =
+        JavaFileObjects.forSourceLines(
+            "test.FooBaseConstructorQualifier",
+            "package test;",
+            "",
+            "import javax.inject.Qualifier;",
+            "",
+            "@Qualifier",
+            "@interface FooBaseConstructorQualifier {}");
+    JavaFileObject fooBaseMethodQualifier =
+        JavaFileObjects.forSourceLines(
+            "test.FooBaseMethodQualifier",
+            "package test;",
+            "",
+            "import javax.inject.Qualifier;",
+            "",
+            "@Qualifier",
+            "@interface FooBaseMethodQualifier {}");
+    Compilation compilation =
+        daggerCompiler().compile(
+            foo,
+            fooBase,
+            fooFieldQualifier,
+            fooConstructorQualifier,
+            fooMethodQualifier,
+            fooBaseFieldQualifier,
+            fooBaseConstructorQualifier,
+            fooBaseMethodQualifier);
+    assertThat(compilation).succeeded();
+    assertThat(compilation)
+        .generatedSourceFile("test.Foo_Factory")
+        .containsElementsIn(
+            JavaFileObjects.forSourceLines(
+                "test.Foo_Factory",
+                "package test;",
+                "",
+                "@ScopeMetadata",
+                // Verifies that Foo_Factory only contains Foo's qualifiers and not FooBase's too.
+                "@QualifierMetadata(\"test.FooConstructorQualifier\")",
+                GeneratedLines.generatedAnnotations(),
+                "public final class Foo_Factory implements Factory<Foo> {}"));
+    assertThat(compilation)
+        .generatedSourceFile("test.Foo_MembersInjector")
+        .containsElementsIn(
+            JavaFileObjects.forSourceLines(
+                "test.Foo_MembersInjector",
+                "package test;",
+                "",
+                // Verifies that Foo_Factory only contains Foo's qualifiers and not FooBase's too.
+                "@QualifierMetadata({",
+                "    \"test.FooFieldQualifier\",",
+                "    \"test.FooMethodQualifier\"",
+                "})",
+                GeneratedLines.generatedAnnotations(),
+                "public final class Foo_MembersInjector implements MembersInjector<Foo> {}"));
+    assertThat(compilation)
+        .generatedSourceFile("test.FooBase_Factory")
+        .containsElementsIn(
+            JavaFileObjects.forSourceLines(
+                "test.Foo_Factory",
+                "package test;",
+                "",
+                "@ScopeMetadata",
+                "@QualifierMetadata(\"test.FooBaseConstructorQualifier\")",
+                GeneratedLines.generatedAnnotations(),
+                "public final class FooBase_Factory implements Factory<FooBase> {}"));
+    assertThat(compilation)
+        .generatedSourceFile("test.FooBase_MembersInjector")
+        .containsElementsIn(
+            JavaFileObjects.forSourceLines(
+                "test.FooBase_MembersInjector",
+                "package test;",
+                "",
+                "@QualifierMetadata({",
+                "    \"test.FooBaseFieldQualifier\",",
+                "    \"test.FooBaseMethodQualifier\"",
+                "})",
+                GeneratedLines.generatedAnnotations(),
+                "public final class FooBase_MembersInjector",
+                "    implements MembersInjector<FooBase> {}"));
+  }
 }
diff --git a/javatests/dagger/internal/codegen/InvalidInjectConstructor.java b/javatests/dagger/internal/codegen/InvalidInjectConstructor.java
new file mode 100644
index 0000000..ac3e61f
--- /dev/null
+++ b/javatests/dagger/internal/codegen/InvalidInjectConstructor.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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;
+
+
+/** A class that invalidly declares 2 inject constructors. */
+@SuppressWarnings("MoreThanOneInjectableConstructor") // Testing too many constructors
+public final class InvalidInjectConstructor {
+
+  @Inject String str;
+
+  @Inject
+  InvalidInjectConstructor() {}
+
+  @Inject
+  InvalidInjectConstructor(String str) {}
+}
diff --git a/javatests/dagger/internal/codegen/InvalidInjectConstructorTest.java b/javatests/dagger/internal/codegen/InvalidInjectConstructorTest.java
new file mode 100644
index 0000000..b49593a
--- /dev/null
+++ b/javatests/dagger/internal/codegen/InvalidInjectConstructorTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2022 The Dagger Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 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;
+
+// Tests an invalid inject constructor that avoids validation in its own library by using
+// a dependency on jsr330 rather than Dagger gets validated when used in a component.
+@RunWith(JUnit4.class)
+public final class InvalidInjectConstructorTest {
+
+  @Test
+  public void usedInvalidConstructorFails() {
+    JavaFileObject component =
+        JavaFileObjects.forSourceLines(
+            "test.TestComponent",
+            "package test;",
+            "",
+            "import dagger.Component;",
+            "import dagger.internal.codegen.InvalidInjectConstructor;",
+            "",
+            "@Component",
+            "interface TestComponent {",
+            "  InvalidInjectConstructor invalidInjectConstructor();",
+            "}");
+    Compilation compilation = daggerCompiler().compile(component);
+    assertThat(compilation).failed();
+    assertThat(compilation).hadErrorCount(2);
+    assertThat(compilation)
+        .hadErrorContaining(
+            "Type dagger.internal.codegen.InvalidInjectConstructor may only contain one injected "
+                + "constructor. Found: ["
+                + "InvalidInjectConstructor(), "
+                + "InvalidInjectConstructor(java.lang.String)"
+                + "]");
+    // TODO(b/215620949): Avoid reporting missing bindings on a type that has errors.
+    assertThat(compilation)
+        .hadErrorContaining(
+            "InvalidInjectConstructor cannot be provided without an @Inject constructor or an "
+                + "@Provides-annotated method.");
+  }
+
+  @Test
+  public void unusedInvalidConstructorFails() {
+    JavaFileObject component =
+        JavaFileObjects.forSourceLines(
+            "test.TestComponent",
+            "package test;",
+            "",
+            "import dagger.Component;",
+            "import dagger.internal.codegen.InvalidInjectConstructor;",
+            "",
+            "@Component",
+            "interface TestComponent {",
+            // Here we're only using the members injection, but we're testing that we still validate
+            // the constructors
+            "  void inject(InvalidInjectConstructor instance);",
+            "}");
+    Compilation compilation = daggerCompiler().compile(component);
+    assertThat(compilation).failed();
+    assertThat(compilation).hadErrorCount(2);
+    assertThat(compilation)
+        .hadErrorContaining(
+            "Type dagger.internal.codegen.InvalidInjectConstructor may only contain one injected "
+                + "constructor. Found: ["
+                + "InvalidInjectConstructor(), "
+                + "InvalidInjectConstructor(java.lang.String)"
+                + "]");
+    // TODO(b/215620949): Avoid reporting missing bindings on a type that has errors.
+    assertThat(compilation)
+        .hadErrorContaining(
+            "InvalidInjectConstructor cannot be provided without an @Inject constructor or an "
+                + "@Provides-annotated method.");
+  }
+}
diff --git a/javatests/dagger/internal/codegen/KeyFactoryTest.java b/javatests/dagger/internal/codegen/KeyFactoryTest.java
index fdf62cb..604bfe2 100644
--- a/javatests/dagger/internal/codegen/KeyFactoryTest.java
+++ b/javatests/dagger/internal/codegen/KeyFactoryTest.java
@@ -16,27 +16,31 @@
 
 package dagger.internal.codegen;
 
+import static androidx.room.compiler.processing.compat.XConverters.toXProcessing;
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XProcessingEnv;
 import com.google.common.collect.Iterables;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.testing.compile.CompilationRule;
-import dagger.BindsInstance;
+import com.squareup.javapoet.TypeName;
 import dagger.Component;
 import dagger.Module;
 import dagger.Provides;
 import dagger.internal.codegen.binding.KeyFactory;
+import dagger.internal.codegen.javac.JavacPluginModule;
 import dagger.internal.codegen.langmodel.DaggerElements;
 import dagger.internal.codegen.langmodel.DaggerTypes;
-import dagger.model.Key;
-import dagger.model.Key.MultibindingContributionIdentifier;
 import dagger.multibindings.ElementsIntoSet;
 import dagger.multibindings.IntoSet;
 import dagger.producers.ProducerModule;
 import dagger.producers.Produces;
+import dagger.spi.model.DaggerAnnotation;
+import dagger.spi.model.DaggerType;
+import dagger.spi.model.Key;
+import dagger.spi.model.Key.MultibindingContributionIdentifier;
 import java.lang.annotation.Retention;
 import java.util.Set;
 import javax.inject.Inject;
@@ -61,12 +65,17 @@
 public class KeyFactoryTest {
   @Rule public CompilationRule compilationRule = new CompilationRule();
 
+  @Inject XProcessingEnv processingEnv;
   @Inject DaggerElements elements;
   @Inject DaggerTypes types;
   @Inject KeyFactory keyFactory;
 
   @Before public void setUp() {
-    DaggerKeyFactoryTest_TestComponent.factory().create(compilationRule).inject(this);
+    DaggerKeyFactoryTest_TestComponent.builder()
+        .javacPluginModule(
+            new JavacPluginModule(compilationRule.getElements(), compilationRule.getTypes()))
+        .build()
+        .inject(this);
   }
 
   @Test public void forInjectConstructorWithResolvedType() {
@@ -76,7 +85,7 @@
         Iterables.getOnlyElement(ElementFilter.constructorsIn(typeElement.getEnclosedElements()));
     Key key =
         keyFactory.forInjectConstructorWithResolvedType(constructor.getEnclosingElement().asType());
-    assertThat(key).isEqualTo(Key.builder(typeElement.asType()).build());
+    assertThat(key).isEqualTo(Key.builder(fromJava(typeElement.asType())).build());
     assertThat(key.toString()).isEqualTo("dagger.internal.codegen.KeyFactoryTest.InjectedClass");
   }
 
@@ -92,7 +101,7 @@
     ExecutableElement providesMethod =
         Iterables.getOnlyElement(ElementFilter.methodsIn(moduleElement.getEnclosedElements()));
     Key key = keyFactory.forProvidesMethod(providesMethod, moduleElement);
-    assertThat(key).isEqualTo(Key.builder(stringType).build());
+    assertThat(key).isEqualTo(Key.builder(fromJava(stringType)).build());
     assertThat(key.toString()).isEqualTo("java.lang.String");
   }
 
@@ -112,10 +121,9 @@
     ExecutableElement providesMethod =
         Iterables.getOnlyElement(ElementFilter.methodsIn(moduleElement.getEnclosedElements()));
     Key key = keyFactory.forProvidesMethod(providesMethod, moduleElement);
-    assertThat(MoreTypes.equivalence().wrap(key.qualifier().get().getAnnotationType()))
-        .isEqualTo(MoreTypes.equivalence().wrap(qualifierElement.asType()));
-    assertThat(MoreTypes.equivalence().wrap(key.type()))
-        .isEqualTo(MoreTypes.equivalence().wrap(stringType));
+    assertThat(TypeName.get(key.qualifier().get().java().getAnnotationType()))
+        .isEqualTo(TypeName.get(qualifierElement.asType()));
+    assertThat(TypeName.get(key.type().java())).isEqualTo(TypeName.get(stringType));
     assertThat(key.toString())
         .isEqualTo(
             "@dagger.internal.codegen.KeyFactoryTest.TestQualifier({"
@@ -141,7 +149,7 @@
     Element injectionField =
         Iterables.getOnlyElement(ElementFilter.fieldsIn(injectableElement.getEnclosedElements()));
     AnnotationMirror qualifier = Iterables.getOnlyElement(injectionField.getAnnotationMirrors());
-    Key injectionKey = Key.builder(type).qualifier(qualifier).build();
+    Key injectionKey = Key.builder(fromJava(type)).qualifier(fromJava(qualifier)).build();
 
     assertThat(provisionKey).isEqualTo(injectionKey);
     assertThat(injectionKey.toString())
@@ -203,7 +211,7 @@
       Key key = keyFactory.forProvidesMethod(providesMethod, moduleElement);
       assertThat(key)
           .isEqualTo(
-              Key.builder(setOfStringsType)
+              Key.builder(fromJava(setOfStringsType))
                   .multibindingContributionIdentifier(
                       new MultibindingContributionIdentifier(providesMethod, moduleElement))
                   .build());
@@ -270,7 +278,7 @@
     for (ExecutableElement producesMethod
         : ElementFilter.methodsIn(moduleElement.getEnclosedElements())) {
       Key key = keyFactory.forProducesMethod(producesMethod, moduleElement);
-      assertThat(key).isEqualTo(Key.builder(stringType).build());
+      assertThat(key).isEqualTo(Key.builder(fromJava(stringType)).build());
       assertThat(key.toString()).isEqualTo("java.lang.String");
     }
   }
@@ -297,7 +305,7 @@
       Key key = keyFactory.forProducesMethod(producesMethod, moduleElement);
       assertThat(key)
           .isEqualTo(
-              Key.builder(setOfStringsType)
+              Key.builder(fromJava(setOfStringsType))
                   .multibindingContributionIdentifier(
                       new MultibindingContributionIdentifier(producesMethod, moduleElement))
                   .build());
@@ -310,6 +318,14 @@
     }
   }
 
+  private DaggerAnnotation fromJava(AnnotationMirror annotation) {
+    return DaggerAnnotation.from(toXProcessing(annotation, processingEnv));
+  }
+
+  private DaggerType fromJava(TypeMirror typeMirror) {
+    return DaggerType.from(toXProcessing(typeMirror, processingEnv));
+  }
+
   @ProducerModule
   static final class SetProducesMethodsModule {
     @Produces @IntoSet String produceString() {
@@ -331,26 +347,8 @@
   }
 
   @Singleton
-  @Component(modules = {TestModule.class})
+  @Component(modules = JavacPluginModule.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/MapBindingComponentProcessorTest.java b/javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java
index aa2a0ff..622ca86 100644
--- a/javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java
+++ b/javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java
@@ -17,6 +17,8 @@
 package dagger.internal.codegen;
 
 import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
+import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
 import static dagger.internal.codegen.Compilers.compilerWithOptions;
 import static dagger.internal.codegen.Compilers.daggerCompiler;
 
@@ -93,22 +95,24 @@
         "    LOGIN;",
         "}");
 
-    JavaFileObject HandlerFile = JavaFileObjects.forSourceLines("test.Handler",
-        "package test;",
-        "",
-        "interface Handler {}");
-    JavaFileObject LoginHandlerFile = JavaFileObjects.forSourceLines("test.LoginHandler",
-        "package test;",
-        "",
-        "class LoginHandler implements Handler {",
-        "  public LoginHandler() {}",
-        "}");
-    JavaFileObject AdminHandlerFile = JavaFileObjects.forSourceLines("test.AdminHandler",
-        "package test;",
-        "",
-        "class AdminHandler implements Handler {",
-        "  public AdminHandler() {}",
-        "}");
+    JavaFileObject handlerFile =
+        JavaFileObjects.forSourceLines("test.Handler", "package test;", "", "interface Handler {}");
+    JavaFileObject loginHandlerFile =
+        JavaFileObjects.forSourceLines(
+            "test.LoginHandler",
+            "package test;",
+            "",
+            "class LoginHandler implements Handler {",
+            "  public LoginHandler() {}",
+            "}");
+    JavaFileObject adminHandlerFile =
+        JavaFileObjects.forSourceLines(
+            "test.AdminHandler",
+            "package test;",
+            "",
+            "class AdminHandler implements Handler {",
+            "  public AdminHandler() {}",
+            "}");
     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
         "package test;",
         "",
@@ -120,119 +124,7 @@
         "interface TestComponent {",
         "  Provider<Map<PathEnum, Provider<Handler>>> dispatcher();",
         "}");
-    JavaFileObject generatedComponent;
-    switch (compilerMode) {
-      case FAST_INIT_MODE:
-        generatedComponent =
-            JavaFileObjects.forSourceLines(
-                "test.DaggerTestComponent",
-                "package test;",
-                "",
-                GeneratedLines.generatedAnnotations(),
-                "final class DaggerTestComponent implements TestComponent {",
-                "  private final MapModuleOne mapModuleOne;",
-                "  private final MapModuleTwo mapModuleTwo;",
-                "  private volatile Provider<Handler> provideAdminHandlerProvider;",
-                "  private volatile Provider<Handler> provideLoginHandlerProvider;",
-                "  private volatile Provider<Map<PathEnum, Provider<Handler>>>",
-                "      mapOfPathEnumAndProviderOfHandlerProvider;",
-                "",
-                "  private Provider<Handler> provideAdminHandlerProvider() {",
-                "    Object local = provideAdminHandlerProvider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(1);",
-                "      provideAdminHandlerProvider = (Provider<Handler>) local;",
-                "    }",
-                "    return (Provider<Handler>) local;",
-                "  }",
-                "",
-                "  private Provider<Handler> provideLoginHandlerProvider() {",
-                "    Object local = provideLoginHandlerProvider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(2);",
-                "      provideLoginHandlerProvider = (Provider<Handler>) local;",
-                "    }",
-                "    return (Provider<Handler>) local;",
-                "  }",
-                "",
-                "  private Map<PathEnum, Provider<Handler>>",
-                "        mapOfPathEnumAndProviderOfHandler() {",
-                "    return ImmutableMap.<PathEnum, Provider<Handler>>of(",
-                "        PathEnum.ADMIN, provideAdminHandlerProvider(),",
-                "        PathEnum.LOGIN, provideLoginHandlerProvider());",
-                "  }",
-                "",
-                "  @Override",
-                "  public Provider<Map<PathEnum, Provider<Handler>>> dispatcher() {",
-                "    Object local = mapOfPathEnumAndProviderOfHandlerProvider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(0);",
-                "      mapOfPathEnumAndProviderOfHandlerProvider =",
-                "          (Provider<Map<PathEnum, Provider<Handler>>>) local;",
-                "    }",
-                "    return (Provider<Map<PathEnum, Provider<Handler>>>) local;",
-                "  }",
-                "",
-                "  private final class SwitchingProvider<T> implements Provider<T> {",
-                "    private final int id;",
-                "",
-                "    SwitchingProvider(int id) {",
-                "      this.id = id;",
-                "    }",
-                "",
-                "    @SuppressWarnings(\"unchecked\")",
-                "    @Override",
-                "    public T get() {",
-                "      switch (id) {",
-                "        case 0:",
-                "            return (T) DaggerTestComponent.this",
-                "                 .mapOfPathEnumAndProviderOfHandler();",
-                "        case 1:",
-                "            return (T) MapModuleOne_ProvideAdminHandlerFactory",
-                "                .provideAdminHandler(DaggerTestComponent.this.mapModuleOne);",
-                "        case 2:",
-                "            return (T) MapModuleTwo_ProvideLoginHandlerFactory",
-                "                .provideLoginHandler(DaggerTestComponent.this.mapModuleTwo);",
-                "        default: throw new AssertionError(id);",
-                "      }",
-                "    }",
-                "  }",
-                "}");
-        break;
-      default:
-        generatedComponent =
-            JavaFileObjects.forSourceLines(
-                "test.DaggerTestComponent",
-                "package test;",
-                "",
-                GeneratedLines.generatedAnnotations(),
-                "final class DaggerTestComponent implements TestComponent {",
-                "  private Provider<Handler> provideAdminHandlerProvider;",
-                "  private Provider<Handler> provideLoginHandlerProvider;",
-                "  private Provider<Map<PathEnum, Provider<Handler>>>",
-                "      mapOfPathEnumAndProviderOfHandlerProvider;",
-                "",
-                "  @SuppressWarnings(\"unchecked\")",
-                "  private void initialize(",
-                "      final MapModuleOne mapModuleOneParam,",
-                "      final MapModuleTwo mapModuleTwoParam) {",
-                "    this.provideAdminHandlerProvider =",
-                "        MapModuleOne_ProvideAdminHandlerFactory.create(mapModuleOneParam);",
-                "    this.provideLoginHandlerProvider =",
-                "        MapModuleTwo_ProvideLoginHandlerFactory.create(mapModuleTwoParam);",
-                "    this.mapOfPathEnumAndProviderOfHandlerProvider =",
-                "        MapProviderFactory.<PathEnum, Handler>builder(2)",
-                "            .put(PathEnum.ADMIN, provideAdminHandlerProvider)",
-                "            .put(PathEnum.LOGIN, provideLoginHandlerProvider)",
-                "            .build();",
-                "  }",
-                "",
-                "  @Override",
-                "  public Provider<Map<PathEnum, Provider<Handler>>> dispatcher() {",
-                "    return mapOfPathEnumAndProviderOfHandlerProvider;",
-                "  }",
-                "}");
-    }
+
     Compilation compilation =
         compilerWithOptions(compilerMode.javacopts())
             .compile(
@@ -240,14 +132,82 @@
                 mapModuleTwoFile,
                 enumKeyFile,
                 pathEnumFile,
-                HandlerFile,
-                LoginHandlerFile,
-                AdminHandlerFile,
+                handlerFile,
+                loginHandlerFile,
+                adminHandlerFile,
                 componentFile);
     assertThat(compilation).succeeded();
     assertThat(compilation)
         .generatedSourceFile("test.DaggerTestComponent")
-        .containsElementsIn(generatedComponent);
+        .containsElementsIn(
+            compilerMode
+                .javaFileBuilder("test.DaggerTestComponent")
+                .addLines(
+                    "package test;",
+                    "",
+                    GeneratedLines.generatedAnnotations(),
+                    "final class DaggerTestComponent implements TestComponent {",
+                    "  private final DaggerTestComponent testComponent = this;",
+                    "  private Provider<Handler> provideAdminHandlerProvider;",
+                    "  private Provider<Handler> provideLoginHandlerProvider;",
+                    "  private Provider<Map<PathEnum, Provider<Handler>>>",
+                    "      mapOfPathEnumAndProviderOfHandlerProvider;")
+                .addLinesIn(
+                    DEFAULT_MODE,
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize(",
+                    "      final MapModuleOne mapModuleOneParam,",
+                    "      final MapModuleTwo mapModuleTwoParam) {",
+                    "    this.provideAdminHandlerProvider =",
+                    "        MapModuleOne_ProvideAdminHandlerFactory.create(mapModuleOneParam);",
+                    "    this.provideLoginHandlerProvider =",
+                    "        MapModuleTwo_ProvideLoginHandlerFactory.create(mapModuleTwoParam);",
+                    "    this.mapOfPathEnumAndProviderOfHandlerProvider =",
+                    "        MapProviderFactory.<PathEnum, Handler>builder(2)",
+                    "            .put(PathEnum.ADMIN, provideAdminHandlerProvider)",
+                    "            .put(PathEnum.LOGIN, provideLoginHandlerProvider)",
+                    "            .build();",
+                    "  }")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize(final MapModuleOne mapModuleOneParam,",
+                    "      final MapModuleTwo mapModuleTwoParam) {",
+                    "    this.provideAdminHandlerProvider =",
+                    "        new SwitchingProvider<>(testComponent, 1);",
+                    "    this.provideLoginHandlerProvider =",
+                    "       new SwitchingProvider<>(testComponent, 2);",
+                    "    this.mapOfPathEnumAndProviderOfHandlerProvider =",
+                    "        new SwitchingProvider<>(testComponent, 0);",
+                    "  }")
+                .addLines(
+                    "  @Override",
+                    "  public Provider<Map<PathEnum, Provider<Handler>>> dispatcher() {",
+                    "    return mapOfPathEnumAndProviderOfHandlerProvider;",
+                    "  }")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "",
+                    "  private static final class SwitchingProvider<T> implements Provider<T> {",
+                    "    @SuppressWarnings(\"unchecked\")",
+                    "    @Override",
+                    "    public T get() {",
+                    "      switch (id) {",
+                    "        case 0: return (T) ImmutableMap.<PathEnum, Provider<Handler>>of(",
+                    "          PathEnum.ADMIN,",
+                    "          testComponent.provideAdminHandlerProvider,",
+                    "          PathEnum.LOGIN,",
+                    "          testComponent.provideLoginHandlerProvider);",
+                    "        case 1: return (T) MapModuleOne_ProvideAdminHandlerFactory",
+                    "            .provideAdminHandler(testComponent.mapModuleOne);",
+                    "        case 2: return (T) MapModuleTwo_ProvideLoginHandlerFactory",
+                    "            .provideLoginHandler(testComponent.mapModuleTwo);",
+                    "        default: throw new AssertionError(id);",
+                    "      }",
+                    "    }",
+                    "  }",
+                    "}")
+                .build());
   }
 
   @Test
@@ -500,22 +460,24 @@
                 "    return new LoginHandler();",
                 "  }",
                 "}");
-    JavaFileObject HandlerFile = JavaFileObjects.forSourceLines("test.Handler",
-        "package test;",
-        "",
-        "interface Handler {}");
-    JavaFileObject LoginHandlerFile = JavaFileObjects.forSourceLines("test.LoginHandler",
-        "package test;",
-        "",
-        "class LoginHandler implements Handler {",
-        "  public LoginHandler() {}",
-        "}");
-    JavaFileObject AdminHandlerFile = JavaFileObjects.forSourceLines("test.AdminHandler",
-        "package test;",
-        "",
-        "class AdminHandler implements Handler {",
-        "  public AdminHandler() {}",
-        "}");
+    JavaFileObject handlerFile =
+        JavaFileObjects.forSourceLines("test.Handler", "package test;", "", "interface Handler {}");
+    JavaFileObject loginHandlerFile =
+        JavaFileObjects.forSourceLines(
+            "test.LoginHandler",
+            "package test;",
+            "",
+            "class LoginHandler implements Handler {",
+            "  public LoginHandler() {}",
+            "}");
+    JavaFileObject adminHandlerFile =
+        JavaFileObjects.forSourceLines(
+            "test.AdminHandler",
+            "package test;",
+            "",
+            "class AdminHandler implements Handler {",
+            "  public AdminHandler() {}",
+            "}");
     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
         "package test;",
         "",
@@ -527,132 +489,88 @@
         "interface TestComponent {",
         "  Provider<Map<String, Provider<Handler>>> dispatcher();",
         "}");
-    JavaFileObject generatedComponent;
-    switch (compilerMode) {
-      case FAST_INIT_MODE:
-        generatedComponent =
-            JavaFileObjects.forSourceLines(
-                "test.DaggerTestComponent",
-                "package test;",
-                "",
-                GeneratedLines.generatedAnnotations(),
-                "final class DaggerTestComponent implements TestComponent {",
-                "  private final MapModuleOne mapModuleOne;",
-                "  private final MapModuleTwo mapModuleTwo;",
-                "  private volatile Provider<Handler> provideAdminHandlerProvider;",
-                "  private volatile Provider<Handler> provideLoginHandlerProvider;",
-                "  private volatile Provider<Map<String, Provider<Handler>>>",
-                "      mapOfStringAndProviderOfHandlerProvider;",
-                "",
-                "  private Provider<Handler> provideAdminHandlerProvider() {",
-                "    Object local = provideAdminHandlerProvider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(1);",
-                "      provideAdminHandlerProvider = (Provider<Handler>) local;",
-                "    }",
-                "    return (Provider<Handler>) local;",
-                "  }",
-                "",
-                "  private Provider<Handler> provideLoginHandlerProvider() {",
-                "    Object local = provideLoginHandlerProvider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(2);",
-                "      provideLoginHandlerProvider = (Provider<Handler>) local;",
-                "    }",
-                "    return (Provider<Handler>) local;",
-                "  }",
-                "",
-                "  private Map<String, Provider<Handler>>",
-                "        mapOfStringAndProviderOfHandler() {",
-                "    return ImmutableMap.<String, Provider<Handler>>of(",
-                "        \"Admin\", provideAdminHandlerProvider(),",
-                "        \"Login\", provideLoginHandlerProvider());",
-                "  }",
-                "",
-                "  @Override",
-                "  public Provider<Map<String, Provider<Handler>>> dispatcher() {",
-                "    Object local = mapOfStringAndProviderOfHandlerProvider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(0);",
-                "      mapOfStringAndProviderOfHandlerProvider =",
-                "          (Provider<Map<String, Provider<Handler>>>) local;",
-                "    }",
-                "    return (Provider<Map<String, Provider<Handler>>>) local;",
-                "  }",
-                "",
-                "  private final class SwitchingProvider<T> implements Provider<T> {",
-                "    private final int id;",
-                "",
-                "    SwitchingProvider(int id) {",
-                "      this.id = id;",
-                "    }",
-                "",
-                "    @SuppressWarnings(\"unchecked\")",
-                "    @Override",
-                "    public T get() {",
-                "      switch (id) {",
-                "        case 0:",
-                "            return (T) DaggerTestComponent.this",
-                "                 .mapOfStringAndProviderOfHandler();",
-                "        case 1:",
-                "            return (T) MapModuleOne_ProvideAdminHandlerFactory",
-                "                .provideAdminHandler(DaggerTestComponent.this.mapModuleOne);",
-                "        case 2:",
-                "            return (T) MapModuleTwo_ProvideLoginHandlerFactory",
-                "                .provideLoginHandler(DaggerTestComponent.this.mapModuleTwo);",
-                "        default: throw new AssertionError(id);",
-                "      }",
-                "    }",
-                "  }",
-                "}");
-        break;
-      default:
-        generatedComponent =
-            JavaFileObjects.forSourceLines(
-                "test.DaggerTestComponent",
-                "package test;",
-                "",
-                GeneratedLines.generatedAnnotations(),
-                "final class DaggerTestComponent implements TestComponent {",
-                "  private Provider<Handler> provideAdminHandlerProvider;",
-                "  private Provider<Handler> provideLoginHandlerProvider;",
-                "  private Provider<Map<String, Provider<Handler>>>",
-                "      mapOfStringAndProviderOfHandlerProvider;",
-                "",
-                "  @SuppressWarnings(\"unchecked\")",
-                "  private void initialize(",
-                "      final MapModuleOne mapModuleOneParam,",
-                "      final MapModuleTwo mapModuleTwoParam) {",
-                "    this.provideAdminHandlerProvider =",
-                "        MapModuleOne_ProvideAdminHandlerFactory.create(mapModuleOneParam);",
-                "    this.provideLoginHandlerProvider =",
-                "        MapModuleTwo_ProvideLoginHandlerFactory.create(mapModuleTwoParam);",
-                "    this.mapOfStringAndProviderOfHandlerProvider =",
-                "        MapProviderFactory.<String, Handler>builder(2)",
-                "            .put(\"Admin\", provideAdminHandlerProvider)",
-                "            .put(\"Login\", provideLoginHandlerProvider)",
-                "            .build();",
-                "  }",
-                "",
-                "  @Override",
-                "  public Provider<Map<String, Provider<Handler>>> dispatcher() {",
-                "    return mapOfStringAndProviderOfHandlerProvider;",
-                "  }",
-                "}");
-    }
+
     Compilation compilation =
         compilerWithOptions(compilerMode.javacopts())
             .compile(
                 mapModuleOneFile,
                 mapModuleTwoFile,
-                HandlerFile,
-                LoginHandlerFile,
-                AdminHandlerFile,
+                handlerFile,
+                loginHandlerFile,
+                adminHandlerFile,
                 componentFile);
     assertThat(compilation).succeeded();
     assertThat(compilation)
         .generatedSourceFile("test.DaggerTestComponent")
-        .containsElementsIn(generatedComponent);
+        .containsElementsIn(
+            compilerMode
+                .javaFileBuilder("test.DaggerTestComponent")
+                .addLines(
+                    "package test;",
+                    "",
+                    GeneratedLines.generatedAnnotations(),
+                    "final class DaggerTestComponent implements TestComponent {",
+                    "  private final DaggerTestComponent testComponent = this;",
+                    "  private Provider<Handler> provideAdminHandlerProvider;",
+                    "  private Provider<Handler> provideLoginHandlerProvider;",
+                    "  private Provider<Map<String, Provider<Handler>>>",
+                    "      mapOfStringAndProviderOfHandlerProvider;")
+                .addLinesIn(
+                    DEFAULT_MODE,
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize(",
+                    "      final MapModuleOne mapModuleOneParam,",
+                    "      final MapModuleTwo mapModuleTwoParam) {",
+                    "    this.provideAdminHandlerProvider =",
+                    "        MapModuleOne_ProvideAdminHandlerFactory.create(mapModuleOneParam);",
+                    "    this.provideLoginHandlerProvider =",
+                    "        MapModuleTwo_ProvideLoginHandlerFactory.create(mapModuleTwoParam);",
+                    "    this.mapOfStringAndProviderOfHandlerProvider =",
+                    "        MapProviderFactory.<String, Handler>builder(2)",
+                    "            .put(\"Admin\", provideAdminHandlerProvider)",
+                    "            .put(\"Login\", provideLoginHandlerProvider)",
+                    "            .build();",
+                    "  }")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize(",
+                    "      final MapModuleOne mapModuleOneParam,",
+                    "      final MapModuleTwo mapModuleTwoParam) {",
+                    "    this.provideAdminHandlerProvider =",
+                    "        new SwitchingProvider<>(testComponent, 1);",
+                    "    this.provideLoginHandlerProvider =",
+                    "        new SwitchingProvider<>(testComponent, 2);",
+                    "    this.mapOfStringAndProviderOfHandlerProvider =",
+                    "        new SwitchingProvider<>(testComponent, 0);",
+                    "  }")
+                .addLines(
+                    "  @Override",
+                    "  public Provider<Map<String, Provider<Handler>>> dispatcher() {",
+                    "    return mapOfStringAndProviderOfHandlerProvider;",
+                    "  }")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "  private static final class SwitchingProvider<T> implements Provider<T> {",
+                    "    @SuppressWarnings(\"unchecked\")",
+                    "    @Override",
+                    "    public T get() {",
+                    "      switch (id) {",
+                    "        case 0: return (T) ImmutableMap.<String, Provider<Handler>>of(",
+                    "          \"Admin\",",
+                    "          testComponent.provideAdminHandlerProvider,",
+                    "          \"Login\",",
+                    "          testComponent.provideLoginHandlerProvider);",
+                    "        case 1: return (T) MapModuleOne_ProvideAdminHandlerFactory",
+                    "            .provideAdminHandler(testComponent.mapModuleOne);",
+                    "        case 2: return (T) MapModuleTwo_ProvideLoginHandlerFactory",
+                    "            .provideLoginHandler(testComponent.mapModuleTwo);",
+                    "        default: throw new AssertionError(id);",
+                    "      }",
+                    "    }",
+                    "  }",
+                    "}")
+                .build());
   }
 
   @Test
@@ -700,22 +618,24 @@
         "public @interface WrappedClassKey {",
         "  Class<?> value();",
         "}");
-    JavaFileObject HandlerFile = JavaFileObjects.forSourceLines("test.Handler",
-        "package test;",
-        "",
-        "interface Handler {}");
-    JavaFileObject LoginHandlerFile = JavaFileObjects.forSourceLines("test.LoginHandler",
-        "package test;",
-        "",
-        "class LoginHandler implements Handler {",
-        "  public LoginHandler() {}",
-        "}");
-    JavaFileObject AdminHandlerFile = JavaFileObjects.forSourceLines("test.AdminHandler",
-        "package test;",
-        "",
-        "class AdminHandler implements Handler {",
-        "  public AdminHandler() {}",
-        "}");
+    JavaFileObject handlerFile =
+        JavaFileObjects.forSourceLines("test.Handler", "package test;", "", "interface Handler {}");
+    JavaFileObject loginHandlerFile =
+        JavaFileObjects.forSourceLines(
+            "test.LoginHandler",
+            "package test;",
+            "",
+            "class LoginHandler implements Handler {",
+            "  public LoginHandler() {}",
+            "}");
+    JavaFileObject adminHandlerFile =
+        JavaFileObjects.forSourceLines(
+            "test.AdminHandler",
+            "package test;",
+            "",
+            "class AdminHandler implements Handler {",
+            "  public AdminHandler() {}",
+            "}");
     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
         "package test;",
         "",
@@ -727,144 +647,91 @@
         "interface TestComponent {",
         "  Provider<Map<WrappedClassKey, Provider<Handler>>> dispatcher();",
         "}");
-    JavaFileObject generatedComponent;
-    switch (compilerMode) {
-      case FAST_INIT_MODE:
-        generatedComponent =
-            JavaFileObjects.forSourceLines(
-                "test.DaggerTestComponent",
-                "package test;",
-                "",
-                GeneratedLines.generatedAnnotations(),
-                "final class DaggerTestComponent implements TestComponent {",
-                "  private final MapModuleOne mapModuleOne;",
-                "  private final MapModuleTwo mapModuleTwo;",
-                "  private volatile Provider<Handler> provideAdminHandlerProvider;",
-                "  private volatile Provider<Handler> provideLoginHandlerProvider;",
-                "  private volatile Provider<Map<WrappedClassKey, Provider<Handler>>>",
-                "      mapOfWrappedClassKeyAndProviderOfHandlerProvider;",
-                "",
-                "  private DaggerTestComponent(",
-                "      MapModuleOne mapModuleOneParam,",
-                "      MapModuleTwo mapModuleTwoParam) {",
-                "    this.mapModuleOne = mapModuleOneParam;",
-                "    this.mapModuleTwo = mapModuleTwoParam;",
-                "  }",
-                "",
-                "  private Provider<Handler> provideAdminHandlerProvider() {",
-                "    Object local = provideAdminHandlerProvider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(1);",
-                "      provideAdminHandlerProvider = (Provider<Handler>) local;",
-                "    }",
-                "    return (Provider<Handler>) local;",
-                "  }",
-                "",
-                "  private Provider<Handler> provideLoginHandlerProvider() {",
-                "    Object local = provideLoginHandlerProvider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(2);",
-                "      provideLoginHandlerProvider = (Provider<Handler>) local;",
-                "    }",
-                "    return (Provider<Handler>) local;",
-                "  }",
-                "",
-                "  private Map<WrappedClassKey, Provider<Handler>>",
-                "      mapOfWrappedClassKeyAndProviderOfHandler() {",
-                "    return ImmutableMap.<WrappedClassKey, Provider<Handler>>of(",
-                "        WrappedClassKeyCreator.createWrappedClassKey(Integer.class),",
-                "        provideAdminHandlerProvider(),",
-                "        WrappedClassKeyCreator.createWrappedClassKey(Long.class),",
-                "        provideLoginHandlerProvider());",
-                "  }",
-                "",
-                "  @Override",
-                "  public Provider<Map<WrappedClassKey, Provider<Handler>>> dispatcher() {",
-                "    Object local = mapOfWrappedClassKeyAndProviderOfHandlerProvider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(0);",
-                "      mapOfWrappedClassKeyAndProviderOfHandlerProvider =",
-                "          (Provider<Map<WrappedClassKey, Provider<Handler>>>) local;",
-                "    }",
-                "    return (Provider<Map<WrappedClassKey, Provider<Handler>>>) local;",
-                "  }",
-                "",
-                "  private final class SwitchingProvider<T> implements Provider<T> {",
-                "    private final int id;",
-                "",
-                "    SwitchingProvider(int id) {",
-                "      this.id = id;",
-                "    }",
-                "",
-                "    @SuppressWarnings(\"unchecked\")",
-                "    @Override",
-                "    public T get() {",
-                "      switch (id) {",
-                "        case 0:",
-                "            return (T) DaggerTestComponent.this",
-                "                 .mapOfWrappedClassKeyAndProviderOfHandler();",
-                "        case 1:",
-                "            return (T) MapModuleOne_ProvideAdminHandlerFactory",
-                "                .provideAdminHandler(DaggerTestComponent.this.mapModuleOne);",
-                "        case 2:",
-                "            return (T) MapModuleTwo_ProvideLoginHandlerFactory",
-                "                .provideLoginHandler(DaggerTestComponent.this.mapModuleTwo);",
-                "        default: throw new AssertionError(id);",
-                "      }",
-                "    }",
-                "  }",
-                "}");
-        break;
-      default:
-        generatedComponent =
-            JavaFileObjects.forSourceLines(
-                "test.DaggerTestComponent",
-                "package test;",
-                "",
-                GeneratedLines.generatedAnnotations(),
-                "final class DaggerTestComponent implements TestComponent {",
-                "  private Provider<Handler> provideAdminHandlerProvider;",
-                "  private Provider<Handler> provideLoginHandlerProvider;",
-                "  private Provider<Map<WrappedClassKey, Provider<Handler>>>",
-                "      mapOfWrappedClassKeyAndProviderOfHandlerProvider;",
-                "",
-                "  @SuppressWarnings(\"unchecked\")",
-                "  private void initialize(",
-                "      final MapModuleOne mapModuleOneParam,",
-                "      final MapModuleTwo mapModuleTwoParam) {",
-                "    this.provideAdminHandlerProvider =",
-                "        MapModuleOne_ProvideAdminHandlerFactory.create(mapModuleOneParam);",
-                "    this.provideLoginHandlerProvider =",
-                "        MapModuleTwo_ProvideLoginHandlerFactory.create(mapModuleTwoParam);",
-                "    this.mapOfWrappedClassKeyAndProviderOfHandlerProvider =",
-                "        MapProviderFactory.<WrappedClassKey, Handler>builder(2)",
-                "            .put(WrappedClassKeyCreator.createWrappedClassKey(Integer.class),",
-                "                provideAdminHandlerProvider)",
-                "            .put(WrappedClassKeyCreator.createWrappedClassKey(Long.class),",
-                "                provideLoginHandlerProvider)",
-                "            .build();",
-                "  }",
-                "",
-                "  @Override",
-                "  public Provider<Map<WrappedClassKey, Provider<Handler>>> dispatcher() {",
-                "    return mapOfWrappedClassKeyAndProviderOfHandlerProvider;",
-                "  }",
-                "}");
-    }
+
     Compilation compilation =
         compilerWithOptions(compilerMode.javacopts())
             .compile(
                 mapModuleOneFile,
                 mapModuleTwoFile,
                 wrappedClassKeyFile,
-                HandlerFile,
-                LoginHandlerFile,
-                AdminHandlerFile,
+                handlerFile,
+                loginHandlerFile,
+                adminHandlerFile,
                 componentFile);
     assertThat(compilation).succeeded();
     assertThat(compilation)
         .generatedSourceFile("test.DaggerTestComponent")
-        .containsElementsIn(generatedComponent);
+        .containsElementsIn(
+            compilerMode
+                .javaFileBuilder("test.DaggerTestComponent")
+                .addLines(
+                    "package test;",
+                    "",
+                    GeneratedLines.generatedAnnotations(),
+                    "final class DaggerTestComponent implements TestComponent {",
+                    "  private final DaggerTestComponent testComponent = this;",
+                    "  private Provider<Handler> provideAdminHandlerProvider;",
+                    "  private Provider<Handler> provideLoginHandlerProvider;",
+                    "  private Provider<Map<WrappedClassKey, Provider<Handler>>>",
+                    "      mapOfWrappedClassKeyAndProviderOfHandlerProvider;")
+                .addLinesIn(
+                    DEFAULT_MODE,
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize(",
+                    "      final MapModuleOne mapModuleOneParam,",
+                    "      final MapModuleTwo mapModuleTwoParam) {",
+                    "    this.provideAdminHandlerProvider =",
+                    "        MapModuleOne_ProvideAdminHandlerFactory.create(mapModuleOneParam);",
+                    "    this.provideLoginHandlerProvider =",
+                    "        MapModuleTwo_ProvideLoginHandlerFactory.create(mapModuleTwoParam);",
+                    "    this.mapOfWrappedClassKeyAndProviderOfHandlerProvider =",
+                    "        MapProviderFactory.<WrappedClassKey, Handler>builder(2)",
+                    "            .put(WrappedClassKeyCreator.createWrappedClassKey(Integer.class),",
+                    "                provideAdminHandlerProvider)",
+                    "            .put(WrappedClassKeyCreator.createWrappedClassKey(Long.class),",
+                    "                provideLoginHandlerProvider)",
+                    "            .build();",
+                    "  }")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize(final MapModuleOne mapModuleOneParam,",
+                    "      final MapModuleTwo mapModuleTwoParam) {",
+                    "    this.provideAdminHandlerProvider =",
+                    "        new SwitchingProvider<>(testComponent, 1);",
+                    "    this.provideLoginHandlerProvider =",
+                    "        new SwitchingProvider<>(testComponent, 2);",
+                    "    this.mapOfWrappedClassKeyAndProviderOfHandlerProvider =",
+                    "        new SwitchingProvider<>(testComponent, 0);",
+                    "  }")
+                .addLines(
+                    "  @Override",
+                    "  public Provider<Map<WrappedClassKey, Provider<Handler>>> dispatcher() {",
+                    "    return mapOfWrappedClassKeyAndProviderOfHandlerProvider;",
+                    "  }")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "  private static final class SwitchingProvider<T> implements Provider<T> {",
+                    "    @SuppressWarnings(\"unchecked\")",
+                    "    @Override",
+                    "    public T get() {",
+                    "      switch (id) {",
+                    "        case 0:",
+                    "        return (T) ImmutableMap.<WrappedClassKey, Provider<Handler>>of(",
+                    "          WrappedClassKeyCreator.createWrappedClassKey(Integer.class),",
+                    "          testComponent.provideAdminHandlerProvider,",
+                    "          WrappedClassKeyCreator.createWrappedClassKey(Long.class),",
+                    "          testComponent.provideLoginHandlerProvider);",
+                    "        case 1: return (T) MapModuleOne_ProvideAdminHandlerFactory",
+                    "            .provideAdminHandler(testComponent.mapModuleOne);",
+                    "        case 2: return (T) MapModuleTwo_ProvideLoginHandlerFactory",
+                    "            .provideLoginHandler(testComponent.mapModuleTwo);",
+                    "        default: throw new AssertionError(id);",
+                    "      }",
+                    "    }",
+                    "  }",
+                    "}")
+                .build());
   }
 
   @Test
@@ -913,22 +780,24 @@
         "    ADMIN,",
         "    LOGIN;",
         "}");
-    JavaFileObject HandlerFile = JavaFileObjects.forSourceLines("test.Handler",
-        "package test;",
-        "",
-        "interface Handler {}");
-    JavaFileObject LoginHandlerFile = JavaFileObjects.forSourceLines("test.LoginHandler",
-        "package test;",
-        "",
-        "class LoginHandler implements Handler {",
-        "  public LoginHandler() {}",
-        "}");
-    JavaFileObject AdminHandlerFile = JavaFileObjects.forSourceLines("test.AdminHandler",
-        "package test;",
-        "",
-        "class AdminHandler implements Handler {",
-        "  public AdminHandler() {}",
-        "}");
+    JavaFileObject handlerFile =
+        JavaFileObjects.forSourceLines("test.Handler", "package test;", "", "interface Handler {}");
+    JavaFileObject loginHandlerFile =
+        JavaFileObjects.forSourceLines(
+            "test.LoginHandler",
+            "package test;",
+            "",
+            "class LoginHandler implements Handler {",
+            "  public LoginHandler() {}",
+            "}");
+    JavaFileObject adminHandlerFile =
+        JavaFileObjects.forSourceLines(
+            "test.AdminHandler",
+            "package test;",
+            "",
+            "class AdminHandler implements Handler {",
+            "  public AdminHandler() {}",
+            "}");
     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
         "package test;",
         "",
@@ -940,92 +809,7 @@
         "interface TestComponent {",
         "  Provider<Map<PathEnum, Handler>> dispatcher();",
         "}");
-    JavaFileObject generatedComponent;
-    switch (compilerMode) {
-      case FAST_INIT_MODE:
-        generatedComponent =
-            JavaFileObjects.forSourceLines(
-                "test.DaggerTestComponent",
-                "package test;",
-                "",
-                GeneratedLines.generatedAnnotations(),
-                "final class DaggerTestComponent implements TestComponent {",
-                "  private final MapModuleOne mapModuleOne;",
-                "  private final MapModuleTwo mapModuleTwo;",
-                "  private volatile Provider<Map<PathEnum, Handler>>",
-                "      mapOfPathEnumAndHandlerProvider;",
-                "",
-                "  private Map<PathEnum, Handler> mapOfPathEnumAndHandler() {",
-                "    return ImmutableMap.<PathEnum, Handler>of(",
-                "        PathEnum.ADMIN,",
-                "        MapModuleOne_ProvideAdminHandlerFactory.provideAdminHandler(",
-                "            mapModuleOne),",
-                "        PathEnum.LOGIN,",
-                "        MapModuleTwo_ProvideLoginHandlerFactory.provideLoginHandler(",
-                "            mapModuleTwo));",
-                "  }",
-                "",
-                "  @Override",
-                "  public Provider<Map<PathEnum, Handler>> dispatcher() {",
-                "    Object local = mapOfPathEnumAndHandlerProvider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(0);",
-                "      mapOfPathEnumAndHandlerProvider = (Provider<Map<PathEnum, Handler>>) local;",
-                "    }",
-                "    return (Provider<Map<PathEnum, Handler>>) local;",
-                "  }",
-                "",
-                "  private final class SwitchingProvider<T> implements Provider<T> {",
-                "    private final int id;",
-                "",
-                "    SwitchingProvider(int id) {",
-                "      this.id = id;",
-                "    }",
-                "",
-                "    @SuppressWarnings(\"unchecked\")",
-                "    @Override",
-                "    public T get() {",
-                "      switch (id) {",
-                "        case 0: return (T) DaggerTestComponent.this.mapOfPathEnumAndHandler();",
-                "        default: throw new AssertionError(id);",
-                "      }",
-                "    }",
-                "  }",
-                "}");
-        break;
-      default:
-        generatedComponent =
-            JavaFileObjects.forSourceLines(
-                "test.DaggerTestComponent",
-                "package test;",
-                "",
-                GeneratedLines.generatedAnnotations(),
-                "final class DaggerTestComponent implements TestComponent {",
-                "  private Provider<Handler> provideAdminHandlerProvider;",
-                "  private Provider<Handler> provideLoginHandlerProvider;",
-                "  private Provider<Map<PathEnum, Handler>> mapOfPathEnumAndHandlerProvider;",
-                "",
-                "  @SuppressWarnings(\"unchecked\")",
-                "  private void initialize(",
-                "        final MapModuleOne mapModuleOneParam,",
-                "        final MapModuleTwo mapModuleTwoParam) {",
-                "    this.provideAdminHandlerProvider =",
-                "        MapModuleOne_ProvideAdminHandlerFactory.create(mapModuleOneParam);",
-                "    this.provideLoginHandlerProvider =",
-                "        MapModuleTwo_ProvideLoginHandlerFactory.create(mapModuleTwoParam);",
-                "    this.mapOfPathEnumAndHandlerProvider =",
-                "        MapFactory.<PathEnum, Handler>builder(2)",
-                "            .put(PathEnum.ADMIN, provideAdminHandlerProvider)",
-                "            .put(PathEnum.LOGIN, provideLoginHandlerProvider)",
-                "            .build();",
-                "  }",
-                "",
-                "  @Override",
-                "  public Provider<Map<PathEnum, Handler>> dispatcher() {",
-                "    return mapOfPathEnumAndHandlerProvider;",
-                "  }",
-                "}");
-    }
+
     Compilation compilation =
         compilerWithOptions(compilerMode.javacopts())
             .compile(
@@ -1033,14 +817,76 @@
                 mapModuleTwoFile,
                 enumKeyFile,
                 pathEnumFile,
-                HandlerFile,
-                LoginHandlerFile,
-                AdminHandlerFile,
+                handlerFile,
+                loginHandlerFile,
+                adminHandlerFile,
                 componentFile);
     assertThat(compilation).succeeded();
     assertThat(compilation)
         .generatedSourceFile("test.DaggerTestComponent")
-        .containsElementsIn(generatedComponent);
+        .containsElementsIn(
+            compilerMode
+                .javaFileBuilder("test.DaggerTestComponent")
+                .addLines(
+                    "package test;",
+                    "",
+                    GeneratedLines.generatedAnnotations(),
+                    "final class DaggerTestComponent implements TestComponent {")
+                .addLinesIn(
+                    DEFAULT_MODE,
+                    "  private Provider<Handler> provideAdminHandlerProvider;",
+                    "  private Provider<Handler> provideLoginHandlerProvider;",
+                    "  private Provider<Map<PathEnum, Handler>> mapOfPathEnumAndHandlerProvider;",
+                    "",
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize(",
+                    "        final MapModuleOne mapModuleOneParam,",
+                    "        final MapModuleTwo mapModuleTwoParam) {",
+                    "    this.provideAdminHandlerProvider =",
+                    "        MapModuleOne_ProvideAdminHandlerFactory.create(mapModuleOneParam);",
+                    "    this.provideLoginHandlerProvider =",
+                    "        MapModuleTwo_ProvideLoginHandlerFactory.create(mapModuleTwoParam);",
+                    "    this.mapOfPathEnumAndHandlerProvider =",
+                    "        MapFactory.<PathEnum, Handler>builder(2)",
+                    "            .put(PathEnum.ADMIN, provideAdminHandlerProvider)",
+                    "            .put(PathEnum.LOGIN, provideLoginHandlerProvider)",
+                    "            .build();",
+                    "  }")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "  private Provider<Map<PathEnum, Handler>> mapOfPathEnumAndHandlerProvider;",
+                    "",
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize(final MapModuleOne mapModuleOneParam,",
+                    "      final MapModuleTwo mapModuleTwoParam) {",
+                    "    this.mapOfPathEnumAndHandlerProvider =",
+                    "        new SwitchingProvider<>(testComponent, 0);",
+                    "  }")
+                .addLines(
+                    "  @Override",
+                    "  public Provider<Map<PathEnum, Handler>> dispatcher() {",
+                    "    return mapOfPathEnumAndHandlerProvider;",
+                    "  }")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "  private static final class SwitchingProvider<T> implements Provider<T> {",
+                    "    @SuppressWarnings(\"unchecked\")",
+                    "    @Override",
+                    "    public T get() {",
+                    "      switch (id) {",
+                    "        case 0: return (T) ImmutableMap.<PathEnum, Handler>of(",
+                    "          PathEnum.ADMIN,",
+                    "          MapModuleOne_ProvideAdminHandlerFactory.provideAdminHandler(",
+                    "          testComponent.mapModuleOne),",
+                    "          PathEnum.LOGIN,",
+                    "          MapModuleTwo_ProvideLoginHandlerFactory.provideLoginHandler(",
+                    "          testComponent.mapModuleTwo));",
+                    "        default: throw new AssertionError(id);",
+                    "      }",
+                    "    }",
+                    "  }",
+                    "}")
+                .build());
   }
 
   @Test
diff --git a/javatests/dagger/internal/codegen/MapBindingExpressionTest.java b/javatests/dagger/internal/codegen/MapRequestRepresentationTest.java
similarity index 79%
rename from javatests/dagger/internal/codegen/MapBindingExpressionTest.java
rename to javatests/dagger/internal/codegen/MapRequestRepresentationTest.java
index 9acc20d..1f59bd8 100644
--- a/javatests/dagger/internal/codegen/MapBindingExpressionTest.java
+++ b/javatests/dagger/internal/codegen/MapRequestRepresentationTest.java
@@ -33,7 +33,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class MapBindingExpressionTest {
+public class MapRequestRepresentationTest {
   @Parameters(name = "{0}")
   public static Collection<Object[]> parameters() {
     return CompilerMode.TEST_PARAMETERS;
@@ -41,7 +41,7 @@
 
   private final CompilerMode compilerMode;
 
-  public MapBindingExpressionTest(CompilerMode compilerMode) {
+  public MapRequestRepresentationTest(CompilerMode compilerMode) {
     this.compilerMode = compilerMode;
   }
 
@@ -89,51 +89,21 @@
             .addLines(
                 "package test;",
                 "",
-                "import dagger.internal.MapBuilder;",
-                "",
                 GeneratedLines.generatedAnnotations(),
                 "final class DaggerTestComponent implements TestComponent {")
             .addLinesIn(
                 FAST_INIT_MODE,
-                "  private volatile Provider<Integer> provideIntProvider;",
-                "  private volatile Provider<Long> provideLong0Provider;",
-                "  private volatile Provider<Long> provideLong1Provider;",
-                "  private volatile Provider<Long> provideLong2Provider;",
+                "  private Provider<Integer> provideIntProvider;",
+                "  private Provider<Long> provideLong0Provider;",
+                "  private Provider<Long> provideLong1Provider;",
+                "  private Provider<Long> provideLong2Provider;",
                 "",
-                "  private Provider<Integer> provideIntProvider() {",
-                "    Object local = provideIntProvider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(0);",
-                "      provideIntProvider = (Provider<Integer>) local;",
-                "    }",
-                "    return (Provider<Integer>) local;",
-                "  }",
-                "",
-                "  private Provider<Long> provideLong0Provider() {",
-                "    Object local = provideLong0Provider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(1);",
-                "      provideLong0Provider = (Provider<Long>) local;",
-                "    }",
-                "    return (Provider<Long>) local;",
-                "  }",
-                "",
-                "  private Provider<Long> provideLong1Provider() {",
-                "    Object local = provideLong1Provider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(2);",
-                "      provideLong1Provider = (Provider<Long>) local;",
-                "    }",
-                "    return (Provider<Long>) local;",
-                "  }",
-                "",
-                "  private Provider<Long> provideLong2Provider() {",
-                "    Object local = provideLong2Provider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(3);",
-                "      provideLong2Provider = (Provider<Long>) local;",
-                "    }",
-                "    return (Provider<Long>) local;",
+                "  @SuppressWarnings(\"unchecked\")",
+                "  private void initialize() {",
+                "    this.provideIntProvider = new SwitchingProvider<>(testComponent, 0);",
+                "    this.provideLong0Provider = new SwitchingProvider<>(testComponent, 1);",
+                "    this.provideLong1Provider = new SwitchingProvider<>(testComponent, 2);",
+                "    this.provideLong2Provider = new SwitchingProvider<>(testComponent, 3);",
                 "  }")
             .addLines(
                 "  @Override",
@@ -145,22 +115,31 @@
                 "  public Map<String, Provider<String>> providerStrings() {",
                 "    return Collections.<String, Provider<String>>emptyMap();",
                 "  }",
-                "",
+                "")
+            .addLinesIn(
+                DEFAULT_MODE,
                 "  @Override",
                 "  public Map<Integer, Integer> ints() {",
-                "    return Collections.<Integer, Integer>singletonMap(0, MapModule.provideInt());",
-                "  }",
-                "",
+                "    return Collections.<Integer, Integer>",
+                "        singletonMap(0, MapModule.provideInt());",
+                "  }")
+            .addLinesIn(
+                FAST_INIT_MODE,
+                "  @Override",
+                "  public Map<Integer, Integer> ints() {",
+                "    return Collections.<Integer, Integer>singletonMap(0,"
+                    + " provideIntProvider.get());",
+                "  }")
+            .addLines(
                 "  @Override",
                 "  public Map<Integer, Provider<Integer>> providerInts() {",
                 "    return Collections.<Integer, Provider<Integer>>singletonMap(")
             .addLinesIn(
                 DEFAULT_MODE, //
                 "        0, MapModule_ProvideIntFactory.create());")
+            .addLinesIn(FAST_INIT_MODE, "        0, provideIntProvider;")
             .addLinesIn(
-                FAST_INIT_MODE,
-                "        0, provideIntProvider());")
-            .addLines(
+                DEFAULT_MODE,
                 "  }",
                 "",
                 "  @Override",
@@ -170,9 +149,21 @@
                 "        .put(1L, MapModule.provideLong1())",
                 "        .put(2L, MapModule.provideLong2())",
                 "        .build();",
+                "  }")
+            .addLinesIn(
+                FAST_INIT_MODE,
                 "  }",
                 "",
                 "  @Override",
+                "  public Map<Long, Long> longs() {",
+                "    return MapBuilder.<Long, Long>newMapBuilder(3)",
+                "        .put(0L, provideLong0Provider.get())",
+                "        .put(1L, provideLong1Provider.get())",
+                "        .put(2L, provideLong2Provider.get())",
+                "        .build();",
+                "  }")
+            .addLines(
+                "  @Override",
                 "  public Map<Long, Provider<Long>> providerLongs() {",
                 "    return MapBuilder.<Long, Provider<Long>>newMapBuilder(3)")
             .addLinesIn(
@@ -182,20 +173,14 @@
                 "        .put(2L, MapModule_ProvideLong2Factory.create())")
             .addLinesIn(
                 FAST_INIT_MODE,
-                "        .put(0L, provideLong0Provider())",
-                "        .put(1L, provideLong1Provider())",
-                "        .put(2L, provideLong2Provider())")
+                "        .put(0L, provideLong0Provider)",
+                "        .put(1L, provideLong1Provider)",
+                "        .put(2L, provideLong2Provider)")
             .addLines( //
                 "        .build();", "  }")
             .addLinesIn(
                 FAST_INIT_MODE,
-                "  private final class SwitchingProvider<T> implements Provider<T> {",
-                "    private final int id;",
-                "",
-                "    SwitchingProvider(int id) {",
-                "      this.id = id;",
-                "    }",
-                "",
+                "  private static final class SwitchingProvider<T> implements Provider<T> {",
                 "    @SuppressWarnings(\"unchecked\")",
                 "    @Override",
                 "    public T get() {",
@@ -340,13 +325,12 @@
             "final class DaggerParent implements Parent {",
             "  private final ParentModule parentModule;",
             "",
-            "  private final class ChildImpl implements Child {",
+            "  private static final class ChildImpl implements Child {",
             "    @Override",
             "    public Map<String, Object> objectMap() {",
             "      return Collections.<String, Object>singletonMap(",
             "          \"parent key\",",
-            "          ParentModule_ParentKeyObjectFactory.parentKeyObject(",
-            "              DaggerParent.this.parentModule));",
+            "          ParentModule_ParentKeyObjectFactory.parentKeyObject(parent.parentModule));",
             "    }",
             "  }",
             "}");
diff --git a/javatests/dagger/internal/codegen/MapBindingExpressionWithGuavaTest.java b/javatests/dagger/internal/codegen/MapRequestRepresentationWithGuavaTest.java
similarity index 75%
rename from javatests/dagger/internal/codegen/MapBindingExpressionWithGuavaTest.java
rename to javatests/dagger/internal/codegen/MapRequestRepresentationWithGuavaTest.java
index 30b0519..db7d039 100644
--- a/javatests/dagger/internal/codegen/MapBindingExpressionWithGuavaTest.java
+++ b/javatests/dagger/internal/codegen/MapRequestRepresentationWithGuavaTest.java
@@ -31,7 +31,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class MapBindingExpressionWithGuavaTest {
+public class MapRequestRepresentationWithGuavaTest {
   @Parameters(name = "{0}")
   public static Collection<Object[]> parameters() {
     return CompilerMode.TEST_PARAMETERS;
@@ -39,7 +39,7 @@
 
   private final CompilerMode compilerMode;
 
-  public MapBindingExpressionWithGuavaTest(CompilerMode compilerMode) {
+  public MapRequestRepresentationWithGuavaTest(CompilerMode compilerMode) {
     this.compilerMode = compilerMode;
   }
 
@@ -130,45 +130,17 @@
                 "final class DaggerTestComponent implements TestComponent {")
             .addLinesIn(
                 FAST_INIT_MODE,
-                "  private volatile Provider<Integer> provideIntProvider;",
-                "  private volatile Provider<Long> provideLong0Provider;",
-                "  private volatile Provider<Long> provideLong1Provider;",
-                "  private volatile Provider<Long> provideLong2Provider;",
+                "  private Provider<Integer> provideIntProvider;",
+                "  private Provider<Long> provideLong0Provider;",
+                "  private Provider<Long> provideLong1Provider;",
+                "  private Provider<Long> provideLong2Provider;",
                 "",
-                "  private Provider<Integer> provideIntProvider() {",
-                "    Object local = provideIntProvider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(0);",
-                "      provideIntProvider = (Provider<Integer>) local;",
-                "    }",
-                "    return (Provider<Integer>) local;",
-                "  }",
-                "",
-                "  private Provider<Long> provideLong0Provider() {",
-                "    Object local = provideLong0Provider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(1);",
-                "      provideLong0Provider = (Provider<Long>) local;",
-                "    }",
-                "    return (Provider<Long>) local;",
-                "  }",
-                "",
-                "  private Provider<Long> provideLong1Provider() {",
-                "    Object local = provideLong1Provider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(2);",
-                "      provideLong1Provider = (Provider<Long>) local;",
-                "    }",
-                "    return (Provider<Long>) local;",
-                "  }",
-                "",
-                "  private Provider<Long> provideLong2Provider() {",
-                "    Object local = provideLong2Provider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(3);",
-                "      provideLong2Provider = (Provider<Long>) local;",
-                "    }",
-                "    return (Provider<Long>) local;",
+                "  @SuppressWarnings(\"unchecked\")",
+                "  private void initialize() {",
+                "    this.provideIntProvider = new SwitchingProvider<>(testComponent, 0);",
+                "    this.provideLong0Provider = new SwitchingProvider<>(testComponent, 1);",
+                "    this.provideLong1Provider = new SwitchingProvider<>(testComponent, 2);",
+                "    this.provideLong2Provider = new SwitchingProvider<>(testComponent, 3);",
                 "  }")
             .addLines(
                 "  @Override",
@@ -179,89 +151,92 @@
                 "  @Override",
                 "  public Map<String, Provider<String>> providerStrings() {",
                 "    return ImmutableMap.<String, Provider<String>>of();",
-                "  }",
-                "",
+                "  }")
+            .addLinesIn(
+                DEFAULT_MODE,
                 "  @Override",
                 "  public Map<Integer, Integer> ints() {",
                 "    return ImmutableMap.<Integer, Integer>of(0, MapModule.provideInt());",
-                "  }",
-                "",
+                "  }")
+            .addLinesIn(
+                FAST_INIT_MODE,
+                "  @Override",
+                "  public Map<Integer, Integer> ints() {",
+                "    return ImmutableMap.<Integer, Integer>of(0, provideIntProvider.get());",
+                "  }")
+            .addLinesIn(
+                DEFAULT_MODE,
                 "  @Override",
                 "  public Map<Integer, Provider<Integer>> providerInts() {",
-                "    return ImmutableMap.<Integer, Provider<Integer>>of(")
+                "    return ImmutableMap.<Integer, Provider<Integer>>of(",
+                "        0, MapModule_ProvideIntFactory.create());",
+                "  }")
             .addLinesIn(
-                DEFAULT_MODE, //
-                "        0, MapModule_ProvideIntFactory.create());")
+                FAST_INIT_MODE,
+                "  @Override",
+                "  public Map<Integer, Provider<Integer>> providerInts() {",
+                "    return ImmutableMap.<Integer, Provider<Integer>>of(0, provideIntProvider);",
+                "  }")
             .addLinesIn(
-                FAST_INIT_MODE, //
-                "        0, provideIntProvider());")
-            .addLines(
-                "  }",
-                "",
+                DEFAULT_MODE,
                 "  @Override",
                 "  public Map<Long, Long> longs() {",
                 "    return ImmutableMap.<Long, Long>of(",
                 "      0L, MapModule.provideLong0(),",
                 "      1L, MapModule.provideLong1(),",
                 "      2L, MapModule.provideLong2());",
-                "  }",
-                "",
+                "  }")
+            .addLinesIn(
+                FAST_INIT_MODE,
                 "  @Override",
-                "  public Map<Long, Provider<Long>> providerLongs() {",
-                "    return ImmutableMap.<Long, Provider<Long>>of(")
+                "  public Map<Long, Long> longs() {",
+                "    return ImmutableMap.<Long, Long>of(",
+                "      0L, provideLong0Provider.get(),",
+                "      1L, provideLong1Provider.get(),",
+                "      2L, provideLong2Provider.get());",
+                "  }")
             .addLinesIn(
                 DEFAULT_MODE,
+                "  @Override",
+                "  public Map<Long, Provider<Long>> providerLongs() {",
+                "    return ImmutableMap.<Long, Provider<Long>>of(",
                 "      0L, MapModule_ProvideLong0Factory.create(),",
                 "      1L, MapModule_ProvideLong1Factory.create(),",
-                "      2L, MapModule_ProvideLong2Factory.create());")
+                "      2L, MapModule_ProvideLong2Factory.create());",
+                "  }")
             .addLinesIn(
                 FAST_INIT_MODE,
-                "      0L, provideLong0Provider(),",
-                "      1L, provideLong1Provider(),",
-                "      2L, provideLong2Provider());")
+                "  @Override",
+                "  public Map<Long, Provider<Long>> providerLongs() {",
+                "    return ImmutableMap.<Long, Provider<Long>>of(",
+                "      0L, provideLong0Provider,",
+                "      1L, provideLong1Provider,",
+                "      2L, provideLong2Provider);",
+                "  }")
             .addLines(
-                "  }",
-                "",
                 "  @Override",
                 "  public Sub sub() {",
-                "    return new SubImpl();",
+                "    return new SubImpl(testComponent);",
                 "  }",
                 "",
-                "  private final class SubImpl implements Sub {")
+                "  private static final class SubImpl implements Sub {")
             .addLinesIn(
                 FAST_INIT_MODE,
-                "    private volatile Provider<Long> provideLong3Provider;",
-                "    private volatile Provider<Long> provideLong4Provider;",
-                "    private volatile Provider<Long> provideLong5Provider;",
-                "    private SubImpl() {}",
+                "    private Provider<Long> provideLong3Provider;",
+                "    private Provider<Long> provideLong4Provider;",
+                "    private Provider<Long> provideLong5Provider;",
                 "",
-                "    private Provider<Long> provideLong3Provider() {",
-                "      Object local = provideLong3Provider;",
-                "      if (local == null) {",
-                "        local = new SwitchingProvider<>(0);",
-                "        provideLong3Provider = (Provider<Long>) local;",
-                "      }",
-                "      return (Provider<Long>) local;",
-                "    }",
-                "",
-                "    private Provider<Long> provideLong4Provider() {",
-                "      Object local = provideLong4Provider;",
-                "      if (local == null) {",
-                "        local = new SwitchingProvider<>(1);",
-                "        provideLong4Provider = (Provider<Long>) local;",
-                "      }",
-                "      return (Provider<Long>) local;",
-                "    }",
-                "",
-                "    private Provider<Long> provideLong5Provider() {",
-                "      Object local = provideLong5Provider;",
-                "      if (local == null) {",
-                "        local = new SwitchingProvider<>(2);",
-                "        provideLong5Provider = (Provider<Long>) local;",
-                "      }",
-                "      return (Provider<Long>) local;",
+                "    @SuppressWarnings(\"unchecked\")",
+                "    private void initialize() {",
+                "      this.provideLong3Provider =",
+                "          new SwitchingProvider<>(testComponent, subImpl, 0);",
+                "      this.provideLong4Provider =",
+                "          new SwitchingProvider<>(testComponent, subImpl, 1);",
+                "      this.provideLong5Provider =",
+                "          new SwitchingProvider<>(testComponent, subImpl, 2);",
                 "    }")
-            .addLines(
+            .addLinesIn(
+                DEFAULT_MODE,
                 "    @Override",
                 "    public Map<Long, Long> longs() {",
                 "      return ImmutableMap.<Long, Long>builderWithExpectedSize(6)",
@@ -272,38 +247,49 @@
                 "          .put(4L, SubcomponentMapModule.provideLong4())",
                 "          .put(5L, SubcomponentMapModule.provideLong5())",
                 "          .build();",
-                "    }",
-                "",
+                "    }")
+            .addLinesIn(
+                FAST_INIT_MODE,
                 "    @Override",
-                "    public Map<Long, Provider<Long>> providerLongs() {",
-                "      return ImmutableMap.<Long, Provider<Long>>builderWithExpectedSize(6)")
+                "    public Map<Long, Long> longs() {",
+                "      return ImmutableMap.<Long, Long>builderWithExpectedSize(6)",
+                "          .put(0L, testComponent.provideLong0Provider.get())",
+                "          .put(1L, testComponent.provideLong1Provider.get())",
+                "          .put(2L, testComponent.provideLong2Provider.get())",
+                "          .put(3L, provideLong3Provider.get())",
+                "          .put(4L, provideLong4Provider.get())",
+                "          .put(5L, provideLong5Provider.get())",
+                "          .build();",
+                "    }")
             .addLinesIn(
                 DEFAULT_MODE,
+                "    @Override",
+                "    public Map<Long, Provider<Long>> providerLongs() {",
+                "      return ImmutableMap.<Long, Provider<Long>>builderWithExpectedSize(6)",
                 "          .put(0L, MapModule_ProvideLong0Factory.create())",
                 "          .put(1L, MapModule_ProvideLong1Factory.create())",
                 "          .put(2L, MapModule_ProvideLong2Factory.create())",
                 "          .put(3L, SubcomponentMapModule_ProvideLong3Factory.create())",
                 "          .put(4L, SubcomponentMapModule_ProvideLong4Factory.create())",
-                "          .put(5L, SubcomponentMapModule_ProvideLong5Factory.create())")
+                "          .put(5L, SubcomponentMapModule_ProvideLong5Factory.create())",
+                "          .build();",
+                "    }")
             .addLinesIn(
                 FAST_INIT_MODE,
-                "          .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();", "    }")
+                "    @Override",
+                "    public Map<Long, Provider<Long>> providerLongs() {",
+                "      return ImmutableMap.<Long, Provider<Long>>builderWithExpectedSize(6)",
+                "          .put(0L, testComponent.provideLong0Provider)",
+                "          .put(1L, testComponent.provideLong1Provider)",
+                "          .put(2L, testComponent.provideLong2Provider)",
+                "          .put(3L, provideLong3Provider)",
+                "          .put(4L, provideLong4Provider)",
+                "          .put(5L, provideLong5Provider)",
+                "          .build();",
+                "    }")
             .addLinesIn(
                 FAST_INIT_MODE,
-                "    private final class SwitchingProvider<T> implements Provider<T> {",
-                "      private final int id;",
-                "",
-                "      SwitchingProvider(int id) {",
-                "        this.id = id;",
-                "      }",
-                "",
+                "    private static final class SwitchingProvider<T> implements Provider<T> {",
                 "      @SuppressWarnings(\"unchecked\")",
                 "      @Override",
                 "      public T get() {",
@@ -317,13 +303,7 @@
                 "    }",
                 "  }",
                 "",
-                "  private final class SwitchingProvider<T> implements Provider<T> {",
-                "    private final int id;",
-                "",
-                "    SwitchingProvider(int id) {",
-                "      this.id = id;",
-                "    }",
-                "",
+                "  private static final class SwitchingProvider<T> implements Provider<T> {",
                 "    @SuppressWarnings(\"unchecked\")",
                 "    @Override",
                 "    public T get() {",
@@ -466,13 +446,12 @@
             "final class DaggerParent implements Parent {",
             "  private final ParentModule parentModule;",
             "",
-            "  private final class ChildImpl implements Child {",
+            "  private static final class ChildImpl implements Child {",
             "    @Override",
             "    public Map<String, Object> objectMap() {",
             "      return ImmutableMap.<String, Object>of(",
             "          \"parent key\",",
-            "          ParentModule_ParentKeyObjectFactory.parentKeyObject(",
-            "              DaggerParent.this.parentModule));",
+            "          ParentModule_ParentKeyObjectFactory.parentKeyObject(parent.parentModule));",
             "    }",
             "  }",
             "}");
diff --git a/javatests/dagger/internal/codegen/MembersInjectionTest.java b/javatests/dagger/internal/codegen/MembersInjectionTest.java
index c442779..ca22d56 100644
--- a/javatests/dagger/internal/codegen/MembersInjectionTest.java
+++ b/javatests/dagger/internal/codegen/MembersInjectionTest.java
@@ -189,8 +189,10 @@
             GeneratedLines.generatedImports(
                 "import dagger.MembersInjector;",
                 "import dagger.internal.InjectedFieldSignature;",
+                "import dagger.internal.QualifierMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class GenericClass_MembersInjector<A, B>",
             "    implements MembersInjector<GenericClass<A, B>> {",
@@ -279,8 +281,10 @@
             GeneratedLines.generatedImports(
                 "import dagger.MembersInjector;",
                 "import dagger.internal.InjectedFieldSignature;",
+                "import dagger.internal.QualifierMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class Child_MembersInjector<T>",
             "    implements MembersInjector<Child<T>> {",
@@ -364,8 +368,10 @@
                 "import dagger.MembersInjector;",
                 "import dagger.internal.DoubleCheck;",
                 "import dagger.internal.InjectedFieldSignature;",
+                "import dagger.internal.QualifierMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class FieldInjection_MembersInjector",
             "    implements MembersInjector<FieldInjection> {",
@@ -444,9 +450,11 @@
             GeneratedLines.generatedImports(
                 "import dagger.MembersInjector;",
                 "import dagger.internal.InjectedFieldSignature;",
+                "import dagger.internal.QualifierMetadata;",
                 "import javax.inject.Named;",
                 "import javax.inject.Provider;"),
             "",
+            "@QualifierMetadata(\"javax.inject.Named\")",
             GeneratedLines.generatedAnnotations(),
             "public final class FieldInjectionWithQualifier_MembersInjector",
             "    implements MembersInjector<FieldInjectionWithQualifier> {",
@@ -514,8 +522,10 @@
                 "import dagger.Lazy;",
                 "import dagger.MembersInjector;",
                 "import dagger.internal.DoubleCheck;",
+                "import dagger.internal.QualifierMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class MethodInjection_MembersInjector",
             "     implements MembersInjector<MethodInjection> {",
@@ -603,8 +613,10 @@
             GeneratedLines.generatedImports(
                 "import dagger.MembersInjector;",
                 "import dagger.internal.InjectedFieldSignature;",
+                "import dagger.internal.QualifierMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class MixedMemberInjection_MembersInjector",
             "    implements MembersInjector<MixedMemberInjection> {",
@@ -686,8 +698,10 @@
             GeneratedLines.generatedImports(
                 "import dagger.MembersInjector;",
                 "import dagger.internal.InjectedFieldSignature;",
+                "import dagger.internal.QualifierMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class AllInjections_MembersInjector ",
             "    implements MembersInjector<AllInjections> {",
@@ -747,14 +761,16 @@
         "}");
     JavaFileObject expectedMembersInjector =
         JavaFileObjects.forSourceLines(
-            "test.AllInjections_MembersInjector",
+            "test.B_MembersInjector",
             "package test;",
             "",
             GeneratedLines.generatedImports(
                 "import dagger.MembersInjector;",
                 "import dagger.internal.InjectedFieldSignature;",
+                "import dagger.internal.QualifierMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class B_MembersInjector implements MembersInjector<B> {",
             "  private final Provider<String> sProvider;",
@@ -815,8 +831,10 @@
             GeneratedLines.generatedImports(
                 "import dagger.MembersInjector;",
                 "import dagger.internal.InjectedFieldSignature;",
+                "import dagger.internal.QualifierMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class OuterType_B_MembersInjector",
             "    implements MembersInjector<OuterType.B> {",
@@ -881,8 +899,10 @@
             GeneratedLines.generatedImports(
                 "import dagger.MembersInjector;",
                 "import dagger.internal.InjectedFieldSignature;",
+                "import dagger.internal.QualifierMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class OuterType_B_MembersInjector",
             "    implements MembersInjector<OuterType.B> {",
@@ -1057,8 +1077,10 @@
             GeneratedLines.generatedImports(
                 "import dagger.MembersInjector;",
                 "import dagger.internal.InjectedFieldSignature;",
+                "import dagger.internal.QualifierMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class Child_MembersInjector implements MembersInjector<Child> {",
             "  private final Provider<Foo> objectProvider;",
@@ -1255,8 +1277,10 @@
             GeneratedLines.generatedImports(
                 "import dagger.MembersInjector;",
                 "import dagger.internal.InjectedFieldSignature;",
+                "import dagger.internal.QualifierMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class InjectedType_MembersInjector ",
             "    implements MembersInjector<InjectedType> {",
@@ -1296,8 +1320,12 @@
             "",
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class InjectedType_Factory implements Factory<InjectedType> {",
             "  private final Provider<Integer> primitiveIntProvider;",
@@ -1400,8 +1428,10 @@
                 GeneratedLines.generatedImports(
                     "import dagger.MembersInjector;",
                     "import dagger.internal.InjectedFieldSignature;",
+                    "import dagger.internal.QualifierMetadata;",
                     "import javax.inject.Provider;"),
                 "",
+                "@QualifierMetadata",
                 GeneratedLines.generatedAnnotations(),
                 "public final class Inaccessible_MembersInjector",
                 "    implements MembersInjector<Inaccessible> {",
@@ -1537,65 +1567,52 @@
             .addLines(
                 "package test;",
                 "",
-                GeneratedLines.generatedImports(
-                    "import com.google.errorprone.annotations.CanIgnoreReturnValue;",
-                    "import other.InaccessiblesModule;",
-                    "import other.InaccessiblesModule_InaccessiblesFactory;",
-                    "import other.UsesInaccessibles;",
-                    "import other.UsesInaccessibles_Factory;",
-                    "import other.UsesInaccessibles_MembersInjector;"),
-                "",
                 GeneratedLines.generatedAnnotations(),
-                "final class DaggerTestComponent implements TestComponent {")
-            .addLinesIn(
-                FAST_INIT_MODE,
-                "  private volatile Object listOfInaccessible = new MemoizedSentinel();",
+                "final class DaggerTestComponent implements TestComponent {",
+                "  private final DaggerTestComponent testComponent = this;",
                 "",
-                "  private List listOfInaccessible() {",
-                "    Object local = listOfInaccessible;",
-                "    if (local instanceof MemoizedSentinel) {",
-                "      synchronized (local) {",
-                "        local = listOfInaccessible;",
-                "        if (local instanceof MemoizedSentinel) {",
-                "          local = InaccessiblesModule_InaccessiblesFactory.inaccessibles();",
-                "          listOfInaccessible =",
-                "              DoubleCheck.reentrantCheck(listOfInaccessible, local);",
-                "        }",
-                "      }",
-                "    }",
-                "    return (List) local;",
-                "  }")
+                "  @SuppressWarnings(\"rawtypes\")",
+                "  private Provider inaccessiblesProvider;")
             .addLinesIn(
                 DEFAULT_MODE,
-                "  @SuppressWarnings(\"rawtypes\")",
-                "  private Provider inaccessiblesProvider;",
-                "",
                 "  @SuppressWarnings(\"unchecked\")",
                 "  private void initialize() {",
                 "    this.inaccessiblesProvider =",
                 "        DoubleCheck.provider(InaccessiblesModule_InaccessiblesFactory.create());",
                 "  }")
-            .addLines(
-                "",
-                "  @Override",
-                "  public UsesInaccessibles usesInaccessibles() {",
-                "    return injectUsesInaccessibles(",
-                "        UsesInaccessibles_Factory.newInstance());",
-                "  }",
-                "",
-                "  @CanIgnoreReturnValue",
-                "  private UsesInaccessibles injectUsesInaccessibles(",
-                "        UsesInaccessibles instance) {",
-                "    UsesInaccessibles_MembersInjector.injectInaccessibles(")
             .addLinesIn(
                 FAST_INIT_MODE,
-                "        instance, (List) listOfInaccessible());")
-            .addLinesIn(
-                DEFAULT_MODE,
-                "        instance, (List) inaccessiblesProvider.get());")
+                "  @SuppressWarnings(\"unchecked\")",
+                "  private void initialize() {",
+                "    this.inaccessiblesProvider =",
+                "        DoubleCheck.provider(",
+                "            new SwitchingProvider<List>(testComponent, 0));",
+                "  }")
             .addLines(
+                "  @Override",
+                "  public UsesInaccessibles usesInaccessibles() {",
+                "    return injectUsesInaccessibles(UsesInaccessibles_Factory.newInstance());",
+                "  }")
+            .addLinesIn(
+                FAST_INIT_MODE,
+                "  @CanIgnoreReturnValue",
+                "  private UsesInaccessibles injectUsesInaccessibles(UsesInaccessibles instance) {",
+                "    UsesInaccessibles_MembersInjector",
+                "        .injectInaccessibles(instance, (List) inaccessiblesProvider.get());",
                 "    return instance;",
                 "  }",
+                "",
+                "  private static final class SwitchingProvider<T> implements Provider<T> {",
+                "    @SuppressWarnings(\"unchecked\")",
+                "    @Override",
+                "    public T get() {",
+                "      switch (id) {",
+                "        case 0:",
+                "          return (T) InaccessiblesModule_InaccessiblesFactory.inaccessibles();",
+                "        default: throw new AssertionError(id);",
+                "      }",
+                "    }",
+                "  }",
                 "}")
             .build();
     assertThat(compilation)
@@ -1738,8 +1755,10 @@
             GeneratedLines.generatedImports(
                 "import dagger.MembersInjector;",
                 "import dagger.internal.InjectedFieldSignature;",
+                "import dagger.internal.QualifierMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class A_MembersInjector implements MembersInjector<A> {",
             "  private final Provider<String> valueCProvider;",
@@ -1776,8 +1795,10 @@
             GeneratedLines.generatedImports(
                 "import dagger.MembersInjector;",
                 "import dagger.internal.InjectedFieldSignature;",
+                "import dagger.internal.QualifierMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class C_MembersInjector implements MembersInjector<C> {",
             "  private final Provider<String> valueCProvider;",
@@ -1855,8 +1876,10 @@
             "",
             GeneratedLines.generatedImports(
                 "import dagger.MembersInjector;",
+                "import dagger.internal.QualifierMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class A_MembersInjector implements MembersInjector<A> {",
             "  private final Provider<String> valueBProvider;",
@@ -1883,8 +1906,10 @@
             GeneratedLines.generatedImports(
                 "import dagger.MembersInjector;",
                 "import dagger.internal.InjectedFieldSignature;",
+                "import dagger.internal.QualifierMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class B_MembersInjector implements MembersInjector<B> {",
             "  private final Provider<String> valueBProvider;",
@@ -1921,4 +1946,223 @@
         .generatedSourceFile("test.B_MembersInjector")
         .hasSourceEquivalentTo(expectedBMembersInjector);
   }
+
+  // Regression test for https://github.com/google/dagger/issues/3143
+  @Test
+  public void testMembersInjectionBindingExistsInParentComponent() {
+    JavaFileObject component =
+        JavaFileObjects.forSourceLines(
+            "test.MyComponent",
+            "package test;",
+            "",
+            "import dagger.Component;",
+            "",
+            "@Component(modules = MyComponentModule.class)",
+            "public interface MyComponent {",
+            "  void inject(Bar bar);",
+            "",
+            "  MySubcomponent subcomponent();",
+            "}");
+
+    JavaFileObject subcomponent =
+        JavaFileObjects.forSourceLines(
+            "test.MySubcomponent",
+            "package test;",
+            "",
+            "import dagger.Subcomponent;",
+            "",
+            "@Subcomponent(modules = MySubcomponentModule.class)",
+            "interface MySubcomponent {",
+            "  Foo foo();",
+            "}");
+
+    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 java.util.Set;",
+            "import javax.inject.Inject;",
+            "",
+            "class Bar {",
+            "  @Inject Set<String> multibindingStrings;",
+            "  @Inject Bar() {}",
+            "}");
+
+    JavaFileObject componentModule =
+        JavaFileObjects.forSourceLines(
+            "test.MyComponentModule",
+            "package test;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "import dagger.multibindings.IntoSet;",
+            "",
+            "@Module",
+            "interface MyComponentModule {",
+            "  @Provides",
+            "  @IntoSet",
+            "  static String provideString() {",
+            "    return \"\";",
+            "  }",
+            "}");
+
+    JavaFileObject subcomponentModule =
+        JavaFileObjects.forSourceLines(
+            "test.MySubcomponentModule",
+            "package test;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "import dagger.multibindings.IntoSet;",
+            "",
+            "@Module",
+            "interface MySubcomponentModule {",
+            "  @Provides",
+            "  @IntoSet",
+            "  static String provideString() {",
+            "    return \"\";",
+            "  }",
+            "}");
+
+    Compilation compilation =
+        compilerWithOptions(compilerMode.javacopts())
+            .compile(component, subcomponent, foo, bar, componentModule, subcomponentModule);
+    assertThat(compilation).succeeded();
+
+    // Check that the injectBar() method is not shared across components.
+    // We avoid sharing them in general because they may be different (e.g. in this case we inject
+    // multibindings that are different across components).
+    assertThat(compilation)
+        .generatedSourceFile("test.DaggerMyComponent")
+        .containsElementsIn(
+            JavaFileObjects.forSourceLines(
+                "test.DaggerMyComponent",
+                "package test;",
+                "",
+                GeneratedLines.generatedAnnotations(),
+                "public final class DaggerMyComponent implements MyComponent {",
+                "  private Set<String> setOfString() {",
+                "    return ImmutableSet.<String>of(",
+                "        MyComponentModule_ProvideStringFactory.provideString());",
+                "  }",
+                "",
+                "  @Override",
+                "  public void inject(Bar bar) {",
+                "    injectBar(bar);",
+                "  }",
+                "",
+                "  @CanIgnoreReturnValue",
+                "  private Bar injectBar(Bar instance) {",
+                "    Bar_MembersInjector.injectMultibindingStrings(instance, setOfString());",
+                "    return instance;",
+                "  }",
+                "",
+                "  private static final class MySubcomponentImpl implements MySubcomponent {",
+                "    private Set<String> setOfString() {",
+                "      return ImmutableSet.<String>of(",
+                "          MyComponentModule_ProvideStringFactory.provideString(),",
+                "          MySubcomponentModule_ProvideStringFactory.provideString());",
+                "    }",
+                "",
+                "    private Bar bar() {",
+                "      return injectBar(Bar_Factory.newInstance());",
+                "    }",
+                "",
+                "    @Override",
+                "    public Foo foo() {",
+                "      return new Foo(bar());",
+                "    }",
+                "",
+                "    @CanIgnoreReturnValue",
+                "    private Bar injectBar(Bar instance) {",
+                "      Bar_MembersInjector.injectMultibindingStrings(instance, setOfString());",
+                "      return instance;",
+                "    }",
+                "  }",
+                "}"));
+  }
+
+  // Test that if both a MembersInjectionBinding and ProvisionBinding both exist in the same
+  // component they share the same inject methods rather than generating their own.
+  @Test
+  public void testMembersInjectionBindingSharesInjectMethodsWithProvisionBinding() {
+    JavaFileObject component =
+        JavaFileObjects.forSourceLines(
+            "test.MyComponent",
+            "package test;",
+            "",
+            "import dagger.Component;",
+            "",
+            "@Component",
+            "public interface MyComponent {",
+            "  Foo foo();",
+            "",
+            "  void inject(Foo foo);",
+            "}");
+
+    JavaFileObject foo =
+        JavaFileObjects.forSourceLines(
+            "test.Foo",
+            "package test;",
+            "",
+            "import javax.inject.Inject;",
+            "",
+            "class Foo {",
+            "  @Inject Bar bar;",
+            "  @Inject Foo() {}",
+            "}");
+
+    JavaFileObject bar =
+        JavaFileObjects.forSourceLines(
+            "test.Bar",
+            "package test;",
+            "",
+            "import javax.inject.Inject;",
+            "",
+            "class Bar {",
+            "  @Inject Bar() {}",
+            "}");
+
+    Compilation compilation =
+        compilerWithOptions(compilerMode.javacopts()).compile(component, foo, bar);
+    assertThat(compilation).succeeded();
+
+    assertThat(compilation)
+        .generatedSourceFile("test.DaggerMyComponent")
+        .containsElementsIn(
+            JavaFileObjects.forSourceLines(
+                "test.DaggerMyComponent",
+                "package test;",
+                "",
+                GeneratedLines.generatedAnnotations(),
+                "public final class DaggerMyComponent implements MyComponent {",
+                "  @Override",
+                "  public Foo foo() {",
+                "    return injectFoo(Foo_Factory.newInstance());",
+                "  }",
+                "",
+                "  @Override",
+                "  public void inject(Foo foo) {",
+                "    injectFoo(foo);",
+                "  }",
+                "",
+                "  @CanIgnoreReturnValue",
+                "  private Foo injectFoo(Foo instance) {",
+                "    Foo_MembersInjector.injectBar(instance, new Bar());",
+                "    return instance;",
+                "  }",
+                "}"));
+  }
 }
diff --git a/javatests/dagger/internal/codegen/MethodSignatureFormatterTest.java b/javatests/dagger/internal/codegen/MethodSignatureFormatterTest.java
deleted file mode 100644
index 6d2f989..0000000
--- a/javatests/dagger/internal/codegen/MethodSignatureFormatterTest.java
+++ /dev/null
@@ -1,112 +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.truth.Truth.assertThat;
-import static javax.lang.model.util.ElementFilter.methodsIn;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import com.google.testing.compile.CompilationRule;
-import dagger.BindsInstance;
-import dagger.Component;
-import dagger.Module;
-import dagger.Provides;
-import dagger.internal.codegen.MethodSignatureFormatterTest.OuterClass.InnerClass;
-import dagger.internal.codegen.binding.InjectionAnnotations;
-import dagger.internal.codegen.binding.MethodSignatureFormatter;
-import dagger.internal.codegen.langmodel.DaggerElements;
-import dagger.internal.codegen.langmodel.DaggerTypes;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-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();
-    }
-
-    static class InnerClass {
-      @Foo(bar = String.class)
-      @Singleton
-      String foo(
-          @SuppressWarnings("unused") int a,
-          @SuppressWarnings("unused") ImmutableList<Boolean> blah) {
-        return "foo";
-      }
-    }
-  }
-
-  @Before
-  public void setUp() {
-    DaggerMethodSignatureFormatterTest_TestComponent.factory().create(compilationRule).inject(this);
-  }
-
-  @Test public void methodSignatureTest() {
-    TypeElement inner = elements.getTypeElement(InnerClass.class);
-    ExecutableElement method = Iterables.getOnlyElement(methodsIn(inner.getEnclosedElements()));
-    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.
-    assertThat(formatted).contains("@Singleton");
-    assertThat(formatted).doesNotContain("@javax.inject.Singleton"); // maybe more importantly
-    assertThat(formatted)
-        .contains("@dagger.internal.codegen.MethodSignatureFormatterTest.OuterClass.Foo"
-            + "(bar=String.class)");
-    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 3c066ba..a5a72f7 100644
--- a/javatests/dagger/internal/codegen/MissingBindingSuggestionsTest.java
+++ b/javatests/dagger/internal/codegen/MissingBindingSuggestionsTest.java
@@ -96,8 +96,7 @@
         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: BarComponent");
+    assertThat(compilation).hadErrorContaining("A binding for Bar exists in BarComponent:");
   }
 
   @Test public void suggestsBindingInNestedSubcomponent() {
@@ -157,8 +156,7 @@
             .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: BazComponent");
+    assertThat(compilation).hadErrorContaining("A binding for Baz exists in BazComponent:");
   }
 
   @Test
@@ -220,11 +218,11 @@
             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",
+                "A binding for Baz exists in Child:",
                 "    Baz is injected at",
-                "        Bar(baz)",
+                "        [Parent] Bar(baz)",
                 "    Bar is requested at",
-                "        Parent.bar()",
+                "        [Parent] Parent.bar()",
                 "The following other entry points also depend on it:",
                 "    Parent.foo()",
                 "    Child.foo() [Parent → Child]"))
@@ -303,11 +301,11 @@
             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",
+                "A binding for Baz exists in Child2:",
                 "    Baz is injected at",
-                "        Bar(baz)",
+                "        [Parent] Bar(baz)",
                 "    Bar is requested at",
-                "        Parent.bar()",
+                "        [Parent] Parent.bar()",
                 "The following other entry points also depend on it:",
                 "    Parent.foo()",
                 "    Child1.foo() [Parent → Child1]",
diff --git a/javatests/dagger/internal/codegen/MissingBindingValidationTest.java b/javatests/dagger/internal/codegen/MissingBindingValidationTest.java
index 1aabd48..887ffa4 100644
--- a/javatests/dagger/internal/codegen/MissingBindingValidationTest.java
+++ b/javatests/dagger/internal/codegen/MissingBindingValidationTest.java
@@ -538,7 +538,7 @@
     assertThat(compilation).hadErrorCount(1);
     assertThat(compilation)
         .hadErrorContainingMatch(
-            "(?s)\\QString cannot be provided\\E.*\\QChild.needsString()\\E")
+            "(?s)\\QString cannot be provided\\E.*\\Q[Child] Child.needsString()\\E")
         .inFile(parent)
         .onLineContaining("interface Parent");
   }
@@ -928,4 +928,239 @@
         .onLineContaining("interface Parent");
   }
 
+  @Test
+  public void sameSubcomponentUsedInDifferentHierarchiesMissingBindingFromOneSide() {
+    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 = Child1Module.class)",
+            "interface Child1 {",
+            "  RepeatedSub getSub();",
+            "}");
+    JavaFileObject child2 =
+        JavaFileObjects.forSourceLines(
+            "test.Child2",
+            "package test;",
+            "",
+            "import dagger.Subcomponent;",
+            "",
+            "@Subcomponent(modules = Child2Module.class)",
+            "interface Child2 {",
+            "  RepeatedSub getSub();",
+            "}");
+    JavaFileObject repeatedSub =
+        JavaFileObjects.forSourceLines(
+            "test.RepeatedSub",
+            "package test;",
+            "",
+            "import dagger.Subcomponent;",
+            "",
+            "@Subcomponent(modules = RepeatedSubModule.class)",
+            "interface RepeatedSub {",
+            "  Object getObject();",
+            "}");
+    JavaFileObject child1Module =
+        JavaFileObjects.forSourceLines(
+            "test.Child1Module",
+            "package test;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "import java.util.Set;",
+            "import dagger.multibindings.Multibinds;",
+            "",
+            "@Module",
+            "interface Child1Module {",
+            "  @Multibinds Set<Integer> multibindIntegerSet();",
+            "}");
+    JavaFileObject child2Module =
+        JavaFileObjects.forSourceLines(
+            "test.Child2Module",
+            "package test;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "import java.util.Set;",
+            "import dagger.multibindings.Multibinds;",
+            "",
+            "@Module",
+            "interface Child2Module {",
+            "  @Multibinds Set<Integer> multibindIntegerSet();",
+            "",
+            "  @Provides",
+            "  static Object provideObject(Set<Integer> intSet) {",
+            "    return new Object();",
+            "  }",
+            "}");
+    JavaFileObject repeatedSubModule =
+        JavaFileObjects.forSourceLines(
+            "test.RepeatedSubModule",
+            "package test;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "import dagger.multibindings.IntoSet;",
+            "import java.util.Set;",
+            "import dagger.multibindings.Multibinds;",
+            "",
+            "@Module",
+            "interface RepeatedSubModule {",
+            "  @Provides",
+            "  @IntoSet",
+            "  static Integer provideInt() {",
+            "    return 9;",
+            "  }",
+            "}");
+
+    Compilation compilation =
+        daggerCompiler()
+            .compile(
+                parent, child1, child2, repeatedSub, child1Module, child2Module, repeatedSubModule);
+    assertThat(compilation).failed();
+    assertThat(compilation).hadErrorCount(1);
+    assertThat(compilation)
+        .hadErrorContaining("A binding for Object exists in [Parent → Child2 → RepeatedSub]:");
+    assertThat(compilation)
+        .hadErrorContaining(
+            "[Parent → Child1 → RepeatedSub] RepeatedSub.getObject() [Parent → Child1 →"
+                + " RepeatedSub]");
+  }
+
+  @Test
+  public void differentComponentPkgSameSimpleNameMissingBinding() {
+    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 = Child1Module.class)",
+            "interface Child1 {",
+            "  foo.Sub getSub();",
+            "}");
+    JavaFileObject child2 =
+        JavaFileObjects.forSourceLines(
+            "test.Child2",
+            "package test;",
+            "",
+            "import dagger.Subcomponent;",
+            "",
+            "@Subcomponent(modules = Child2Module.class)",
+            "interface Child2 {",
+            "  bar.Sub getSub();",
+            "}");
+    JavaFileObject sub1 =
+        JavaFileObjects.forSourceLines(
+            "foo.Sub",
+            "package foo;",
+            "",
+            "import dagger.Subcomponent;",
+            "",
+            "@Subcomponent(modules = test.RepeatedSubModule.class)",
+            "public interface Sub {",
+            "  Object getObject();",
+            "}");
+    JavaFileObject sub2 =
+        JavaFileObjects.forSourceLines(
+            "bar.Sub",
+            "package bar;",
+            "",
+            "import dagger.Subcomponent;",
+            "",
+            "@Subcomponent(modules = test.RepeatedSubModule.class)",
+            "public interface Sub {",
+            "  Object getObject();",
+            "}");
+    JavaFileObject child1Module =
+        JavaFileObjects.forSourceLines(
+            "test.Child1Module",
+            "package test;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "import java.util.Set;",
+            "import dagger.multibindings.Multibinds;",
+            "",
+            "@Module",
+            "interface Child1Module {",
+            "  @Multibinds Set<Integer> multibindIntegerSet();",
+            "}");
+    JavaFileObject child2Module =
+        JavaFileObjects.forSourceLines(
+            "test.Child2Module",
+            "package test;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "import java.util.Set;",
+            "import dagger.multibindings.Multibinds;",
+            "",
+            "@Module",
+            "interface Child2Module {",
+            "  @Multibinds Set<Integer> multibindIntegerSet();",
+            "",
+            "  @Provides",
+            "  static Object provideObject(Set<Integer> intSet) {",
+            "    return new Object();",
+            "  }",
+            "}");
+    JavaFileObject repeatedSubModule =
+        JavaFileObjects.forSourceLines(
+            "test.RepeatedSubModule",
+            "package test;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "import dagger.multibindings.IntoSet;",
+            "import java.util.Set;",
+            "import dagger.multibindings.Multibinds;",
+            "",
+            "@Module",
+            "public interface RepeatedSubModule {",
+            "  @Provides",
+            "  @IntoSet",
+            "  static Integer provideInt() {",
+            "    return 9;",
+            "  }",
+            "}");
+
+    Compilation compilation =
+        daggerCompiler()
+            .compile(
+                parent, child1, child2, sub1, sub2, child1Module, child2Module, repeatedSubModule);
+    assertThat(compilation).failed();
+    assertThat(compilation).hadErrorCount(1);
+    assertThat(compilation).hadErrorContaining("A binding for Object exists in bar.Sub:");
+    assertThat(compilation)
+        .hadErrorContaining("[foo.Sub] foo.Sub.getObject() [Parent → Child1 → foo.Sub]");
+  }
 }
diff --git a/javatests/dagger/internal/codegen/ModuleFactoryGeneratorTest.java b/javatests/dagger/internal/codegen/ModuleFactoryGeneratorTest.java
index 84c7be4..3882b47 100644
--- a/javatests/dagger/internal/codegen/ModuleFactoryGeneratorTest.java
+++ b/javatests/dagger/internal/codegen/ModuleFactoryGeneratorTest.java
@@ -189,16 +189,28 @@
   }
 
   @Test public void validatesIncludedModules() {
-    JavaFileObject module = JavaFileObjects.forSourceLines("test.Parent",
-        "package test;",
-        "",
-        "import dagger.Module;",
-        "",
-        "@Module(includes = Void.class)",
-        "class TestModule {}");
+    JavaFileObject module =
+        JavaFileObjects.forSourceLines(
+            "test.Parent",
+            "package test;",
+            "",
+            "import dagger.Module;",
+            "",
+            "@Module(",
+            "    includes = {",
+            "        Void.class,",
+            "        String.class,",
+            "    }",
+            ")",
+            "class TestModule {}");
 
     Compilation compilation = daggerCompiler().compile(module);
     assertThat(compilation).failed();
+    // In java 11, errors reported on individual items in an annotation value's list will show up
+    // as separate errors to the user on the associated lines reported. However, in java 8, errors
+    // reported on individual items in an annotation value's list will show up as a single error
+    // (which ever is reported first) on the list itself, rather than on the items reported.
+    assertThat(compilation).hadErrorCount(1);
     assertThat(compilation)
         .hadErrorContaining(
             "java.lang.Void is listed as a module, but is not annotated with @Module");
@@ -224,8 +236,12 @@
             "",
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
-                "import dagger.internal.Preconditions;"),
+                "import dagger.internal.Preconditions;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class TestModule_ProvideStringFactory implements Factory<String> {",
             "  private final TestModule module;",
@@ -270,8 +286,13 @@
             "TestModule_ProvideStringFactory",
             "package test;",
             "",
-            GeneratedLines.generatedImports("import dagger.internal.Factory;"),
+            GeneratedLines.generatedImports(
+                "import dagger.internal.Factory;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class TestModule_ProvideStringFactory implements Factory<String> {",
             "  private final TestModule module;",
@@ -315,8 +336,13 @@
             "TestModule_ProvideStringFactory",
             "package test;",
             "",
-            GeneratedLines.generatedImports("import dagger.internal.Factory;"),
+            GeneratedLines.generatedImports(
+                "import dagger.internal.Factory;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class TestModule_ProvideStringFactory implements Factory<String> {",
             "  private final TestModule module;",
@@ -388,9 +414,16 @@
                 "import dagger.MembersInjector;",
                 "import dagger.internal.Factory;",
                 "import dagger.internal.Preconditions;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;",
                 "import java.util.List;",
                 "import javax.inject.Provider;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata({",
+            "    \"test.QualifierA\",",
+            "    \"test.QualifierB\"",
+            "})",
             GeneratedLines.generatedAnnotations(),
             "public final class TestModule_ProvideObjectsFactory",
             "    implements Factory<List<Object>> {",
@@ -459,8 +492,12 @@
             "",
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
-                "import dagger.internal.Preconditions;"),
+                "import dagger.internal.Preconditions;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class TestModule_ProvideStringFactory implements Factory<String> {",
             "  private final TestModule module;",
@@ -512,8 +549,12 @@
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
                 "import dagger.internal.Preconditions;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;",
                 "import java.util.List;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class TestModule_ProvideWildcardListFactory implements "
                 + "Factory<List<List<?>>> {",
@@ -565,8 +606,12 @@
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
                 "import dagger.internal.Preconditions;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;",
                 "import java.util.Set;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class TestModule_ProvideStringsFactory implements Factory<Set<String>> {",
             "  private final TestModule module;",
@@ -876,9 +921,13 @@
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
                 "import dagger.internal.Preconditions;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;",
                 "import java.util.List;",
                 "import javax.inject.Provider;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class ParentModule_ProvideListBFactory<A extends CharSequence,",
             "    B, C extends Number & Comparable<C>> implements Factory<List<B>> {",
@@ -915,8 +964,12 @@
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
                 "import dagger.internal.Preconditions;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class ParentModule_ProvideBElementFactory<A extends CharSequence,",
             "    B, C extends Number & Comparable<C>> implements Factory<B> {",
@@ -954,8 +1007,12 @@
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
                 "import dagger.internal.Preconditions;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class ParentModule_ProvideBEntryFactory<A extends CharSequence,",
             "    B, C extends Number & Comparable<C>> implements Factory<B>> {",
@@ -992,8 +1049,12 @@
             "",
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
-                "import dagger.internal.Preconditions;"),
+                "import dagger.internal.Preconditions;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class ChildNumberModule_ProvideNumberFactory",
             "    implements Factory<Number> {",
@@ -1024,8 +1085,12 @@
             "",
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
-                "import dagger.internal.Preconditions;"),
+                "import dagger.internal.Preconditions;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class ChildIntegerModule_ProvideIntegerFactory",
             "    implements Factory<Integer> {",
@@ -1098,8 +1163,12 @@
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
                 "import dagger.internal.Preconditions;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;",
                 "import java.util.Map;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class ParameterizedModule_ProvideMapStringNumberFactory",
             "    implements Factory<Map<String, Number>> {",
@@ -1130,8 +1199,12 @@
             "",
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
-                "import dagger.internal.Preconditions;"),
+                "import dagger.internal.Preconditions;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class ParameterizedModule_ProvideNonGenericTypeFactory",
             "    implements Factory<Object> {",
@@ -1163,8 +1236,12 @@
             GeneratedLines.generatedImports(
                 "import dagger.internal.Factory;",
                 "import dagger.internal.Preconditions;",
+                "import dagger.internal.QualifierMetadata;",
+                "import dagger.internal.ScopeMetadata;",
                 "import javax.inject.Provider;"),
             "",
+            "@ScopeMetadata",
+            "@QualifierMetadata",
             GeneratedLines.generatedAnnotations(),
             "public final class ParameterizedModule_ProvideNonGenericTypeWithDepsFactory",
             "    implements Factory<String> {",
@@ -1402,6 +1479,8 @@
                 "test.TestModule_GetFactory",
                 "package test;",
                 "",
+                "@ScopeMetadata",
+                "@QualifierMetadata",
                 GeneratedLines.generatedAnnotations(),
                 "public final class TestModule_GetFactory implements Factory<Integer> {",
                 "  @Override",
@@ -1425,6 +1504,8 @@
                 "test.TestModule_CreateFactory",
                 "package test;",
                 "",
+                "@ScopeMetadata",
+                "@QualifierMetadata",
                 GeneratedLines.generatedAnnotations(),
                 "public final class TestModule_CreateFactory implements Factory<Boolean> {",
                 "  @Override",
@@ -1442,6 +1523,202 @@
                 "}"));
   }
 
+  @Test
+  public void testScopedMetadataOnStaticProvides() {
+    JavaFileObject module =
+        JavaFileObjects.forSourceLines(
+            "test.ScopedBinding",
+            "package test;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "import javax.inject.Singleton;",
+            "",
+            "@Module",
+            "interface MyModule {",
+            "  @NonScope",
+            "  @Singleton",
+            "  @Provides",
+            "  static String provideString() {",
+            "    return \"\";",
+            "  }",
+            "}");
+    JavaFileObject nonScope =
+        JavaFileObjects.forSourceLines(
+            "test.NonScope",
+            "package test;",
+            "",
+            "@interface NonScope {}");
+    Compilation compilation = daggerCompiler().compile(module, nonScope);
+    assertThat(compilation).succeeded();
+    assertThat(compilation)
+        .generatedSourceFile("test.MyModule_ProvideStringFactory")
+        .containsElementsIn(
+            JavaFileObjects.forSourceLines(
+                "test.MyModule_ProvideStringFactory",
+                "package test;",
+                "",
+                "@ScopeMetadata(\"javax.inject.Singleton\")",
+                "@QualifierMetadata",
+                GeneratedLines.generatedAnnotations(),
+                "public final class MyModule_ProvideStringFactory implements Factory<String> {}"));
+  }
+
+  @Test
+  public void testScopedMetadataOnNonStaticProvides() {
+    JavaFileObject module =
+        JavaFileObjects.forSourceLines(
+            "test.ScopedBinding",
+            "package test;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "import javax.inject.Singleton;",
+            "",
+            "@Module",
+            "class MyModule {",
+            "  @NonScope",
+            "  @Singleton",
+            "  @Provides",
+            "  String provideString() {",
+            "    return \"\";",
+            "  }",
+            "}");
+    JavaFileObject nonScope =
+        JavaFileObjects.forSourceLines(
+            "test.NonScope",
+            "package test;",
+            "",
+            "@interface NonScope {}");
+    Compilation compilation = daggerCompiler().compile(module, nonScope);
+    assertThat(compilation).succeeded();
+    assertThat(compilation)
+        .generatedSourceFile("test.MyModule_ProvideStringFactory")
+        .containsElementsIn(
+            JavaFileObjects.forSourceLines(
+                "test.MyModule_ProvideStringFactory",
+                "package test;",
+                "",
+                "@ScopeMetadata(\"javax.inject.Singleton\")",
+                "@QualifierMetadata",
+                GeneratedLines.generatedAnnotations(),
+                "public final class MyModule_ProvideStringFactory implements Factory<String> {}"));
+  }
+
+  @Test
+  public void testScopeMetadataWithCustomScope() {
+    JavaFileObject module =
+        JavaFileObjects.forSourceLines(
+            "test.ScopedBinding",
+            "package test;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "import javax.inject.Singleton;",
+            "",
+            "@Module",
+            "interface MyModule {",
+            "  @NonScope(\"someValue\")",
+            "  @CustomScope(\"someOtherValue\")",
+            "  @Provides",
+            "  static String provideString() {",
+            "    return \"\";",
+            "  }",
+            "}");
+    JavaFileObject customScope =
+        JavaFileObjects.forSourceLines(
+            "test.CustomScope",
+            "package test;",
+            "",
+            "import javax.inject.Scope;",
+            "",
+            "@Scope",
+            "@interface CustomScope {",
+            "  String value();",
+            "}");
+    JavaFileObject nonScope =
+        JavaFileObjects.forSourceLines(
+            "test.NonScope",
+            "package test;",
+            "",
+            "@interface NonScope {",
+            "  String value();",
+            "}");
+    Compilation compilation = daggerCompiler().compile(module, customScope, nonScope);
+    assertThat(compilation).succeeded();
+    assertThat(compilation)
+        .generatedSourceFile("test.MyModule_ProvideStringFactory")
+        .containsElementsIn(
+            JavaFileObjects.forSourceLines(
+                "test.MyModule_ProvideStringFactory",
+                "package test;",
+                "",
+                "@ScopeMetadata(\"test.CustomScope\")",
+                "@QualifierMetadata",
+                GeneratedLines.generatedAnnotations(),
+                "public final class MyModule_ProvideStringFactory implements Factory<String> {}"));
+  }
+
+  @Test
+  public void testQualifierMetadataOnProvides() {
+    JavaFileObject module =
+        JavaFileObjects.forSourceLines(
+            "test.ScopedBinding",
+            "package test;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "import javax.inject.Singleton;",
+            "",
+            "@Module",
+            "interface MyModule {",
+            "  @Provides",
+            "  @NonQualifier",
+            "  @MethodQualifier",
+            "  static String provideString(@NonQualifier @ParamQualifier int i) {",
+            "    return \"\";",
+            "  }",
+            "}");
+    JavaFileObject methodQualifier =
+        JavaFileObjects.forSourceLines(
+            "test.MethodQualifier",
+            "package test;",
+            "",
+            "import javax.inject.Qualifier;",
+            "",
+            "@Qualifier",
+            "@interface MethodQualifier {}");
+    JavaFileObject paramQualifier =
+        JavaFileObjects.forSourceLines(
+            "test.ParamQualifier",
+            "package test;",
+            "",
+            "import javax.inject.Qualifier;",
+            "",
+            "@Qualifier",
+            "@interface ParamQualifier {}");
+    JavaFileObject nonQualifier =
+        JavaFileObjects.forSourceLines(
+            "test.NonQualifier",
+            "package test;",
+            "",
+            "@interface NonQualifier {}");
+    Compilation compilation =
+        daggerCompiler().compile(module, methodQualifier, paramQualifier, nonQualifier);
+    assertThat(compilation).succeeded();
+    assertThat(compilation)
+        .generatedSourceFile("test.MyModule_ProvideStringFactory")
+        .containsElementsIn(
+            JavaFileObjects.forSourceLines(
+                "test.MyModule_ProvideStringFactory",
+                "package test;",
+                "",
+                "@ScopeMetadata",
+                "@QualifierMetadata({\"test.MethodQualifier\", \"test.ParamQualifier\"})",
+                GeneratedLines.generatedAnnotations(),
+                "public final class MyModule_ProvideStringFactory implements Factory<String> {}"));
+  }
+
   private static final String BINDS_METHOD = "@Binds abstract Foo bindFoo(FooImpl impl);";
   private static final String MULTIBINDS_METHOD = "@Multibinds abstract Set<Foo> foos();";
   private static final String STATIC_PROVIDES_METHOD =
diff --git a/javatests/dagger/internal/codegen/OptionalBindingRequestFulfillmentTest.java b/javatests/dagger/internal/codegen/OptionalBindingRequestFulfillmentTest.java
index 9b43530..c78d199 100644
--- a/javatests/dagger/internal/codegen/OptionalBindingRequestFulfillmentTest.java
+++ b/javatests/dagger/internal/codegen/OptionalBindingRequestFulfillmentTest.java
@@ -107,41 +107,42 @@
             .addLines(
                 "package test;",
                 "",
-                "import com.google.common.base.Optional;",
-                "",
                 GeneratedLines.generatedAnnotations(),
                 "final class DaggerTestComponent implements TestComponent {")
             .addLinesIn(
                 FAST_INIT_MODE,
-                "  private volatile Provider<Maybe> provideMaybeProvider;",
+                "  private Provider<Maybe> provideMaybeProvider;",
                 "",
-                "  private Provider<Maybe> maybeProvider() {",
-                "    Object local = provideMaybeProvider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(0);",
-                "      provideMaybeProvider = (Provider<Maybe>) local;",
-                "    }",
-                "    return (Provider<Maybe>) local;",
+                "  @SuppressWarnings(\"unchecked\")",
+                "  private void initialize() {",
+                "    this.provideMaybeProvider = new SwitchingProvider<>(testComponent, 0);",
                 "  }")
-            .addLines(
+            .addLinesIn(
+                DEFAULT_MODE,
                 "  @Override",
                 "  public Optional<Maybe> maybe() {",
-                "    return Optional.of(",
-                "        Maybe_MaybeModule_ProvideMaybeFactory.provideMaybe());",
-                "  }",
-                "",
+                "    return Optional.of(Maybe_MaybeModule_ProvideMaybeFactory.provideMaybe());",
+                "  }")
+            .addLinesIn(
+                FAST_INIT_MODE,
+                "  @Override",
+                "  public Optional<Maybe> maybe() {",
+                "    return Optional.of(provideMaybeProvider.get());",
+                "  }")
+            .addLinesIn(
+                DEFAULT_MODE,
                 "  @Override",
                 "  public Optional<Provider<Lazy<Maybe>>> providerOfLazyOfMaybe() {",
-                "    return Optional.of(ProviderOfLazy.create(")
+                "    return Optional.of(ProviderOfLazy.create(",
+                "        Maybe_MaybeModule_ProvideMaybeFactory.create()));",
+                "  }")
             .addLinesIn(
-                DEFAULT_MODE, //
-                "        Maybe_MaybeModule_ProvideMaybeFactory.create()));")
-            .addLinesIn(
-                FAST_INIT_MODE, //
-                "        maybeProvider()));")
+                FAST_INIT_MODE,
+                "  @Override",
+                "  public Optional<Provider<Lazy<Maybe>>> providerOfLazyOfMaybe() {",
+                "    return Optional.of(ProviderOfLazy.create(provideMaybeProvider));",
+                "  }")
             .addLines(
-                "  }",
-                "",
                 "  @Override",
                 "  public Optional<DefinitelyNot> definitelyNot() {",
                 "    return Optional.<DefinitelyNot>absent();",
@@ -154,21 +155,13 @@
                 "  }")
             .addLinesIn(
                 FAST_INIT_MODE,
-                "  private final class SwitchingProvider<T> implements Provider<T> {",
-                "    private final int id;",
-                "",
-                "    SwitchingProvider(int id) {",
-                "      this.id = id;",
-                "    }",
-                "",
+                "  private static final class SwitchingProvider<T> implements Provider<T> {",
                 "    @SuppressWarnings(\"unchecked\")",
                 "    @Override",
                 "    public T get() {",
                 "      switch (id) {",
-                "        case 0:",
-                "          return (T) Maybe_MaybeModule_ProvideMaybeFactory.provideMaybe();",
-                "        default:",
-                "          throw new AssertionError(id);",
+                "        case 0: return (T) Maybe_MaybeModule_ProvideMaybeFactory.provideMaybe();",
+                "        default: throw new AssertionError(id);",
                 "      }",
                 "    }",
                 "  }",
diff --git a/javatests/dagger/internal/codegen/ProductionComponentProcessorTest.java b/javatests/dagger/internal/codegen/ProductionComponentProcessorTest.java
index 4043e76..e0ad55a 100644
--- a/javatests/dagger/internal/codegen/ProductionComponentProcessorTest.java
+++ b/javatests/dagger/internal/codegen/ProductionComponentProcessorTest.java
@@ -16,9 +16,9 @@
 
 package dagger.internal.codegen;
 
-import static com.google.common.truth.Truth.assertAbout;
 import static com.google.testing.compile.CompilationSubject.assertThat;
-import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
+import static dagger.internal.codegen.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;
 
@@ -224,321 +224,111 @@
             "    ListenableFuture<A> a();",
             "  }",
             "}");
-    JavaFileObject generatedComponent;
-    switch (compilerMode) {
-      case FAST_INIT_MODE:
-        generatedComponent =
-            JavaFileObjects.forSourceLines(
-                "test.DaggerTestClass_SimpleComponent",
-                "package test;",
-                "",
-                GeneratedLines.generatedImports(
-                    "import com.google.common.util.concurrent.ListenableFuture;",
-                    "import dagger.internal.DoubleCheck;",
-                    "import dagger.internal.InstanceFactory;",
-                    "import dagger.internal.MemoizedSentinel;",
-                    "import dagger.internal.Preconditions;",
-                    "import dagger.internal.SetFactory;",
-                    "import dagger.producers.Producer;",
-                    "import dagger.producers.internal.CancellationListener;",
-                    "import dagger.producers.internal.Producers;",
-                    "import dagger.producers.monitoring.ProductionComponentMonitor;",
-                    "import java.util.concurrent.Executor;",
-                    "import javax.inject.Provider;"),
-                "",
-                GeneratedLines.generatedAnnotations(),
-                "final class DaggerTestClass_SimpleComponent",
-                "    implements TestClass.SimpleComponent, CancellationListener {",
-                "  private final TestClass.BModule bModule;",
-                "  private volatile Object productionImplementationExecutor =",
-                "      new MemoizedSentinel();",
-                "  private volatile Provider<Executor> productionImplementationExecutorProvider;",
-                "  private volatile Object productionComponentMonitor = new MemoizedSentinel();",
-                "  private volatile Provider<ProductionComponentMonitor> monitorProvider;",
-                "  private volatile Provider<TestClass.B> bProvider;",
-                "  private Producer<TestClass.A> aEntryPoint;",
-                "  private Provider<TestClass.SimpleComponent> simpleComponentProvider;",
-                "  private Producer<TestClass.B> bProducer;",
-                "  private Producer<TestClass.A> aProducer;",
-                "",
-                "  private DaggerTestClass_SimpleComponent(",
-                "      TestClass.AModule aModuleParam,",
-                "      TestClass.BModule bModuleParam) {",
-                "    this.bModule = bModuleParam;",
-                "    initialize(aModuleParam, bModuleParam);",
-                "  }",
-                "",
-                "  public static Builder builder() {",
-                "    return new Builder();",
-                "  }",
-                "",
-                "  public static TestClass.SimpleComponent create() {",
-                "    return new Builder().build();",
-                "  }",
-                "",
-                "  private Executor productionImplementationExecutor() {",
-                "    Object local = productionImplementationExecutor;",
-                "    if (local instanceof MemoizedSentinel) {",
-                "      synchronized (local) {",
-                "        local = productionImplementationExecutor;",
-                "        if (local instanceof MemoizedSentinel) {",
-                "          local =",
-                "              TestClass_BModule_ExecutorFactory.executor(bModule);",
-                "          productionImplementationExecutor =",
-                "              DoubleCheck.reentrantCheck(",
-                "                  productionImplementationExecutor, local);",
-                "        }",
-                "      }",
-                "    }",
-                "    return (Executor) local;",
-                "  }",
-                "",
-                "  private Provider<Executor> productionImplementationExecutorProvider() {",
-                "    Object local = productionImplementationExecutorProvider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(0);",
-                "      productionImplementationExecutorProvider = (Provider<Executor>) local;",
-                "    }",
-                "    return (Provider<Executor>) local;",
-                "  }",
-                "",
-                "  private ProductionComponentMonitor productionComponentMonitor() {",
-                "    Object local = productionComponentMonitor;",
-                "    if (local instanceof MemoizedSentinel) {",
-                "      synchronized (local) {",
-                "        local = productionComponentMonitor;",
-                "        if (local instanceof MemoizedSentinel) {",
-                "          local =",
-                "              TestClass_SimpleComponent_MonitoringModule_MonitorFactory",
-                "                  .monitor(",
-                "                      simpleComponentProvider,",
-                "                      SetFactory.<ProductionComponentMonitor.Factory>empty());",
-                "          productionComponentMonitor =",
-                "              DoubleCheck.reentrantCheck(",
-                "                  productionComponentMonitor, local);",
-                "        }",
-                "      }",
-                "    }",
-                "    return (ProductionComponentMonitor) local;",
-                "  }",
-                "",
-                "  private Provider<ProductionComponentMonitor>",
-                "      productionComponentMonitorProvider() {",
-                "      Object local = monitorProvider;",
-                "      if (local == null) {",
-                "        local = new SwitchingProvider<>(1);",
-                "        monitorProvider = (Provider<ProductionComponentMonitor>) local;",
-                "      }",
-                "      return (Provider<ProductionComponentMonitor>) local;",
-                "  }",
-                "",
-                "  private TestClass.B b() {",
-                "    return TestClass_BModule_BFactory.b(bModule, new TestClass.C());",
-                "  }",
-                "",
-                "  private Provider<TestClass.B> bProvider() {",
-                "    Object local = bProvider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(2);",
-                "      bProvider = (Provider<TestClass.B>) local;",
-                "    }",
-                "    return (Provider<TestClass.B>) local;",
-                "  }",
-                "",
-                "  @SuppressWarnings(\"unchecked\")",
-                "  private void initialize(",
-                "      final TestClass.AModule aModuleParam,",
-                "      final TestClass.BModule bModuleParam) {",
-                "    this.simpleComponentProvider =",
-                "        InstanceFactory.create((TestClass.SimpleComponent) this);",
-                "    this.bProducer = Producers.producerFromProvider(bProvider());",
-                "    this.aProducer =",
-                "        TestClass_AModule_AFactory.create(",
-                "            aModuleParam,",
-                "            productionImplementationExecutorProvider(),",
-                "            productionComponentMonitorProvider(),",
-                "            bProducer);",
-                "    this.aEntryPoint = Producers.entryPointViewOf(aProducer, this);",
-                "  }",
-                "",
-                "  @Override",
-                "  public ListenableFuture<TestClass.A> a() {",
-                "    return aEntryPoint.get();",
-                "  }",
-                "",
-                "  @Override",
-                "  public void onProducerFutureCancelled(boolean mayInterruptIfRunning) {",
-                "    Producers.cancel(aProducer, mayInterruptIfRunning);",
-                "    Producers.cancel(bProducer, mayInterruptIfRunning);",
-                "  }",
-                "",
-                "  static final class Builder {",
-                "    private TestClass.AModule aModule;",
-                "    private TestClass.BModule bModule;",
-                "",
-                "    private Builder() {}",
-                "",
-                "    public Builder aModule(TestClass.AModule aModule) {",
-                "      this.aModule = Preconditions.checkNotNull(aModule);",
-                "      return this;",
-                "    }",
-                "",
-                "    public Builder bModule(TestClass.BModule bModule) {",
-                "      this.bModule = Preconditions.checkNotNull(bModule);",
-                "      return this;",
-                "    }",
-                "",
-                "    public TestClass.SimpleComponent build() {",
-                "      if (aModule == null) {",
-                "        this.aModule = new TestClass.AModule();",
-                "      }",
-                "      if (bModule == null) {",
-                "        this.bModule = new TestClass.BModule();",
-                "      }",
-                "      return new DaggerTestClass_SimpleComponent(aModule, bModule);",
-                "    }",
-                "  }",
-                "",
-                "  private final class SwitchingProvider<T> implements Provider<T> {",
-                "    private final int id;",
-                "",
-                "    SwitchingProvider(int id) {",
-                "      this.id = id;",
-                "    }",
-                "",
-                "    @SuppressWarnings(\"unchecked\")",
-                "    @Override",
-                "    public T get() {",
-                "      switch (id) {",
-                "        case 0: return (T) DaggerTestClass_SimpleComponent.this",
-                "            .productionImplementationExecutor();",
-                "        case 1: return (T)",
-                "            DaggerTestClass_SimpleComponent.this.productionComponentMonitor();",
-                "        case 2: return (T)",
-                "            DaggerTestClass_SimpleComponent.this.b();",
-                "        default: throw new AssertionError(id);",
-                "      }",
-                "    }",
-                "  }",
-                "}");
-        break;
-      default:
-        generatedComponent =
-            JavaFileObjects.forSourceLines(
-                "test.DaggerTestClass_SimpleComponent",
-                "package test;",
-                "",
-                GeneratedLines.generatedImports(
-                    "import com.google.common.util.concurrent.ListenableFuture;",
-                    "import dagger.internal.DoubleCheck;",
-                    "import dagger.internal.InstanceFactory;",
-                    "import dagger.internal.Preconditions;",
-                    "import dagger.internal.SetFactory;",
-                    "import dagger.producers.Producer;",
-                    "import dagger.producers.internal.CancellationListener;",
-                    "import dagger.producers.internal.Producers;",
-                    "import dagger.producers.monitoring.ProductionComponentMonitor;",
-                    "import java.util.concurrent.Executor;",
-                    "import javax.inject.Provider;"),
-                "",
-                GeneratedLines.generatedAnnotations(),
-                "final class DaggerTestClass_SimpleComponent",
-                "    implements TestClass.SimpleComponent, CancellationListener {",
-                "  private Producer<TestClass.A> aEntryPoint;",
-                "  private Provider<Executor> executorProvider;",
-                "  private Provider<Executor> productionImplementationExecutorProvider;",
-                "  private Provider<TestClass.SimpleComponent> simpleComponentProvider;",
-                "  private Provider<ProductionComponentMonitor> monitorProvider;",
-                "  private Provider<TestClass.B> bProvider;",
-                "  private Producer<TestClass.B> bProducer;",
-                "  private Producer<TestClass.A> aProducer;",
-                "",
-                "  private DaggerTestClass_SimpleComponent(",
-                "      TestClass.AModule aModuleParam,",
-                "      TestClass.BModule bModuleParam) {",
-                "    initialize(aModuleParam, bModuleParam);",
-                "  }",
-                "",
-                "  public static Builder builder() {",
-                "    return new Builder();",
-                "  }",
-                "",
-                "  public static TestClass.SimpleComponent create() {",
-                "    return new Builder().build();",
-                "  }",
-                "",
-                "  @SuppressWarnings(\"unchecked\")",
-                "  private void initialize(",
-                "      final TestClass.AModule aModuleParam,",
-                "      final TestClass.BModule bModuleParam) {",
-                "    this.executorProvider =",
-                "        TestClass_BModule_ExecutorFactory.create(bModuleParam);",
-                "    this.productionImplementationExecutorProvider =",
-                "        DoubleCheck.provider((Provider) executorProvider);",
-                "    this.simpleComponentProvider = ",
-                "        InstanceFactory.create((TestClass.SimpleComponent) this);",
-                "    this.monitorProvider =",
-                "        DoubleCheck.provider(",
-                "            TestClass_SimpleComponent_MonitoringModule_MonitorFactory.create(",
-                "                simpleComponentProvider,",
-                "                SetFactory.<ProductionComponentMonitor.Factory>empty()));",
-                "    this.bProvider = TestClass_BModule_BFactory.create(",
-                "        bModuleParam, TestClass_C_Factory.create());",
-                "    this.bProducer = Producers.producerFromProvider(bProvider);",
-                "    this.aProducer = TestClass_AModule_AFactory.create(",
-                "        aModuleParam,",
-                "        productionImplementationExecutorProvider,",
-                "        monitorProvider,",
-                "        bProducer);",
-                "    this.aEntryPoint = Producers.entryPointViewOf(aProducer, this);",
-                "  }",
-                "",
-                "  @Override",
-                "  public ListenableFuture<TestClass.A> a() {",
-                "    return aEntryPoint.get();",
-                "  }",
-                "",
-                "  @Override",
-                "  public void onProducerFutureCancelled(boolean mayInterruptIfRunning) {",
-                "    Producers.cancel(aProducer, mayInterruptIfRunning);",
-                "    Producers.cancel(bProducer, mayInterruptIfRunning);",
-                "  }",
-                "",
-                "  static final class Builder {",
-                "    private TestClass.AModule aModule;",
-                "    private TestClass.BModule bModule;",
-                "",
-                "    private Builder() {}",
-                "",
-                "    public Builder aModule(TestClass.AModule aModule) {",
-                "      this.aModule = Preconditions.checkNotNull(aModule);",
-                "      return this;",
-                "    }",
-                "",
-                "    public Builder bModule(TestClass.BModule bModule) {",
-                "      this.bModule = Preconditions.checkNotNull(bModule);",
-                "      return this;",
-                "    }",
-                "",
-                "    public TestClass.SimpleComponent build() {",
-                "      if (aModule == null) {",
-                "        this.aModule = new TestClass.AModule();",
-                "      }",
-                "      if (bModule == null) {",
-                "        this.bModule = new TestClass.BModule();",
-                "      }",
-                "      return new DaggerTestClass_SimpleComponent(aModule, bModule);",
-                "    }",
-                "  }",
-                "}");
-    }
-    assertAbout(javaSource())
-        .that(component)
-        .withCompilerOptions(compilerMode.javacopts())
-        .processedWith(new ComponentProcessor())
-        .compilesWithoutError()
-        .and()
-        .generatesSources(generatedComponent);
+
+    Compilation compilation = compilerWithOptions(compilerMode.javacopts()).compile(component);
+    assertThat(compilation).succeeded();
+    assertThat(compilation)
+        .generatedSourceFile("test.DaggerTestClass_SimpleComponent")
+        .containsElementsIn(
+            compilerMode
+                .javaFileBuilder("test.DaggerTestClass_SimpleComponent")
+                .addLines(
+                    "package test;",
+                    "",
+                    GeneratedLines.generatedAnnotations(),
+                    "final class DaggerTestClass_SimpleComponent",
+                    "    implements TestClass.SimpleComponent, CancellationListener {",
+                    "  private Producer<TestClass.A> aEntryPoint;",
+                    "  private Provider<Executor> executorProvider;",
+                    "  private Provider<Executor> productionImplementationExecutorProvider;",
+                    "  private Provider<TestClass.SimpleComponent> simpleComponentProvider;",
+                    "  private Provider<ProductionComponentMonitor> monitorProvider;",
+                    "  private Provider<TestClass.B> bProvider;",
+                    "  private Producer<TestClass.B> bProducer;",
+                    "  private Producer<TestClass.A> aProducer;")
+                .addLinesIn(
+                    DEFAULT_MODE,
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize(",
+                    "      final TestClass.AModule aModuleParam,",
+                    "      final TestClass.BModule bModuleParam) {",
+                    "    this.executorProvider =",
+                    "        TestClass_BModule_ExecutorFactory.create(bModuleParam);",
+                    "    this.productionImplementationExecutorProvider =",
+                    "        DoubleCheck.provider((Provider) executorProvider);",
+                    "    this.simpleComponentProvider = ",
+                    "        InstanceFactory.create((TestClass.SimpleComponent) simpleComponent);",
+                    "    this.monitorProvider =",
+                    "        DoubleCheck.provider(",
+                    "            TestClass_SimpleComponent_MonitoringModule_MonitorFactory.create(",
+                    "                simpleComponentProvider,",
+                    "                SetFactory.<ProductionComponentMonitor.Factory>empty()));",
+                    "    this.bProvider = TestClass_BModule_BFactory.create(",
+                    "        bModuleParam, TestClass_C_Factory.create());",
+                    "    this.bProducer = Producers.producerFromProvider(bProvider);",
+                    "    this.aProducer = TestClass_AModule_AFactory.create(",
+                    "        aModuleParam,",
+                    "        productionImplementationExecutorProvider,",
+                    "        monitorProvider,",
+                    "        bProducer);",
+                    "    this.aEntryPoint = Producers.entryPointViewOf(aProducer, this);",
+                    "  }")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "  @SuppressWarnings(\"unchecked\")",
+                    "  private void initialize(",
+                    "      final TestClass.AModule aModuleParam,",
+                    "      final TestClass.BModule bModuleParam) {",
+                    "    this.executorProvider = new SwitchingProvider<>(simpleComponent, 0);",
+                    "    this.productionImplementationExecutorProvider =",
+                    "        DoubleCheck.provider((Provider) executorProvider);",
+                    "    this.simpleComponentProvider =",
+                    "        InstanceFactory.create((TestClass.SimpleComponent) simpleComponent);",
+                    "    this.monitorProvider = DoubleCheck.provider(",
+                    "        new SwitchingProvider<ProductionComponentMonitor>(",
+                    "            simpleComponent, 1));",
+                    "    this.bProvider = new SwitchingProvider<>(simpleComponent, 2);",
+                    "    this.bProducer = Producers.producerFromProvider(bProvider);",
+                    "    this.aProducer = TestClass_AModule_AFactory.create(",
+                    "        aModuleParam,",
+                    "        productionImplementationExecutorProvider,",
+                    "        monitorProvider,",
+                    "        bProducer);",
+                    "    this.aEntryPoint = Producers.entryPointViewOf(aProducer, this);",
+                    "  }")
+                .addLines(
+                    "  @Override",
+                    "  public ListenableFuture<TestClass.A> a() {",
+                    "    return aEntryPoint.get();",
+                    "  }",
+                    "",
+                    "  @Override",
+                    "  public void onProducerFutureCancelled(boolean mayInterruptIfRunning) {",
+                    "    Producers.cancel(aProducer, mayInterruptIfRunning);",
+                    "    Producers.cancel(bProducer, mayInterruptIfRunning);",
+                    "  }")
+                .addLinesIn(
+                    FAST_INIT_MODE,
+                    "  private static final class SwitchingProvider<T> implements Provider<T> {",
+                    "    @SuppressWarnings(\"unchecked\")",
+                    "    @Override",
+                    "    public T get() {",
+                    "      switch (id) {",
+                    "        case 0: return (T) TestClass_BModule_ExecutorFactory.executor(",
+                    "            simpleComponent.bModule);",
+                    "        case 1: return (T)",
+                    "            TestClass_SimpleComponent_MonitoringModule_MonitorFactory",
+                    "                .monitor(",
+                    "                    simpleComponent.simpleComponentProvider,",
+                    "                    SetFactory.<ProductionComponentMonitor.Factory>empty());",
+                    "        case 2: return (T) TestClass_BModule_BFactory.b(",
+                    "            simpleComponent.bModule, new TestClass.C());",
+                    "        default: throw new AssertionError(id);",
+                    "      }",
+                    "    }",
+                    "  }",
+                    "}")
+                .build());
   }
 
   @Test public void nullableProducersAreNotErrors() {
@@ -648,20 +438,16 @@
             new JavaFileBuilder(compilerMode, "test.DaggerRoot")
                 .addLines(
                     "package test;",
+                    "",
                     GeneratedLines.generatedAnnotations(),
                     "final class DaggerParent implements Parent, CancellationListener {",
-                    "  private final class ChildImpl implements Child, CancellationListener {",
+                    "  private static final class ChildImpl",
+                    "      implements Child, CancellationListener {",
                     "    @Override",
-                    "    public ProductionScoped productionScoped() {")
-                .addLinesIn(
-                    CompilerMode.DEFAULT_MODE, //
-                    "      return DaggerParent.this.productionScopedProvider.get();")
-                .addLinesIn(
-                    CompilerMode.FAST_INIT_MODE, //
-                    "      return DaggerParent.this.productionScoped();")
-                .addLines(
-                    "    }", //
-                    "  }", //
+                    "    public ProductionScoped productionScoped() {",
+                    "      return parent.productionScopedProvider.get();",
+                    "    }",
+                    "  }",
                     "}")
                 .build());
   }
diff --git a/javatests/dagger/internal/codegen/RawTypeInjectionTest.java b/javatests/dagger/internal/codegen/RawTypeInjectionTest.java
new file mode 100644
index 0000000..62eaea4
--- /dev/null
+++ b/javatests/dagger/internal/codegen/RawTypeInjectionTest.java
@@ -0,0 +1,204 @@
+/*
+ * 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.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 class RawTypeInjectionTest {
+  @Test
+  public void rawEntryPointTest() {
+    JavaFileObject component =
+        JavaFileObjects.forSourceLines(
+            "test.TestComponent",
+            "package test;",
+            "",
+            "import dagger.Component;",
+            "",
+            "@Component",
+            "interface TestComponent {",
+            "  Foo foo();",  // Fail: requesting raw type
+            "}");
+    JavaFileObject foo =
+        JavaFileObjects.forSourceLines(
+            "test.Foo",
+            "package test;",
+            "",
+            "import javax.inject.Inject;",
+            "",
+            "class Foo<T> {",
+            "  @Inject Foo() {}",
+            "}");
+
+    Compilation compilation = daggerCompiler().compile(component, foo);
+    assertThat(compilation).failed();
+    assertThat(compilation)
+        .hadErrorContaining("Foo cannot be provided without an @Provides-annotated method.")
+        .inFile(component)
+        .onLine(6);
+  }
+
+  @Test
+  public void rawProvidesRequestTest() {
+    JavaFileObject component =
+        JavaFileObjects.forSourceLines(
+            "test.TestComponent",
+            "package test;",
+            "",
+            "import dagger.Component;",
+            "",
+            "@Component(modules = TestModule.class)",
+            "interface TestComponent {",
+            "  int integer();",
+            "}");
+    JavaFileObject foo =
+        JavaFileObjects.forSourceLines(
+            "test.Foo",
+            "package test;",
+            "",
+            "import javax.inject.Inject;",
+            "",
+            "class Foo<T> {",
+            "  @Inject Foo() {}",
+            "}");
+    JavaFileObject module =
+        JavaFileObjects.forSourceLines(
+            "test.TestModule",
+            "package test;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "",
+            "@Module",
+            "class TestModule {",
+            "  @Provides",
+            "  int provideFoo(Foo foo) {", // Fail: requesting raw type
+            "    return 0;",
+            "  }",
+            "}");
+
+
+    Compilation compilation = daggerCompiler().compile(component, foo, module);
+    assertThat(compilation).failed();
+    assertThat(compilation)
+        .hadErrorContaining("Foo cannot be provided without an @Provides-annotated method.")
+        .inFile(component)
+        .onLine(6);
+  }
+
+  @Test
+  public void rawInjectConstructorRequestTest() {
+    JavaFileObject component =
+        JavaFileObjects.forSourceLines(
+            "test.TestComponent",
+            "package test;",
+            "",
+            "import dagger.Component;",
+            "",
+            "@Component",
+            "interface TestComponent {",
+            "  Foo foo();",
+            "}");
+    JavaFileObject foo =
+        JavaFileObjects.forSourceLines(
+            "test.Foo",
+            "package test;",
+            "",
+            "import javax.inject.Inject;",
+            "",
+            "class Foo<T> {",
+            "  @Inject Foo() {}",
+            "}");
+    JavaFileObject bar =
+        JavaFileObjects.forSourceLines(
+            "test.Bar",
+            "package test;",
+            "",
+            "import javax.inject.Inject;",
+            "",
+            "class Bar {",
+            "  @Inject Bar(Foo foo) {}", // Fail: requesting raw type
+            "}");
+
+
+    Compilation compilation = daggerCompiler().compile(component, foo, bar);
+    assertThat(compilation).failed();
+    assertThat(compilation)
+        .hadErrorContaining("Foo cannot be provided without an @Provides-annotated method.")
+        .inFile(component)
+        .onLine(6);
+  }
+
+  @Test
+  public void rawProvidesReturnTest() {
+    JavaFileObject component =
+        JavaFileObjects.forSourceLines(
+            "test.TestComponent",
+            "package test;",
+            "",
+            "import dagger.Component;",
+            "",
+            "@Component(modules = TestModule.class)",
+            "interface TestComponent {",
+            // Test that we can request the raw type if it's provided by a module.
+            "  Foo foo();",
+            "}");
+    JavaFileObject foo =
+        JavaFileObjects.forSourceLines(
+            "test.Foo",
+            "package test;",
+            "",
+            "import javax.inject.Inject;",
+            "",
+            "class Foo<T> {",
+            "  @Inject Foo() {}",
+            "}");
+    JavaFileObject module =
+        JavaFileObjects.forSourceLines(
+            "test.TestModule",
+            "package test;",
+            "",
+            "import dagger.Module;",
+            "import dagger.Provides;",
+            "",
+            "@Module",
+            "class TestModule {",
+            // Test that Foo<T> can still be requested and is independent of Foo (otherwise we'd
+            // get a cyclic dependency error).
+            "  @Provides",
+            "  Foo provideFoo(Foo<Integer> fooInteger) {",
+            "    return fooInteger;",
+            "  }",
+            "",
+            "  @Provides",
+            "  int provideInt() {",
+            "    return 0;",
+            "  }",
+            "}");
+
+    Compilation compilation = daggerCompiler().compile(component, foo, module);
+    assertThat(compilation).succeeded();
+  }
+}
diff --git a/javatests/dagger/internal/codegen/ScopingValidationTest.java b/javatests/dagger/internal/codegen/ScopingValidationTest.java
index 58ab2af..740a445 100644
--- a/javatests/dagger/internal/codegen/ScopingValidationTest.java
+++ b/javatests/dagger/internal/codegen/ScopingValidationTest.java
@@ -79,6 +79,9 @@
             message(
                 "MyComponent (unscoped) may not reference scoped bindings:",
                 "    @Singleton class ScopedType",
+                "    ScopedType is requested at",
+                "        MyComponent.string()",
+                "",
                 "    @Provides @Singleton String ScopedModule.string()"));
   }
 
@@ -236,7 +239,11 @@
                 "MyComponent scoped with @Singleton "
                     + "may not reference bindings with different scopes:",
                 "    @PerTest class ScopedType",
+                "    ScopedType is requested at",
+                "        MyComponent.string()",
+                "",
                 "    @Provides @PerTest String ScopedModule.string()",
+                "",
                 "    @Provides @Per(MyComponent.class) boolean "
                     + "ScopedModule.bool()"))
         .inFile(componentFile)
@@ -251,7 +258,9 @@
             message(
                 "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)
diff --git a/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentTest.java b/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentTest.java
index a343a60..e1c49d6 100644
--- a/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentTest.java
+++ b/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentTest.java
@@ -266,6 +266,8 @@
             "",
             GeneratedLines.generatedAnnotations(),
             "final class DaggerParent implements Parent {",
+            "  private final DaggerParent parent = this;",
+            "",
             "  private DaggerParent() {}",
             "",
             "  public static Builder builder() {",
@@ -278,7 +280,7 @@
             "",
             "  @Override",
             "  public Child child() {",
-            "    return new ChildImpl();",
+            "    return new ChildImpl(parent);",
             "  }",
             "",
             "  static final class Builder {",
@@ -295,8 +297,14 @@
             "    }",
             "  }",
             "",
-            "  private final class ChildImpl implements Child {",
-            "    private ChildImpl() {}",
+            "  private static final class ChildImpl implements Child {",
+            "    private final DaggerParent parent;",
+            "",
+            "    private final ChildImpl childImpl = this;",
+            "",
+            "    private ChildImpl(DaggerParent parent) {",
+            "      this.parent = parent;",
+            "    }",
             "",
             "    @Override",
             "    public Set<Object> objectSet() {",
diff --git a/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentWithGuavaTest.java b/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentWithGuavaTest.java
index a081130..6e6236c 100644
--- a/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentWithGuavaTest.java
+++ b/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentWithGuavaTest.java
@@ -272,7 +272,7 @@
             "",
             GeneratedLines.generatedAnnotations(),
             "final class DaggerParent implements Parent {",
-            "  private final class ChildImpl implements Child {",
+            "  private static final class ChildImpl implements Child {",
             "    @Override",
             "    public Set<Object> objectSet() {",
             "      return ImmutableSet.<Object>of(",
@@ -330,8 +330,10 @@
                 "import java.util.Set;"),
             "",
             GeneratedLines.generatedAnnotations(),
-            "final class DaggerTestComponent implements TestComponent, "
-                + "CancellationListener {",
+            "final class DaggerTestComponent implements TestComponent, ",
+            "    CancellationListener {",
+            "  private final DaggerTestComponent testComponent = this;",
+            "",
             "  private DaggerTestComponent() {}",
             "",
             "  public static Builder builder() {",
diff --git a/javatests/dagger/internal/codegen/SubcomponentBuilderValidationTest.java b/javatests/dagger/internal/codegen/SubcomponentBuilderValidationTest.java
index 0fd7e08..2b3c49f 100644
--- a/javatests/dagger/internal/codegen/SubcomponentBuilderValidationTest.java
+++ b/javatests/dagger/internal/codegen/SubcomponentBuilderValidationTest.java
@@ -18,7 +18,7 @@
 
 import static com.google.testing.compile.CompilationSubject.assertThat;
 import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.SUBCOMPONENT_BUILDER;
+import static dagger.internal.codegen.base.ComponentCreatorAnnotation.SUBCOMPONENT_BUILDER;
 import static dagger.internal.codegen.binding.ErrorMessages.creatorMessagesFor;
 
 import com.google.testing.compile.Compilation;
diff --git a/javatests/dagger/internal/codegen/SubcomponentCreatorRequestFulfillmentTest.java b/javatests/dagger/internal/codegen/SubcomponentCreatorRequestFulfillmentTest.java
index 32ae245..245b051 100644
--- a/javatests/dagger/internal/codegen/SubcomponentCreatorRequestFulfillmentTest.java
+++ b/javatests/dagger/internal/codegen/SubcomponentCreatorRequestFulfillmentTest.java
@@ -21,13 +21,13 @@
 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.binding.ComponentCreatorAnnotation.SUBCOMPONENT_BUILDER;
-import static dagger.internal.codegen.binding.ComponentCreatorAnnotation.SUBCOMPONENT_FACTORY;
+import static dagger.internal.codegen.base.ComponentCreatorAnnotation.SUBCOMPONENT_BUILDER;
+import static dagger.internal.codegen.base.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 dagger.internal.codegen.base.ComponentCreatorAnnotation;
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
@@ -103,23 +103,25 @@
             "final class DaggerC implements C {",
             "  @Override",
             "  public Sub.Builder sBuilder() {",
-            "    return new SubBuilder();",
+            "    return new SubBuilder(c);",
             "  }",
             "",
             "  @Override",
             "  public UsesSubcomponent usesSubcomponent() {",
-            "    return new UsesSubcomponent(new SubBuilder());",
+            "    return new UsesSubcomponent(new SubBuilder(c));",
             "  }",
             "",
-            "  private final class SubBuilder implements Sub.Builder {",
+            "  private static final class SubBuilder implements Sub.Builder {",
             "    @Override",
             "    public Sub build() {",
-            "      return new SubImpl();",
+            "      return new SubImpl(c);",
             "    }",
             "  }",
             "",
-            "  private final class SubImpl implements Sub {",
-            "    private SubImpl() {}",
+            "  private static final class SubImpl implements Sub {",
+            "    private SubImpl(DaggerC c) {",
+            "      this.c = c;",
+            "    }",
             "  }",
             "}");
 
diff --git a/javatests/dagger/internal/codegen/SubcomponentCreatorValidationTest.java b/javatests/dagger/internal/codegen/SubcomponentCreatorValidationTest.java
index 371253a..f9b6e58 100644
--- a/javatests/dagger/internal/codegen/SubcomponentCreatorValidationTest.java
+++ b/javatests/dagger/internal/codegen/SubcomponentCreatorValidationTest.java
@@ -19,18 +19,18 @@
 import static com.google.testing.compile.CompilationSubject.assertThat;
 import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
 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.base.ComponentCreatorAnnotation.SUBCOMPONENT_BUILDER;
+import static dagger.internal.codegen.base.ComponentCreatorAnnotation.SUBCOMPONENT_FACTORY;
+import static dagger.internal.codegen.base.ComponentCreatorKind.BUILDER;
+import static dagger.internal.codegen.base.ComponentCreatorKind.FACTORY;
+import static dagger.internal.codegen.base.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 dagger.internal.codegen.base.ComponentCreatorAnnotation;
 import java.util.Collection;
 import javax.tools.JavaFileObject;
 import org.junit.Test;
@@ -52,17 +52,19 @@
 
   @Test
   public void testRefSubcomponentAndSubCreatorFails() {
-    JavaFileObject componentFile = preprocessedJavaFile("test.ParentComponent",
-        "package test;",
-        "",
-        "import dagger.Component;",
-        "import javax.inject.Provider;",
-        "",
-        "@Component",
-        "interface ParentComponent {",
-        "  ChildComponent child();",
-        "  ChildComponent.Builder builder();",
-        "}");
+    JavaFileObject componentFile =
+        preprocessedJavaFile(
+            "test.ParentComponent",
+            "package test;",
+            "",
+            "import dagger.Component;",
+            "import javax.inject.Provider;",
+            "",
+            "@Component",
+            "interface ParentComponent {",
+            "  ChildComponent child();",
+            "  ChildComponent.Builder childComponentBuilder();",
+            "}");
     JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent",
         "package test;",
         "",
@@ -82,7 +84,7 @@
             String.format(
                 moreThanOneRefToSubcomponent(),
                 "test.ChildComponent",
-                process("[child(), builder()]")))
+                process("[child(), childComponentBuilder()]")))
         .inFile(componentFile);
   }
 
@@ -244,16 +246,18 @@
 
   @Test
   public void testCreatorMissingFactoryMethodFails() {
-    JavaFileObject componentFile = preprocessedJavaFile("test.ParentComponent",
-        "package test;",
-        "",
-        "import dagger.Component;",
-        "import javax.inject.Provider;",
-        "",
-        "@Component",
-        "interface ParentComponent {",
-        "  ChildComponent.Builder builder();",
-        "}");
+    JavaFileObject componentFile =
+        preprocessedJavaFile(
+            "test.ParentComponent",
+            "package test;",
+            "",
+            "import dagger.Component;",
+            "import javax.inject.Provider;",
+            "",
+            "@Component",
+            "interface ParentComponent {",
+            "  ChildComponent.Builder childComponentBuilder();",
+            "}");
     JavaFileObject childComponentFile = preprocessedJavaFile("test.ChildComponent",
         "package test;",
         "",
@@ -726,8 +730,7 @@
                 "  interface Factory {",
                 "    ChildComponent create(String s, Integer i);",
                 "  }")
-            .addLines( //
-                "}")
+            .addLines("}")
             .build();
     Compilation compilation = compile(componentFile, childComponentFile);
     assertThat(compilation).failed();
diff --git a/javatests/dagger/internal/codegen/SubcomponentValidationTest.java b/javatests/dagger/internal/codegen/SubcomponentValidationTest.java
index c63522e..0a24043 100644
--- a/javatests/dagger/internal/codegen/SubcomponentValidationTest.java
+++ b/javatests/dagger/internal/codegen/SubcomponentValidationTest.java
@@ -222,7 +222,7 @@
     assertThat(compilation).failed();
     assertThat(compilation)
         .hadErrorContaining(
-            "A module may only occur once an an argument in a Subcomponent factory method, "
+            "A module may only occur once as an argument in a Subcomponent factory method, "
                 + "but test.TestModule was already passed.")
         .inFile(componentFile)
         .onLine(7)
@@ -456,7 +456,9 @@
                 "package test;",
                 "",
                 GeneratedLines.generatedAnnotations(),
-                "final class DaggerParentComponent implements ParentComponent {")
+                "final class DaggerParentComponent implements ParentComponent {",
+                "  private Provider<Dep1> dep1Provider;",
+                "  private Provider<Dep2> dep2Provider;")
             .addLinesIn(
                 DEFAULT_MODE,
                 "  @SuppressWarnings(\"unchecked\")",
@@ -465,54 +467,30 @@
                 "    this.dep2Provider = DoubleCheck.provider(Dep2_Factory.create());",
                 "  }",
                 "")
-            .addLines(
-                "  @Override", //
-                "  public Dep1 dep1() {")
             .addLinesIn(
                 FAST_INIT_MODE,
-                "   Object local = dep1;",
-                "    if (local instanceof MemoizedSentinel) {",
-                "      synchronized (local) {",
-                "        local = dep1;",
-                "        if (local instanceof MemoizedSentinel) {",
-                "          local = injectDep1(Dep1_Factory.newInstance());",
-                "          dep1 = DoubleCheck.reentrantCheck(dep1, local);",
-                "        }",
-                "      }",
-                "    }",
-                "    return (Dep1) local;")
-            .addLinesIn(
-                DEFAULT_MODE, //
-                "    return dep1Provider.get();")
+                "  @SuppressWarnings(\"unchecked\")",
+                "  private void initialize() {",
+                "    this.dep1Provider = DoubleCheck.provider(",
+                "        new SwitchingProvider<Dep1>(parentComponent, 0));",
+                "    this.dep2Provider = DoubleCheck.provider(",
+                "        new SwitchingProvider<Dep2>(parentComponent, 1));",
+                "  }")
             .addLines(
-                "  }", //
+                "  @Override",
+                "  public Dep1 dep1() {",
+                "    return dep1Provider.get();",
+                "  }",
                 "",
                 "  @Override",
-                "  public Dep2 dep2() {")
-            .addLinesIn(
-                FAST_INIT_MODE,
-                "   Object local = dep2;",
-                "    if (local instanceof MemoizedSentinel) {",
-                "      synchronized (local) {",
-                "        local = dep2;",
-                "        if (local instanceof MemoizedSentinel) {",
-                "          local = injectDep2(Dep2_Factory.newInstance());",
-                "          dep2 = DoubleCheck.reentrantCheck(dep2, local);",
-                "        }",
-                "      }",
-                "    }",
-                "    return (Dep2) local;")
-            .addLinesIn(
-                DEFAULT_MODE, //
-                "    return dep2Provider.get();")
-            .addLines(
+                "  public Dep2 dep2() {",
+                "    return dep2Provider.get();",
                 "  }",
                 "",
                 "  @Override",
                 "  public ChildComponent childComponent() {",
-                "    return new ChildComponentImpl();",
-                "  }",
-                "")
+                "    return new ChildComponentImpl(parentComponent);",
+                "  }")
             .addLinesIn(
                 FAST_INIT_MODE,
                 "  @CanIgnoreReturnValue",
@@ -527,38 +505,17 @@
                 "    return instance;",
                 "  }")
             .addLines(
-                "",
-                "  private final class ChildComponentImpl implements ChildComponent {",
-                "    private final ChildModule childModule;",
-                "",
-                "    private ChildComponentImpl() {",
-                "      this.childModule = new ChildModule();",
+                "  private static final class ChildComponentImpl implements ChildComponent {",
+                "    private NeedsDep1 needsDep1() {",
+                "      return new NeedsDep1(parentComponent.dep1Provider.get());",
                 "    }",
-                "")
-            .addLinesIn(
-                DEFAULT_MODE,
-                "    private NeedsDep1 needsDep1() {",
-                "      return new NeedsDep1(DaggerParentComponent.this.dep1Provider.get());",
-                "    }")
-            .addLinesIn(
-                FAST_INIT_MODE,
-                "    private NeedsDep1 needsDep1() {",
-                "      return new NeedsDep1(DaggerParentComponent.this.dep1());",
-                "    }")
-            .addLines(
+                "",
                 "    private A a() {",
                 "      return injectA(",
                 "          A_Factory.newInstance(",
-                "              needsDep1(),")
-            .addLinesIn(
-                DEFAULT_MODE,
-                "              DaggerParentComponent.this.dep1Provider.get(),",
-                "              DaggerParentComponent.this.dep2Provider.get()));")
-            .addLinesIn(
-                FAST_INIT_MODE,
-                "              DaggerParentComponent.this.dep1(),",
-                "              DaggerParentComponent.this.dep2()));")
-            .addLines(
+                "          needsDep1(),",
+                "          parentComponent.dep1Provider.get(),",
+                "          parentComponent.dep2Provider.get()));",
                 "    }",
                 "",
                 "    @Override",
@@ -572,6 +529,21 @@
                 "      A_MembersInjector.injectMethodA(instance);",
                 "      return instance;",
                 "    }",
+                "  }")
+            .addLinesIn(
+                FAST_INIT_MODE,
+                "  private static final class SwitchingProvider<T> implements Provider<T> {",
+                "    @SuppressWarnings(\"unchecked\")",
+                "    @Override",
+                "    public T get() {",
+                "      switch (id) {",
+                "        case 0:",
+                "          return (T) parentComponent.injectDep1(Dep1_Factory.newInstance());",
+                "        case 1:",
+                "          return (T) parentComponent.injectDep2(Dep2_Factory.newInstance());",
+                "        default: throw new AssertionError(id);",
+                "      }",
+                "    }",
                 "  }",
                 "}")
             .build();
@@ -658,12 +630,12 @@
             "final class DaggerParentComponent implements ParentComponent {",
             "  @Override",
             "  public Foo.Sub newInstanceSubcomponent() {",
-            "    return new F_SubImpl();",
+            "    return new F_SubImpl(parentComponent);",
             "  }",
             "",
             "  @Override",
             "  public NoConflict newNoConflictSubcomponent() {",
-            "    return new NoConflictImpl();",
+            "    return new NoConflictImpl(parentComponent);",
             "  }",
             "",
             "  static final class Builder {",
@@ -672,23 +644,13 @@
             "    }",
             "  }",
             "",
-            "  private final class F_SubImpl implements Foo.Sub {",
-            "    @Override",
-            "    public Bar.Sub newBarSubcomponent() {",
-            "      return new B_SubImpl();",
-            "    }",
+            "  private static final class ts_SubImpl implements Sub {}",
             "",
-            "    private final class B_SubImpl implements Bar.Sub {",
-            "      @Override",
-            "      public Sub newSubcomponentInSubpackage() {",
-            "        return new ts_SubI();",
-            "      }",
+            "  private static final class B_SubImpl implements Bar.Sub {}",
             "",
-            "      private final class ts_SubI implements Sub {}",
-            "    }",
-            "  }",
+            "  private static final class F_SubImpl implements Foo.Sub {}",
             "",
-            "  private final class NoConflictImpl implements NoConflict {}",
+            "  private static final class NoConflictImpl implements NoConflict {}",
             "}");
     Compilation compilation =
         compilerWithOptions(compilerMode.javacopts())
@@ -740,7 +702,7 @@
             "final class DaggerParentComponent implements ParentComponent {",
             "  @Override",
             "  public Sub newSubcomponent() {",
-            "    return new t_SubImpl();",
+            "    return new t_SubImpl(parentComponent);",
             "  }",
             "",
             "  static final class Builder {",
@@ -749,17 +711,10 @@
             "    }",
             "  }",
             "",
-            "  private final class t_SubImpl implements Sub {",
-            "    @Override",
-            "    public test.deep.many.levels.that.match.test.Sub newDeepSubcomponent() {",
-            "      return new tdmltmt_SubImpl();",
-            "    }",
+            "  private static final class tdmltmt_SubImpl",
+            "      implements test.deep.many.levels.that.match.test.Sub {}",
             "",
-            "    private final class tdmltmt_SubImpl implements ",
-            "        test.deep.many.levels.that.match.test.Sub {",
-            "      private tdmltmt_SubImpl() {}",
-            "    }",
-            "  }",
+            "  private static final class t_SubImpl implements Sub {}",
             "}");
     Compilation compilation =
         compilerWithOptions(compilerMode.javacopts()).compile(parent, sub, deepSub);
@@ -805,22 +760,13 @@
             "final class DaggerParentComponent implements ParentComponent {",
             "  @Override",
             "  public Sub newSubcomponent() {",
-            "    return new $_SubImpl();",
+            "    return new $_SubImpl(parentComponent);",
             "  }",
             "",
-            "  private final class $_SubImpl implements Sub {",
-            "    private $_SubImpl() {}",
+            "  private static final class tdmltmt_SubImpl",
+            "      implements test.deep.many.levels.that.match.test.Sub {}",
             "",
-            "    @Override",
-            "    public test.deep.many.levels.that.match.test.Sub newDeepSubcomponent() {",
-            "      return new tdmltmt_SubImpl();",
-            "    }",
-            "",
-            "    private final class tdmltmt_SubImpl implements ",
-            "        test.deep.many.levels.that.match.test.Sub {",
-            "      private tdmltmt_SubImpl() {}",
-            "    }",
-            "  }",
+            "  private static final class $_SubImpl implements Sub {}",
             "}",
             "");
 
@@ -882,19 +828,23 @@
             "final class DaggerParentComponent implements ParentComponent {",
             "  @Override",
             "  public E.F.Sub top1() {",
-            "    return new F_SubImpl();",
+            "    return new F_SubImpl(parentComponent);",
             "  }",
             "",
             "  @Override",
             "  public top2.a.b.c.d.E.F.Sub top2() {",
-            "    return new F2_SubImpl();",
+            "    return new F2_SubImpl(parentComponent);",
             "  }",
             "",
-            "  private final class F_SubImpl implements E.F.Sub {",
-            "    private F_SubImpl() {}",
+            "  private static final class F_SubImpl implements E.F.Sub {",
+            "    private F_SubImpl(DaggerParentComponent parentComponent) {",
+            "      this.parentComponent = parentComponent;",
+            "    }",
             "  }",
-            "  private final class F2_SubImpl implements top2.a.b.c.d.E.F.Sub {",
-            "    private F2_SubImpl() {}",
+            "  private static final class F2_SubImpl implements top2.a.b.c.d.E.F.Sub {",
+            "    private F2_SubImpl(DaggerParentComponent parentComponent) {",
+            "      this.parentComponent = parentComponent;",
+            "    }",
             "  }",
             "}");
 
@@ -939,10 +889,10 @@
             "final class DaggerC implements C {",
             "  @Override",
             "  public Foo.C newInstanceC() {",
-            "    return new F_CImpl();",
+            "    return new F_CImpl(c);",
             "  }",
             "",
-            "  private final class F_CImpl implements Foo.C {}",
+            "  private static final class F_CImpl implements Foo.C {}",
             "}");
 
     Compilation compilation =
@@ -1000,36 +950,40 @@
             "final class DaggerC implements C {",
             "  @Override",
             "  public C.Foo.Sub.Builder fooBuilder() {",
-            "    return new F_SubBuilder();",
+            "    return new F_SubBuilder(c);",
             "  }",
             "",
             "  @Override",
             "  public C.Bar.Sub.Builder barBuilder() {",
-            "    return new B_SubBuilder();",
+            "    return new B_SubBuilder(c);",
             "  }",
             "",
             // TODO(bcorso): Reverse the order of subcomponent and builder so that subcomponent
             // comes first.
-            "  private final class F_SubBuilder implements C.Foo.Sub.Builder {",
+            "  private static final class F_SubBuilder implements C.Foo.Sub.Builder {",
             "    @Override",
             "    public C.Foo.Sub build() {",
-            "      return new F_SubImpl();",
+            "      return new F_SubImpl(c);",
             "    }",
             "  }",
             "",
-            "  private final class F_SubImpl implements C.Foo.Sub {",
-            "    private F_SubImpl() {}",
-            "  }",
-            "",
-            "  private final class B_SubBuilder implements C.Bar.Sub.Builder {",
+            "  private static final class B_SubBuilder implements C.Bar.Sub.Builder {",
             "    @Override",
             "    public C.Bar.Sub build() {",
-            "      return new B_SubImpl();",
+            "      return new B_SubImpl(c);",
             "    }",
             "  }",
             "",
-            "  private final class B_SubImpl implements C.Bar.Sub {",
-            "    private B_SubImpl() {}",
+            "  private static final class F_SubImpl implements C.Foo.Sub {",
+            "    private F_SubImpl(DaggerC c) {",
+            "      this.c = c;",
+            "    }",
+            "  }",
+            "",
+            "  private static final class B_SubImpl implements C.Bar.Sub {",
+            "    private B_SubImpl(DaggerC c) {",
+            "      this.c = c;",
+            "    }",
             "  }",
             "}");
     Compilation compilation =
diff --git a/javatests/dagger/internal/codegen/SwitchingProviderTest.java b/javatests/dagger/internal/codegen/SwitchingProviderTest.java
index 615b09d..fb0f7e5 100644
--- a/javatests/dagger/internal/codegen/SwitchingProviderTest.java
+++ b/javatests/dagger/internal/codegen/SwitchingProviderTest.java
@@ -17,7 +17,7 @@
 package dagger.internal.codegen;
 
 import static com.google.testing.compile.CompilationSubject.assertThat;
-import static com.google.testing.compile.Compiler.javac;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
 
 import com.google.common.collect.ImmutableList;
 import com.google.testing.compile.Compilation;
@@ -69,7 +69,7 @@
                 "package test;",
             GeneratedLines.generatedAnnotations(),
                 "final class DaggerTestComponent implements TestComponent {",
-                "  private final class SwitchingProvider<T> implements Provider<T> {",
+                "  private static final class SwitchingProvider<T> implements Provider<T> {",
                 "    @SuppressWarnings(\"unchecked\")",
                 "    private T get0() {",
                 "      switch (id) {",
@@ -249,36 +249,30 @@
                 "",
                 GeneratedLines.generatedAnnotations(),
                 "final class DaggerTestComponent implements TestComponent {",
-                "  private volatile Provider<String> sProvider;",
+                "  private Provider<String> sProvider;",
                 "",
-                "  private Provider<String> stringProvider() {",
-                "    Object local = sProvider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(0);",
-                "      sProvider = (Provider<String>) local;",
-                "    }",
-                "    return (Provider<String>) local;",
+                "  @SuppressWarnings(\"unchecked\")",
+                "  private void initialize() {",
+                "    this.sProvider = new SwitchingProvider<>(testComponent, 0);",
                 "  }",
                 "",
                 "  @Override",
                 "  public Provider<Object> objectProvider() {",
-                "    return (Provider) stringProvider();",
+                "    return ((Provider) sProvider);",
                 "  }",
                 "",
                 "  @Override",
                 "  public Provider<CharSequence> charSequenceProvider() {",
-                "    return (Provider) stringProvider();",
+                "    return ((Provider) sProvider);",
                 "  }",
                 "",
-                "  private final class SwitchingProvider<T> implements Provider<T> {",
+                "  private static final class SwitchingProvider<T> implements Provider<T> {",
                 "    @SuppressWarnings(\"unchecked\")",
                 "    @Override",
                 "    public T get() {",
                 "      switch (id) {",
-                "        case 0:",
-                "          return (T) TestModule_SFactory.s();",
-                "        default:",
-                "          throw new AssertionError(id);",
+                "        case 0: return (T) TestModule_SFactory.s();",
+                "        default: throw new AssertionError(id);",
                 "      }",
                 "    }",
                 "  }",
@@ -334,47 +328,32 @@
                 "",
                 GeneratedLines.generatedAnnotations(),
                 "final class DaggerTestComponent implements TestComponent {",
-                "  private volatile Object charSequence = new MemoizedSentinel();",
-                "  private volatile Provider<CharSequence> cProvider;",
+                "  private Provider<String> sProvider;",
+                "  private Provider<CharSequence> cProvider;",
                 "",
-                "  private CharSequence charSequence() {",
-                "    Object local = charSequence;",
-                "    if (local instanceof MemoizedSentinel) {",
-                "      synchronized (local) {",
-                "        local = charSequence;",
-                "        if (local instanceof MemoizedSentinel) {",
-                "          local = TestModule_SFactory.s();",
-                "          charSequence = DoubleCheck.reentrantCheck(charSequence, local);",
-                "        }",
-                "      }",
-                "    }",
-                "    return (CharSequence) local;",
+                "  @SuppressWarnings(\"unchecked\")",
+                "  private void initialize() {",
+                "    this.sProvider = new SwitchingProvider<>(testComponent, 0);",
+                "    this.cProvider = DoubleCheck.provider((Provider) sProvider);",
                 "  }",
                 "",
                 "  @Override",
                 "  public Provider<Object> objectProvider() {",
-                "    return (Provider) charSequenceProvider();",
+                "    return ((Provider) cProvider);",
                 "  }",
                 "",
                 "  @Override",
                 "  public Provider<CharSequence> charSequenceProvider() {",
-                "    Object local = cProvider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(0);",
-                "      cProvider = (Provider<CharSequence>) local;",
-                "    }",
-                "    return (Provider<CharSequence>) local;",
+                "    return cProvider;",
                 "  }",
                 "",
-                "  private final class SwitchingProvider<T> implements Provider<T> {",
+                "  private static final class SwitchingProvider<T> implements Provider<T> {",
                 "    @SuppressWarnings(\"unchecked\")",
                 "    @Override",
                 "    public T get() {",
                 "      switch (id) {",
-                "        case 0:",
-                "          return (T) DaggerTestComponent.this.charSequence();",
-                "        default:",
-                "          throw new AssertionError(id);",
+                "        case 0: return (T) TestModule_SFactory.s();",
+                "        default: throw new AssertionError(id);",
                 "      }",
                 "    }",
                 "  }",
@@ -544,24 +523,18 @@
                 "  @SuppressWarnings(\"rawtypes\")",
                 "  private static final Provider ABSENT_JDK_OPTIONAL_PROVIDER =",
                 "      InstanceFactory.create(Optional.empty());",
-                "",
-                "  private volatile Provider<Optional<Present>> optionalOfPresentProvider;",
-                "",
+                "  private Provider<Optional<Present>> optionalOfPresentProvider;",
                 "  private Provider<Optional<Absent>> optionalOfAbsentProvider;",
                 "",
                 "  @SuppressWarnings(\"unchecked\")",
                 "  private void initialize() {",
+                "    this.optionalOfPresentProvider = new SwitchingProvider<>(testComponent, 0);",
                 "    this.optionalOfAbsentProvider = absentJdkOptionalProvider();",
                 "  }",
                 "",
                 "  @Override",
                 "  public Provider<Optional<Present>> providerOfOptionalOfPresent() {",
-                "    Object local = optionalOfPresentProvider;",
-                "    if (local == null) {",
-                "      local = new SwitchingProvider<>(0);",
-                "      optionalOfPresentProvider = (Provider<Optional<Present>>) local;",
-                "    }",
-                "    return (Provider<Optional<Present>>) local;",
+                "    return optionalOfPresentProvider;",
                 "  }",
                 "",
                 "  @Override",
@@ -571,20 +544,18 @@
                 "",
                 "  private static <T> Provider<Optional<T>> absentJdkOptionalProvider() {",
                 "    @SuppressWarnings(\"unchecked\")",
-                "    Provider<Optional<T>> provider = ",
-                "          (Provider<Optional<T>>) ABSENT_JDK_OPTIONAL_PROVIDER;",
+                "    Provider<Optional<T>> provider =",
+                "        (Provider<Optional<T>>) ABSENT_JDK_OPTIONAL_PROVIDER;",
                 "    return provider;",
                 "  }",
                 "",
-                "  private final class SwitchingProvider<T> implements Provider<T> {",
+                "  private static final class SwitchingProvider<T> implements Provider<T> {",
                 "    @SuppressWarnings(\"unchecked\")",
                 "    @Override",
                 "    public T get() {",
                 "      switch (id) {",
-                "        case 0: // java.util.Optional<test.Present>",
-                "          return (T) Optional.of(TestModule_PFactory.p());",
-                "        default:",
-                "          throw new AssertionError(id);",
+                "        case 0: return (T) Optional.of(TestModule_PFactory.p());",
+                "        default: throw new AssertionError(id);",
                 "      }",
                 "    }",
                 "  }",
@@ -592,8 +563,6 @@
   }
 
   private Compiler compilerWithAndroidMode() {
-    return javac()
-        .withProcessors(new ComponentProcessor())
-        .withOptions(CompilerMode.FAST_INIT_MODE.javacopts());
+    return compilerWithOptions(CompilerMode.FAST_INIT_MODE.javacopts());
   }
 }
diff --git a/javatests/dagger/internal/codegen/UnresolvableDependencyTest.java b/javatests/dagger/internal/codegen/UnresolvableDependencyTest.java
new file mode 100644
index 0000000..4a2ff98
--- /dev/null
+++ b/javatests/dagger/internal/codegen/UnresolvableDependencyTest.java
@@ -0,0 +1,231 @@
+/*
+ * 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.common.truth.Truth.assertThat;
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static dagger.internal.codegen.Compilers.compilerWithOptions;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
+import static java.util.stream.Collectors.joining;
+
+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 UnresolvableDependencyTest {
+
+  @Test
+  public void referencesUnresolvableDependency() {
+    JavaFileObject fooComponent =
+        JavaFileObjects.forSourceLines(
+            "test.FooComponent",
+            "package test;",
+            "",
+            "import dagger.Component;",
+            "",
+            "@Component",
+            "interface FooComponent {",
+            "  Foo foo();",
+            "}");
+
+    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 javax.inject.Inject;",
+            "",
+            "class Bar {",
+            "  @Inject",
+            "  Bar(UnresolvableDependency dep) {}",
+            "}");
+
+    Compilation compilation = daggerCompiler().compile(fooComponent, foo, bar);
+    assertThat(compilation).failed();
+    assertThat(compilation).hadErrorCount(3);
+    assertThat(compilation).hadErrorContaining(
+        "cannot find symbol"
+            + "\n  symbol:   class UnresolvableDependency"
+            + "\n  location: class test.Bar");
+    String trace = "\n  "
+        + "\n  Dependency trace:"
+        + "\n      => element (CLASS): test.Bar"
+        + "\n      => element (CONSTRUCTOR): Bar(UnresolvableDependency)"
+        + "\n      => type (EXECUTABLE constructor): (UnresolvableDependency)void"
+        + "\n      => type (ERROR parameter type): UnresolvableDependency";
+    assertThat(compilation).hadErrorContaining(
+        "InjectProcessingStep was unable to process 'Bar(UnresolvableDependency)' because "
+            + "'UnresolvableDependency' could not be resolved." + trace);
+    assertThat(compilation).hadErrorContaining(
+        "ComponentProcessingStep was unable to process 'test.FooComponent' because "
+            + "'UnresolvableDependency' could not be resolved." + trace);
+
+    // Only include a minimal portion of the stacktrace to minimize breaking tests due to refactors.
+    String stacktraceErrorMessage =
+        "dagger.internal.codegen.base"
+            + ".DaggerSuperficialValidation$ValidationException$KnownErrorType";
+
+    // Check that the stacktrace is not included in the error message by default.
+    assertThat(
+            compilation.errors().stream()
+                .map(error -> error.getMessage(null))
+                .collect(joining("\n")))
+        .doesNotContain(stacktraceErrorMessage);
+
+    // Recompile with the option enabled and check that the stacktrace is now included
+    compilation =
+        compilerWithOptions("-Adagger.includeStacktraceWithDeferredErrorMessages=ENABLED")
+            .compile(fooComponent, foo, bar);
+    assertThat(compilation).failed();
+    assertThat(compilation).hadErrorCount(3);
+    assertThat(compilation).hadErrorContaining(stacktraceErrorMessage);
+  }
+
+  @Test
+  public void referencesUnresolvableAnnotationOnType() {
+    JavaFileObject fooComponent =
+        JavaFileObjects.forSourceLines(
+            "test.FooComponent",
+            "package test;",
+            "",
+            "import dagger.Component;",
+            "",
+            "@Component",
+            "interface FooComponent {",
+            "  Foo foo();",
+            "}");
+
+    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 javax.inject.Inject;",
+            "",
+            "@UnresolvableAnnotation",
+            "class Bar {",
+            "  @Inject",
+            "  Bar(String dep) {}",
+            "}");
+
+    Compilation compilation = daggerCompiler().compile(fooComponent, foo, bar);
+    assertThat(compilation).failed();
+    assertThat(compilation).hadErrorCount(3);
+    assertThat(compilation).hadErrorContaining(
+        "cannot find symbol"
+            + "\n  symbol: class UnresolvableAnnotation");
+    String trace = "\n  "
+        + "\n  Dependency trace:"
+        + "\n      => element (CLASS): test.Bar"
+        + "\n      => annotation: @UnresolvableAnnotation"
+        + "\n      => type (ERROR annotation type): UnresolvableAnnotation";
+    assertThat(compilation).hadErrorContaining(
+        "InjectProcessingStep was unable to process 'Bar(java.lang.String)' because "
+            + "'UnresolvableAnnotation' could not be resolved." + trace);
+    assertThat(compilation).hadErrorContaining(
+        "ComponentProcessingStep was unable to process 'test.FooComponent' because "
+            + "'UnresolvableAnnotation' could not be resolved." + trace);
+  }
+
+  @Test
+  public void referencesUnresolvableAnnotationOnTypeOnParameter() {
+    JavaFileObject fooComponent =
+        JavaFileObjects.forSourceLines(
+            "test.FooComponent",
+            "package test;",
+            "",
+            "import dagger.Component;",
+            "",
+            "@Component",
+            "interface FooComponent {",
+            "  Foo foo();",
+            "}");
+
+    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 javax.inject.Inject;",
+            "",
+            "class Bar {",
+            "  @Inject",
+            "  Bar(@UnresolvableAnnotation String dep) {}",
+            "}");
+
+    Compilation compilation = daggerCompiler().compile(fooComponent, foo, bar);
+    assertThat(compilation).failed();
+    assertThat(compilation).hadErrorCount(3);
+    assertThat(compilation).hadErrorContaining(
+        "cannot find symbol"
+            + "\n  symbol:   class UnresolvableAnnotation"
+            + "\n  location: class test.Bar");
+    String trace = "\n  "
+        + "\n  Dependency trace:"
+        + "\n      => element (CLASS): test.Bar"
+        + "\n      => element (CONSTRUCTOR): Bar(java.lang.String)"
+        + "\n      => element (PARAMETER): dep"
+        + "\n      => annotation: @UnresolvableAnnotation"
+        + "\n      => type (ERROR annotation type): UnresolvableAnnotation";
+    assertThat(compilation).hadErrorContaining(
+        "InjectProcessingStep was unable to process 'Bar(java.lang.String)' because "
+            + "'UnresolvableAnnotation' could not be resolved." + trace);
+    assertThat(compilation).hadErrorContaining(
+        "ComponentProcessingStep was unable to process 'test.FooComponent' because "
+            + "'UnresolvableAnnotation' could not be resolved." + trace);
+  }
+}
diff --git a/javatests/dagger/internal/codegen/ValidationReportTest.java b/javatests/dagger/internal/codegen/ValidationReportTest.java
index d1c3a2e..0e49d30 100644
--- a/javatests/dagger/internal/codegen/ValidationReportTest.java
+++ b/javatests/dagger/internal/codegen/ValidationReportTest.java
@@ -18,15 +18,17 @@
 
 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 static dagger.internal.codegen.Compilers.daggerCompiler;
 
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XTypeElement;
 import com.google.common.collect.ImmutableSet;
 import com.google.testing.compile.Compilation;
 import com.google.testing.compile.JavaFileObjects;
 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.Processor;
 import javax.annotation.processing.RoundEnvironment;
 import javax.lang.model.element.TypeElement;
 import javax.tools.JavaFileObject;
@@ -37,45 +39,42 @@
 @RunWith(JUnit4.class)
 public class ValidationReportTest {
   private static final JavaFileObject TEST_CLASS_FILE =
-      JavaFileObjects.forSourceLines("test.TestClass",
+      JavaFileObjects.forSourceLines(
+          "test.TestClass",
           "package test;",
           "",
           "final class TestClass {}");
 
   @Test
   public void basicReport() {
-    Compilation compilation =
-        javac()
-            .withProcessors(
-                new SimpleTestProcessor() {
-                  @Override
-                  void test() {
-                    Builder<TypeElement> reportBuilder =
-                        ValidationReport.about(getTypeElement("test.TestClass"));
-                    reportBuilder.addError("simple error");
-                    reportBuilder.build().printMessagesTo(processingEnv.getMessager());
-                  }
-                })
-            .compile(TEST_CLASS_FILE);
+    Processor processor =
+        new SimpleTestProcessor() {
+          @Override
+          void test() {
+            ValidationReport.about(getTypeElement("test.TestClass"))
+                .addError("simple error")
+                .build()
+                .printMessagesTo(processingEnv.getMessager());
+          }
+        };
+    Compilation compilation = daggerCompiler(processor).compile(TEST_CLASS_FILE);
     assertThat(compilation).failed();
     assertThat(compilation).hadErrorContaining("simple error").inFile(TEST_CLASS_FILE).onLine(3);
   }
 
   @Test
   public void messageOnDifferentElement() {
-    Compilation compilation =
-        javac()
-            .withProcessors(
-                new SimpleTestProcessor() {
-                  @Override
-                  void test() {
-                    Builder<TypeElement> reportBuilder =
-                        ValidationReport.about(getTypeElement("test.TestClass"));
-                    reportBuilder.addError("simple error", getTypeElement(String.class));
-                    reportBuilder.build().printMessagesTo(processingEnv.getMessager());
-                  }
-                })
-            .compile(TEST_CLASS_FILE);
+    Processor processor =
+        new SimpleTestProcessor() {
+          @Override
+          void test() {
+            ValidationReport.about(getTypeElement("test.TestClass"))
+                .addError("simple error", getTypeElement(String.class))
+                .build()
+                .printMessagesTo(processingEnv.getMessager());
+          }
+        };
+    Compilation compilation = daggerCompiler(processor).compile(TEST_CLASS_FILE);
     assertThat(compilation).failed();
     assertThat(compilation)
         .hadErrorContaining("[java.lang.String] simple error")
@@ -85,29 +84,30 @@
 
   @Test
   public void subreport() {
-    Compilation compilation =
-        javac()
-            .withProcessors(
-                new SimpleTestProcessor() {
-                  @Override
-                  void test() {
-                    Builder<TypeElement> reportBuilder =
-                        ValidationReport.about(getTypeElement("test.TestClass"));
-                    reportBuilder.addError("simple error");
-                    ValidationReport<TypeElement> parentReport =
-                        ValidationReport.about(getTypeElement(String.class))
-                            .addSubreport(reportBuilder.build())
-                            .build();
-                    assertThat(parentReport.isClean()).isFalse();
-                    parentReport.printMessagesTo(processingEnv.getMessager());
-                  }
-                })
-            .compile(TEST_CLASS_FILE);
+    Processor processor =
+        new SimpleTestProcessor() {
+          @Override
+          void test() {
+            ValidationReport parentReport =
+                ValidationReport.about(getTypeElement(String.class))
+                    .addSubreport(
+                        ValidationReport.about(getTypeElement("test.TestClass"))
+                            .addError("simple error")
+                            .build())
+                    .build();
+            assertThat(parentReport.isClean()).isFalse();
+            parentReport.printMessagesTo(processingEnv.getMessager());
+          }
+        };
+    Compilation compilation = daggerCompiler(processor).compile(TEST_CLASS_FILE);
     assertThat(compilation).failed();
     assertThat(compilation).hadErrorContaining("simple error").inFile(TEST_CLASS_FILE).onLine(3);
   }
 
-  private static abstract class SimpleTestProcessor extends AbstractProcessor {
+  private abstract static class SimpleTestProcessor extends AbstractProcessor {
+    @SuppressWarnings("HidingField") // Subclasses should always use the XProcessing version.
+    protected XProcessingEnv processingEnv;
+
     @Override
     public Set<String> getSupportedAnnotationTypes() {
       return ImmutableSet.of("*");
@@ -115,16 +115,17 @@
 
     @Override
     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+      processingEnv = XProcessingEnv.create(super.processingEnv);
       test();
       return false;
     }
 
-    protected final TypeElement getTypeElement(Class<?> clazz) {
+    protected final XTypeElement getTypeElement(Class<?> clazz) {
       return getTypeElement(clazz.getCanonicalName());
     }
 
-    protected final TypeElement getTypeElement(String canonicalName) {
-      return processingEnv.getElementUtils().getTypeElement(canonicalName);
+    protected final XTypeElement getTypeElement(String canonicalName) {
+      return processingEnv.requireTypeElement(canonicalName);
     }
 
     abstract void test();
diff --git a/javatests/dagger/internal/codegen/bindinggraphvalidation/BUILD b/javatests/dagger/internal/codegen/bindinggraphvalidation/BUILD
index 551e091..9e7b1a4 100644
--- a/javatests/dagger/internal/codegen/bindinggraphvalidation/BUILD
+++ b/javatests/dagger/internal/codegen/bindinggraphvalidation/BUILD
@@ -28,10 +28,10 @@
     deps = [
         "//java/dagger/internal/codegen/bindinggraphvalidation",
         "//javatests/dagger/internal/codegen:compilers",
-        "@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",
+        "//third_party/java/auto:common",
+        "//third_party/java/compile_testing",
+        "//third_party/java/javapoet",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
 )
diff --git a/javatests/dagger/internal/codegen/bindinggraphvalidation/SetMultibindingValidationTest.java b/javatests/dagger/internal/codegen/bindinggraphvalidation/SetMultibindingValidationTest.java
new file mode 100644
index 0000000..bc8b0c6
--- /dev/null
+++ b/javatests/dagger/internal/codegen/bindinggraphvalidation/SetMultibindingValidationTest.java
@@ -0,0 +1,291 @@
+/*
+ * 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.bindinggraphvalidation;
+
+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 class SetMultibindingValidationTest {
+  private static final JavaFileObject FOO =
+      JavaFileObjects.forSourceLines(
+          "test.Foo",
+          "package test;",
+          "",
+          "public interface Foo {}");
+
+  private static final JavaFileObject FOO_IMPL =
+      JavaFileObjects.forSourceLines(
+          "test.FooImpl",
+          "package test;",
+          "",
+          "import javax.inject.Inject;",
+          "",
+          "public final class FooImpl implements Foo {",
+          "  @Inject FooImpl() {}",
+          "}");
+
+  @Test public void testMultipleSetBindingsToSameFoo() {
+    JavaFileObject module = JavaFileObjects.forSourceLines("test.TestModule",
+        "package test;",
+        "",
+        "import dagger.Binds;",
+        "import dagger.multibindings.IntoSet;",
+        "import javax.inject.Inject;",
+        "",
+        "@dagger.Module",
+        "interface TestModule {",
+        "  @Binds @IntoSet Foo bindFoo(FooImpl impl);",
+        "",
+        "  @Binds @IntoSet Foo bindFooAgain(FooImpl impl);",
+        "}");
+    JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
+        "package test;",
+        "",
+        "import dagger.Component;",
+        "import java.util.Set;",
+        "",
+        "@Component(modules = TestModule.class)",
+        "interface TestComponent {",
+        "  Set<Foo> setOfFoo();",
+        "}");
+    Compilation compilation = daggerCompiler().compile(FOO, FOO_IMPL, module, component);
+    assertThat(compilation).failed();
+    assertThat(compilation)
+        .hadErrorContaining(
+            "Multiple set contributions into Set<Foo> for the same contribution key: FooImpl");
+  }
+
+  @Test public void testMultipleSetBindingsToSameFooThroughMultipleBinds() {
+    JavaFileObject module = JavaFileObjects.forSourceLines("test.TestModule",
+        "package test;",
+        "",
+        "import dagger.Binds;",
+        "import dagger.multibindings.IntoSet;",
+        "import javax.inject.Inject;",
+        "",
+        "@dagger.Module",
+        "interface TestModule {",
+        "  @Binds @IntoSet Object bindObject(FooImpl impl);",
+        "",
+        "  @Binds @IntoSet Object bindObjectAgain(Foo impl);",
+        "",
+        "  @Binds Foo bindFoo(FooImpl impl);",
+        "}");
+    JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
+        "package test;",
+        "",
+        "import dagger.Component;",
+        "import java.util.Set;",
+        "",
+        "@Component(modules = TestModule.class)",
+        "interface TestComponent {",
+        "  Set<Object> setOfObject();",
+        "}");
+    Compilation compilation = daggerCompiler().compile(FOO, FOO_IMPL, module, component);
+    assertThat(compilation).failed();
+    assertThat(compilation)
+        .hadErrorContaining(
+            "Multiple set contributions into Set<Object> for the same contribution key: FooImpl");
+  }
+
+  @Test public void testMultipleSetBindingsViaElementsIntoSet() {
+    JavaFileObject module = JavaFileObjects.forSourceLines("test.TestModule",
+        "package test;",
+        "",
+        "import dagger.Binds;",
+        "import dagger.Provides;",
+        "import dagger.multibindings.ElementsIntoSet;",
+        "import java.util.HashSet;",
+        "import java.util.Set;",
+        "import javax.inject.Inject;",
+        "import javax.inject.Qualifier;",
+        "",
+        "@dagger.Module",
+        "interface TestModule {",
+        "",
+        "  @Qualifier",
+        "  @interface Internal {}",
+        "",
+        "  @Provides @Internal static Set<Foo> provideSet() { return new HashSet<>(); }",
+        "",
+        "  @Binds @ElementsIntoSet Set<Foo> bindSet(@Internal Set<Foo> fooSet);",
+        "",
+        "  @Binds @ElementsIntoSet Set<Foo> bindSetAgain(@Internal Set<Foo> fooSet);",
+        "}");
+    JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
+        "package test;",
+        "",
+        "import dagger.Component;",
+        "import java.util.Set;",
+        "",
+        "@Component(modules = TestModule.class)",
+        "interface TestComponent {",
+        "  Set<Foo> setOfFoo();",
+        "}");
+    Compilation compilation = daggerCompiler().compile(FOO, module, component);
+    assertThat(compilation).failed();
+    assertThat(compilation)
+        .hadErrorContaining(
+            "Multiple set contributions into Set<Foo> for the same contribution key: "
+            + "@TestModule.Internal Set<Foo>");
+  }
+
+  @Test public void testMultipleSetBindingsToSameFooSubcomponents() {
+    JavaFileObject parentModule = JavaFileObjects.forSourceLines("test.ParentModule",
+        "package test;",
+        "",
+        "import dagger.Binds;",
+        "import dagger.multibindings.IntoSet;",
+        "import javax.inject.Inject;",
+        "",
+        "@dagger.Module",
+        "interface ParentModule {",
+        "  @Binds @IntoSet Foo bindFoo(FooImpl impl);",
+        "}");
+    JavaFileObject childModule = JavaFileObjects.forSourceLines("test.ChildModule",
+        "package test;",
+        "",
+        "import dagger.Binds;",
+        "import dagger.multibindings.IntoSet;",
+        "import javax.inject.Inject;",
+        "",
+        "@dagger.Module",
+        "interface ChildModule {",
+        "  @Binds @IntoSet Foo bindFoo(FooImpl impl);",
+        "}");
+    JavaFileObject parentComponent = JavaFileObjects.forSourceLines("test.ParentComponent",
+        "package test;",
+        "",
+        "import dagger.Component;",
+        "import java.util.Set;",
+        "",
+        "@Component(modules = ParentModule.class)",
+        "interface ParentComponent {",
+        "  Set<Foo> setOfFoo();",
+        "  ChildComponent child();",
+        "}");
+    JavaFileObject childComponent = JavaFileObjects.forSourceLines("test.ChildComponent",
+        "package test;",
+        "",
+        "import dagger.Subcomponent;",
+        "import java.util.Set;",
+        "",
+        "@Subcomponent(modules = ChildModule.class)",
+        "interface ChildComponent {",
+        "  Set<Foo> setOfFoo();",
+        "}");
+    Compilation compilation = daggerCompiler().compile(
+        FOO, FOO_IMPL, parentModule, childModule, parentComponent, childComponent);
+    assertThat(compilation).failed();
+    assertThat(compilation)
+        .hadErrorContaining(
+            "Multiple set contributions into Set<Foo> for the same contribution key: FooImpl");
+    assertThat(compilation)
+        .hadErrorContaining(
+            "ParentComponent → ChildComponent");
+  }
+
+  @Test public void testMultipleSetBindingsToSameKeyButDifferentBindings() {
+    // Use an impl with local multibindings to create different bindings. We still want this to fail
+    // even though there are separate bindings because it is likely an unintentional error anyway.
+    JavaFileObject fooImplWithMult = JavaFileObjects.forSourceLines("test.FooImplWithMult",
+        "package test;",
+        "",
+        "import java.util.Set;",
+        "import javax.inject.Inject;",
+        "",
+        "public final class FooImplWithMult implements Foo {",
+        "  @Inject FooImplWithMult(Set<Long> longSet) {}",
+        "}");
+    // Scoping the @Binds is necessary to ensure it goes to different bindings
+    JavaFileObject parentModule = JavaFileObjects.forSourceLines("test.ParentModule",
+        "package test;",
+        "",
+        "import dagger.Binds;",
+        "import dagger.Provides;",
+        "import dagger.multibindings.IntoSet;",
+        "import javax.inject.Inject;",
+        "import javax.inject.Singleton;",
+        "",
+        "@dagger.Module",
+        "interface ParentModule {",
+        "  @Singleton",
+        "  @Binds @IntoSet Foo bindFoo(FooImplWithMult impl);",
+        "",
+        "  @Provides @IntoSet static Long provideLong() {",
+        "    return 0L;",
+        "  }",
+        "}");
+    JavaFileObject childModule = JavaFileObjects.forSourceLines("test.ChildModule",
+        "package test;",
+        "",
+        "import dagger.Binds;",
+        "import dagger.Provides;",
+        "import dagger.multibindings.IntoSet;",
+        "import javax.inject.Inject;",
+        "",
+        "@dagger.Module",
+        "interface ChildModule {",
+        "  @Binds @IntoSet Foo bindFoo(FooImplWithMult impl);",
+        "",
+        "  @Provides @IntoSet static Long provideLong() {",
+        "    return 1L;",
+        "  }",
+        "}");
+    JavaFileObject parentComponent = JavaFileObjects.forSourceLines("test.ParentComponent",
+        "package test;",
+        "",
+        "import dagger.Component;",
+        "import java.util.Set;",
+        "import javax.inject.Singleton;",
+        "",
+        "@Singleton",
+        "@Component(modules = ParentModule.class)",
+        "interface ParentComponent {",
+        "  Set<Foo> setOfFoo();",
+        "  ChildComponent child();",
+        "}");
+    JavaFileObject childComponent = JavaFileObjects.forSourceLines("test.ChildComponent",
+        "package test;",
+        "",
+        "import dagger.Subcomponent;",
+        "import java.util.Set;",
+        "",
+        "@Subcomponent(modules = ChildModule.class)",
+        "interface ChildComponent {",
+        "  Set<Foo> setOfFoo();",
+        "}");
+    Compilation compilation = daggerCompiler().compile(
+        FOO, fooImplWithMult, parentModule, childModule, parentComponent, childComponent);
+    assertThat(compilation).failed();
+    assertThat(compilation)
+        .hadErrorContaining(
+            "Multiple set contributions into Set<Foo> for the same contribution key: "
+            + "FooImplWithMult");
+    assertThat(compilation)
+        .hadErrorContaining(
+            "ParentComponent → ChildComponent");
+  }
+}
diff --git a/javatests/dagger/internal/codegen/javapoet/BUILD b/javatests/dagger/internal/codegen/javapoet/BUILD
index 438d377..b108d6e 100644
--- a/javatests/dagger/internal/codegen/javapoet/BUILD
+++ b/javatests/dagger/internal/codegen/javapoet/BUILD
@@ -28,10 +28,10 @@
     deps = [
         "//java/dagger/internal/codegen/javapoet",
         "//java/dagger/internal/codegen/langmodel",
-        "@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",
+        "//third_party/java/auto:common",
+        "//third_party/java/compile_testing",
+        "//third_party/java/javapoet",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
 )
diff --git a/javatests/dagger/internal/codegen/javapoet/ExpressionTest.java b/javatests/dagger/internal/codegen/javapoet/ExpressionTest.java
index acfa460..8360a1b 100644
--- a/javatests/dagger/internal/codegen/javapoet/ExpressionTest.java
+++ b/javatests/dagger/internal/codegen/javapoet/ExpressionTest.java
@@ -18,8 +18,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import com.google.auto.common.MoreTypes;
 import com.google.testing.compile.CompilationRule;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.TypeName;
 import dagger.internal.codegen.langmodel.DaggerElements;
 import dagger.internal.codegen.langmodel.DaggerTypes;
 import javax.lang.model.type.PrimitiveType;
@@ -70,11 +71,10 @@
     Expression boxedExpression = primitiveExpression.box(types);
 
     assertThat(boxedExpression.codeBlock().toString()).isEqualTo("(java.lang.Integer) 5");
-    assertThat(MoreTypes.equivalence().equivalent(boxedExpression.type(), type(Integer.class)))
-        .isTrue();
+    assertThat(TypeName.get(boxedExpression.type())).isEqualTo(TypeName.get(type(Integer.class)));
   }
 
   private TypeMirror type(Class<?> clazz) {
-    return elements.getTypeElement(clazz).asType();
+    return elements.getTypeElement(ClassName.get(clazz)).asType();
   }
 }
diff --git a/javatests/dagger/internal/codegen/validation/BUILD b/javatests/dagger/internal/codegen/validation/BUILD
index 03fd684..6271768 100644
--- a/javatests/dagger/internal/codegen/validation/BUILD
+++ b/javatests/dagger/internal/codegen/validation/BUILD
@@ -27,7 +27,7 @@
     javacopts = DOCLINT_HTML_AND_SYNTAX,
     deps = [
         "//java/dagger/internal/codegen/validation",
-        "@google_bazel_common//third_party/java/junit",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
 )
diff --git a/javatests/dagger/lint/BUILD b/javatests/dagger/lint/BUILD
index 268340d..a2026c9 100644
--- a/javatests/dagger/lint/BUILD
+++ b/javatests/dagger/lint/BUILD
@@ -23,7 +23,7 @@
     name = "DaggerKotlinIssueDetectorTest",
     srcs = ["DaggerKotlinIssueDetectorTest.kt"],
     deps = [
-        "@google_bazel_common//third_party/java/junit",
+        "//third_party/java/junit",
         "//java/dagger/lint:lint-artifact-lib",
         "@maven//:com_android_tools_lint_lint_checks",
         "@maven//:com_android_tools_lint_lint_tests",
diff --git a/javatests/dagger/lint/DaggerKotlinIssueDetectorTest.kt b/javatests/dagger/lint/DaggerKotlinIssueDetectorTest.kt
index f112392..e197699 100644
--- a/javatests/dagger/lint/DaggerKotlinIssueDetectorTest.kt
+++ b/javatests/dagger/lint/DaggerKotlinIssueDetectorTest.kt
@@ -16,6 +16,7 @@
 package dagger.lint
 
 import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestMode
 import com.android.tools.lint.detector.api.Detector
 import com.android.tools.lint.detector.api.Issue
 import org.junit.Test
@@ -183,6 +184,8 @@
         ).indented()
       )
       .allowCompilationErrors(false)
+      // Unlikely that @JvmStatic would be aliased, so skipping these modes.
+      .skipTestModes(TestMode.TYPE_ALIAS, TestMode.IMPORT_ALIAS)
       .run()
       .expect(
         """
diff --git a/javatests/dagger/producers/BUILD b/javatests/dagger/producers/BUILD
index 9404d85..f8b098e 100644
--- a/javatests/dagger/producers/BUILD
+++ b/javatests/dagger/producers/BUILD
@@ -31,12 +31,12 @@
     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:testlib",
-        "@google_bazel_common//third_party/java/junit",
-        "@google_bazel_common//third_party/java/mockito",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/guava:testlib",
+        "//third_party/java/guava/collect",
+        "//third_party/java/guava/util/concurrent",
+        "//third_party/java/junit",
+        "//third_party/java/mockito",
+        "//third_party/java/truth",
     ],
 )
diff --git a/javatests/dagger/spi/BUILD b/javatests/dagger/spi/BUILD
index 849037b..ef1dca0 100644
--- a/javatests/dagger/spi/BUILD
+++ b/javatests/dagger/spi/BUILD
@@ -32,13 +32,13 @@
     deps = [
         "//java/dagger:core",
         "//java/dagger/internal/codegen:processor",
-        "//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/jsr330_inject",
-        "@google_bazel_common//third_party/java/junit",
-        "@google_bazel_common//third_party/java/truth",
+        "//third_party/java/auto:service",
+        "//third_party/java/compile_testing",
+        "//third_party/java/guava/base",
+        "//third_party/java/guava/collect",
+        "//third_party/java/jsr330_inject",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
     ],
 )
diff --git a/test_defs.bzl b/test_defs.bzl
index 6f23088..cdb9f51 100644
--- a/test_defs.bzl
+++ b/test_defs.bzl
@@ -19,7 +19,9 @@
 # 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 = {
+    "Shards": ["-Adagger.keysPerComponentShard=2"],
     "FastInit": ["-Adagger.fastInit=enabled"],
+    "FastInit_Shards": ["-Adagger.fastInit=enabled", "-Adagger.keysPerComponentShard=2"],
 }
 
 # TODO(ronshapiro): convert this to use bazel_common
@@ -106,6 +108,7 @@
     if functional:
         for (variant_name, extra_javacopts) in BUILD_VARIANTS.items():
             variant_javacopts = (javacopts or []) + extra_javacopts
+
             _gen_tests(
                 library_rule_type,
                 test_rule_type,
diff --git a/java/dagger/hilt/android/example/BUILD b/third_party/java/asm/BUILD
similarity index 63%
copy from java/dagger/hilt/android/example/BUILD
copy to third_party/java/asm/BUILD
index 2a4c194..bebdb12 100644
--- a/java/dagger/hilt/android/example/BUILD
+++ b/third_party/java/asm/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Dagger Authors.
+# 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.
@@ -11,17 +11,22 @@
 # WITHOUT 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.
+
+# BUILD rules for http://asm.ow2.org/
 
 package(default_visibility = ["//:src"])
 
-filegroup(
-    name = "srcs_filegroup",
-    srcs = glob(
-        ["**/*"],
-        # Exclude Gradle build folder to enable working along side Bazel
-        exclude = ["**/build/**"],
-    ),
+alias(
+    name = "asm",
+    actual = "@maven//:org_ow2_asm_asm",
+)
+
+alias(
+    name = "asm-tree",
+    actual = "@maven//:org_ow2_asm_asm_tree",
+)
+
+alias(
+    name = "asm-commons",
+    actual = "@maven//:org_ow2_asm_asm_commons",
 )
diff --git a/third_party/java/auto/BUILD b/third_party/java/auto/BUILD
new file mode 100644
index 0000000..7b90e28
--- /dev/null
+++ b/third_party/java/auto/BUILD
@@ -0,0 +1,113 @@
+# 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.
+
+# BUILD rules for https://github.com/google/auto
+
+load("@rules_java//java:defs.bzl", "java_library", "java_plugin")
+
+package(default_visibility = ["//:src"])
+
+alias(
+    name = "common",
+    actual = "@maven//:com_google_auto_auto_common",
+)
+
+java_plugin(
+    name = "auto_value_processor",
+    processor_class = "com.google.auto.value.processor.AutoValueProcessor",
+    visibility = ["//visibility:private"],
+    deps = [
+        ":common",
+        ":service",
+        "//third_party/java/guava",
+        "@maven//:com_google_auto_value_auto_value",
+    ],
+)
+
+java_plugin(
+    name = "auto_annotation_processor",
+    processor_class = "com.google.auto.value.processor.AutoAnnotationProcessor",
+    visibility = ["//visibility:private"],
+    deps = [
+        ":common",
+        ":service",
+        "//third_party/java/guava",
+        "@maven//:com_google_auto_value_auto_value",
+    ],
+)
+
+java_plugin(
+    name = "auto_oneof_processor",
+    processor_class = "com.google.auto.value.processor.AutoOneOfProcessor",
+    visibility = ["//visibility:private"],
+    deps = [
+        ":common",
+        ":service",
+        "//third_party/java/guava",
+        "@maven//:com_google_auto_value_auto_value",
+    ],
+)
+
+java_library(
+    name = "value",
+    exported_plugins = [
+        ":auto_annotation_processor",
+        ":auto_oneof_processor",
+        ":auto_value_processor",
+    ],
+    tags = ["maven:compile_only"],
+    exports = [
+        "@maven//:com_google_auto_value_auto_value_annotations",
+    ],
+)
+
+java_plugin(
+    name = "auto_factory_processor",
+    generates_api = 1,
+    processor_class = "com.google.auto.factory.processor.AutoFactoryProcessor",
+    visibility = ["//visibility:private"],
+    deps = [
+        ":common",
+        ":service",
+        "//third_party/java/google_java_format",
+        "//third_party/java/guava",
+        "//third_party/java/javapoet",
+        "@maven//:com_google_auto_factory_auto_factory",
+    ],
+)
+
+java_library(
+    name = "factory",
+    exported_plugins = [":auto_factory_processor"],
+    exports = ["@maven//:com_google_auto_factory_auto_factory"],
+)
+
+java_plugin(
+    name = "auto_service_processor",
+    processor_class = "com.google.auto.service.processor.AutoServiceProcessor",
+    visibility = ["//visibility:private"],
+    deps = [
+        ":common",
+        "//third_party/java/guava",
+        "@maven//:com_google_auto_service_auto_service",
+        "@maven//:com_google_auto_service_auto_service_annotations",
+    ],
+)
+
+java_library(
+    name = "service",
+    exported_plugins = [":auto_service_processor"],
+    tags = ["maven:compile_only"],
+    exports = ["@maven//:com_google_auto_service_auto_service_annotations"],
+)
diff --git a/java/dagger/hilt/android/example/BUILD b/third_party/java/byte_buddy/BUILD
similarity index 63%
copy from java/dagger/hilt/android/example/BUILD
copy to third_party/java/byte_buddy/BUILD
index 2a4c194..062e3ca 100644
--- a/java/dagger/hilt/android/example/BUILD
+++ b/third_party/java/byte_buddy/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Dagger Authors.
+# 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.
@@ -11,17 +11,13 @@
 # WITHOUT 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.
+
+# BUILD rules for https://github.com/mockito/mockito
 
 package(default_visibility = ["//:src"])
 
-filegroup(
-    name = "srcs_filegroup",
-    srcs = glob(
-        ["**/*"],
-        # Exclude Gradle build folder to enable working along side Bazel
-        exclude = ["**/build/**"],
-    ),
+alias(
+    name = "byte_buddy",
+    testonly = 1,
+    actual = "@maven//:net_bytebuddy_byte_buddy",
 )
diff --git a/java/dagger/hilt/android/example/BUILD b/third_party/java/byte_buddy_agent/BUILD
similarity index 63%
copy from java/dagger/hilt/android/example/BUILD
copy to third_party/java/byte_buddy_agent/BUILD
index 2a4c194..942078c 100644
--- a/java/dagger/hilt/android/example/BUILD
+++ b/third_party/java/byte_buddy_agent/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Dagger Authors.
+# 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.
@@ -11,17 +11,13 @@
 # WITHOUT 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.
+
+# BUILD rules for https://github.com/mockito/mockito
 
 package(default_visibility = ["//:src"])
 
-filegroup(
-    name = "srcs_filegroup",
-    srcs = glob(
-        ["**/*"],
-        # Exclude Gradle build folder to enable working along side Bazel
-        exclude = ["**/build/**"],
-    ),
+alias(
+    name = "byte_buddy_agent",
+    testonly = 1,
+    actual = "@maven//:net_bytebuddy_byte_buddy_agent",
 )
diff --git a/java/dagger/hilt/android/example/BUILD b/third_party/java/checker_framework/BUILD
similarity index 61%
copy from java/dagger/hilt/android/example/BUILD
copy to third_party/java/checker_framework/BUILD
index 2a4c194..01f3d70 100644
--- a/java/dagger/hilt/android/example/BUILD
+++ b/third_party/java/checker_framework/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Dagger Authors.
+# 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.
@@ -11,17 +11,18 @@
 # WITHOUT 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.
+
+# BUILD rules for https://checkerframework.org/
+
+load("@rules_java//java:defs.bzl", "java_library")
 
 package(default_visibility = ["//:src"])
 
-filegroup(
-    name = "srcs_filegroup",
-    srcs = glob(
-        ["**/*"],
-        # Exclude Gradle build folder to enable working along side Bazel
-        exclude = ["**/build/**"],
-    ),
+java_library(
+    name = "dataflow",
+    exports = ["@maven//:org_checkerframework_dataflow"],
+    runtime_deps = [
+        "@maven//:org_checkerframework_checker_qual",
+        "@maven//:org_checkerframework_javacutil",
+    ],
 )
diff --git a/java/dagger/hilt/android/example/BUILD b/third_party/java/checker_framework_annotations/BUILD
similarity index 63%
copy from java/dagger/hilt/android/example/BUILD
copy to third_party/java/checker_framework_annotations/BUILD
index 2a4c194..5fd74bb 100644
--- a/java/dagger/hilt/android/example/BUILD
+++ b/third_party/java/checker_framework_annotations/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Dagger Authors.
+# 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.
@@ -11,17 +11,12 @@
 # WITHOUT 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.
+
+# BUILD rules for https://checkerframework.org/
 
 package(default_visibility = ["//:src"])
 
-filegroup(
-    name = "srcs_filegroup",
-    srcs = glob(
-        ["**/*"],
-        # Exclude Gradle build folder to enable working along side Bazel
-        exclude = ["**/build/**"],
-    ),
+alias(
+    name = "checker_framework_annotations",
+    actual = "@maven//:org_checkerframework_checker_compat_qual",
 )
diff --git a/third_party/java/compile_testing/BUILD b/third_party/java/compile_testing/BUILD
new file mode 100644
index 0000000..8aff4a4
--- /dev/null
+++ b/third_party/java/compile_testing/BUILD
@@ -0,0 +1,34 @@
+# 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.
+
+# BUILD rules for https://github.com/google/compile-testing
+
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(default_visibility = ["//:src"])
+
+java_library(
+    name = "compile_testing",
+    testonly = 1,
+    exports = ["@maven//:com_google_testing_compile_compile_testing"],
+    runtime_deps = [
+        "//third_party/java/auto:value",
+        "//third_party/java/error_prone:annotations",
+        "//third_party/java/guava",
+        "//third_party/java/jsr305_annotations",
+        "//third_party/java/junit",
+        "//third_party/java/truth",
+        "@local_jdk//:lib/tools.jar",
+    ],
+)
diff --git a/java/dagger/hilt/android/example/BUILD b/third_party/java/diffutils/BUILD
similarity index 63%
copy from java/dagger/hilt/android/example/BUILD
copy to third_party/java/diffutils/BUILD
index 2a4c194..711ff69 100644
--- a/java/dagger/hilt/android/example/BUILD
+++ b/third_party/java/diffutils/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Dagger Authors.
+# 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.
@@ -11,17 +11,12 @@
 # WITHOUT 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.
+
+# BUILD rules for https://github.com/google/truth
 
 package(default_visibility = ["//:src"])
 
-filegroup(
-    name = "srcs_filegroup",
-    srcs = glob(
-        ["**/*"],
-        # Exclude Gradle build folder to enable working along side Bazel
-        exclude = ["**/build/**"],
-    ),
+alias(
+    name = "diffutils",
+    actual = "@maven//:com_googlecode_java_diff_utils_diffutils",
 )
diff --git a/third_party/java/error_prone/BUILD b/third_party/java/error_prone/BUILD
new file mode 100644
index 0000000..26a9682
--- /dev/null
+++ b/third_party/java/error_prone/BUILD
@@ -0,0 +1,47 @@
+# 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.
+
+# BUILD rules for https://github.com/google/error-prone. Note that Bazel already
+# applies the Error Prone compiler to all java compilations - this package exports
+# dependencies for Error Prone's libraries
+
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(default_visibility = ["//:src"])
+
+java_library(
+    name = "annotations",
+    tags = ["maven:compile_only"],
+    exports = ["@maven//:com_google_errorprone_error_prone_annotations"],
+)
+
+alias(
+    name = "error_prone_javac",
+    actual = "@maven//:com_google_errorprone_javac_shaded",
+)
+
+java_library(
+    name = "check_api",
+    exports = [
+        "@maven//:com_google_errorprone_error_prone_annotation",
+        "@maven//:com_google_errorprone_error_prone_check_api",
+    ],
+    runtime_deps = [
+        ":annotations",
+        ":error_prone_javac",
+        "//third_party/java/checker_framework:dataflow",
+        "//third_party/java/diffutils",
+        "//third_party/java/jsr305_annotations",
+    ],
+)
diff --git a/third_party/java/google_java_format/BUILD b/third_party/java/google_java_format/BUILD
new file mode 100644
index 0000000..c2313d9
--- /dev/null
+++ b/third_party/java/google_java_format/BUILD
@@ -0,0 +1,30 @@
+# 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.
+
+# BUILD rules for https://github.com/google/google-java-format
+
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(default_visibility = ["//:src"])
+
+java_library(
+    name = "google_java_format",
+    exports = [
+        "@maven//:com_google_googlejavaformat_google_java_format",
+    ],
+    runtime_deps = [
+        "//third_party/java/error_prone:error_prone_javac",
+        "//third_party/java/guava",
+    ],
+)
diff --git a/third_party/java/grpc/BUILD b/third_party/java/grpc/BUILD
new file mode 100644
index 0000000..e58da69
--- /dev/null
+++ b/third_party/java/grpc/BUILD
@@ -0,0 +1,37 @@
+# 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.
+
+# BUILD rules for https://github.com/grpc/grpc-java
+
+package(default_visibility = ["//:src"])
+
+alias(
+    name = "core",
+    actual = "@maven//:io_grpc_grpc_core",
+)
+
+alias(
+    name = "netty",
+    actual = "@maven//:io_grpc_grpc_netty",
+)
+
+alias(
+    name = "context",
+    actual = "@maven//:io_grpc_grpc_context",
+)
+
+alias(
+    name = "protobuf",
+    actual = "@maven//:io_grpc_grpc_protobuf",
+)
diff --git a/third_party/java/guava/BUILD b/third_party/java/guava/BUILD
new file mode 100644
index 0000000..bc3364b
--- /dev/null
+++ b/third_party/java/guava/BUILD
@@ -0,0 +1,41 @@
+# 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.
+
+# BUILD rules for https://github.com/google/guava
+
+load("@rules_java//java:defs.bzl", "java_library", "java_plugin")
+
+package(default_visibility = ["//:src"])
+
+java_library(
+    name = "guava",
+    exported_plugins = [":beta-checker"],
+    exports = [
+        "@maven//:com_google_guava_failureaccess",
+        "@maven//:com_google_guava_guava",
+    ],
+)
+
+java_library(
+    name = "testlib",
+    testonly = 1,
+    exports = ["@maven//:com_google_guava_guava_testlib"],
+    runtime_deps = [":guava"],
+)
+
+java_plugin(
+    name = "beta-checker",
+    visibility = ["//visibility:private"],
+    deps = ["@maven//:com_google_guava_guava_beta_checker"],
+)
diff --git a/java/dagger/hilt/android/example/BUILD b/third_party/java/guava/base/BUILD
similarity index 63%
copy from java/dagger/hilt/android/example/BUILD
copy to third_party/java/guava/base/BUILD
index 2a4c194..8ec4e6a 100644
--- a/java/dagger/hilt/android/example/BUILD
+++ b/third_party/java/guava/base/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Dagger Authors.
+# 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.
@@ -11,17 +11,12 @@
 # WITHOUT 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.
+
+# BUILD rules for https://github.com/google/guava
 
 package(default_visibility = ["//:src"])
 
-filegroup(
-    name = "srcs_filegroup",
-    srcs = glob(
-        ["**/*"],
-        # Exclude Gradle build folder to enable working along side Bazel
-        exclude = ["**/build/**"],
-    ),
+alias(
+    name = "base",
+    actual = "//third_party/java/guava",
 )
diff --git a/java/dagger/hilt/android/example/BUILD b/third_party/java/guava/cache/BUILD
similarity index 63%
copy from java/dagger/hilt/android/example/BUILD
copy to third_party/java/guava/cache/BUILD
index 2a4c194..8da3dfb 100644
--- a/java/dagger/hilt/android/example/BUILD
+++ b/third_party/java/guava/cache/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Dagger Authors.
+# 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.
@@ -11,17 +11,12 @@
 # WITHOUT 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.
+
+# BUILD rules for https://github.com/google/guava
 
 package(default_visibility = ["//:src"])
 
-filegroup(
-    name = "srcs_filegroup",
-    srcs = glob(
-        ["**/*"],
-        # Exclude Gradle build folder to enable working along side Bazel
-        exclude = ["**/build/**"],
-    ),
+alias(
+    name = "cache",
+    actual = "//third_party/java/guava",
 )
diff --git a/java/dagger/hilt/android/example/BUILD b/third_party/java/guava/collect/BUILD
similarity index 63%
copy from java/dagger/hilt/android/example/BUILD
copy to third_party/java/guava/collect/BUILD
index 2a4c194..a90aceb 100644
--- a/java/dagger/hilt/android/example/BUILD
+++ b/third_party/java/guava/collect/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Dagger Authors.
+# 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.
@@ -11,17 +11,12 @@
 # WITHOUT 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.
+
+# BUILD rules for https://github.com/google/guava
 
 package(default_visibility = ["//:src"])
 
-filegroup(
-    name = "srcs_filegroup",
-    srcs = glob(
-        ["**/*"],
-        # Exclude Gradle build folder to enable working along side Bazel
-        exclude = ["**/build/**"],
-    ),
+alias(
+    name = "collect",
+    actual = "//third_party/java/guava",
 )
diff --git a/java/dagger/hilt/android/example/BUILD b/third_party/java/guava/graph/BUILD
similarity index 63%
copy from java/dagger/hilt/android/example/BUILD
copy to third_party/java/guava/graph/BUILD
index 2a4c194..c77ca68 100644
--- a/java/dagger/hilt/android/example/BUILD
+++ b/third_party/java/guava/graph/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Dagger Authors.
+# 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.
@@ -11,17 +11,12 @@
 # WITHOUT 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.
+
+# BUILD rules for https://github.com/google/guava
 
 package(default_visibility = ["//:src"])
 
-filegroup(
-    name = "srcs_filegroup",
-    srcs = glob(
-        ["**/*"],
-        # Exclude Gradle build folder to enable working along side Bazel
-        exclude = ["**/build/**"],
-    ),
+alias(
+    name = "graph",
+    actual = "//third_party/java/guava",
 )
diff --git a/java/dagger/hilt/android/example/BUILD b/third_party/java/guava/io/BUILD
similarity index 63%
copy from java/dagger/hilt/android/example/BUILD
copy to third_party/java/guava/io/BUILD
index 2a4c194..ad6c7a2 100644
--- a/java/dagger/hilt/android/example/BUILD
+++ b/third_party/java/guava/io/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Dagger Authors.
+# 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.
@@ -11,17 +11,12 @@
 # WITHOUT 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.
+
+# BUILD rules for https://github.com/google/guava
 
 package(default_visibility = ["//:src"])
 
-filegroup(
-    name = "srcs_filegroup",
-    srcs = glob(
-        ["**/*"],
-        # Exclude Gradle build folder to enable working along side Bazel
-        exclude = ["**/build/**"],
-    ),
+alias(
+    name = "io",
+    actual = "//third_party/java/guava",
 )
diff --git a/java/dagger/hilt/android/example/BUILD b/third_party/java/guava/util/concurrent/BUILD
similarity index 63%
copy from java/dagger/hilt/android/example/BUILD
copy to third_party/java/guava/util/concurrent/BUILD
index 2a4c194..00dd5ad 100644
--- a/java/dagger/hilt/android/example/BUILD
+++ b/third_party/java/guava/util/concurrent/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Dagger Authors.
+# 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.
@@ -11,17 +11,12 @@
 # WITHOUT 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.
+
+# BUILD rules for https://github.com/google/guava
 
 package(default_visibility = ["//:src"])
 
-filegroup(
-    name = "srcs_filegroup",
-    srcs = glob(
-        ["**/*"],
-        # Exclude Gradle build folder to enable working along side Bazel
-        exclude = ["**/build/**"],
-    ),
+alias(
+    name = "concurrent",
+    actual = "//third_party/java/guava",
 )
diff --git a/java/dagger/hilt/android/example/BUILD b/third_party/java/hamcrest/BUILD
similarity index 63%
copy from java/dagger/hilt/android/example/BUILD
copy to third_party/java/hamcrest/BUILD
index 2a4c194..3ce9487 100644
--- a/java/dagger/hilt/android/example/BUILD
+++ b/third_party/java/hamcrest/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Dagger Authors.
+# 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.
@@ -11,17 +11,13 @@
 # WITHOUT 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.
+
+# BUILD rules for https://github.com/hamcrest/JavaHamcrest
 
 package(default_visibility = ["//:src"])
 
-filegroup(
-    name = "srcs_filegroup",
-    srcs = glob(
-        ["**/*"],
-        # Exclude Gradle build folder to enable working along side Bazel
-        exclude = ["**/build/**"],
-    ),
+alias(
+    name = "hamcrest",
+    testonly = 1,
+    actual = "@maven//:org_hamcrest_hamcrest_core",
 )
diff --git a/third_party/java/incap/BUILD b/third_party/java/incap/BUILD
new file mode 100644
index 0000000..7958754
--- /dev/null
+++ b/third_party/java/incap/BUILD
@@ -0,0 +1,35 @@
+# 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.
+
+# BUILD rules for https://github.com/tbroyer/gradle-incap-helper
+
+load("@rules_java//java:defs.bzl", "java_library", "java_plugin")
+
+package(default_visibility = ["//:src"])
+
+java_library(
+    name = "incap",
+    exported_plugins = [":processor"],
+    exports = ["@maven//:net_ltgt_gradle_incap_incap"],
+)
+
+java_plugin(
+    name = "processor",
+    processor_class = "net.ltgt.gradle.incap.processor.IncrementalAnnotationProcessorProcessor",
+    visibility = ["//visibility:private"],
+    deps = [
+        "@maven//:net_ltgt_gradle_incap_incap",
+        "@maven//:net_ltgt_gradle_incap_incap_processor",
+    ],
+)
diff --git a/java/dagger/hilt/android/example/BUILD b/third_party/java/javapoet/BUILD
similarity index 63%
copy from java/dagger/hilt/android/example/BUILD
copy to third_party/java/javapoet/BUILD
index 2a4c194..7d056d7 100644
--- a/java/dagger/hilt/android/example/BUILD
+++ b/third_party/java/javapoet/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Dagger Authors.
+# 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.
@@ -11,17 +11,12 @@
 # WITHOUT 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.
+
+# BUILD rules for https://github.com/square/javapoet
 
 package(default_visibility = ["//:src"])
 
-filegroup(
-    name = "srcs_filegroup",
-    srcs = glob(
-        ["**/*"],
-        # Exclude Gradle build folder to enable working along side Bazel
-        exclude = ["**/build/**"],
-    ),
+alias(
+    name = "javapoet",
+    actual = "@maven//:com_squareup_javapoet",
 )
diff --git a/java/dagger/hilt/android/example/BUILD b/third_party/java/jsr250_annotations/BUILD
similarity index 63%
copy from java/dagger/hilt/android/example/BUILD
copy to third_party/java/jsr250_annotations/BUILD
index 2a4c194..effdea8 100644
--- a/java/dagger/hilt/android/example/BUILD
+++ b/third_party/java/jsr250_annotations/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Dagger Authors.
+# 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.
@@ -11,17 +11,12 @@
 # WITHOUT 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.
+
+# BUILD rules for https://en.wikipedia.org/wiki/JSR_250
 
 package(default_visibility = ["//:src"])
 
-filegroup(
-    name = "srcs_filegroup",
-    srcs = glob(
-        ["**/*"],
-        # Exclude Gradle build folder to enable working along side Bazel
-        exclude = ["**/build/**"],
-    ),
+alias(
+    name = "jsr250_annotations",
+    actual = "@maven//:javax_annotation_jsr250_api",
 )
diff --git a/java/dagger/hilt/android/example/BUILD b/third_party/java/jsr305_annotations/BUILD
similarity index 63%
copy from java/dagger/hilt/android/example/BUILD
copy to third_party/java/jsr305_annotations/BUILD
index 2a4c194..3ae1825 100644
--- a/java/dagger/hilt/android/example/BUILD
+++ b/third_party/java/jsr305_annotations/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Dagger Authors.
+# 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.
@@ -11,17 +11,12 @@
 # WITHOUT 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.
+
+# BUILD rules for https://jcp.org/en/jsr/detail?id=305
 
 package(default_visibility = ["//:src"])
 
-filegroup(
-    name = "srcs_filegroup",
-    srcs = glob(
-        ["**/*"],
-        # Exclude Gradle build folder to enable working along side Bazel
-        exclude = ["**/build/**"],
-    ),
+alias(
+    name = "jsr305_annotations",
+    actual = "@maven//:com_google_code_findbugs_jsr305",
 )
diff --git a/java/dagger/hilt/android/example/BUILD b/third_party/java/jsr330_inject/BUILD
similarity index 63%
copy from java/dagger/hilt/android/example/BUILD
copy to third_party/java/jsr330_inject/BUILD
index 2a4c194..c8603b7 100644
--- a/java/dagger/hilt/android/example/BUILD
+++ b/third_party/java/jsr330_inject/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Dagger Authors.
+# 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.
@@ -11,17 +11,17 @@
 # WITHOUT 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.
+
+# BUILD rules for javax.inject (https://www.jcp.org/en/jsr/detail?id=330)
 
 package(default_visibility = ["//:src"])
 
-filegroup(
-    name = "srcs_filegroup",
-    srcs = glob(
-        ["**/*"],
-        # Exclude Gradle build folder to enable working along side Bazel
-        exclude = ["**/build/**"],
-    ),
+alias(
+    name = "jsr330_inject",
+    actual = "@maven//:javax_inject_javax_inject",
+)
+
+alias(
+    name = "tck",
+    actual = "@maven//:javax_inject_javax_inject_tck",
 )
diff --git a/java/dagger/hilt/android/example/BUILD b/third_party/java/junit/BUILD
similarity index 63%
copy from java/dagger/hilt/android/example/BUILD
copy to third_party/java/junit/BUILD
index 2a4c194..e40e2f7 100644
--- a/java/dagger/hilt/android/example/BUILD
+++ b/third_party/java/junit/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Dagger Authors.
+# 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.
@@ -11,17 +11,16 @@
 # WITHOUT 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.
+
+# BUILD rules for https://github.com/junit-team
+
+load("@rules_java//java:defs.bzl", "java_library")
 
 package(default_visibility = ["//:src"])
 
-filegroup(
-    name = "srcs_filegroup",
-    srcs = glob(
-        ["**/*"],
-        # Exclude Gradle build folder to enable working along side Bazel
-        exclude = ["**/build/**"],
-    ),
+java_library(
+    name = "junit",
+    testonly = 1,
+    exports = ["@maven//:junit_junit"],
+    runtime_deps = ["//third_party/java/hamcrest"],
 )
diff --git a/third_party/java/mockito/BUILD b/third_party/java/mockito/BUILD
new file mode 100644
index 0000000..d940443
--- /dev/null
+++ b/third_party/java/mockito/BUILD
@@ -0,0 +1,31 @@
+# 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.
+
+# BUILD rules for https://github.com/mockito/mockito
+
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(default_visibility = ["//:src"])
+
+java_library(
+    name = "mockito",
+    testonly = 1,
+    exports = ["@maven//:org_mockito_mockito_core"],
+    runtime_deps = [
+        "//third_party/java/byte_buddy",
+        "//third_party/java/byte_buddy_agent",
+        "//third_party/java/hamcrest",
+        "//third_party/java/objenesis",
+    ],
+)
diff --git a/java/dagger/hilt/android/example/BUILD b/third_party/java/objenesis/BUILD
similarity index 63%
copy from java/dagger/hilt/android/example/BUILD
copy to third_party/java/objenesis/BUILD
index 2a4c194..68ea07d 100644
--- a/java/dagger/hilt/android/example/BUILD
+++ b/third_party/java/objenesis/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Dagger Authors.
+# 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.
@@ -11,17 +11,13 @@
 # WITHOUT 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.
+
+# BUILD rules for https://github.com/mockito/mockito
 
 package(default_visibility = ["//:src"])
 
-filegroup(
-    name = "srcs_filegroup",
-    srcs = glob(
-        ["**/*"],
-        # Exclude Gradle build folder to enable working along side Bazel
-        exclude = ["**/build/**"],
-    ),
+alias(
+    name = "objenesis",
+    testonly = 1,
+    actual = "@maven//:org_objenesis_objenesis",
 )
diff --git a/java/dagger/hilt/android/example/BUILD b/third_party/java/protobuf/BUILD
similarity index 63%
copy from java/dagger/hilt/android/example/BUILD
copy to third_party/java/protobuf/BUILD
index 2a4c194..b06ee12 100644
--- a/java/dagger/hilt/android/example/BUILD
+++ b/third_party/java/protobuf/BUILD
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Dagger Authors.
+# 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.
@@ -11,17 +11,12 @@
 # WITHOUT 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.
+
+# BUILD rules for https://github.com/google/protobuf/tree/master/java
 
 package(default_visibility = ["//:src"])
 
-filegroup(
-    name = "srcs_filegroup",
-    srcs = glob(
-        ["**/*"],
-        # Exclude Gradle build folder to enable working along side Bazel
-        exclude = ["**/build/**"],
-    ),
+alias(
+    name = "protobuf",
+    actual = "@maven//:com_google_protobuf_protobuf_java",
 )
diff --git a/third_party/java/truth/BUILD b/third_party/java/truth/BUILD
new file mode 100644
index 0000000..73eb05f
--- /dev/null
+++ b/third_party/java/truth/BUILD
@@ -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.
+
+# BUILD rules for https://github.com/google/truth
+
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(default_visibility = ["//:src"])
+
+java_library(
+    name = "truth",
+    testonly = 1,
+    exports = [
+        "@maven//:com_google_truth_extensions_truth_java8_extension",
+        "@maven//:com_google_truth_truth",
+    ],
+    # TODO(cpovirk): This would make more sense in deps, but Bazel won't allow
+    # that unless we add dummy srcs.
+    runtime_deps = [
+        "//third_party/java/asm",
+    ],
+)
diff --git a/tools/bazel_compat.bzl b/tools/bazel_compat.bzl
new file mode 100644
index 0000000..e9354da
--- /dev/null
+++ b/tools/bazel_compat.bzl
@@ -0,0 +1,65 @@
+# Copyright (C) 202 The Dagger Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 with Bazel.
+"""
+
+load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_android_library")
+
+def compat_kt_android_library(name, **kwargs):
+    bazel_kt_android_library(name, kwargs)
+
+def bazel_kt_android_library(name, kwargs):
+    """A macro that wraps Bazel's kt_android_library.
+
+    This macro wraps Bazel's kt_android_library to output the jars files
+    in the expected locations (b/203519416). It also adds a dependency on
+    kotlin_stdlib if there are kotlin sources.
+
+    Args:
+      name: the name of the library.
+      kwargs: Additional arguments of the library.
+    """
+
+    # If there are any kotlin sources, add the kotlin_stdlib, otherwise
+    # java-only projects may be missing a required runtime dependency on it.
+    if any([src.endswith(".kt") for src in kwargs.get("srcs", [])]):
+        # Add the kotlin_stdlib, otherwise it will be missing from java-only projects.
+        # We use deps rather than exports because exports isn't picked up by the pom file.
+        # See https://github.com/google/dagger/issues/3119
+        required_deps = ["@maven//:org_jetbrains_kotlin_kotlin_stdlib"]
+        kwargs["deps"] = kwargs.get("deps", []) + required_deps
+
+    # TODO(b/203519416): Bazel's kt_android_library outputs its jars under a target
+    # suffixed with "_kt". Thus, we have to do a bit of name aliasing to ensure that
+    # the jars exist at the expected targets.
+    kt_android_library(
+        name = "{}_internal".format(name),
+        **kwargs
+    )
+
+    native.alias(
+        name = name,
+        actual = ":{}_internal_kt".format(name),
+    )
+
+    native.alias(
+        name = "lib{}.jar".format(name),
+        actual = ":{}_internal_kt.jar".format(name),
+    )
+
+    native.alias(
+        name = "lib{}-src.jar".format(name),
+        actual = ":{}_internal_kt-sources.jar".format(name),
+    )
diff --git a/tools/maven.bzl b/tools/maven.bzl
index 2c648a2..f842c88 100644
--- a/tools/maven.bzl
+++ b/tools/maven.bzl
@@ -74,7 +74,6 @@
         javadoc_exclude_packages,
         javadoc_android_api_level,
         shaded_deps,
-        shaded_rules,
         manifest,
         lint_deps,
         proguard_specs
@@ -96,7 +95,6 @@
         javadoc_exclude_packages,
         javadoc_android_api_level,
         shaded_deps,
-        shaded_rules,
         manifest,
         lint_deps,
         proguard_specs):
@@ -131,7 +129,6 @@
       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'
@@ -148,7 +145,6 @@
     )
 
     shaded_deps = shaded_deps or []
-    shaded_rules = shaded_rules or []
     artifact_targets = [artifact_target] + (artifact_target_libs or [])
     lint_deps = lint_deps or []
 
@@ -172,7 +168,6 @@
             name = name + "-classes",
             testonly = testonly,
             jars = artifact_targets + shaded_deps,
-            rules = shaded_rules,
             merge_meta_inf_files = merge_meta_inf_files,
         )
         if lint_deps:
@@ -217,7 +212,6 @@
             name = name,
             testonly = testonly,
             jars = artifact_targets + shaded_deps,
-            rules = shaded_rules,
             merge_meta_inf_files = merge_meta_inf_files,
         )
 
diff --git a/tools/shader/build.gradle b/tools/shader/build.gradle
new file mode 100644
index 0000000..2d1bc18
--- /dev/null
+++ b/tools/shader/build.gradle
@@ -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.
+ */
+
+import java.util.jar.Manifest;
+import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer
+import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext
+import shadow.org.apache.tools.zip.ZipOutputStream
+import shadow.org.apache.tools.zip.ZipEntry
+
+plugins {
+  id 'java-library'
+  // Use shadow version >= 7.1.1 to get log4j vulnerability patches:
+  //   https://github.com/johnrengelman/shadow/releases/tag/7.1.1
+  id 'com.github.johnrengelman.shadow' version '7.1.1'
+}
+
+repositories {
+  mavenCentral()
+  mavenLocal()
+}
+
+java {
+    sourceCompatibility = JavaVersion.VERSION_1_8
+}
+
+// This transformation makes sure the input jar's MANIFEST.MF properties,
+// such as "Created-by:" get merged into the final artifact jar (although it's
+// not clear how necessary this is).
+/** A transform that merges in the MANIFEST.MF files from the inputJar.  */
+class ManifestMerger implements Transformer {
+    private static final String MANIFEST_MF = "META-INF/MANIFEST.MF";
+
+    private Manifest manifest;
+
+    @Override
+    boolean canTransformResource(FileTreeElement element) {
+        return MANIFEST_MF.equalsIgnoreCase(element.relativePath.pathString);
+    }
+
+    @Override
+    void transform(TransformerContext context) {
+        if (manifest == null) {
+            manifest = new Manifest(context.is);
+        } else {
+            Manifest toMerge = new Manifest(context.is);
+            manifest.getMainAttributes()
+                .putAll(toMerge.getMainAttributes().entrySet());
+            manifest.getEntries().putAll(toMerge.getEntries().entrySet());
+        }
+    }
+
+    @Override
+    boolean hasTransformedResource() { true }
+
+    @Override
+    void modifyOutputStream(
+            ZipOutputStream os, boolean preserveFileTimestamps) {
+        os.putNextEntry(new ZipEntry(MANIFEST_MF));
+        if (manifest != null) {
+            ByteArrayOutputStream content = new ByteArrayOutputStream();
+            manifest.write(content);
+            os.write(content.toByteArray());
+        }
+    }
+}
+
+configurations {
+    shaded
+}
+
+shadowJar {
+    archiveClassifier = "" // postfix for output jar
+    configurations = [project.configurations.shaded]
+    transform(ManifestMerger.class)
+
+    // Add a 'relocate' declaration for each shaded rule.
+    //   Format: "key1,value1;key2,value2;key3,value3"
+    def rules = project.getProperty("shadedRules").split(";")
+    for (def i = 0; i < rules.size(); i++) {
+        def rule = rules[i].split(",")
+        relocate "${rule[0]}", "${rule[1]}"
+    }
+}
+
+dependencies {
+    shaded files(project.getProperty("inputJar"))
+}
diff --git a/java/dagger/example/gradle/simple/gradle/wrapper/gradle-wrapper.jar b/tools/shader/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
rename from java/dagger/example/gradle/simple/gradle/wrapper/gradle-wrapper.jar
rename to tools/shader/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/java/dagger/example/gradle/android/simple/gradle/wrapper/gradle-wrapper.properties b/tools/shader/gradle/wrapper/gradle-wrapper.properties
similarity index 91%
copy from java/dagger/example/gradle/android/simple/gradle/wrapper/gradle-wrapper.properties
copy to tools/shader/gradle/wrapper/gradle-wrapper.properties
index 4d9ca16..0f80bbf 100644
--- a/java/dagger/example/gradle/android/simple/gradle/wrapper/gradle-wrapper.properties
+++ b/tools/shader/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/java/dagger/example/gradle/android/simple/gradlew b/tools/shader/gradlew
similarity index 100%
rename from java/dagger/example/gradle/android/simple/gradlew
rename to tools/shader/gradlew
diff --git a/util/deploy-all.sh b/util/deploy-all.sh
new file mode 100755
index 0000000..a3febb8
--- /dev/null
+++ b/util/deploy-all.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+set -eu
+
+bash $(dirname $0)/deploy-dagger.sh "$@"
+
+bash $(dirname $0)/deploy-hilt.sh "$@"
+
+bash $(dirname $0)/deploy-hilt-gradle-plugin.sh "$@"
\ No newline at end of file
diff --git a/util/deploy-dagger.sh b/util/deploy-dagger.sh
index ddf4926..2806047 100755
--- a/util/deploy-dagger.sh
+++ b/util/deploy-dagger.sh
@@ -14,114 +14,145 @@
 # 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.
+# @param {string} module_name the JPMS module name to include in the jar. This
+# is an optional parameter and can only be used with jar files.
 _deploy() {
-  local library=$1
-  local pomfile=$2
-  local srcjar=$3
-  local javadoc=$4
+  local shaded_rules=$1
+  local library=$2
+  local pomfile=$3
+  local srcjar=$4
+  local javadoc=$5
+  local module_name=$6
   bash $(dirname $0)/deploy-library.sh \
+      "$shaded_rules" \
       "$library" \
       "$pomfile" \
       "$srcjar" \
       "$javadoc" \
+      "$module_name" \
       "$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
+  java/dagger/core-javadoc.jar \
+  "dagger"
 
 _deploy \
+  "" \
   gwt/libgwt.jar \
   gwt/pom.xml \
   gwt/libgwt.jar \
-  gwt/libgwt.jar
+  gwt/libgwt.jar \
+  ""
 
+# This artifact uses the shaded classes from dagger-spi, so we use the same
+# shading rules so that our references to those classes are shaded the same way.
 _deploy \
+  "com.google.auto.common,dagger.spi.shaded.auto.common;androidx.room.compiler.processing,dagger.spi.shaded.androidx.room.compiler.processing" \
   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
+  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
+  java/dagger/producers/artifact-javadoc.jar \
+  ""
 
 _deploy \
+  "com.google.auto.common,dagger.spi.shaded.auto.common;androidx.room.compiler.processing,dagger.spi.shaded.androidx.room.compiler.processing" \
   java/dagger/spi/artifact.jar \
   java/dagger/spi/pom.xml \
   java/dagger/spi/artifact-src.jar \
-  java/dagger/spi/artifact-javadoc.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
+  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 \
+  "" \
+  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
+  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
+  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
+  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
+  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
+  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-gradle-plugin.sh b/util/deploy-hilt-gradle-plugin.sh
new file mode 100755
index 0000000..2030494
--- /dev/null
+++ b/util/deploy-hilt-gradle-plugin.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+set -eu
+
+readonly MVN_GOAL="$1"
+readonly VERSION_NAME="$2"
+shift 2
+readonly EXTRA_MAVEN_ARGS=("$@")
+
+# 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
+  local markerOutDir=$plugindir/build/repo/com/google/dagger/hilt/android/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[@]}}"
+  mvn "$MVN_GOAL" \
+    -Dfile="$(find $markerOutDir -name "*-$suffix.pom")" \
+    -DpomFile="$(find $markerOutDir -name "*-$suffix.pom")" \
+    "${EXTRA_MAVEN_ARGS[@]:+${EXTRA_MAVEN_ARGS[@]}}"
+}
+
+# Gradle Plugin is built with Gradle, but still deployed via Maven (mvn)
+_deploy_plugin
\ No newline at end of file
diff --git a/util/deploy-hilt.sh b/util/deploy-hilt.sh
index 3c25c10..325ef13 100755
--- a/util/deploy-hilt.sh
+++ b/util/deploy-hilt.sh
@@ -14,75 +14,63 @@
 # 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.
+# @param {string} module_name the JPMS module name to include in the jar. This
+# is an optional parameter and can only be used with jar files.
 _deploy() {
-  local library=$1
-  local pomfile=$2
-  local srcjar=$3
-  local javadoc=$4
+  local shaded_rules=$1
+  local library=$2
+  local pomfile=$3
+  local srcjar=$4
+  local javadoc=$5
+  local module_name=$6
   bash $(dirname $0)/deploy-library.sh \
+      "$shaded_rules" \
       "$library" \
       "$pomfile" \
       "$srcjar" \
       "$javadoc" \
+      "$module_name" \
       "$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
+  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
+  java/dagger/hilt/android/testing/artifact-javadoc.jar \
+  ""
 
 _deploy \
+  "com.google.auto.common,dagger.hilt.android.shaded.auto.common" \
   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
+  java/dagger/hilt/processor/artifact-javadoc.jar \
+  ""
 
 _deploy \
+  "com.google.auto.common,dagger.hilt.android.shaded.auto.common" \
   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
+  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
+  java/dagger/hilt/artifact-core-javadoc.jar \
+  ""
diff --git a/util/deploy-library.sh b/util/deploy-library.sh
index a744402..0296294 100755
--- a/util/deploy-library.sh
+++ b/util/deploy-library.sh
@@ -9,18 +9,29 @@
 # 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.
+# @param {string} module_name the JPMS module name to include in the jar. This
+# is an optional parameter and can only be used with jar files.
 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 shaded_rules=$1
+  local library=$2
+  local pomfile=$3
+  local srcjar=$4
+  local javadoc=$5
+  local module_name=$6
+  local mvn_goal=$7
+  local version_name=$8
+  shift 8
   local extra_maven_args=("$@")
 
-  bazel build --define=pom_version="$version_name" \
-    $library $pomfile
+  bazel build --define=pom_version="$version_name" $library $pomfile
+
+  # Shade the library if shaded_rules exist
+  if [[ ! -z "$shaded_rules" ]]; then
+    bash $(dirname $0)/shade-library.sh \
+      $(bazel_output_file $library) $shaded_rules
+    # The output jar name is the same as the input library appended with -shaded
+    library="${library%.*}-shaded.${library##*.}"
+  fi
 
   # TODO(bcorso): Consider moving this into the "gen_maven_artifact" macro, this
   # requires having the version checked-in for the build system.
@@ -29,6 +40,12 @@
     $(bazel_output_file $pomfile) \
     $version_name
 
+  # TODO(bcorso): Consider moving this into the "gen_maven_artifact" macro once
+  # all our targets are using gen_maven_artifact
+  add_automatic_module_name_manifest_entry \
+    $(bazel_output_file $library) \
+    "${module_name}"
+
   if [ -n "$srcjar" ] && [ -n "$javadoc" ] ; then
     bazel build --define=pom_version="$version_name" \
       $srcjar $javadoc
@@ -81,6 +98,22 @@
   fi
 }
 
+add_automatic_module_name_manifest_entry() {
+  local library=$1
+  local module_name=$2
+  if [ -n "$module_name" ] ; then
+    if [[ $library =~ \.jar$ ]]; then
+      local temp_dir=$(mktemp -d)
+      echo "Automatic-Module-Name: ${module_name}" > $temp_dir/module_name_file
+      # The "m" flag is specifically for adding manifest entries.
+      jar ufm $library $temp_dir/module_name_file
+    else
+      echo "Could not add module name to $library"
+      exit 1
+    fi
+  fi
+}
+
 find_pom_value() {
   local pomfile=$1
   local attribute=$2
diff --git a/util/deploy-to-maven-central.sh b/util/deploy-to-maven-central.sh
index 1a71f5f..0bf4b64 100755
--- a/util/deploy-to-maven-central.sh
+++ b/util/deploy-to-maven-central.sh
@@ -10,26 +10,22 @@
 readonly VERSION_NAME=$2
 shift 2
 
-if [[ ! "$VERSION_NAME" =~ ^2\. ]]; then
-  echo 'Version name must begin with "2."'
-  exit 2
+$(dirname $0)/validate-dagger-version.sh "$VERSION_NAME"
+
+BAZEL_VERSION=$(bazel --version)
+if [[ $BAZEL_VERSION != *"4.2.1"* ]]; then
+  echo "Must use Bazel version 4.2.1"
+  exit 4
 fi
 
-if [[ "$VERSION_NAME" =~ " " ]]; then
-  echo "Version name must not have any spaces"
-  exit 3
+if [[ -z "${ANDROID_HOME}" ]]; then
+  echo "ANDROID_HOME environment variable must be set"
+  exit 5
 fi
 
 bash $(dirname $0)/run-local-tests.sh
 
-bash $(dirname $0)/deploy-dagger.sh \
-  "gpg:sign-and-deploy-file" \
-  "$VERSION_NAME" \
-  "-DrepositoryId=sonatype-nexus-staging" \
-  "-Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/" \
-  "-Dgpg.keyname=${KEY}"
-
-bash $(dirname $0)/deploy-hilt.sh \
+bash $(dirname $0)/deploy-all.sh \
   "gpg:sign-and-deploy-file" \
   "$VERSION_NAME" \
   "-DrepositoryId=sonatype-nexus-staging" \
@@ -39,30 +35,8 @@
 # Note: we detach from head before making any sed changes to avoid commiting
 # a particular version to master.
 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 build_defs.bzl
-
-# 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}"
-
+bash $(dirname $0)/publish-tagged-release.sh $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"
-git push origin gh-pages
-cd ..
-rm -rf gh-pages
+bash $(dirname $0)/publish-tagged-docs.sh $VERSION_NAME
diff --git a/util/install-local-snapshot.sh b/util/install-local-snapshot.sh
index be6030c..2225e51 100755
--- a/util/install-local-snapshot.sh
+++ b/util/install-local-snapshot.sh
@@ -4,11 +4,7 @@
 
 echo -e "Installing maven snapshot locally...\n"
 
-bash $(dirname $0)/deploy-dagger.sh \
-  "install:install-file" \
-  "LOCAL-SNAPSHOT"
-
-bash $(dirname $0)/deploy-hilt.sh \
+bash $(dirname $0)/deploy-all.sh \
   "install:install-file" \
   "LOCAL-SNAPSHOT"
 
diff --git a/util/latest-dagger-version.sh b/util/latest-dagger-version.sh
new file mode 100755
index 0000000..55b3efd
--- /dev/null
+++ b/util/latest-dagger-version.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+set -eu
+
+function github-rest-api {
+  local GITHUB_REST_API=$1
+
+  local GITHUB_API_HEADER_ACCEPT="Accept: application/vnd.github.v3+json"
+
+  curl -s $GITHUB_REST_API -H $GITHUB_API_HEADER_ACCEPT
+}
+
+function github-latest-release-tag {
+  local REPO_NAME=$1
+
+  # Grab the last two latest releases:
+  # (We skip the latest release if we haven't set release notes yet).
+  local RELEASE_API="https://api.github.com/repos/$REPO_NAME/releases?per_page=2"
+
+  # This gets the latest release info (as json) from github.
+  local RELEASE_JSON=$(github-rest-api $RELEASE_API)
+
+  # This pulls out the "body" from the json (i.e. the release notes)
+  local RELEASE_NOTES=$(echo $RELEASE_JSON | jq '.[0].body')
+
+  if [ "$RELEASE_NOTES" ]
+  then
+    # Return the latest release tag
+    echo $RELEASE_JSON | jq '.[0].tag_name'
+  else
+    # If there are no release notes in the latest release then we use the
+    # 2nd most latest version since we don't want to update the version until
+    # the release notes are set.
+    echo "Ignoring the latest release since the release notes have not been set."
+    echo "Using the previous release's version as latest."
+
+    # Return the 2nd most recent release tag
+    echo $RELEASE_JSON | jq '.[1].tag_name'
+  fi
+}
+
+function dagger-latest-release {
+  # Get the latest Dagger release tag, e.g. "dagger-2.31.2" or "dagger-2.32"
+  local DAGGER_RELEASE_TAG=$(github-latest-release-tag "google/dagger")
+
+  # Converts the "tag_name" to a version, e.g. "dagger-2.32" => "2.32"
+  echo $DAGGER_RELEASE_TAG | grep -oP "(?<=dagger-)\d+\.\d+(\.\d+)?"
+}
+
+type jq >/dev/null 2>&1 || {
+  echo >&2 "jq is not installed.  Try 'sudo apt-get install jq'.";
+  exit 1;
+}
+
+dagger-latest-release
diff --git a/util/publish-snapshot-on-commit.sh b/util/publish-snapshot-on-commit.sh
deleted file mode 100755
index 63cc3ea..0000000
--- a/util/publish-snapshot-on-commit.sh
+++ /dev/null
@@ -1,27 +0,0 @@
-# see https://coderwall.com/p/9b_lfq
-
-set -eux
-
-if [ "$GITHUB_REPOSITORY" == "google/dagger" ] && \
-   [ "$GITHUB_EVENT_NAME" == "push" ] && \
-   [ "$GITHUB_REF" == "refs/heads/master" ]; then
-  echo -e "Publishing maven snapshot...\n"
-
-  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" \
-    "-Durl=https://oss.sonatype.org/content/repositories/snapshots" \
-    "--settings=$(dirname $0)/settings.xml"
-
-  echo -e "Published maven snapshot"
-else
-  echo -e "Not publishing snapshot for branch=${$GITHUB_REF}"
-fi
diff --git a/util/publish-tagged-docs.sh b/util/publish-tagged-docs.sh
new file mode 100755
index 0000000..8c5cddf
--- /dev/null
+++ b/util/publish-tagged-docs.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+# TODO(bcorso): Consider sharing this script with utils/generate-latest-docs.sh
+
+set -eux
+
+if [ $# -lt 1 ]; then
+  echo "usage $0 <version-name>"
+  exit 1;
+fi
+readonly VERSION_NAME=$1
+shift 1
+
+$(dirname $0)/validate-dagger-version.sh "$VERSION_NAME"
+
+# Publish javadocs to gh-pages
+bazel build //:user-docs.jar
+
+# If a token exists, then use the token to clone the repo. This allows our
+# automated workflows to commit without manually authenticating.
+if [[ ! -z "$GH_TOKEN" ]]; then
+  git clone --quiet --branch=gh-pages https://x-access-token:${GH_TOKEN}@github.com/google/dagger gh-pages > /dev/null
+else
+  git clone --quiet --branch=gh-pages https://github.com/google/dagger gh-pages > /dev/null
+fi
+
+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"
+git push origin gh-pages
+cd ..
+rm -rf gh-pages
diff --git a/util/publish-tagged-release.sh b/util/publish-tagged-release.sh
new file mode 100755
index 0000000..11a131f
--- /dev/null
+++ b/util/publish-tagged-release.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+set -eux
+
+if [ $# -lt 1 ]; then
+  echo "usage $0 <version-name>"
+  exit 1;
+fi
+readonly VERSION_NAME=$1
+shift 1
+
+$(dirname $0)/validate-dagger-version.sh "$VERSION_NAME"
+
+# 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 build_defs.bzl
+
+# 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}"
diff --git a/util/run-local-emulator-tests.sh b/util/run-local-emulator-tests.sh
index 347a632..30b31be 100755
--- a/util/run-local-emulator-tests.sh
+++ b/util/run-local-emulator-tests.sh
@@ -11,14 +11,8 @@
 )
 for project in "${GRADLE_PROJECTS[@]}"; do
     echo "Running gradle Android emulator tests for $project"
-    ./$project/gradlew -p $project connectedAndroidTest --continue --no-daemon --stacktrace
+    ./$project/gradlew -p $project connectedAndroidTest --continue --no-daemon --stacktrace --configuration-cache
 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 --continue --no-daemon --stacktrace --configuration-cache
-
 # Close logcat
 if [ -n "$LOGCAT_PID" ] ; then kill $LOGCAT_PID; fi
diff --git a/util/run-local-gradle-android-tests.sh b/util/run-local-gradle-android-tests.sh
index a9418a5..6ff9220 100755
--- a/util/run-local-gradle-android-tests.sh
+++ b/util/run-local-gradle-android-tests.sh
@@ -4,19 +4,20 @@
 
 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  --continue --no-daemon --stacktrace
+    # Enable config cache if AGP is 4.2.0 or greater.
+    # Note that this is a lexicographical comparison.
+    if [[ "$AGP_VERSION_INPUT" > "4.1.0" ]]
+    then
+      CONFIG_CACHE_ARG="--configuration-cache"
+    else
+      CONFIG_CACHE_ARG=""
+    fi
+    AGP_VERSION=$AGP_VERSION_INPUT ./$project/gradlew -p $project buildDebug --no-daemon --stacktrace $CONFIG_CACHE_ARG
+    AGP_VERSION=$AGP_VERSION_INPUT ./$project/gradlew -p $project testDebug  --continue --no-daemon --stacktrace $CONFIG_CACHE_ARG
 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
index 479158d..ecbbd35 100755
--- a/util/run-local-gradle-tests.sh
+++ b/util/run-local-gradle-tests.sh
@@ -3,9 +3,8 @@
 set -ex
 
 readonly GRADLE_PROJECTS=(
-    "java/dagger/example/gradle/simple"
-    "java/dagger/hilt/android/plugin"
-    "javatests/artifacts/dagger/simple"
+    "javatests/artifacts/dagger"
+    "javatests/artifacts/hilt-android/pluginMarker"
 )
 for project in "${GRADLE_PROJECTS[@]}"; do
     echo "Running gradle tests for $project"
diff --git a/util/run-local-tests.sh b/util/run-local-tests.sh
index ea78671..3d7ba90 100755
--- a/util/run-local-tests.sh
+++ b/util/run-local-tests.sh
@@ -17,5 +17,7 @@
 # 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"
+util/run-local-gradle-android-tests.sh "4.2.0"
+util/run-local-gradle-android-tests.sh "7.0.0"
+
 
diff --git a/util/settings.xml b/util/settings.xml
deleted file mode 100644
index 91f444b..0000000
--- a/util/settings.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<settings>
-  <servers>
-    <server>
-      <id>sonatype-nexus-snapshots</id>
-      <username>${env.CI_DEPLOY_USERNAME}</username>
-      <password>${env.CI_DEPLOY_PASSWORD}</password>
-    </server>
-  </servers>
-</settings>
diff --git a/util/shade-library.sh b/util/shade-library.sh
new file mode 100644
index 0000000..73767d6
--- /dev/null
+++ b/util/shade-library.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+set -eux
+
+readonly INPUT_JAR=$1
+readonly SHADE_RULES=$2
+
+_shade_libary() {
+  local shader=$(dirname $0)/../tools/shader
+  local output="${INPUT_JAR%.*}-shaded.${INPUT_JAR##*.}"
+
+  ./$shader/gradlew -p $shader shadowJar \
+      -PinputJar="../../$INPUT_JAR" \
+      -PshadedRules=$SHADE_RULES
+
+  # Copy the shaded jar to the specified output
+  cp $shader/build/libs/shader.jar $output
+}
+
+_shade_libary
diff --git a/util/shasum.sh b/util/shasum.sh
new file mode 100755
index 0000000..a71c298
--- /dev/null
+++ b/util/shasum.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+set -eu
+
+if [ $# -lt 1 ]; then
+  echo "usage $0 <version-name>"
+  exit 1;
+fi
+readonly VERSION_NAME=$1
+shift 1
+
+$(dirname $0)/validate-dagger-version.sh "$VERSION_NAME"
+
+pushd $(mktemp -d)
+wget https://github.com/google/dagger/archive/dagger-$VERSION_NAME.zip -P .
+OUTPUT=$(shasum -a 256 dagger-$VERSION_NAME.zip)
+echo "SHA sum: $OUTPUT"
+popd
diff --git a/util/validate-dagger-version.sh b/util/validate-dagger-version.sh
new file mode 100755
index 0000000..a595aba
--- /dev/null
+++ b/util/validate-dagger-version.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+set -eu
+
+if [ $# -lt 1 ]; then
+  echo "usage $0 <version-name>"
+  exit 1;
+fi
+readonly VERSION_NAME=$1
+shift 1
+
+if [[ ! "$VERSION_NAME" =~ ^2\. ]]; then
+  echo 'Version name must begin with "2."'
+  exit 2
+fi
+
+if [[ "$VERSION_NAME" =~ " " ]]; then
+  echo "Version name must not have any spaces"
+  exit 3
+fi
diff --git a/workspace_defs.bzl b/workspace_defs.bzl
index 2a63e43..9229782 100644
--- a/workspace_defs.bzl
+++ b/workspace_defs.bzl
@@ -16,36 +16,43 @@
 
 load("//:build_defs.bzl", "POM_VERSION")
 
-_DAGGER_VERSION = POM_VERSION
-_HILT_VERSION = POM_VERSION
+# For tagged releases, the POM_VERSION will be set to the version of the release.
+# However, for CI testing the POM_VERSION will not be set, so we use the
+# HEAD-SNAPSHOT artifacts instead.
+# TODO(bcorso): Ideally, we would use the LOCAL-SNAPSHOT artifacts for CI testing;
+# however, maven_install doesn't work with local maven repositories
+# (See issue: https://github.com/bazelbuild/rules_jvm_external/issues/305).
+_VERSION = POM_VERSION if POM_VERSION != "${project.version}" else "HEAD-SNAPSHOT"
 
 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,
+    "com.google.dagger:dagger:" + _VERSION,
+    "com.google.dagger:dagger-compiler:" + _VERSION,
+    "com.google.dagger:dagger-producers:" + _VERSION,
+    "com.google.dagger:dagger-spi:" + _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,
+    "com.google.dagger:dagger-android-processor:" + _VERSION,
+    "com.google.dagger:dagger-android-support:" + _VERSION,
+    "com.google.dagger:dagger-android:" + _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,
+    "com.google.dagger:dagger:" + _VERSION,
+    "com.google.dagger:dagger-compiler:" + _VERSION,
+    "com.google.dagger:hilt-android:" + _VERSION,
+    "com.google.dagger:hilt-android-testing:" + _VERSION,
+    "com.google.dagger:hilt-android-compiler:" + _VERSION,
+    "com.google.dagger:hilt-core:" + _VERSION,
 ]
 
 DAGGER_REPOSITORIES = [
     "https://maven.google.com",
     "https://repo1.maven.org/maven2",
+    "https://oss.sonatype.org/content/repositories/snapshots",
 ]
 
 DAGGER_ANDROID_REPOSITORIES = DAGGER_REPOSITORIES
@@ -162,9 +169,11 @@
             ":hilt_aggregated_deps_processor",
             ":hilt_alias_of_processor",
             ":hilt_define_component_processor",
+            ":hilt_early_entry_points_processor",
             ":hilt_generates_root_input_processor",
             ":hilt_originating_element_processor",
             ":hilt_root_processor",
+            ":hilt_component_tree_deps_processor",
             ":hilt_view_model_processor",
         ],
         visibility = ["//visibility:public"],
@@ -173,6 +182,7 @@
             "%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//:com_google_dagger_hilt_core" % repo_name,
             "%s//:javax_annotation_jsr250_api" % repo_name,  # For @Generated
         ],
     )
@@ -217,6 +227,13 @@
     )
 
     native.java_plugin(
+        name = "hilt_early_entry_points_processor",
+        generates_api = 1,
+        processor_class = "dagger.hilt.processor.internal.earlyentrypoint.EarlyEntryPointProcessor",
+        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",
@@ -238,6 +255,13 @@
     )
 
     native.java_plugin(
+        name = "hilt_component_tree_deps_processor",
+        generates_api = 1,
+        processor_class = "dagger.hilt.processor.internal.root.ComponentTreeDepsProcessor",
+        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",
@@ -252,7 +276,6 @@
         exported_plugins = [
             ":hilt_bind_value_processor",
             ":hilt_custom_test_application_processor",
-            ":hilt_testroot_processor",
             ":hilt_uninstall_modules_processor",
         ],
         visibility = ["//visibility:public"],
@@ -278,15 +301,8 @@
     )
 
     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",
+        processor_class = "dagger.hilt.processor.internal.uninstallmodules.UninstallModulesProcessor",
         deps = ["%s//:com_google_dagger_hilt_android_compiler" % repo_name],
     )